veil sits between your app and any third-party model. It swaps real identifiers for stable code-names before the prompt leaves your machine, and swaps them back in the reply. The model is useful. It just never learns who anyone is — and your secrets never leave at all.
A real Claude fixes a real bug — seeing only pseudonyms. Captured, not mocked:
What we send to Claude (your `claude` login — no API token; tools off) // owner: EMAIL_1 (escalations: EMAIL_2) const TOKEN_TTL = 15 * 60; // <-- should be 24 * 60 * 60 * 1000 … (config.ts → URL_1 / IP_1 / PATH_1; .env withheld) calling the real model… Claude's answer (reverse-mapped for you) The TTL is 15 * 60 (15 minutes in seconds) but the store reads it as milliseconds — wrong unit and duration. Fix: const TOKEN_TTL = 24 * 60 * 60 * 1000; A frontier model fixed your bug and never saw an email, path, URL, or your .env.
you type remind alice@acme.com about /Users/baris/q3.pdf model sees remind EMAIL_1 about PATH_1 ← pseudonymized you get Reminded alice@acme.com. ← restored locally
The same person is always EMAIL_1 within a conversation, so the model can still reason about "who you mentioned earlier" — without ever learning the real name.
Does the swap-and-restore round-trip and holds the mapping. Binds localhost only — the one part that sees real data.
Sorts the tiers, talks to the model (Anthropic, Ollama, …), streams the reply back, restoring names as tokens arrive.
Regex for emails / paths / IPs / URLs; a small local model (GLiNER) for the people, places and companies regex can't catch.
Drop it in front of a coding agent (Claude, Cursor) and every file it reads is sanitized before the model sees it.