# What Pleach writes to your database (/docs/emitted-data)



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](/docs/schema). This
page is the lens above it — what gets written, and why.

## The destination is yours [#the-destination-is-yours]

With the [Memory storage adapter](/docs/storage), 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`](/docs/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 [#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](/docs/storage) (default for Postgres / Supabase) |
| `harness_event_log`          | Every observable event during a turn      | The [event log](/docs/event-log) — on with a persistent adapter                 |
| `harness_auditable_calls`    | One row per LLM call                      | The [audit ledger](/docs/audit-ledger) adapter                                  |
| `harness_config_manifest`    | Once per session, content-addressed       | The [config manifest](/docs/config-manifest) substrate                          |
| `harness_checkpoints`        | At stage boundaries                       | [Checkpointing](/docs/checkpointing) enabled                                    |
| `harness_outbox`             | A mutation is buffered for the backend    | [Sync](/docs/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`](/docs/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`](/docs/gateway) cost attribution                             |
| `pleach_byok_credentials`    | A tenant key is stored                    | [`@pleach/gateway`](/docs/gateway/byok) BYOK                                    |

## The two write streams [#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 &#x2A;*[audit ledger](/docs/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 &#x2A;*[event log](/docs/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`](/docs/eval).

The [config manifest](/docs/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 [#whats-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_checkpoints` only when you
  enable it. Off means no per-stage snapshots.
* **Sync** writes `harness_outbox` only when the durable-sync
  outbox is configured. Off means writes go straight to the
  adapter with no buffering table.
* **Error propagation** writes `harness_errors` only under
  `enableErrorPropagation: true`. Off means errors surface as
  stream events without a persisted table.
* **Compliance records** (`harness_audit_records`) are written by
  [`@pleach/compliance`](/docs/compliance) and 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`](/docs/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 [#whats-in-the-rows--and-what-isnt]

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](/docs/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](/docs/schema#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](/docs/security) for the full posture.

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="Schema" href="/docs/schema" description="The column-by-column reference for every table here." />

  <Card title="Audit ledger" href="/docs/audit-ledger" description="The decision stream — one row per LLM call." />

  <Card title="Config manifest" href="/docs/config-manifest" description="The substrate snapshot every event-log row references." />

  <Card title="Scrubbers" href="/docs/scrubbers" description="Redact PII on the write path before it persists." />
</Cards>
