Skip to content

CLI reference

tapes is the server. It runs the services, owns the database, and carries the operator tooling around them. Capturing a session and reading one back are client concerns and live in tapesctl — see The client CLI below.

Run tapes <command> --help for the complete, version-matched flag list.

Command Use
tapes init [--preset ...] Create a local .tapes/ configuration directory.
tapes local [up|status|down] Manage local PostgreSQL and Ollama dependencies.
tapes serve Run proxy, read API, private ingest API, derive worker, and optional embed worker together.
tapes status Show active config, provider/upstream, API reachability, and capture summary.
tapes auth Store OpenAI or Anthropic credentials in .tapes/credentials.toml.
tapes config get|set|list Manage persistent scalar settings.
tapes backfill Replay existing capture artifacts into a deployment.
tapes raw equivalence Prove stored capture bytes re-reduce to the stored reduction. See Proving the capture ratchet.
tapes dev Developer maintenance utilities.
tapes version Print version information.

The common local command is:

Terminal window
tapes serve

It accepts provider/upstream, PostgreSQL, listening, embedding, and project flags. Useful examples:

Terminal window
tapes serve --provider anthropic --upstream https://api.anthropic.com
tapes serve --api-web-ui
tapes serve --embed-spans=false

For split deployments, service subcommands are available:

Terminal window
tapes serve proxy
tapes serve api
tapes serve derive-worker
tapes serve embed-worker
tapes serve ingest

The last three are operator-oriented: the derive worker projects dirty sessions, the independent embed worker populates search vectors, and the private ingest sidecar receives completed turns from a trusted gateway. See HTTP APIs before exposing any endpoint.

Launching an agent under capture, listing sessions, exporting one, and seeding demo data are all client operations against a running server. They live in tapesctl:

Terminal window
curl -sSfL https://download.tapes.dev/tapesctl/install | bash
Terminal window
tapesctl start claude --tapes-url http://localhost:8082
tapesctl sessions list --tapes-url http://localhost:8081
tapesctl export <session-id> --detail spans -o session.jsonl
tapesctl seed --tapes-url http://localhost:8081
tapesctl skill sync <name> --claude

The capture commands (start, capture, sync) address the private ingest API on :8082; the read commands address the read API on :8081. A command that needs a server takes --tapes-url, falling back to TAPES_URL and then to tapesctl config set tapes-url. The flag is rendered in the help of commands that make no HTTP call at all — config, skill list, skill sync, version, plugin uninstall — because it propagates from the top level; those commands ignore it.

Arguments after -- go directly to the agent. See Agent integrations and the tapesctl README for the full surface.

raw_turns keeps two views of the same upstream response: raw_response, the bytes exactly as they arrived, and response, the reduced turn a capture adapter produced from them. The reduction is lossy, and while a second reducer runs inside the capture adapter, two capture paths can reduce the same traffic differently.

The fix is to have exactly one reducer, server-side. Getting there is a deliberate three-step ratchet, configured on the capture adapter:

Mode What the adapter sends Stored fidelity
off its reduction only reduced
dual its reduction and the verbatim bytes raw
raw verbatim bytes only; tapes reduces raw

dual exists to make the middle step provable. It changes nothing an operator sees — ingest keeps the adapter’s reduction — while putting the bytes in the database next to it. That makes the question “would raw have produced this same row?” answerable offline, over real traffic:

Terminal window
tapes raw equivalence --since 24h --limit 5000

For each wire turn in the window that has both halves, the command decodes the stored bytes, re-reduces them through the same server-side path raw would run, and compares the result against the stored reduction. It exits non-zero if anything diverged or failed to reduce, so it can gate the step in CI.

Run it inside the cluster against a tenant’s database:

Terminal window
kubectl exec -n <tenant-ns> deploy/tapes-api -- \
tapes raw equivalence --since 24h --limit 5000

or locally against a forwarded database, with --json for machine consumption:

Terminal window
tapes raw equivalence \
--postgres "postgres://user:[email protected]:15432/tapes" \
--since 24h --json

The comparison is read-only, and it never prints response content — a difference is reported as a JSON path plus the shape of what differs, because these are real prompts.

Every examined turn lands in exactly one class. equivalent is the one that supports a ratchet step. divergent, undecodable, unreducible and no_reducer all block it: the last three are worse than a divergence, because under raw those rows would carry no reduction at all. The skipped_* classes describe turns the flip does not affect — no bytes were captured, the bytes were withheld or dropped over a limit, or the turn was already captured raw-only.

Two fields are excluded from the comparison, and the report always prints them:

  • created_at — reducers stamp it at reduction time, so two reductions of identical bytes taken at different instants differ by construction.
  • usage.total_duration_ns — the wall clock from request to fully-assembled response. Only the party that watched the stream can measure it; a reduction of stored bytes cannot.

Everything else is compared strictly, so a third difference is reported rather than absorbed.

Because both excluded fields are ones raw restores from the capture adapter’s meta block rather than from the bytes, the report also counts which stamps would actually have been available. A window can be perfectly equivalent and still lose data on the flip: if usage.total_duration_ns shows fallback, those turns carry no usable meta.elapsed_seconds, and under raw their duration — and the derived span’s duration_ns — would land empty. Check that line before ratcheting, not just the verdict.

Commands not intended as everyday workflow

Section titled “Commands not intended as everyday workflow”

backfill is for replaying existing capture artifacts into a deployment. dev contains developer maintenance utilities. Consult their --help only when operating those workflows.

Tapes no longer provides chat or checkout commands. It captures external agents; it does not host a chat client or expose history branching.

The start, capture, export, seed, sessions, search, and skill commands have moved to tapesctl; tapes keeps the server and the operator tooling. Span search itself stays server-side — only the client command moved.