Inspiration

Companies want AI agents living inside their real communication channels — Slack, email, SMS — because that's where the actual work happens. But the moment a message from an internal channel gets forwarded to a cloud LLM, whatever's inside it leaves the building: an internal IP, a customer's email, an API key, a name, a card number fragment.

This is one of the most cited blockers to enterprise LLM adoption. Most teams "solve" it by not solving it — they just never connect their sensitive channels to an LLM at all, or they self-host a model, which is expensive, slower, and still requires trusting a pile of new infra.

We wanted to build the layer that removes that objection entirely: let the agent live in the real channel, but make sure the model never sees what it shouldn't.

What it does

Privacy Guard sits between a Caspian-connected agent and the LLM it calls.

  1. A message arrives on Slack or Email, both wired to the same on_message handler.
  2. Before it reaches the LLM, sanitize() scans the text — regex for emails, IPs, credit cards, API keys, and phone numbers; spaCy NER for names — and replaces every sensitive span with a typed placeholder like [IP_ADDRESS_F5BA].
  3. The real values are encrypted locally (Fernet) and held in an in-memory, TTL-bound map — never written to disk, never sent anywhere.
  4. The LLM (running on Featherless.ai, our inference partner) reasons only over the safe text. Because placeholders are typed, it stays logically coherent — it can still say "please whitelist [IP_ADDRESS_F5BA]."
  5. restore() decrypts and re-injects the real values right before message.reply() sends the answer back out.

The same sanitize/restore pair is also exposed as a standalone MCP server, so any MCP-capable client — Claude Desktop, Cursor, or another agent entirely — can call the same redaction layer directly, not just our demo agent.

How we built it

  • caspian-sdk for the channel layer — one on_message handler, connected to Slack and Email, satisfying the multi-channel requirement without writing separate integration code per platform.
  • Featherless.ai for inference — OpenAI-compatible, so swapping models is a one-line change, not a rewrite.
  • Regex + spaCy NER for detection — regex handles structurally predictable data (emails, IPs, card numbers with Luhn validation, API-key-shaped strings) reliably; NER catches names and orgs on a best-effort basis.
  • cryptography's Fernet (AES-128-CBC + HMAC-SHA256) for encrypting the real values in the in-memory vault — a standard, audited library, not custom crypto.
  • MCP (mcp Python SDK) to expose sanitize/restore as reusable tools over stdio, wired into Cursor via .cursor/mcp.json.
  • Property-based tests (via hypothesis) fuzzing the sanitize → restore round trip to make sure nothing gets corrupted or lost on the way back out.

Challenges we ran into

  • Keeping the LLM's output coherent without ever seeing real data. Plain random tokens make the model's reasoning fall apart. Typed, deterministic placeholders ([EMAIL_A1B2] instead of xJ9$2z) fixed this — the model still understands what kind of thing it's talking about.
  • Making sure the LLM doesn't mangle the placeholders. An LLM "helpfully" reformatting [IP_ADDRESS_F5BA] into something slightly different would silently break the restore step. We handle this with an explicit system-prompt instruction to copy placeholders verbatim.
  • Overlapping spans. A credit-card-shaped number sitting next to an API key needed careful right-to-left span replacement and category-priority resolution so nothing gets double-tagged or corrupted.
  • Being honest about detection limits. Regex is reliable for structured data, but names have no fixed pattern — NER will miss edge cases. We chose to disclose this clearly rather than overclaim "we catch everything."
  • Avoiding infinite reply loops. The agent needed to recognize and ignore its own messages and only respond to group-channel mentions, not every message in a channel.

What we learned

  • Redaction-before-LLM is a real, established pattern (the same category of technique used by tools like Microsoft Presidio) — it's not a gimmick, it's a legitimate answer to a real enterprise adoption blocker.
  • It's not a cryptographic zero-knowledge proof, and it's important to call it what it actually is: local tokenization with key custody. Overselling the security story is worse than being precise about it.
  • Building this as a layer, not a standalone product, made it a genuine complement to what Caspian already does — Caspian gets agents into real channels; Privacy Guard is the missing piece that makes enterprises comfortable letting them stay there.

What's next

  • Configurable per-category redaction policy (e.g. "always redact API keys, never redact names") for different risk tolerances.
  • Deploying the MCP server over HTTP/SSE so it's remotely callable, not just spawned locally.
  • Expanding channel coverage (Telegram, WhatsApp) through the same handler, no new architecture needed.

Built With

  • caspian-sdk
  • cryptography
  • featherless-ai
  • i'd-suggest:-python
  • mcp
  • pytest
  • python
  • slack
  • spacy
Share this project:

Updates

Submission history