DEV Community

Hamza
Hamza

Posted on Originally published at tekmag.thsite.top

Tokenhush: The Local Secret-Redacting Gateway for AI Coding Tools

Originally published at https://tekmag.thsite.top/tokenhush-the-local-secret-redacting-gateway-for-ai-coding-tools/

Tokenhush is a local HTTP gateway that sits between your AI coding tool and the vendor API. Before a request leaves your machine, it replaces detected secrets with session-scoped placeholders and restores the originals on the way back, so the model never sees a real key, card number, or private key. It ships as a single pure Go binary that listens on 127.0.0.1 only, needs no root certificate, and covers 14 coding tools out of the box.

I verified these details directly against the official GitHub repository and cross-referenced them with the GitHub API metadata. The numbers hold up: six built-in detectors, 14 tool setup snippets, loopback-only binding, an Apache-2.0 license, and a project that has existed for about two weeks as of this writing.

AI coding assistants are useful precisely because they read a lot: open files, the whole repository, config files, and the keys sitting next to them. A meaningful share of that leaves your machine with every request, and once it is sent there is no undo. Tokenhush inserts one checkpoint in front of the tool. You keep working the same way, except the secrets stop going along for the ride.

Key Takeaways

  • Loopback-only gateway on 127.0.0.1: no TLS termination, no root certificate, no MITM.
  • Five of six built-in detectors are on by default (prefix, jwt, pem, luhn, email); the entropy detector stays off to protect function calling from false positives.
  • Secrets become session-scoped placeholders in the request, get restored in the response, and the mapping lives in memory only.
  • One binary for macOS, Linux, and Windows, with 14 ready-to-paste setup snippets for tools like Claude Code, Codex CLI, Aider, and Zed.

Architecture: one checkpoint, no trusted party added

Tokenhush is a base-URL gateway in the plainest sense. Any client that can override its API base URL (Anthropic-style or OpenAI-compatible) can be pointed at http://127.0.0.1:8787. The gateway binds that address and, when the host has an IPv6 loopback, [::1] as well. It then forwards the cleaned request to your configured upstream provider. That is the whole architecture: one process on loopback, between your tool and the API.

"No TLS termination, no root certificate, and no secret ever touching disk. The entire placeholder-to-secret mapping lives in process memory and dies when the gateway restarts." The only files the gateway writes are metadata: a run file with the pid, port, and start time, a control token stored with 0600 permissions, a verified rules cache, and an anti-rollback mark. No request bodies, no detected secrets, no mappings.

The design also fails closed. If a detector fails, the gateway refuses the request rather than forwarding it unredacted. A request with a non-identity Content-Encoding is rejected with a 415 instead of being decoded for detection. The control surface is exactly one endpoint (GET /status) behind a per-run bearer token, with no second endpoint and no allowlist-mutation API. The project's official site frames it the same way: local, reversible secret redaction with no MITM and no root certificate.

How it works: redact on the way out, restore on the way back

The outbound path walks the entire JSON request body. Nested objects and arrays included, not just top-level fields. Every match from the enabled detectors becomes a session placeholder with a stable shape like __PII_api_key_ae9c0b46a8f3__, and the cleaned request goes upstream. The model receives placeholders, and the README's own screenshot shows a tool receiving detected secrets across multiple files with each value replaced.

The inbound path buffers the whole response, SSE streams included, before anything is committed. Placeholders this session minted are swapped back to the originals, and only your client receives the real values. A placeholder the gateway did not mint in this session is returned unchanged, which is what blocks prompt-injection tricks that try to get the model to echo a real secret back into a request.

The stable-per-session design matters for multi-turn work. If the same key appears in request one and request ten, both use the same placeholder, so the model can reference it consistently while still never seeing the value. The tradeoff is that tokenization is invisible: the response path never redacts, only restores. You can watch this live in the gateway's stderr, where each redaction logs the detector type, the matched byte length, and a masked form, never the full value.

Detectors: six built in, five on, one deliberately off

The prefix detector catches vendor key shapes like sk-, AKIA, ghp_, glpat-, xox*, AIza, and npm_. The jwt detector catches JSON Web Tokens, the pem detector catches PEM private-key headers, the luhn detector catches credit-card numbers that pass the Luhn check, and the email detector catches addresses ending in a known public suffix such as .com or .co.uk. All five run by default.

The sixth, entropy, is off by design. The GitHub README states that its false positives on real agent traffic, long tool names and session identifiers, broke function calling during testing. You turn it on only for a workload that carries no images, data URLs, or long random identifiers. You can also extend detection without touching the binary: signed rule packs fetched with tokenhush rules sync add detections through the same Rule contract, and a non-weakening floor refuses any pack that disables a built-in detector, drops a required category, or carries an allow action. Named sensitive keys, for example a declared password field, are redacted by value at any object depth.

Supported tools: 14 snippets, two base-URL forms

Any client with a configurable base URL works. Fourteen ship with a ready-to-paste snippet from tokenhush env <tool>: Claude Code, Codex CLI, Aider, Cline, Roo Code, opencode, Qwen Code, Charm Crush, Zed, Continue.dev, Open WebUI, Goose, OpenHands, and Kilo Code. The exact file to edit for each is documented in the repo's tool-setup guide.

The rules cover every tool in two forms. Anthropic-style clients take the bare origin, http://127.0.0.1:8787. OpenAI-compatible clients take the /v1 suffix, http://127.0.0.1:8787/v1. If you run a relay or any other endpoint instead of a provider, set the upstream target first, or the gateway forwards to the wrong destination. This plugs into the broader shift toward self-hosting pieces of the AI stack: we covered Coder Agent Relay, which moves execution into your own cloud, and Spec Kit, which adds guardrails to agent-driven development. Tokenhush is the same idea applied to data protection.

What it does not cover: Cursor agent traffic, the ChatGPT and Claude desktop apps, and browser web UIs. None of those honor a configurable base URL, and covering them would require system-level interception, which the project explicitly refuses to do.

Installation: one binary, no toolchain, checksums verified

Tokenhush is pure Go built with CGO_ENABLED=0, a single binary per platform, and per the GitHub API records, it is Apache-2.0 licensed. The installers for Linux and Windows download the published release, verify its sha256 against the release checksums file, and install to a per-user directory with no admin rights. macOS uses a Homebrew cask. The current release is v0.7.1, and the releases page carries the full list.

macOS:

brew install --cask fregie/tap/tokenhush

Linux:

curl -fsSL https://raw.githubusercontent.com/fregie/tokenhush/main/install.sh | bash

Windows:

irm https://raw.githubusercontent.com/fregie/tokenhush/main/install.ps1 | iex

Or build it yourself with Go 1.25 and later:

go install github.com/fregie/tokenhush/cmd/tokenhush@main

Starting the gateway is one command, and it stays in the foreground until you stop it:

tokenhush run

Configuration: strict schema, two optional outbound requests

Tokenhush reads a tokenhush.yaml file, and a missing file means defaults. The schema is closed, which means an unknown key is an error, not a warning. The main knobs: listen host and port (only loopback addresses are accepted; 0.0.0.0 is rejected), the six detector switches, an allowlist of literals that are never redacted, upstream routing, and size and time limits on request bodies and responses.

Routing is by request path, not provider name. Without any config, OpenAI-compatible paths fall back to api.openai.com and Anthropic paths to api.anthropic.com. If you use a relay, point the matching path at your own origin:

upstreams:
  - match: /v1/chat/completions
    target: https://your-provider.example.com

The gateway makes at most two requests of its own, and both go to updates.tokenhush.com with 30-day retention: an update check that runs only when you call tokenhush update, and a rule sync that runs only when you call tokenhush rules sync. Each can be switched off entirely with the TOKENHUSH_NO_UPDATE_CHECK=1 and TOKENHUSH_NO_RULE_SYNC=1 environment variables, and tokenhush privacy prints the full egress disclosure.

What it does not do

Know the limits before you rely on it. Encoded secrets are not caught: a key that is base64-, hex-, or URL-encoded before it leaves is not detected, by design. Secrets in JSON object keys are not caught, only values. The response path does not redact, it restores. There is no doctor command, no allowlist-mutation command, no service command, and no runtime plugin loading, since extensions are compile-time. For teams managing agent workflows, this is one piece of a larger hygiene problem: we looked at how secrets in CI workflows can leak, and Tokenhush addresses the developer-machine side of the same exposure.

Conclusion

"Tokenhush trades blanket traffic interception for a narrow checkpoint: one loopback proxy that a tool agrees to use, instead of a root certificate that trusts everything." That tradeoff is the point. Provider opt-outs exist but vary by vendor, drift with updates, and miss anything you paste into a prompt by accident. A local checkpoint that holds the mapping in memory and fails closed is a smaller trust change, and the base-URL constraint is what keeps it that way.

The open question is coverage. Fourteen tools is a good list, but the desktop apps and browser UIs that cannot be re-pointed remain outside the model entirely, and that is where the residual risk sits. Watch for signed rule packs to ship in practice and for the tool list to grow. Run the echo-upstream check from the docs on a throwaway gateway and watch the placeholders pass through. If you use a tool that is missing from the 14, or you hit a false positive with entropy mode, drop it in the comments.

{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Does Tokenhush see my API keys?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, in process and in memory. It reads each request body to find and replace secrets. What it never does is persist them: no request or response bodies, no detected secrets, and no placeholder-to-secret mapping are written to disk. The redaction log is masked and goes to stderr only."
}
},
{
"@type": "Question",
"name": "Does it write secrets to disk?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. The only files it writes are metadata: a run file with pid, port, and start time, a control token stored with 0600 permissions, a verified rules cache, and an update anti-rollback mark. Request bodies and mappings stay in memory."
}
},
{
"@type": "Question",
"name": "Does Tokenhush work offline?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The data path is local: the gateway binds loopback and talks to your provider, which needs a network connection anyway. The only two requests Tokenhush itself can make to the vendor, the update check and the rule sync, are each switchable off. Beyond those, no traffic leaves the machine except your own requests to your provider."
}
},
{
"@type": "Question",
"name": "Will it break function calling in my AI coding tool?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The five default detectors are the conservative set, and placeholders are stable per session and restored on the response path, so the tool still receives the values it sent. The entropy detector is off by default precisely because its false positives on long tool names and session identifiers broke function calling in testing."
}
}
]
}

Frequently asked questions

Does Tokenhush see my API keys?

Yes, in process and in memory. It reads each request body to find and replace secrets. What it never does is persist them: no request or response bodies, no detected secrets, and no placeholder-to-secret mapping are written to disk. The redaction log is masked and goes to stderr only.

Does it write secrets to disk?

No. The only files it writes are metadata: a run file with pid, port, and start time, a control token stored with 0600 permissions, a verified rules cache, and an update anti-rollback mark. Request bodies and mappings stay in memory.

Does Tokenhush work offline?

The data path is local: the gateway binds loopback and talks to your provider, which needs a network connection anyway. The only two requests Tokenhush itself can make to the vendor, the update check and the rule sync, are each switchable off. Beyond those, no traffic leaves the machine except your own requests to your provider.

Will it break function calling in my AI coding tool?

The five default detectors are the conservative set, and placeholders are stable per session and restored on the response path, so the tool still receives the values it sent. The entropy detector is off by default precisely because its false positives on long tool names and session identifiers broke function calling in testing.


References

Top comments (0)