Where the model runsBefore we get to what redactproxy does, we need to understand where the work actually happens.
Claude Code is the part on your machine: a terminal tool that reads your files, runs your commands and collects the output.
The model is not on your machine at all: it runs on an AI provider's servers, Anthropic for example. Claude Code does no reasoning of its own, so anything it needs an answer about, including the scan output and the config file it just read, is sent to those servers over the API.
RedactProxyredactproxy is an open-source tool from the Cyber Security & Privacy Foundation. It sits between Claude Code and the provider, on your own machine, and rewrites that API traffic in both directions. On the way out it replaces real client values with stable fake ones. On the way back it puts the real ones in again, before Claude Code ever sees the response.
What changed, and what didn't:
- The
mail. subdomain survives, and the same organisation placeholder appears in both the hostname and the email address. The relationship between them is intact. - The host octet
.19 survives. Only the /24 network changed, so hosts that were adjacent stay adjacent. - The AWS key still looks like an AWS key, so the model knows what kind of secret it found without seeing the secret.
- The connection string collapses into one opaque placeholder, because the whole credential span is sensitive.
- The
nginx banner, the latency, the port, the Dell OUI comment: untouched. None of them identify the client.
Stable placeholdersAn
engagement is redactproxy's word for one client project. Inside one, the same real value always gets the same fake. The hostname that became
tok5198ede8bdbb1ada.internal this morning is still that same fake tomorrow, and in every request in between. This is not a convenience. It is the reason the tool is usable at all.
The model can still work out that two hosts belong to the same organisation. It just never learns which organisation.
Because
tok1a2b3c4d5e6f7890.com and
mail.tok1a2b3c4d5e6f7890.com are consistently the same fake, the model can reason that a finding on one host relates to a finding on another, that an email address belongs to the same company as a web server, that the same credential turned up in two places. All the analytical work survives. The identity does not.
The mappings live in the engagement's own folder and survive restarts, so the placeholder the model saw yesterday is still the same one today. Each engagement is self-contained and shares nothing with the others.
Placeholder shapes
Redaction that destroys structure destroys usefulness. Where a value's shape carries something useful but not identifying, the shape is kept:
The ranges are not arbitrary. Every fake comes from a space that cannot collide with something real: an IPv4 block reserved for equipment testing rather than one of the private ranges internal engagements actually target, the 555 phone exchange reserved for fiction, MAC addresses that can never belong to a real manufacturer. Fake credentials carry the string FAKE in a position where a real key can only hold a digit or a letter A to F, so no vendor could ever issue one.
What it detectsDetection is regex plus a validation step. No model in the loop, no network call, no learning. The detector set covers, roughly:
- Network identity: domains and hostnames (bare or inside URLs), IPv4, IPv6, MAC addresses.
- People: email addresses, NANP and international phone numbers.
- Credentials for 25+ vendors: AWS, GitHub, GitLab, Slack, Stripe, Razorpay, Google, npm, DigitalOcean, Cloudflare, Azure, Docker Hub, CircleCI, Terraform, Snyk, Vault, Twilio, SendGrid, OpenAI, Anthropic and more, plus JWTs, bearer tokens, connection strings, PEM private keys and password hashes.
- Regional PII: Indian Aadhaar and PAN numbers.
- AD artifacts: machine account names, GPP
cpassword values. - Client identity: whatever you add by hand, which is the part that matters most. See below.
There is also an allowlist, split into categories you can toggle. Your own out-of-band testing services (
burpcollaborator.net,
interact.sh,
webhook.site) are never the client's, and common CDN and public dev platform hostnames say nothing about who the client is. Every category can be switched off per engagement.
Only the parts of a request that carry content get scanned, and only the values that match get replaced. Everything else is left exactly as it was. MCP tool calls and results are scanned too, because an MCP server is local infrastructure producing exactly the client data this exists to keep in.
Fail closedIf the proxy cannot finish redacting a request, it returns an error instead of sending it on. A malformed request, a detector that errors, a store that cannot save a new mapping: all of them fail the request rather than let it through.
An unredacted forward is the one outcome this project treats as worse than a broken request.
Setting up an engagementgo install github.com/CSPF-Founder/redactproxy/cmd/redactproxy@latest
Then, in a folder for the engagement. Name it after an engagement code, not the client; the Known gaps section explains why that matters more than it looks.
cd ~/engagements/eng-2026-014
redactproxy wizard --engagement eng-2026-014
The wizard asks four things. First,
customer name variations: the legal name, the trading name, abbreviations, product names, internal codenames. No detector can match a name, so this is the only way they get redacted. They become case-insensitive substring matches, so
XYZCorp also catches
XYZCorporation.
Second,
domains. Give the base domain only. Subdomains, email addresses at that domain and URLs all resolve from it automatically. Internal-only names work too: an AD forest, or any private scheme that will never appear on a public suffix list.
Third,
which API this engagement talks to. Real Claude by default. redactproxy never asks for an Anthropic credential; it forwards Claude Code's own authentication untouched. Anything else speaking the Anthropic Messages API works too, z.ai for example, and each engagement remembers its own choice, so two client projects can run against different providers side by side.
Fourth, it offers two conveniences for the folder: a
CLAUDE.md note explaining the placeholder shapes, and a
.claude/settings.local.json that points Claude Code at the proxy and closes several channels that bypass the proxy entirely.
Then:
redactproxy # terminal 1
claude # terminal 2, already pointed at the proxy
That terminal running the proxy is also a live console. Type
show to see every mapping minted so far,
remove <value> to drop a bad one, or
rules block "XyzExample" to add a value mid-session without interrupting anything.
Two files the wizard writesThe
CLAUDE.md note is not decoration. Without it, a session sees strange placeholder values with no explanation: it corrects them as typos, hesitates to use them in tool calls, or retypes them from memory slightly wrong. The note tells the model these are stable identifiers to copy verbatim. It also covers a trap worth knowing yourself: if the model decodes base64 inline, the decoded content lands in its own output completely unprotected, because the encoded form passed through unredacted. Decoding to a file with
Bash and reading the file back gives that content a normal pass through redaction.
The settings hardening closes paths that never touch
ANTHROPIC_BASE_URL at all. The
Artifact tool is a confirmed leak path: a report published through it goes straight to a hosted claude.ai URL, entirely unredacted, through a separate service call the proxy never sees. The wizard removes it from the session entirely rather than prompting for it each time, because a permission prompt can be approved out of habit and a tool that was never offered cannot leak anything. It also turns off
WebFetch's safety check, which sends the target hostname to Anthropic before the fetch, whichever provider the engagement uses. A domain being reconned is exactly the value this tool exists to keep off side channels.
Known gapsredactproxy ships a
Known gaps page, and it is worth reading before you point this at real client data. The ones that matter most:
- Your folder name is the big one, and redaction cannot fix it. Claude Code puts its working directory into the
system field of every request, and that field is deliberately never scanned. A folder called xyz-example-bank-pentest sends "xyz example bank" to the model on every single request no matter what your rules say. The tool warns about it, at wizard time and at startup, but the only fix is to name engagement folders after an engagement code. - Encoded data passes straight through. A
.env piped through base64, an xxd dump, Terraform state: none of it looks like anything to a regex. Decode locally first. - Names and prose are not detected. This is what
rules block is for, and why it is the wizard's first question. A company name shows up in URL paths, ticket references, code comments and commit messages, and no detector can recognise it.
Who this is for
Pentest and consulting teams who want the productivity of an AI coding agent on a live engagement, and who would rather not explain to a client why their internal hostnames are in a third party's logs.
It was built for pentest work, and that shapes the vocabulary: engagements, clients, findings. Nothing in the redaction is specific to offensive work, though. Blue teams and infrastructure teams hit the same problem: a SOC analyst pasting alerts full of internal hostnames, a sysadmin debugging a manifest with credentials in it, anyone under an NDA who wants an AI agent working on real data. The detectors only care about the shapes they recognise, not why you are looking at them.