See what your AI agents send to model providers — and get alerted when a secret goes with it.
Beagle gives you a local, searchable record of sessions from Claude Code, Codex, opencode, and pi. It captures prompts, responses, and tool use, then scans every captured call for secrets. No Beagle account, no cloud service, and no permanent agent configuration changes.
npm install -g @boundedhq/beagleOther install options · macOS and Linux · arm64 and x64
beagle demoThis runs a self-contained drill through Beagle's real capture and alert path,
then opens the result in the dashboard — no agent, account, tokens, or outbound
connection required. The drill is badged [demo] and kept out of real-leak
totals; clear it with beagle demo --clean. Or see the alert fire on a real
agent.
beagle detect # find supported agents
beagle run claude # or codex, opencode, piUse the agent normally. Beagle automatically chooses the right capture mode for the way that agent is signed in.
beagle ui # searchable dashboard
beagle leaks # secrets detected across captured sessionsEvery captured call is already scanned. If a secret leaves, Beagle sends an
OS notification and highlights the exact call in the dashboard. To capture
future sessions automatically, run beagle watch <agent>.
Catching secrets is the headline, but the record Beagle keeps answers the questions you ask while building with agents. (Everything below describes wire capture, the full-fidelity mode; subscription-login sessions are the agent's own report — see Capture modes.)
- What system prompt actually went out? Open any session — the transcript starts with the system prompt as captured, then every request and response in order.
- What tools does the model see? A call's raw view holds the complete
captured request — including the full
toolsarray, names, descriptions, and JSON schemas — as foldable JSON. - What's new this turn vs. re-sent history? Transcripts show each turn's delta instead of re-printing the whole conversation; each call gets a one-line "what this turn did" summary and records tokens in/out whenever the provider reports usage — usually enough to answer "why was that turn 50k tokens?"
- What came back? Responses reassembled from the stream: the model's text, then its tool calls, each in the order emitted.
- Did that internal hostname ever leave?
beagle searchchecks every call still in the local store — for strings no detector could know about. A hit is definitive proof it was sent; "no matches" is bounded by the retention window (7 days of payloads by default).
If you build agents, Beagle doubles as a context debugger: develop under
beagle run, then read what the model actually saw. (In the shot above,
redact-on-capture — the default — masked the key before it was ever
written to disk; run beagle config redact-on-capture off if you want Beagle's
copy byte-exact.)
The command is the same for every agent — beagle run <agent> for one
session, beagle watch <agent> for always-on. What changes is how Beagle
captures, and it picks that automatically from how the agent is signed in:
| Your agent | Using | What happens |
|---|---|---|
| Claude Code | Anthropic API key | wire capture — full fidelity |
| Claude Code | Claude.ai subscription (Pro/Max) | telemetry capture, auto-detected |
| Codex | OpenAI API key | wire capture — full fidelity |
| Codex | "Sign in with ChatGPT" | telemetry capture, auto-detected |
| opencode | OpenAI provider | wire capture — full fidelity |
| pi | OpenAI provider | wire capture — full fidelity |
For opencode and pi, both OpenAI API keys and ChatGPT sign-in are supported. Anthropic/Claude and other providers are future support targets.
beagle detect only checks your local PATH and known application or
configuration directories; it sends nothing anywhere. Recognized agents that
Beagle cannot capture yet — including desktop apps — are listed separately
with a link to the
agent-support vote.
If Beagle can't tell how an agent is signed in, it asks once at the terminal
and remembers your answer (beagle config run-mode <agent> wire|telemetry|auto
to change it; --wire / --telemetry force a mode for one run). And if a
wrong guess ever slips through, Beagle warns you when a session ends with
nothing captured — and tells you which mode to force.
What's telemetry capture? Two agents can't be wire-redirected when signed
in with a subscription: Codex's ChatGPT login is locked to its built-in
provider (the endpoint override Beagle uses for API-key Codex doesn't reach
it), and Anthropic restricts Claude.ai subscription OAuth to its official
client. So for those two, Beagle captures sessions from the agent's own
usage reporting instead. Your prompts, tool inputs, and tool outputs (including
files the agent reads) are still scanned and searchable — but it's the agent's
self-report, not observed wire bytes. Those rows are badged self-reported in the
dashboard (wire rows say observed), and alerts can lag a few seconds.
Nothing leaves your machine: the report goes to a loopback receiver on
127.0.0.1, and the vendor's reporting flags are set per run, never written
to your agent's config. One footnote: if your agent already exports telemetry
to a company collector, that export is redirected to Beagle for the duration
of the run — your collector won't receive events from that session. One
limitation: Codex encrypts its reasoning, so telemetry capture cannot show what
the model was thinking between tools.
beagle watch <agent> makes that agent captured on every run, not just ones
you remember to wrap. It shows you each change — the exact shim path, service
definition, and rc line — and asks y/N before making it:
- a PATH shim for that agent,
- a background service so coverage survives reboots,
- and — only if the shim isn't already winning your PATH — one guarded
block in your shell rc (
~/.zshrc/~/.bash_profile/config.fish), with an offer to refresh your current shell so coverage is live right away.
Every change is marker-owned and recorded; beagle unwatch (name an agent,
pick from a list, or --all) reverts them all.
beagle run wraps one session without modifying your agent configuration.
Captures stay in Beagle's local store (~/.local/state/beagle), and its local
daemon exits after it has been idle.
In wire-capture mode, beagle run starts a loopback proxy and points the agent
at it for that run — via ANTHROPIC_BASE_URL for Claude Code, via a per-run
provider override for Codex, via a temporary merged config file for opencode,
and via a one-run -e extension for pi. Your real config files are never
modified, and anything Beagle generates is deleted when the run ends. The agent
talks to 127.0.0.1; Beagle streams the bytes to the real provider unmodified
and unbuffered (SSE reaches your agent immediately) and keeps a copy locally:
agent ──HTTP──▶ beagle (127.0.0.1) ──HTTPS──▶ api.anthropic.com
│
├─ scan outbound body for secrets → alert
└─ store request/response locally (SQLite, 0600)
What Beagle is not:
- Not a TLS man-in-the-middle. No CA certificate is installed, no TLS is
intercepted, no system proxy is configured. If an app doesn't honor the
redirect, its traffic simply doesn't route through Beagle — it can't
silently observe anything else. (That's also why desktop apps, IDE
extensions, and web UIs aren't covered in v1 — they launch their own
processes.
beagle statusalways tells you exactly what is and isn't covered.) - Not a cloud service. No account, no server, no phone-home — Beagle itself sends nothing anywhere.
- Not a blocker. v1 observes and alerts; a finding never causes Beagle to rewrite or drop agent traffic. (An optional setting censors detected secrets in Beagle's own local records — that changes what Beagle keeps, never what goes over the wire.)
beagle detect # find supported agents and recognized coverage gaps
beagle demo # run a local drill, save it as [demo], open dashboard
beagle demo --clean # remove saved demo sessions
beagle run <agent> # capture one session; your agent config stays untouched
beagle watch <agent> # make that agent always-on (guided; asks before each change)
beagle unwatch [<agent>] # stop watching; restores your setup
# (no agent: pick from a list; --all for everything)
beagle status # trust strip: coverage, store size, retention, changes
beagle leaks # the leak log — every detected secret, deduped
beagle search [string] # was this string sent? searches the local store
# (no arg → reads stdin, keeps it out of history)
beagle show <id> # one captured call, summarized
beagle ui # open the dashboard (loopback, one-time link)
beagle purge [all|panic] # erase captured data (panic = zero records in place)
beagle stop # stop the daemon; pause always-on until next watch
beagle uninstall # remove everything Beagle installed (see Uninstall)
beagle config [...] # redact-on-capture, exclusions, per-agent run-modebeagle help lists them all. The whole loop works headless — a skeptic never
has to start the viewer.
- Local only. The only outbound connections are the ones your agent was already making, forwarded verbatim.
- Your setup, untouched.
beagle rundoes not modify your agent config.beagle watchasks before each change, records every one in a manifest, and reverts them all onunwatch/uninstall — see Always-on above for exactly what it touches. - Your API key never rests. Auth headers are scrubbed before anything is written; the credential exists only in memory, in flight.
- The store is the liability, minimized.
0600files, 7-day rolling payload window, and — on by default —redact-on-capture: a detected secret is masked ([REDACTED:type:hash]) before it is ever written, so Beagle never becomes a plaintext store of the very secrets it catches. Turn it off (beagle config redact-on-capture off) for the raw-fidelity view. One-command panic purge zeroes every record in place and compacts the store (SQLitesecure_delete+ VACUUM) rather than just dropping rows — best-effort on SSDs and copy-on-write filesystems, where full-disk encryption is the real backstop. - Auditable. Found a hole? See SECURITY.md for private reporting.
No language/runtime dependencies — Beagle ships as a single self-contained
binary (macOS and Linux, x64 and arm64; Windows is post-v1). Linux desktop
notification banners use notify-send when it is available. The npm route
needs npm; the script route needs only curl.
# npm (primary) — the prebuilt binary for your platform. No post-install
# script, no code fetched at install time.
npm install -g @boundedhq/beagle
# or the one-line script (downloads from GitHub Releases, verifies the
# sha256 checksum before installing, never runs post-install code). For a
# transparency tool, read it before you pipe it:
# curl -fsSL https://github.com/boundedhq/beagle/releases/latest/download/install.sh -o install.sh
# less install.sh && sh install.sh
curl -fsSL https://github.com/boundedhq/beagle/releases/latest/download/install.sh | sh
# or build from source (requires Bun ≥ 1.3)
git clone https://github.com/boundedhq/beagle && cd beagle
bun install && bun run build # → dist/beagle(A Homebrew formula lives in packaging/beagle.rb for a future tap; npm already
covers macOS and Linux, so the tap isn't wired into releases yet.)
Want to see Beagle fire on a live session before trusting it? Plant a synthetic canary and ask an agent to read it. This value has the shape of an AWS secret access key but no paired access key ID, so it cannot authenticate (AWS access-key documentation):
mkdir -p /tmp/beagle-canary
CANARY='wJa1rXUtnF3MI4K7MDENGbPxRf9CYZ8qLm2Vt0Bn'
printf 'AWS_SECRET_ACCESS_KEY=%s\n' "$CANARY" > /tmp/beagle-canary/.env
beagle run claude # or codex, opencode, pi
# ask: "read /tmp/beagle-canary/.env and tell me what's in it"When the agent sends the file contents, Beagle notifies you and highlights the
captured call. Telemetry-captured sessions can lag a few seconds. Never test
with real credentials; remove the canary afterward with
rm -r /tmp/beagle-canary.
AWS's familiar documentation example ends in EXAMPLE; Beagle suppresses
obvious example and placeholder values, so use the synthetic canary above.
Beagle never checks for updates on its own — the only outbound traffic on your machine is your agents' own calls, and an update check would break that promise. Updating is the same channel you installed with:
# npm
npm install -g @boundedhq/beagle@latest
# install script — re-run it; it fetches the latest release and verifies the checksum
curl -fsSL https://github.com/boundedhq/beagle/releases/latest/download/install.sh | sh
# from source
git pull && bun run build # → dist/beagle (if you symlinked it into
# your PATH — see Development — you're done)Then restart the daemon — the new binary on disk doesn't change the
daemon already running from the old one. Beagle won't restart it behind your
back (it may be mid-capture for another agent), but beagle run and
beagle ui tell you when it's stale:
beagle ▲ the running daemon is v0.1.0 but this beagle is v0.2.0 — it won't have this version's fixes until restarted.
Restart it: beagle stop (the next 'beagle run' starts a fresh one on the new binary)
- Plain use:
beagle stop— safer than a rawkill, because it refuses while another agent's capture is live. The nextbeagle run/beagle uistarts a fresh daemon on the new binary. - Service-installed (via
beagle watch): here the warning sayskill <pid>instead, and that really is the right move — launchd/systemd respawns the daemon immediately from the updated binary path. (Don't usebeagle stopfor updates: it pauses always-on until the nextbeagle watch.)
If the update changed the store schema, the new daemon migrates it in place
on startup (additive, data-preserving). Read commands against a
not-yet-migrated store refuse in plain language and say which side to
restart — never a stack trace. beagle --version prints the binary on disk;
beagle status shows whether a daemon is running and its pid.
Beagle must leave no trace — that's part of the trust contract. One command does the whole safe teardown, in the right order (unwatch every agent → stop the daemon → erase captured data → remove the state dir):
beagle uninstall # then remove the binary the way you installed it:
npm uninstall -g @boundedhq/beagle # (npm) or: rm /usr/local/bin/beagle (curl / source)beagle uninstall restores your PATH and config before deleting anything,
and — when the store is readable — zeroes its contents before unlinking it,
rather than a bare rm -rf (best-effort — on SSDs and copy-on-write
filesystems full physical erasure can't be guaranteed from userspace;
full-disk encryption is the backstop). It's different from beagle purge,
which clears the data while keeping you set up. Everything Beagle ever
changed is listed by beagle status while it's installed.
Does my API key pass through Beagle? In wire-capture mode, yes (that's what a proxy is); at rest, never. Auth headers are stripped before capture and are not stored, logged, or displayed.
What exactly is stored, and where?
Request/response bodies, headers (minus credentials), timing and token
counts — in a SQLite file under ~/.local/state/beagle (honors
$XDG_STATE_HOME), mode 0600, payloads pruned on a 7-day rolling window.
beagle purge erases it on demand.
Can it see traffic from apps I didn't run under it?
No. Coverage is opt-in per agent (run for one session, watch for
always-on). There is no system proxy, no packet capture, no TLS
interception — which also means GUI apps and IDE extensions aren't covered
in v1.
How do I find out whether a secret leaked?
You don't have to go looking — every outbound call is scanned automatically,
and every credential finding is in beagle leaks and highlighted in the
dashboard.
Don't feed real keys into commands to check. beagle search is for strings
the detector can't know about (an internal password, a customer hostname);
run it with no argument and it reads the term from stdin, keeping it out of
shell history. The search runs locally against your local store.
Why should I trust the detector?
Its rules are derived from the MIT-licensed gitleaks ruleset — a curated
subset, re-tiered and precision-tuned for agent traffic, then vendored as
data and sha256-pinned at load (see
THIRD-PARTY-NOTICES.md). The matcher is ~200 lines
you can read in one sitting (src/core/scanner/). A CI
regression gate requires every hand-written negative fixture to remain free of
loud (structured) findings and every positive fixture to remain detected
(tests/precision.test.ts). That guards known cases;
it is not a measured false-positive rate over representative traffic, and
Beagle does not publish one without a larger, representative corpus. Detection
tiers are honest: structured credential hits (AWS secret access keys,
GitHub/Stripe keys, private keys, Luhn-checked cards) alert loudly;
entropy-only hits stay a quiet "possible." AWS access key IDs are recognized
for conservative redact-on-capture, but they are identifiers rather than
credentials, so they never create a leak event or alert.
What happens if Beagle crashes mid-run?
In wire-capture mode, the proxy fails open for observation, never blocking
your agent: if capture fails, your agent's traffic still flows; the gap is
recorded as capture truncated rather than silently papered over.
src/core/— the dependency-free runtime foundation (separately LOC-budgeted, stdlib-only, nobun:*); a subset of the trust path, not the full audit scopeTRUST_PATH_SCOPE— the explicit capture-to-alert audit manifest and its ≤5,000-LOC budget (core plus the trust-path modules below)src/adapters/,src/parsers/,src/transform/,src/notifier/— Bun surface (bun:sqlite, workers), telemetry/format parsers, redact-on-capture, and alert delivery; on the trust path (budgeted)src/daemon/— capture → ingest → alert orchestration (daemon.ts, budgeted) plus the control-plane socket (control.ts, outside the manifest)src/viewer/,src/install/, and most ofsrc/cli/— application and orchestration code: reviewed and disclosed in the total LOC report, but not in the budgeted trust manifest (src/cli/demo.tsis the explicit exception)rules/— vendored, pinned detection rules (data; see THIRD-PARTY-NOTICES.md)
bun install
bun run check # lint + LOC budget + typecheck + tests
bun run build # → dist/beagle (self-contained binary)
# run your build as `beagle` from anywhere (both pick up later rebuilds):
export PATH="$PWD/dist:$PATH" # this shell only (add to your shell rc to persist)
ln -sf "$PWD/dist/beagle" /usr/local/bin/beagle # or install it system-wide (may need sudo)CI keeps the trust-critical surface small and testable: the dependency-free core is capped at 2,000 lines, the declared capture-to-alert trust path at 5,000 lines, and the release binary at 100 MB. Scanner and proxy-latency regression tests are also release gates. If a scan reaches a resource limit, Beagle marks it incomplete and, with default redaction, withholds unverified content.
See CONTRIBUTING.md. Beagle is a product of Bounded, MIT-licensed.

