This page covers the
snapshot/restore/lineage state-replication system. It does not apply to store/recall semantic memory, which has no sequencing or lineage concept — see Memory Model for how the two systems differ.Sequence numbers, not content hashes, define order
Eachagent_id has its own independent sequence starting at 0. The server enforces order at write time: a snapshot() call’s sequence_number must equal latest confirmed sequence for this agent + 1, or it’s rejected with 409 sequence_gap. The very first snapshot for a new agent must be sequence_number: 0 with parent_snapshot: null — this genesis check is what proves a chain has no missing prefix.
(agent_id, sequence_number) with an unchanged client_payload_hash is idempotent — the server returns the original receipt rather than erroring, which is what makes replay-after-crash safe. The same sequence number with a different hash is a real conflict (409 sequence_conflict): two different payloads are trying to claim the same position, and the server refuses to silently pick a winner.
Point-in-time recovery: restoreAt, not rollback
There is no rollback() call that moves a “current state” pointer. Instead:
restore({ agentId })fetches the latest confirmed checkpoint.restoreAt({ agentId, sequence })fetches the checkpoint at any specific sequence number in that agent’s history — including ones that are no longer the latest.
restoreAt with that number — nothing is deleted, nothing needs to be un-done. Nothing about calling restoreAt on an old sequence affects what restore() returns afterward; the server’s “latest” pointer only ever moves forward via new snapshot() calls.
”Branching” means a new agent_id
sovseal has nofork() primitive. If you want two independent lines of state descending from the same point, the pattern is: restoreAt the checkpoint you want to branch from, decrypt and verify it (see Verified Semantic Recall), then call snapshot() for that payload as sequence 0 under a new agent_id. The two agent IDs then have fully independent sequence chains — there’s no shared ancestry tracked by the server beyond whatever you encode into the payload yourself.
Every restore is verified, not just fetched
restore/restoreAt return { receipt, ciphertextUrl } — metadata and an encrypted blob, not verified plaintext. The receipt’s client_payload_hash is an anchor you check yourself: decrypt, re-derive sha256(canonicalize(payload)), and compare. A mismatch means either the ciphertext was tampered with (the AES-GCM auth tag catches this) or a different valid record was substituted in its place (the hash comparison catches this, because the hash covers the whole payload including its sequence_number and parent_snapshot). See Verified Semantic Recall for the full two-check pattern and a ready-to-use implementation — this is not automatic, you must run it.
Crash safety and conflicting writers
Local durability and replication crash-safety are properties of the write-behind sync worker, not of the SDK’s snapshot/restore calls themselves — see Replication & Sync for the full mechanics: the local LanceDB write-ahead log, the sync worker’s poll/retry loop, and what happens when two writers collide on the same sequence number (a split-brain halt, not an automatic merge — sovseal refuses to guess which version is correct).Next steps
Memory Model
The full record shape for both semantic memory and state snapshots, and the complete SDK method table.
Verified Semantic Recall
The tamper/substitution check you run on every restore — with working code.
Replication & Sync
The write-behind worker: batching, retry, idempotent replay, and split-brain handling.
Zero-Knowledge Guarantees
How lineage integrity fits into the broader cryptographic threat model.