claude-sniff pauses Claude Code’s Bedrock invokes

claude-sniff runs on your own machine, between Claude Code and Amazon Bedrock. It can hold each model call, let you rewrite the JSON body and then send the call on with a fresh AWS signature. The project went up on Show HN on September 4, and the project’s GitHub README lays out the setup and the limits.
It works as a forward proxy, the ordinary kind a client is configured to send its traffic through. The README explains why: “This is not ANTHROPIC_BASE_URL=localhost. Claude Code on Bedrock does not speak that.” In Bedrock mode (CLAUDE_CODE_USE_BEDROCK=1), Claude Code talks to Bedrock Runtime through the AWS SDK, so redirecting the base URL won’t work. The tool picks up Claude Code’s HTTPS traffic through HTTP_PROXY instead. It decrypts that traffic with a local mitmproxy certificate authority that Claude Code has been told to trust. One listener and one web UI cover everything, and you don’t need a wrapper around claude.
You need Python 3.12+ and uv. Claude Code must already reach Bedrock, with the same region and profile as a working claude session. Claude Code needs AWS_REGION set explicitly because it does not read the region from ~/.aws/config. The process running main.py also needs its own AWS credentials, from AWS_PROFILE, SSO or the default chain.
To start it, run uv sync and then uv run python main.py --aws-profile your-profile. The listener comes up on http://127.0.0.1:9090 and the UI on http://127.0.0.1:8765. The first run writes the mitmproxy CA to ~/.mitmproxy/mitmproxy-ca-cert.pem. The UI has four tabs (Sessions, Usage, Proxy, Info) and opens on Sessions. Proxy and Intercept are toggles in the header. Both start off, and neither is saved between runs.
The rest of the setup has three stages:
- Route: export
HTTP_PROXYandHTTPS_PROXYpointing athttp://127.0.0.1:9090, then startclaudein the same shell. A Claude Code process does not inherit exports from another shell, so you have to pin every new one this way. With Proxy off, HTTPS goes through as a tunnel with Bedrock’s real certificate, and the CA isn’t used. - Sniff: turn Proxy on in the UI. Export
NODE_EXTRA_CA_CERTS,SSL_CERT_FILE,REQUESTS_CA_BUNDLEandAWS_CA_BUNDLE, all pointing at the CA file, then restartclaude. The Info page has these exports ready to copy. Sessions tagged withx-claude-code-session-idappear under Sessions, and each request becomes a row inlogs/<client>/<session-id>.jsonl. - Intercept: turn Intercept on. You can’t enable it until Proxy is on. After that, every
invokeorinvoke-with-response-streamcall that carries the session header is held.
Held requests wait in one global FIFO queue, and only the request at the front can be edited. You have three options for it. Forward it unchanged and the original goes out with its original signature. Forward an edited body and the request is re-signed with SigV4, AWS’s request-signing scheme. SigV4 hashes the body into the signature, so any edit invalidates the original one. Drop returns a visible error to Claude Code, writes an error row to the JSONL log and never calls Bedrock. Turning Intercept or Proxy off sends everything still in the queue as it was.
The new signature comes from the AWS chain of the main.py process (--aws-profile, AWS_PROFILE or SSO), not from Claude Code’s credentials. So that identity also needs InvokeModel on the same models. A DEV Community setup guide lists bedrock:InvokeModelWithResponseStream next to bedrock:InvokeModel in Claude Code’s usual Bedrock policy.
The logs strip the Authorization and x-amz-security-token headers. The token header carries the session token that temporary AWS credentials (keys starting with ASIA) must include, according to a practical guide on AWS’s builder site. Request bodies are not masked: prompts and system prompts are stored as-is, and “does not mask request bodies” is on the README’s list of things the tool does not do. logs/ is gitignored. Even so, it holds every prompt in plain text, which makes it the most sensitive directory in the checkout.
The Usage tab and the cost sidebar work with Proxy off, because they also read Claude Code’s own journals in ~/.claude/projects. That is the same session JSONL that local cost auditors already parse. Prices use EU regional on-demand rates (eu-central-1) and show in the sidebar and for each request. If a request has the same UUID in both logs/ and the journal, it appears once, with the proxy’s dollar figure. Rows found only in the journal show cost but no chat content. The model family is worked out from keywords in the model name. You have to map account-scoped application inference profile IDs yourself, either with CLAUDE_PROXY_INFERENCE_PROFILES='{"yourId":"sonnet-4.6"}' or with a gitignored inference-profiles.json. The repo ships no mappings. An unknown model or profile is priced at $0. Pricing is calculated when the data is read, so the logs never get a costUSD field.
The other flag defaults are --port 9090, --ui-port 8765, --logs-dir logs/ and --claude-projects-dir ~/.claude/projects. --no-ui turns the UI off, and uv run python main.py ui runs the UI alone, without interception. The UI is served from ui/dist. For hot reload, keep main.py running and run bun run dev in ui.
The README’s troubleshooting list follows the setup stages. If nothing shows up, the claude process was started before the proxy variables were exported, or before the CA variables when Proxy is on. TLS errors with Proxy on mean the CA exports are missing or an older process without them is still running. A $0 cost means an inference-profile ID needs mapping. If Intercept never holds anything, the call was not a Bedrock invoke or had no x-claude-code-session-id header. An empty Sessions tab with Proxy off is expected.
The tool does not intercept or edit Bedrock’s responses. It is not a reverse proxy behind ANTHROPIC_BASE_URL, and it ships no request overlays or sample jailbreaks. The README’s instruction on the certificate is brief: “Trust the CA only while Proxy is on.”
Up next
Gage scans Claude Code sessions for bugs the diff hides
Claude flagged the risk mid-session, the author said “go ahead”, and a feature broke with nothing in the commit to show it. Gage, an open source Rust CLI, scans those transcripts and cites the lines.
Related stories
- Toolog keeps a forensic log of Claude Code tool calls
- lcc cut one Claude Code session 49.5% without a summary
- catnap keeps Claude Code from compacting mid-task
- Script checks whether your network breaks Claude API uploads
- Context-tax prices what Claude Code sends before you type
- Claude Design can draw with your real components
Comments
No comments yet. Be the first.
Join the conversation
Sign in to leave a comment. It appears after moderation.

