What Pleach writes to your database
Every table the runtime and its plugins write — what's on by default, what you opt into, and what each row carries. The destination is your database, not ours.
The runtime writes to your database. Point it at your Postgres, your Supabase project, an OpenTelemetry collector, or an in-memory buffer, and that's where the rows land — your storage, your control plane. The package has no phone-home: nothing it persists leaves the destination you configure.
This page is the data-emission map. It answers two questions: what appears in your database when you run Pleach, and which of it is on by default versus enabled by a config flag or a plugin.
For the column-by-column schema, see Schema. This page is the lens above it — what gets written, and why.
The destination is yours
With the Memory storage adapter, nothing is
persisted at all — rows live in process and vanish on restart.
With a Postgres or Supabase adapter, the tables below are created
in your database and written by the adapter you configured. The
@pleach/observe SDK widens the destination set
further — the same audit rows can go to your OTel collector
instead of a SQL table.
Nothing is written to a Pleach-operated service. The runtime is a library; the storage is a target you own.
What gets written
The package writes two primary streams plus a set of substrate and plugin tables. Each row below names the table, when it's written, and what enables it.
| Table | Written when | Enabled by |
|---|---|---|
harness_sessions | A session is created or its state changes | A persistent storage adapter (default for Postgres / Supabase) |
harness_event_log | Every observable event during a turn | The event log — on with a persistent adapter |
harness_auditable_calls | One row per LLM call | The audit ledger adapter |
harness_config_manifest | Once per session, content-addressed | The config manifest substrate |
harness_checkpoints | At stage boundaries | Checkpointing enabled |
harness_outbox | A mutation is buffered for the backend | Sync enabled |
harness_errors | A structured error fires | enableErrorPropagation: true on the runtime |
chat_session_links | A session binds to an upstream chat | SessionRuntimeConfig.chatId set |
harness_audit_records | A cross-cutting audit record is logged | @pleach/compliance or a custom audit hook |
harness_session_members | A user is granted session access | Multi-user sessions (useTeam) |
harness_session_comments | A comment is posted on a session | Optional surface; the runtime never writes it |
pleach_gateway_cost_events | A routed call resolves a cost | @pleach/gateway cost attribution |
pleach_byok_credentials | A tenant key is stored | @pleach/gateway BYOK |
The two write streams
Most of what the runtime emits lands in one of two streams, and the distinction matters when you decide what to retain.
The audit ledger (harness_auditable_calls)
records load-bearing decisions — which model fired, why it was
chosen, what it cost, whether the cache hit. One row per LLM call,
joinable to your billing schema by tenantId and turnId. This
is the stream you query for cost and compliance.
The event log (harness_event_log) records
what happened — messages sent, tools dispatched, interrupts
raised, subagents spawned, exports queued. Append-only,
ULID-keyed. This is the stream you hydrate a session from and
replay through @pleach/eval.
The config manifest sits beside the event
log: each event-log row references the manifest_hash of the
substrate that produced it, so the two together are a complete,
offline-replayable record.
What's opt-in
The substrate tables — sessions, the event log, the audit ledger, the config manifest — are the runtime's working state with a persistent adapter. The rest is something you turn on:
- Checkpointing writes
harness_checkpointsonly when you enable it. Off means no per-stage snapshots. - Sync writes
harness_outboxonly when the durable-sync outbox is configured. Off means writes go straight to the adapter with no buffering table. - Error propagation writes
harness_errorsonly underenableErrorPropagation: true. Off means errors surface as stream events without a persisted table. - Compliance records (
harness_audit_records) are written by@pleach/complianceand custom audit hooks — nothing lands here unless a plugin writes it. - Gateway tables (
pleach_gateway_cost_events,pleach_byok_credentials) exist only when you run@pleach/gateway. - Team and comment tables (
harness_session_members,harness_session_comments) are written only by the multi-user surfaces, never by a single-user runtime.
What's in the rows — and what isn't
Rows carry runtime metadata: identifiers, timestamps, event types, model and cost fields, content hashes. Message content and tool payloads land in the event log when you persist them; the audit ledger stores decision metadata, not prompt bodies.
Before any PII-adjacent field is persisted, the
compliance scrubbers can redact it on the write
path — the redaction is applied to the row, not bolted on at read
time. Tenant isolation is enforced by the
RLS template every table ships, keyed
on tenant_id or organization_id.
What never gets written: telemetry to a Pleach service, usage beacons, license-check pings. The runtime doesn't call home. See Security for the full posture.
Where to go next
Lineage
Read-side cross-session dependency graph — typed edges between sessions, artifact provenance, and a queryable session-to-session graph for observability.
Audit ledger
The ProviderDecisionLedger write interface, the AuditableCall row it persists, the three compliance plug-points, and how to wire your own adapter.