Architecture
tapes consists of two main components:
Proxy Server Intercepts LLM requests, forwards to upstream provider, and stores conversation turns in the Merkle DAG
API Server Provides HTTP endpoints for inspecting and querying the stored conversation data
Both servers share the same storage backend (SQLite or in-memory) when run together via tapes serve.
Data Flow
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ │ │ │ │ │
│ Claude Code│────────▶│ Proxy :8080 │────────▶│ Upstream LLM │
│ Ollama CLI │◀────────│ │◀────────│ (Anthropic, │
│ AI SDK │ │ intercept + │ │ Ollama, etc.) │
│ curl ... │ │ forward │ │ │
│ │ └────────┬─────────┘ └──────────────────┘
└─────────────┘ │
│ store
▼
┌──────────────────┐
│ │
│ Merkle DAG │
│ ┌────────────┐ │
│ │ SQLite or │ │
│ │ in-memory │ │
│ └────────────┘ │
│ │
└────────┬─────────┘
│
│ query
▼
┌─────────────┐ ┌──────────────────┐
│ │ │ │
│ Deck TUI │◀───────▶│ API :8081 │
│ tapes CLI │ │ │
│ MCP clients│ │ /v1/sessions │
│ │ │ /v1/stats │
│ │ │ /v1/search │
│ │ │ /v1/mcp │
│ │ │ /swagger │
└─────────────┘ └──────────────────┘
Requests flow through the proxy, which forwards to your upstream LLM and stores each conversation turn as a hashed node in the Merkle DAG. The API server reads from the same storage to serve session, search, and MCP queries — and self-documents at /swagger via Scalar.
Merkle DAG
Each conversation turn is hashed and linked to its parent, forming an append-only directed acyclic graph:
(root)
│
▼
┌──────┐ ┌──────┐
│ req │────▶│ res │ Session A
│ a1b2 │ │ c3d4 │
└──────┘ └──┬───┘
│
▼
┌──────┐ ┌──────┐
│ req │────▶│ res │ Turn 2
│ e5f6 │ │ 7g8h │
└──────┘ └──┬───┘
│
├──────────────┐
▼ ▼
┌──────┐ ┌──────┐
│ req │ │ req │ Branched
│ i9j0 │ │ k1l2 │ (checkout)
└──────┘ └──────┘
Every node contains a content hash of the message, model, and parent reference. This enables cryptographic verification of conversation history and branching via tapes checkout.