What it is
Every node mutation in the graph carries a _prev_hash property: the SHA-256 of the previous version's serialized form. New version's _hash = SHA-256(prev_hash + this_version_payload). The chain is verifiable end-to-end.
What this gets you
Tamper-evidence
Anyone with read access can verify the chain. CALL db.verifyAuditChain(siteId) walks every node's history and reports broken links. A successful walk is cryptographic proof that nothing was retroactively edited.
Compliance-grade exports
The export bundle includes the full chain. An auditor can verify offline without trusting our infrastructure.
Forensic queries
"Show me every change made to type X between time T1 and T2, by user Y" → db.changes(t1, t2, label) filtered by actor.
Late corrections without rewriting history
A retroactive edit is a separate operation that preserves the original. db.correctValidFrom(nodeId, newValidFrom) creates a new version that supersedes; the original stays in the chain. Auditors see both.
Run the verifier yourself
The audit chain is only meaningful if you can verify it without trusting us. So we ship the verifier as a customer-runnable binary:
$ npx @staticowl/verify-audit-chain --graph my-site
✓ chain status: CLEAN
records walked: 4,865
last sequence: 4,865
graph: my-site
endpoint: https://invariantdb.com
The chain has been walked end-to-end. Every mutation's
hash was successfully verified against the prior entry.
No tampering detected.
$ echo $?
0
How it works: the binary calls GET /graphs/<graph>/audit/verify on your engine endpoint, with read-only credentials. The engine walks every entry in the WAL, recomputes each hash against its predecessor, and returns a clean / broken status. The binary formats the result and exits accordingly.
Exit codes (for CI integration)
0— chain is clean. Use this in CI to gate releases or deploys.1— chain is broken (tampering or corruption) OR an upstream error. Alert immediately.2— usage error (missing required flag).
JSON output for tooling
Pass --json to get a machine-readable payload instead of human-readable text:
$ npx @staticowl/verify-audit-chain --graph my-site --json
{
"status": "clean",
"records": 4865,
"last_seq": 4865
}
Pipe it to jq, ship it to your SIEM, alert on .status != "clean". Same exit-code semantics; the JSON is the truth, the human form is the courtesy.
Where to get it
Today: the source is in the StaticOwl repo at bin/verify-audit-chain.mjs — a single Node ESM file, no external dependencies beyond node:url. Customers can curl it down and run it directly with Node 20+. The @staticowl/verify-audit-chain npm package will publish in the next sprint so the npx form above works without a download step.
Run it against any of your graphs
The verifier doesn't care which graph it's pointed at, and you don't need elevated credentials. An API key with audit:read capability is enough. Wire it into your CI/CD or your scheduled SOC checks; treat a non-zero exit the way you'd treat any other tamper alarm.
Combined with WORM mode
WORM (write-once-read-many) mode marks specific content types as immutable once published. Combined with the audit chain: published content cannot be edited (WORM) AND cannot be tampered with (chain). Two independent mechanisms; defense in depth.
What it doesn't do
- It's not a blockchain. There's no distributed consensus; the chain is a Merkle-link, not a ledger.
- It's not a substitute for backups. Use the per-graph S3 backups + DLM EBS snapshots.
- It's not the whole compliance story — you also need the access log, WORM, and exportable bundles. We ship all four.