tattle
See what your coding agent already sent, and what it sends next.
tattle audits coding agents (Claude Code, Codex) for secrets and personal data leaving your machine:
tattle(retro audit): reads your past agent transcripts and reports what already went out. It covers what went to the model provider and what the agent's tools sent to third parties.tattle run -- <agent>(live proxy): runs an agent through a local proxy and logs every outbound HTTP(S) request, including the runtime's own telemetry.
Both flag API keys, tokens, private keys, .env contents, emails, phone numbers, street addresses, home paths, and your name, login or machine name. They show where each one went, with the value masked.
It was prompted by a post from @This_Liss: their Claude Code put personal data in HTTP headers because the sites it curled asked agents to identify their users. tattle retro flags that pattern in past sessions, and escalates it when the page the agent just read had asked who its user is. The live proxy catches it as it happens.
Install
uv tool install git+https://github.com/colefoster/tattle
Requires Python 3.12+. pipx install git+https://github.com/colefoster/tattle also works. The PyPI package will be tattle-audit. Don't pip install tattle: that name on PyPI belongs to an unrelated project.
Retro: what already left
tattle # scan every past session, print a summary, write ~/.tattle/retro/report.html
tattle retro --since 30d # only recent sessions (also 12h, 2w, 2026-10-01)
tattle retro --open # open the HTML report
tattle retro --share # stricter masking: project names and private hosts hidden too
It reads ~/.claude/projects/**/*.jsonl (subagents included), ~/.codex/sessions/**, archived Codex sessions and ~/.codex/history.jsonl. The report keeps two channels apart:
- Sent to the model provider. Everything in a transcript was part of a prompt: your messages, files the agent read, command output and tool calls. Each finding shows the session, when it was last seen, where it appeared (user message, file read, command output…) and a masked preview.
- Sent to third parties by tools. Network calls rebuilt from WebFetch and WebSearch, web-ish MCP tools and shell commands:
curl,wget, HTTPie,gh api,git push/clone/fetch,ssh,scp,rsync. URLs, query strings, headers (User-Agent, Authorization),-ucredentials, credentials embedded in URLs and request bodies are all scanned. Localhost calls are left out.
Live-looking secrets are listed together under Rotate these. Values that look like docs or fixtures (AKIA…EXAMPLE, fake, low entropy) are kept separate as placeholders.
Retro is reconstructed from transcripts. It cannot see traffic the agent runtime sent on its own (telemetry, update checks, feature flags) or anything a command fetched indirectly. The live proxy catches that.
Transcripts are streamed one line at a time, and pasted screenshots are dropped before parsing. On a real Mac with 1,521 transcript files (3.7 GB, 1,559 sessions), a full scan took 87 s with a peak RSS of 128 MB.
Live: what it sends next
tattle run -- claude -p "fetch example.com" # run any command through the proxy
tattle run --live --open -- codex # stream requests as they happen, open the report at the end
tattle tail # live view of the latest session from another terminal
tattle report --open # rebuild and open the latest report
tattle report --share # stricter masking for a report you will show others
tattle proxy # standalone proxy; prints the env vars to export
tattle ls # list sessions
When the command exits, tattle prints a summary and writes ~/.tattle/sessions/<id>/report.html:
tattle 25 requests · 4 hosts · 1.3 MB sent · 4 high · 2 medium · 2 expected auth
high .env-style DATABASE_PASSWORD hunt…(18 chars) body → api.anthropic.com
high OpenAI API key sk-p…(56 chars) body → api.anthropic.com
high value from your .env plan…(31 chars) body → api.anthropic.com
high your email •••@•••.com body → api.anthropic.com
medium email address •••@•••.net body → api.anthropic.com
medium home directory path /Users/•••/ body → api.anthropic.com
What it flags
| Category | Detectors |
|---|---|
| Secrets | Anthropic, OpenAI, AWS, GitHub, Slack, Stripe, Google and Hugging Face keys; private-key blocks; JWTs; .env-style *_KEY= / *_TOKEN= / *_PASSWORD= assignments; any value from a .env* file in the working directory |
| Personal | your git email (global and repo), your full name, login name, machine name, home-directory paths, other email addresses, phone numbers, street addresses, and any string you add to ~/.tattle/watch.txt |
It scans the path, query string, every header and the decoded body. A credential in an Authorization, x-api-key or Cookie header is marked expected: it is how the agent signs in, so it is shown dimmed rather than as a leak. Hosts are grouped as model API, telemetry, packages & code, or web.
Values are masked before anything touches disk. The log and report keep a key's first four characters and its length, an email's first letters and TLD, and nothing else. --share drops even those and blanks account IDs in paths.
How HTTPS works, and how to undo it
tattle runs mitmproxy with a small addon. On first run mitmproxy creates a local CA in ~/.tattle/ca/.
tattle never installs that CA system-wide. Only the process started by tattle run trusts it, through environment variables: HTTPS_PROXY, NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, GIT_SSL_CAINFO and CODEX_CA_CERTIFICATE. Your browser and other apps are untouched.
Teardown is one command:
tattle clean # deletes ~/.tattle: the CA key and every session
A client that pins certificates or ignores those variables will refuse the connection. tattle records that as a refused interception, so it is not silently missing from the report.
Real run: Claude Code, 2026-10-25
tattle run -- claude -p "Fetch https://example.com and tell me its title in one line" on a Mac:
- 27 requests to 5 hosts, 1.4 MB sent, in 12 seconds. The page fetch itself was a single 0-byte
GET example.com(viacurl). - ~1.2 MB went to
api.anthropic.com(prompts, config, event logging); 146 KB of logs went to Datadog; Claude Code also checked npm for two MCP packages. - Flagged: the user's own email and home-directory path inside the prompt body sent to the model API. No secrets left in a URL, header or third-party request.
The same run in a project with a planted fake .env flagged the fake OpenAI key, the fake password and the fake email inside the request to api.anthropic.com, after the agent read the file.
Limits
- It sees HTTP(S) only, not raw TCP, DNS or WebSockets frames.
- Pattern detection misses secrets with no recognisable shape that are not in a local
.env. - Localhost traffic bypasses the proxy (
NO_PROXY=localhost,127.0.0.1,::1).
Develop
uv sync && uv run pytest
MIT licensed.
Metadata
Release files for tattle-audit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| tattle_audit-0.1.0.tar.gz | 31.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tattle_audit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.3 kB
Release files / tattle_audit-0.1.0.tar.gz
| Download URL | tattle_audit-0.1.0.tar.gz |
|---|---|
| Size | 31.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8185630f378f80e3b22a277d20c982ff6e81585738319ffd80bb38c1ab996831
|
|
BLAKE2b-256 checksum How to use checksums |
74afbd3d5a1c7fab1240c9889e089d62bfc567b77a832c17f1cc95b707237335
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / tattle_audit-0.1.0-py3-none-any.whl
| Download URL | tattle_audit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 34.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
730f2d6df975b20e765fd29e216785bc3f45816aba93f3b6d9ffb99919c784fd
|
|
BLAKE2b-256 checksum How to use checksums |
5c209d4b7eace8902c12835b5dd8350c38874da3aadc06693eb5cff0ab3ff54f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for tattle-audit 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.