Skip to main content
Verified Semantic Recall (VSR) is a verification pattern applied on every restore from the replication endpoint. It catches two distinct classes of attack that a single cryptographic primitive cannot cover alone: bit-level ciphertext tampering, and wholesale record substitution.
VSR is not automatic. Neither @sovseal/sdk nor sovseal-sdk runs this check for you or throws a dedicated error class — there is no VsrFailureError. The SDK gives you the primitives (decryptJson, canonicalize, CryptoService.sha256Hex) and the anchor (receipt.client_payload_hash); you perform the comparison and decide what “fail closed” means for your agent. This is deliberate: your incident response is yours to own, not the library’s to guess at.

What the pattern checks — and why you need both

Two independent verifications on every record restored from the server:
  1. AES-256-GCM authentication tag. decryptJson verifies the ciphertext bytes are exactly what was written — any bit flip, truncation, or extension causes it to throw.
  2. Re-derived hash comparison. Re-run sha256(canonicalize(payload)) on the decrypted result and compare it to receipt.client_payload_hash, the anchor the client computed at write time over the full payload object (which itself includes agent_id, sequence_number, parent_snapshot, policy_hash, active_context, and timestamp). This catches what the auth tag alone cannot: a malicious server returning a different record you wrote earlier — one that decrypts perfectly but belongs to the wrong point in your lineage.
The auth tag proves the bytes weren’t altered. It does not prove those bytes are the record you asked for. A server could swap in an older, still-validly-encrypted snapshot and the tag would pass. The hash comparison is what catches that — because client_payload_hash is computed over the entire payload, including parent_snapshot, so a substituted record’s re-derived hash will not match the one issued at its original write.

The checks, end to end


Attack and caught-by table


Implementing the check

Wrap every call site that restores state with this pattern — or a shared helper like the one above — rather than calling client.restore() directly and trusting the result.

Where this applies


Limits — what this does not protect against

This is a network-layer defense. It cannot protect you from the following, which need separate mitigations:
  • Compromised device. If your AES-256 key is exfiltrated, the checks still pass — an attacker with the key can forge valid records. Protect key material via the OS keychain and restrict physical access.
  • Weak or guessable passphrase. Key derivation is only as strong as the secret it derives from.
  • Freshness. This confirms a record is exactly what was written — it does not confirm it is the most recent write for that agent. Use sequence_number and lineage walking for freshness guarantees.
  • Denial of service. A malicious server can simply refuse to return your records. The check doesn’t prevent that; it only ensures that if you do get records back, they are authentic.

Operational guidance

Treat a verification failure as a security incident, not a transient error. It means either the replication endpoint is compromised or your local state disagrees with what the server holds. Do not silently retry.
  • Quarantine, don’t retry. Retrying rarely helps and may amplify damage if the server is actively malicious.
  • Log both hashes. When the comparison fails, log the expected hash and the one on the receipt — that delta is your forensic evidence.
  • Wire it into your own observability. There is no built-in event to subscribe to; raise your own alert from the catch block above.

Next steps

Zero-Knowledge Guarantees

The broader cryptographic contract this pattern operates inside, including the formal threat model.

Deterministic Lineage

Why parent-pointers make substitution structurally detectable.

Cryptographic Trust Center

Consolidated threat model, key custody boundaries, and honest compliance posture.

AES-256-GCM

Primitive-level detail on the authentication tag and nonce generation.