Before the model
Claude can help with a support ticket without ever seeing the customer.
Veil is a proxy that sits between your app and the Claude API. It swaps secrets and personal data for placeholders on the way out, and puts the real values back in the reply.
Try it
Edit the text on the left. Detection and swapping run in your browser and nothing is sent anywhere. The reply in step 3 is simulated, to show how restoring works.
1. Your app sends
Edit this text5 sensitive values found
2. Claude receives
Placeholders onlyCustomer Dana Reyes (, ) says checkout fails. Card on file: . Server logs show our key leaking in the stack trace.
3. Your app gets back
Simulated reply, restoredModel saw: Thanks. I'll email ‹EMAIL_1› and rotate ‹ANTHROPIC_KEY_1› right away.
Why use it
Prompts collect sensitive data without anyone deciding to send it. A stack trace includes an API key. A support ticket includes a phone number and a card. A retrieved document includes an email address. Each of these goes to a third-party service in plain text.
You can clean every code path by hand, but each team ends up rewriting the same fragile pattern matching, and one missed path is enough. Veil does it in one place, for every request, with no change to your application logic.
Why placeholders instead of deleting
Veil replaces a value with a stable name, so Claude can still reason about it. The same email is always ‹EMAIL_1› within a request, so a question like who reported this and when did they last write in still makes sense. When Claude mentions ‹EMAIL_1› in its answer, Veil puts the real address back before your app sees it.
How it works
- 1
Your app calls Veil
Your SDK sends a normal Messages API request to Veil on your own machine, using your own Anthropic key. - 2
Veil scans the request
Every string in the request body is scanned, including the system prompt, messages, tool definitions, tool results and tool inputs. Structural fields such as roles, ids and the model name are left alone. - 3
Values become placeholders
Each detected value is replaced with a placeholder such as‹EMAIL_1›. The mapping lives in memory for the length of that one request and is then discarded. - 4
Veil forwards the clean request
The redacted request goes to the Claude API with your headers. Your key is passed through and never stored. - 5
Veil restores the reply
Placeholders in the reply from Claude, in normal responses and in streams, are replaced with the original values. A placeholder split across two stream chunks is held back until it is complete. - 6
Veil writes an audit line
One line per request records the time, model and how many values of each kind were swapped. It never records the values.
Set it up
You need Node 22.18 or newer. The proxy has no runtime dependencies and no build step.
1. Run the proxy
git clone https://github.com/tcvdh/leakybyte.git
cd leakybyte
npm install
npm run proxy
# LeakyByte proxy on http://127.0.0.1:87872. Point your SDK at it
Change the base URL. Nothing else in your code changes.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "http://127.0.0.1:8787", // the only change
});
const msg = await client.messages.create({
model: "claude-sonnet-5-5",
max_tokens: 300,
messages: [{ role: "user", content: "Why did the deploy for dana@acme.io fail?" }],
});import anthropic
client = anthropic.Anthropic(base_url="http://127.0.0.1:8787")Or test with curl
curl -i http://127.0.0.1:8787/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5-5","max_tokens":200,
"messages":[{"role":"user","content":"Email dana@acme.io about the outage"}]}'The response includes a header, x-leakybyte-redacted: 1, with the number of distinct values that were swapped, and the reply contains the real address.
Example
This is real output from the command line tool, using the same engine as the proxy.
Customer dana.reyes@northwind.example called from +1 415-555-0134.
Card 4242 4242 4242 4242, server 10.4.2.19.Customer ‹EMAIL_1› called from ‹PHONE_1›.
Card ‹CARD_1›, server ‹IP_1›.The audit log gets one line like this for the request:
{"ts":"2026-03-02T09:14:07.512Z","model":"claude-sonnet-5-5","stream":false,"redacted":{"EMAIL":1,"PHONE":1}}What it detects
| Placeholder | Finds | Notes |
|---|---|---|
| ‹ANTHROPIC_KEY_n› | sk-ant-… keys | |
| ‹AWS_KEY_n› | AKIA… and ASIA… access key ids | |
| ‹GITHUB_TOKEN_n› | ghp_, gho_, ghu_, ghs_, ghr_ tokens | |
| ‹API_KEY_n› | Other sk-… keys | Generic pattern |
| ‹JWT_n› | JSON Web Tokens | |
| ‹EMAIL_n› | Email addresses | |
| ‹CARD_n› | Card numbers, 13 to 19 digits | Must pass the Luhn check, so random digit strings are left alone |
| ‹SSN_n› | US social security numbers | Format 123-45-6789 |
| ‹PHONE_n› | Phone numbers | Common international and US formats |
| ‹IP_n› | IPv4 addresses |
Reference
Settings
| Variable | Default | What it does |
|---|---|---|
| PORT | 8787 | Port to listen on |
| LEAKYBYTE_UPSTREAM | https://api.anthropic.com | Where redacted requests are sent |
| LEAKYBYTE_AUDIT | leakybyte-audit.jsonl | Path of the audit log, one JSON object per line |
Routes
| Request | What happens |
|---|---|
| POST /v1/messages | Redacted, forwarded, and restored. Normal and streaming responses. |
| POST /v1/messages/count_tokens | Redacted and forwarded. |
| Any other POST | Blocked with status 501, so nothing leaves unredacted. |
| GET and other methods | Forwarded as they are. |
Errors
| Status | Meaning | What to do |
|---|---|---|
| 400 | The request body was not valid JSON | Check the body your client sends |
| 501 | A POST route Veil cannot redact | Use /v1/messages, or call the API directly for that route |
| 502 | Veil could not reach the upstream | Check LEAKYBYTE_UPSTREAM and your network |
Limits to know about
- Detection is pattern-based. It does not find names, street addresses or other free-text personal details.
- A secret split across two separate text blocks is not matched.
- Error responses from the upstream are passed through as they are.
- Tested against a mock Claude server, including streaming and tool calls. Not yet validated at scale against the live API.
- If Claude writes a placeholder it was never given, it stays as written.
Using Veil on a real project?
Tell us what you are building and what is missing. We read and reply to every message.