tapesctl
tapesctl is the command-line client for tapes. Tapes
records what coding agents actually did — every LLM call an agent made, as
sessions, traces, and spans. The server stores and serves that data;
tapesctl is what captures it and what reads it back.
The split is worth stating once, because it decides which documentation answers a given question:
- tapes is the server. It owns
serve,local up,seed, and the HTTP APIs. Running one is documented in the server’s own docs at https://tapes.dev/docs/. - tapesctl is the client. Every capture and read verb lives here.
You bring your own server. tapesctl never guesses one: with no server
configured, commands that need one refuse to run rather than send a capture to
whatever happens to be listening.
The two ports
Section titled “The two ports”A tapes deployment serves reads and ingest on separate listeners, and
tapesctl commands are split across them. Passing the wrong one is the single
most expensive mistake available here: a capture pointed at the read port still
exits 0. It reports no turns were captured, and the reason — every turn
rejected by a route that does not exist there — is a warning in a log file you
were not watching.
| port | listener | the commands that use it |
|---|---|---|
8081 |
read API | sessions, traces, spans, search, export, seed, skill generate, cassettes |
8082 |
ingest | start, capture, sync |
Those are the defaults of a local tapes serve. A deployment that fronts both
behind one hostname gives you one URL for everything; check with whoever runs
it. What does not vary is which side of the split a command is on.
Because a configured default holds one URL, a machine that both captures and reads a local server cannot name both with configuration alone. Configure the one you type least often and pass the other explicitly. See Configuration.
Install
Section titled “Install”curl -sSfL https://download.tapes.dev/tapesctl/install | bashConfirm it landed:
tapesctl versiontapesctl 0.1.0All in all, just another tape in the stereoBoth lines are expected. The second is the release smoke test’s canary.
The version number is not a release identifier — every release to date
reports 0.1.0, because the crate version has never been bumped and releases
are tagged independently. Do not use tapesctl --version to work out which
build you have, and do not treat 0.1.0 in a bug report as meaningful. Read
the version trap before relying on it for anything.
Supported platforms are Linux and macOS, on x86-64 and arm64.
Two minutes: capture, then read
Section titled “Two minutes: capture, then read”Capture a Claude session. The harness behaves as it would unproxied — its traffic is forwarded to its own provider API — and the capture proxy dies with it. The URL is the ingest port:
tapesctl start claude --tapes-url http://localhost:8082Before the harness launches, and again when it exits, tapesctl prints to
stdout; while the harness holds the terminal it prints nothing at all:
tapesctl: capturing; logs at ~/.tapes/logs/start-20260813-180411-54233.logtapesctl: captured session f47ac10b-58cc-4372-a567-0e02b2c3d479 (pass --web-url for a console link)tapesctl: logs at ~/.tapes/logs/start-20260813-180411-54233.logNow read it back, against the read port:
tapesctl sessions list --limit 20 --tapes-url http://localhost:8081{ "items": [ { "auth_subject": "local:jasonwc", "harness_id": "claude", "harness_session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "id": "01JDQ8F3K2M4N6P8R0T2V4X6Z8", "last_seen_at": "2026-08-13T18:19:52Z", "rollup": { "status": "ended", "turn_count": 12 }, "started_at": "2026-08-13T18:04:11Z" } ], "next_cursor": ""}Note the two ids. The one start printed is harness_session_id; the one every
read command takes is id. They are different values in different namespaces,
and feeding the printed one to sessions get returns a 404. That is a live
defect, not a misunderstanding — pass the printed id to
sessions list --harness-id claude --harness-session-id <id> to resolve it to
the tapes id; Session ids explains the split and
why the filter comes as a pair.
Read the session with the id from the listing:
tapesctl sessions get 01JDQ8F3K2M4N6P8R0T2V4X6Z8 --tapes-url http://localhost:8081Every read command prints the server’s JSON pretty-printed and nothing else, so
it composes with jq.
Where to go next
Section titled “Where to go next”- Capture — how capture actually works: the two lanes, which harness uses which mechanism, what attribution means, and the session-id reality.
- Commands — the full reference: every command, its flags, its environment equivalents, its exit codes and error families.
- Configuration — the precedence chain,
config.toml, and every filetapesctlwrites. - Cassettes — the command surface your deployment serves, discovered at runtime.
- Troubleshooting — the failures that actually happen, starting with a capture that landed nowhere.
What tapesctl does not do
Section titled “What tapesctl does not do”- No telemetry.
tapesctlreports nothing about you anywhere. There is no variable to set because there is nothing to turn off. - No authentication on the read API. Read commands send no credentials.
- No server.
tapesctldoes not run, embed, or start a tapes server, andlocal upis the server’s verb, not this client’s.