How Recondo captures, verifies, and persists agent traffic
A Rust gateway terminating TLS in the middle of the agent-to-LLM connection, a content-addressable object store, an append-only PostgreSQL schema, and an operator-controlled control plane.
Stack at a glance
Data flow
One request from a coding agent passes through the following pipeline. Steps 2–6 happen inside the gateway process; steps 7+ happen in the persistence layer.
Agent issues CONNECT
Agent sends CONNECT api.anthropic.com:443 to the gateway with HTTPS_PROXY set.
TLS MITM
Gateway generates a per-host leaf cert signed by its CA. Agent trusts it via NODE_EXTRA_CA_CERTS. TLS terminates inside the gateway.
Capture bytes
Request bytes copied into a buffer. Response bytes streamed and accumulated; SSE frames decoded by the stream/ module, WebSocket frames by websocket/.
Hash & compress
Each body SHA-256 hashed, gzipped, and written to the content-addressable store at objects/req/<hash> and objects/resp/<hash>.
Provider parse
The providers/ module detects the upstream API and extracts model, token counts, cost, tool-call structure, intent metadata.
Session detection
session/ bucket-sorts turns into sessions using time gaps, prompt-hash changes, and sequence cues.
Persist
One row inserted into sessions (or merged), one into turns, n rows into tool_calls. The capture metadata file is written under captures/{timestamp}_{uuid}.json.
Re-encrypt & forward
Gateway opens its own TLS connection upstream, replays the request, streams the response back to the agent.
Gateway modules
The Rust gateway is sliced into focused modules. Each is independently tested
and the inter-module boundaries are enforced by an architecture lint
(just lint-arch).
tls/ CA generation, per-host leaf certificates, system trust store management
capture/ Request/response interception, capture pipeline orchestration
stream/ SSE stream accumulator for streaming LLM responses
websocket/ WebSocket frame parsing, encoding, masking, and relay
providers/ LLM provider detection and parsing — Anthropic, OpenAI, Gemini
schema/ Core capture data type — CaptureRecord
db/ Session/turn record types and DB ops — SQLite + PostgreSQL
session/ Session boundary detection — time gaps, prompt hash changes, sequences
store/ Content-addressable object storage — local filesystem, S3
storage/ Storage backend abstractions, graph store, pipeline
hash/ SHA-256 content hashing
wal/ Write-ahead log for crash-safe capture persistence
gateway/ Main gateway server — TCP listener, TLS handshake, request routing
operator/ Operator sidecar — runtime control and reporting
Plus operational subsystems: config/, health/, status/,
metrics/, alerts/, drift/, artifacts/.
Database schema
Capture-critical tables are append-only — a PostgreSQL trigger refuses
any UPDATE or DELETE. What was captured stays captured.
sessions Session metadata: provider, model, token/cost totals, intent, git context mutable turns Turn records with content hashes, token counts, object store refs append-only tool_calls Tool invocations within a turn append-only anomaly_events Detected anomalies — prompt injection, secret exposure, drift mutable access_audit_log Log of API-key access events append-only
Operational tables — alerts, GDPR deletion requests, agent baselines, session risk,
export schedules, attachments, heartbeats, policies, registered keys, compliance frameworks —
live in api/migrations/.
Storage hardening
- Append-only capture.
turnsandtool_callsare write-once. A PostgreSQL trigger blocksUPDATE/DELETE— captures cannot be silently rewritten. - Hash-verified bytes.
Every body is SHA-256 hashed and stored content-addressed.
Run
recondo verify <session-id>to re-hash and compare. - Encryption at rest. KMS customer-managed keys, S3 server-side encryption. The cloud provider can't read your captures.
- Encryption in transit. TLS to and from the gateway. The gateway terminates the agent leg only to capture plaintext, then re-encrypts upstream.
- Object Lock. S3 bucket runs in Object Lock COMPLIANCE mode (365-day retention by default). Deleting from the database is not the same as deleting from the object store.
- Lifecycle policies. Standard → Infrequent Access at 90 days → Glacier at 365 days.
Read the source
The Recondo repository
is the source of truth. The gateway/ directory holds the Rust gateway
and CLI; api/ the GraphQL server; dashboard/ the React UI.