Tamper-evident agent audit
Every model call your agent makes, as one row you own.
Each row is hash-chained to the one before it and lands in your Postgres. Add it to the agent you already run; no loop rewrite.
Free · FSL-1.1 fair-source · your database · no phone-home · no account
import { createPleachRuntime, setOrchestratorAdapterCtor } from "@pleach/core/runtime";
// wrap the provider + store you already run — no loop rewrite
setOrchestratorAdapterCtor(MyProviderAdapter);
const runtime = createPleachRuntime({
storage: new MyPostgresStorageAdapter(pool),
checkpointer: new MyPostgresCheckpointer(pool),
plugins: [myDomainPlugin],
});
// core now governs YOUR loop: family-lock, replay, checkpoints, audit rowShipped in @pleach/core@0.1.0. Core drives the loop you already run — family-lock, replay, checkpoints, and one audit row per call, over your infrastructure. The runnable end-to-end is on Adoption paths.
SELECT * FROM harness_auditable_calls ORDER BY created_at DESC LIMIT 1;
prev_hash links it to the call before it; subagent spend rolls up to parent_turn_id.verifyChainships in@pleach/core; you run it, not us.audit:graph-stagesfails the build on an out-of-lattice edge.- A Go implementation round-trips the same recorded turns.
- Competitor claims carry the month they were written. Landscape →
Capabilities
The row is the product. Everything else reads it.
As of 2026-06, these are the capabilities we didn't find shipping elsewhere among Helicone, Langfuse, LangSmith, Portkey, LangGraph, Temporal, and the rest. The landscape has the honest version, in both directions.
Backfill or delete a row and the chain breaks.
Every row carries a hash of the row before it, in a database only you hold. The verifier is yours to run, and it names the first row that doesn't match.
verifyChain(rows)
// → { ok: true }
// → { ok: false, failedIndex, expected, actual, reason }Replay a recorded turn. Any drift is named.
Replay re-serves the recorded responses against the same version and input, and names the first event that diverges. A live model can't promise byte-identical output, so rerunning the same model and judging the result equivalent is the next step; it isn't shipped yet.
headless-replay: cache miss → ReplayDivergenceErrorSELECT seq, event, payload FROM session_manifest WHERE session_id = 's_91c' ORDER BY seq;
seq. An agent reads it back to know what it did; you replay or fork from any event — ckpt_7 makes the rewind exact.Every subagent call carries the turn that caused it.
Each row stores parent_turn_id and subagent_depth, along with the tool that ran and the tokens it used. One GROUP BY rolls nested fan-out back to the user message.
GROUP BY parent_turn_idSELECT tenant_id,
SUM(token_usage) AS spend
FROM harness_auditable_calls
GROUP BY tenant_id, turn_id;turn_id as row 1, subagent_depth = 1. Finance reads the per-turn rollup as a single GROUP BY rather than stitching child sessions together after the fact.Every turn runs the same four stages.
@pleach/core sits in exactly one of these four stages. The audit:graph-stages CI gate fails the build on an out-of-lattice edge — the diagram is the invariant, not a best-practice diagram.The runtime is a contract, not a TypeScript API: HTTP + SSE routes, the audit row, the checkpoint envelope, the sync clock. @pleach/core is the reference implementation; the Go implementation isn't published as a SKU yet. Read the contract or the architecture deep-dive.
A gateway governs the model call. Pleach governs the agent that made it.
A proxy's row is what your code claimed: project, tag, customer, sent as a header. Pleach's row is what the runtime did: turn, tool, subagent tree, written to your Postgres.
How that plays against Merge Gateway, Portkey, and the observability planes →Ways in
Keep your loop. Add the runtime around it.
Your provider, store, and tools stay put. Start with the smallest entry point that fits; the same row travels with you if you grow into more. Compare the paths →
Core brownfield
Wrap your LLM client and database as small adapters; core drives the turn loop over them, with family-lock, replay, checkpoints, and interrupts. On the AI SDK or LangChain, the migration guides cover the graft.
Greenfield
A fresh SessionRuntimeon core's defaults: sessions, graph, replay, checkpoints, interrupts.
@pleach/core →Soon · @pleach/observe keeps your loop and writes only the audit row, to Postgres, Supabase, or an OTel callback. Not on npm yet.
FAQ
What teams ask before adopting
- How is Pleach different from the AI SDK or LangChain?
- They ship faster for single-shot agents and broad chain composition. Pleach is the runtime when the agent is the product: per-customer cost, replay that names drift, time-travel checkpoints, and a typed audit row joinable to your billing and compliance schemas. See /docs/comparison.
- Already on Anthropic Enterprise or OpenAI Enterprise — what does Pleach add?
- It composes underneath. Your contract keeps doing its job; Pleach adds per-customer cost rollup inside one Workspace or Project, a tamper-evident audit row in your own Postgres, and replay across model snapshots. No new vendor — an npm install plus a table. See /docs/migrating-from-anthropic-enterprise.
- What license does Pleach use? Is it open source?
- Fair source — FSL-1.1-Apache-2.0. Source-available, free in production. Each release converts to Apache-2.0 two years after it ships. Not OSI-approved open source during that window.