Copied to clipboard

Architecture

tapes is built around four components:

Proxy Server Intercepts LLM requests, forwards to the upstream provider, and appends each completed request/response to the raw turn log
Ingest Server Sidecar HTTP endpoint that accepts completed conversation turns when an external gateway handles upstream LLM traffic
Deriver Reads the raw turn log and projects it into sessions, traces, and spans. Pure and idempotent — re-running it yields the same result. Runs continuously as tapes serve derive-worker
API Server Serves the derived sessions / traces / spans for inspection, search, and MCP, plus Prometheus /metrics

All servers share the same PostgreSQL database (with pg_duckdb + pgvector) when run together via tapes serve.

Data Flow


  ┌─────────────┐         ┌──────────────────┐         ┌──────────────────┐
  │             │         │                  │         │                  │
  │  Claude Code│────────▶│   Proxy :8080    │────────▶│   Upstream LLM   │
  │  Ollama CLI │◀────────│   (or Ingest     │◀────────│  (Anthropic,     │
  │  AI SDK     │         │    sidecar)      │         │   Ollama, etc.)  │
  │  curl ...   │         │  intercept +     │         │                  │
  │             │         │  forward         │         │                  │
  └─────────────┘         └────────┬─────────┘         └──────────────────┘
                                   │
                                   │ append raw turn
                                   ▼
                          ┌──────────────────┐
                          │  PostgreSQL      │
                          │  ┌────────────┐  │
                          │  │ raw_turns  │  │  ← append-only capture log
                          │  └─────┬──────┘  │
                          │        │ derive  │
                          │        ▼         │
                          │  ┌────────────┐  │
                          │  │ sessions / │  │  ← derived model
                          │  │ traces /   │  │     (pg_duckdb)
                          │  │ spans      │  │
                          │  ├────────────┤  │
                          │  │ pgvector   │  │  ← span embeddings
                          │  └────────────┘  │
                          └────────┬─────────┘
                                   │
                                   │ query
                                   ▼
  ┌─────────────┐         ┌──────────────────┐
  │             │         │                  │
  │  Deck TUI   │◀───────▶│   API :8081      │
  │  tapes CLI  │         │                  │
  │  MCP clients│         │  /v1/sessions    │
  │  Prometheus │         │  /v1/traces      │
  │             │         │  /v1/stats       │
  │             │         │  /v1/search/...  │
  │             │         │  /v1/mcp         │
  │             │         │  /metrics        │
  └─────────────┘         │  /swagger        │
                          │                  │
                          └──────────────────┘

Requests flow through the proxy (or arrive via the ingest sidecar), which forwards to your upstream LLM and appends each completed turn to the raw_turns log. The deriver projects those raw turns into sessions, traces, and spans. The API server reads the derived model to serve session, search, and MCP queries — and self-documents at /swagger via Scalar.

Data Model

tapes separates capture from interpretation. The proxy and ingest sidecar only append; everything you query is derived:


  raw_turns                deriver                 sessions / traces / spans
  (append-only)         (pure, idempotent)         (what you query)

  ┌──────────┐                                     ┌──────────────────────┐
  │ raw turn │──┐                                  │ session              │
  ├──────────┤  │                                  │  ├─ trace (turn 1)   │
  │ raw turn │  ├──────────▶  derive  ──────────▶  │  │   ├─ span (llm)   │
  ├──────────┤  │                                  │  │   ├─ span (tool)  │
  │ raw turn │──┘                                  │  │   └─ span (sub)   │
  └──────────┘                                     │  └─ trace (turn 2)   │
                                                   └──────────────────────┘
  • raw_turns — the durable, append-only capture log. Every request/response is recorded exactly as it happened. Nothing is interpreted here.
  • deriver — a pure, idempotent projection. Given the same raw turns it always produces the same derived model, so it can be re-run safely (for example to backfill after a schema change).
  • sessions / traces / spans — the model you actually browse. A session is one agent run; a trace is one user-visible turn; spans are the nested operations within a turn (llm calls, tools, subagents, injected context). Span links capture dataflow between spans, including cross-turn compaction seams.

Internally the deriver content-addresses raw turns with a merkle structure to deduplicate re-sent history and preserve provenance. That layer is an implementation detail of the deriver — it is not a browsable surface, and the API exposes only sessions, traces, and spans.

Last updated: