Redact secrets
Redact secrets before they reach the model
Tokenhush is a local, loopback-only HTTP gateway that sits between an AI coding tool and the model provider. Outbound, it walks the declared-JSON request body, replaces each detected secret or piece of PII with a reversible session placeholder, and forwards the rewritten body; inbound, it buffers the whole response and restores the originals before your tool receives them. Use it when you want to redact secrets before sending to an LLM, without trusting a machine-wide certificate.
Loopback only, no MITM, no root certificate; the secret-to-placeholder mapping lives in memory for the session.
Why secrets travel inside ordinary requests
Coding tools compose requests out of repository context. A key in a .env file, a token in a config file, or a credential pasted into a prompt is not a special case to the tool: it is text, and it travels as part of the request body.
File-level hygiene helps before the request exists. .gitignore keeps a file out of git, and provider opt-outs reduce what a service keeps — but neither one runs at send time, and once a request has been sent there is no undo. Redaction at send time is a different checkpoint: the body is rewritten after the tool composes it and before the provider receives it. That is where the loopback gateway sits, and it is the redaction-relevant difference from the approaches in the comparison.
PII redaction for LLM requests works the same way. An address or a card number that passes the Luhn check is found by the same deterministic pass and replaced with a placeholder, so the provider receives the shape of the request without the values.
Outbound redaction, inbound restore
Point a tool at the loopback gateway (127.0.0.1:8787) instead of the vendor endpoint. One request, two directions, and only one of them rewrites: substitution runs on the request path, and the originals are restored on the response path.
Outbound request
Your tool sends the request
The body reaches the gateway before any vendor endpoint sees it. No traffic is intercepted: the tool is configured to call the loopback address.
The gateway walks and rewrites the body
The whole declared-JSON body is read and the enabled detectors run. Each match becomes a session placeholder such as
__PII_api_key_ae9c0b46a8f3__, and the rewrite happens before the request is forwarded.The provider receives the rewritten body
The upstream sees the placeholders, not the values they stand for. A value no detector flagged still travels as your tool wrote it, which is why detector quality is the honest boundary of the tool.
Inbound response
The response is buffered whole
Status and headers are recorded and the body —
text/event-streamincluded — is buffered and decoded first. Nothing is committed yet.Placeholders are restored to the originals
Placeholders this session minted are restored; a foreign placeholder — one this session did not mint — is returned unchanged rather than guessed at.
Your tool receives the response
Backfill runs last and the response is committed once, so the client never holds a partial response the gateway could not take back.
Placeholders are never filled back in on the way out. Only your client gets the originals.
Back-fill runs in exactly one direction: on the response path to your client. The outbound writer holds only the forward map — it has no reverse map and no restore method — so a placeholder that appears in a new outbound body stays a placeholder.
Redaction: four approaches, side by side
Every approach below can reduce what leaves the machine. They differ in when the reduction runs, and in what would pass through a request that still carries a secret.
| Approach | What it does about secrets in a request | Where it runs |
|---|---|---|
| Opt-outs and .gitignore | Nothing runs at send time: a file you remember to exclude stays out of git, and a key in a file you forgot still travels with the request. | File-level hygiene, applied before a request is composed. |
| Turning off features that send too much | Fewer bytes leave, but the setting does nothing about a secret that is already in the request body. | Provider-side settings, applied per provider rather than at the request. |
| A local MITM proxy with a root certificate | Any client on the machine can be inspected and rewritten, including clients with no base-URL setting; it terminates TLS, so a root certificate is trusted machine-wide. | System-wide, between every client and every endpoint. |
| Tokenhush | The declared-JSON request body is walked and each detected match becomes a session placeholder before the request is forwarded; no TLS termination, no root certificate, mapping in memory only. | Loopback only: one checkpoint on the request path of the tools you point at a base URL. |
Detectors are precision-first: a value no detector flags still travels. The documented claim is high-confidence interception plus full auditability, not a guarantee that nothing sensitive can leave.
What send-time redaction does not do
Redacting secrets before they reach the model is not the same as preventing every form of secret leakage. These limits travel with the design and are written down rather than discovered later.
Encoded secrets are not caught
A secret that is base64-, hex-, or URL-encoded before it leaves is not detected: the rewrite has no normalization pass. JSON string escaping is handled in both directions, and a non-identity request Content-Encoding is refused with 415 rather than decoded for detection.
The response path does not redact
Response-scoped rules may allow, warn, or block only; substitution is request-path only, and a rule that tries to redact a response is rejected when it is compiled.
A secret in a JSON object key is not caught
Only values are walked, so a secret used as an object key is forwarded unchanged.
The full boundary list and the residual risks that come with it are recorded in the security model. Coverage is exactly the tools you can point at a base URL; a client that ignores base URLs needs a different interception design.
Watch the redaction happen, without a real provider
The verification guide proves the whole round trip with a local echo upstream that returns request bodies unchanged. The upstream terminal shows the body with the placeholder; the gateway prints one masked stderr line per redaction and one count per restore; the client gets the original value back.
- The upstream never sees the valueThe echo upstream is the stand-in for the model. It prints the request body it received — the placeholder, not the secret.
- The log is metadata-onlyA redaction line carries the type, the length, and a masked form; a restore line carries only a count.
- The metadata is queryabletokenhush status --json reports counters such as redactions, so a machine can check the same round trip a human just watched.
tokenhush run
# point a tool at http://127.0.0.1:8787
tokenhush status --jsonWhere this is written down
The request and response paths, the detector contract, and the risks that remain all come from the public documentation.
- Concepts
Architecture: layers and rule abstraction
The data path at a glance, the frozen byte surfaces, and where the rewrite sits between tool and provider.
Read the architecture doc - Security
Security model and residual risks
The eight invariants the core keeps, the nine residual risks it accepts, and the test that pins each one.
Read the security model - Concepts
Plugins and the Rule extension point
The six built-in detectors, the allowlist, and the signed rule packs that may add detections without weakening a built-in.
Read the plugin contract
Open source
Point one tool at the gateway
Start with the verification guide: a local echo upstream, one request that carries a fake secret, and a log that shows exactly what the upstream received. Then move to tool setup for the client you use.
- Apache-2.0
- No root certificate