Skip to main content
Sanning holds a commitment to what your agent did. It never holds the thing itself. This page covers the half you keep, because a commitment to bytes nobody kept cannot be checked later. The log store is the SDK’s answer to that. Configure one and the retention is handled for you, in a layout the hand-over pack and an auditor can both read.

What the store holds

Two things, written before the event is anchored:
  • The content you passed to anchor, which was hashed in your process and never sent.
  • The canonical event record, the bytes payload_hash commits to. In TypeScript they are also on the result as recordBytes, in Python as record_bytes.
If the store write fails, nothing is anchored. There is no best-effort mode, because under this write path nothing else holds those bytes. The layout is a versioned contract named sanning.logstore/v1. A store root looks like this:
Anyone holding that directory can resolve an event’s bytes with no connector and no call to Sanning.

Configure a store

Pass a store when you build the anchorer. FsLogStore writes to a local directory and is the development destination.
Anchoring in production with no store configured is a warning, not a silent success. Both SDKs say the same thing: the bytes this commitment names are not being retained, and the anchor front can refuse the envelope. If you drain the bytes yourself instead, keep them keyed by event id. They are what an auditor’s check runs against.

Why the pack command reads the store

A hand-over pack is assembled by the bundle command, months after the run. It reads the proofs from the Sanning index and the bytes from your store.
Replace CONSOLE_API_KEY with the key from the console. There is no flag for it, because a credential in argv lands in shell history and this is a command people paste into tickets. The flags mean the same in both languages: --logs is optional in the flag grammar and required in practice. The verb needs your record objects to work out which events are this agent’s: subject.producer_id lives in the record, and a minimum-disclosure envelope does not carry it. Without the store the command exits 2 and says so. The verb verifies the whole pack, bundle and disclosed logs, before it writes anything, so a pack that would fail at the auditor is never produced.

When a pack can be built

Sanning commits your records to the public record on an interval, rather than one transaction per event. Until the interval holding your records is sealed, there is no inclusion proof to put in a pack, and the command tells you so:
That means not yet, rather than you have no evidence. A second line names how many records in the window are anchored and waiting for a stamp. Those records exist, they are signed, and they are retained. Read the second line before treating the first as a failure. Both languages print that pair, and the command exits 1.

Store the bytes in a bucket you own

For production, put the store behind object storage you control. The seam is the same in both languages: an object store inside a log store.
It is your bucket and Sanning never sees it. The store takes your own client, so any credential arrangement works, and an endpoint with path-style addressing makes S3-compatible backends behave alike. On first use the store checks the bucket once, before it writes a byte, and reports what it could not establish rather than assuming. A bucket that already exists cannot have Object Lock turned on afterwards, so the check tells you to create a fresh one instead of sending you to a console page that cannot help.
A locator points at where the anchored bytes live, and it rides inside the signed envelope, which is published permanently. Bucket names and object keys routinely carry tenant, agent and model names, so treat anything that can reach a locator as public. A ref or payloadRef you pass yourself is signed verbatim.

Sync a bucket down before you build a pack

The bundle command reads a local store root. It refuses an s3:// argument rather than pretending to read one: it exits 2 and tells you to sync the store down and point --logs at the local copy. Reading the store over S3 directly is a known gap. Copy the prefix down with your provider’s own CLI first.

API keys and scopes

The scope the pack command needs, and what a missing one looks like.

Dev and production

What production means, and why a pack carries one environment.