Operations

Backup and restore

Portable, cryptographically verifiable backups. Online capture, offline restore, and integrity refuse on any file drift. The same shape is available on managed cloud and on self-hosted deployments.

Guarantees at a glance

Online capture

Writer stays online during backup. Concurrent reads continue against the checkpointed view.

Cryptographic lineage

Every archive ships a manifest that hashes every file the archive should restore to.

Integrity refuse

Restore fails hard on any file drift. There is no "restore with a warning" path.

Full audit trail

The append-only history log is included, so the bitemporal audit chain survives restore end-to-end.

Managed cloud

On managed cloud, backups run for you. The signup tier and pricing page describe the current cadence and retention window; the shape below is what the platform commits to regardless of tier.

Cadence
Nightly automated per-graph backup, plus continuous durability of every commit. Manual on-demand backup is available from the dashboard and from the API at any time.
Storage
Archives are stored in versioned object storage in the graph's home region, encrypted at rest with a per-account key.
Restore SLA
Restore is initiated from the dashboard or the API. Small graphs (under a few GB of history) restore within minutes; larger graphs restore within the window your contract commits to. If your workload has a hard RTO, request the number in writing during onboarding and it will be on the order sheet.
Retention
Per-tier retention window applies to automated backups. Manual backups are retained until you delete them or until your account is closed. Retention is a policy on the archive index, not on the archives themselves — extending retention on a specific archive is a support ticket.
Verification
Every archive is re-hashed after write and before any restore. Managed cloud runs a periodic sample verify so drift is caught long before you ever ask to restore.
Isolation
Backups are per-graph. Restoring one graph never touches another, and one account's archives are never visible to another account.
Honest posture: managed cloud restore SLAs are contract terms, not marketing terms. If your workload has a specific recovery-time objective or recovery-point objective, tell us during onboarding and we'll write it into your agreement. We do not publish a blanket "RTO of N minutes for every graph" number because the honest answer depends on the size of the history you are restoring.

Trigger a backup from the dashboard

Open the graph, click Backups, and click Take backup now. The archive appears in the list when it lands in object storage. Each entry shows the point-in-time timestamp, the archive size, and the lineage hash.

Trigger a backup over the API

The endpoint below is the same on managed cloud and on self-host. On managed cloud, output_path is optional — if you omit it the archive is written to the managed backup store and returned in the list of managed archives. If you pass a presigned_put_url, the archive is streamed straight into your own bucket.

Self-host

On self-host you own where the archives land. The engine exposes one endpoint for backup and one for restore. Both are authenticated as admin operations.

POST /graphs/{name}/backup

Takes a live graph and writes a portable archive. The writer stays online; a short flush checkpoint captures a consistent view of the graph, then the archive streams out while ordinary reads continue against the checkpointed state.

Request body — JSON:

{ "output_path": "/var/backups/invariantdb/my-graph-2026-07-10.tar.gz" }

Response — JSON on success, HTTP 200:

{ "output_path": "/var/backups/invariantdb/my-graph-2026-07-10.tar.gz.gz", "bytes": 4183429120 }

Archive shape. The archive is a gzipped tar stream. It contains:

The archive is portable. It can be moved between machines, uploaded to cold storage, or restored into a different self-host deployment or into managed cloud — the format does not depend on the machine that produced it.

POST /graphs/{name}/restore

Restore is offline for the target graph name and creates a new graph. It refuses if the target name already exists, so you cannot silently clobber a live graph. To restore over an existing graph, restore under a new name first and then swap.

Request body — JSON:

{ "input_path": "/var/backups/invariantdb/my-graph-2026-07-10.tar.gz" }

Response — JSON on success, HTTP 200:

{ "name": "my-graph-verify", "current_snapshot_seq": 4821 }

What restore does. The engine walks every file in the archive, re-hashes it, and compares against the lineage manifest. Any mismatch is a hard error and the graph will not open. Once the archive verifies, any in-flight recovery frames present in the archive replay automatically, so the graph opens at exactly the state the backup captured.

Failure modes are typed. If the archive is truncated, if the lineage manifest is missing, if a file hash drifts, or if the target name already exists, the response is a stable, subcoded error. Callers can branch on the subcode without parsing English strings; see the Cypher reference and stable error contract.

Bitemporal history is included

InvariantDB is a bitemporal database. Every write records both when it was true (valid time) and when the system learned about it (recorded time). That history lives in an append-only log inside the graph. It is the source of truth for AT VALID, AT RECORDED, and OVER VALID BETWEEN queries, and for tamper-evident audit chain verification.

Every archive contains the full history log. Restore reproduces the full audit trail end-to-end — not just the current snapshot. After a restore:

An archive that would not reproduce the audit chain byte-for-byte is refused at write time, so a corrupt archive never reaches your object store.

Point-in-time restore across the bitemporal axes is served at query time with AT VALID and AT RECORDED. Backup + restore is the durability substrate underneath. You do not need to restore an old archive to see what the graph looked like last Tuesday — you query the live graph with AT RECORDED <last Tuesday>. See Bitemporal.

Snapshot chain

The graph on disk is a chain of immutable, versioned snapshots. Each snapshot is a full point-in-time view. Between snapshots, the engine reuses unchanged data across versions so on-disk footprint stays compact even as history grows.

A backup archive preserves that chain end-to-end. Every archive is a full, self-contained point-in-time snapshot — not a delta against a previous archive. Two consequences for operators:

Restore reconstructs the chain on the target machine, then opens the graph against the current snapshot. Prior snapshots remain readable, so any AT RECORDED query that ran before the backup runs against the restored graph too.

Encryption at rest

On managed cloud, every archive is encrypted at rest with a per-account key managed by the platform. Encryption is transparent — the archive is decrypted when you initiate a restore, and never leaves the platform in cleartext.

On self-host, encryption at rest is the operator's responsibility for the archive itself. The engine writes the archive to output_path; wrap that path in an encrypted volume, encrypted object storage, or a pre-signed upload to an encrypted bucket, and encryption at rest is covered.

Cryptographic erasure — the GDPR Article 17 mechanism where a subject's data becomes unrecoverable across every archive by destroying the per-subject wrapping key — applies to backups by design. Once db.destroySubjectKey runs, that subject's encrypted properties are irrecoverable in every archive that was written before or after the destruction, without touching the archives themselves. See Crypto-shred.

Retention

Retention is a policy on the archive index. There are three ways to set it:

Retention on self-host is entirely operator-controlled — the engine writes archives to the path you specify and never deletes them.

Cross-region disaster recovery

Cross-region DR is not yet productized as a self-service feature. If your workload requires cross-region failover with a specific RTO and RPO, ask about it during onboarding — we run cross-region topologies for production customers today, and the SLA lives in your contract, not on this page.

What is available today:

Backup + restore + verify roundtrip

End-to-end example against a self-host deployment. The same shape works against managed cloud — substitute your endpoint and API key.

1. Take a backup

# Take a backup of the graph. The writer stays online. curl -fsS \ -H "Authorization: Bearer $ADMIN_API_KEY" \ -H "content-type: application/json" \ -X POST https://invariantdb.com/graphs/my-graph/backup \ -d '{"output_path":"/var/backups/invariantdb/my-graph-2026-07-10.tar.gz"}'

The response reports the bytes written. The archive itself carries the lineage manifest as its verification anchor.

2. Restore under a new name

# Restore into a new graph. Fails if my-graph-verify already exists. curl -fsS \ -H "Authorization: Bearer $ADMIN_API_KEY" \ -H "content-type: application/json" \ -X POST https://invariantdb.com/graphs/my-graph-verify/restore \ -d '{"input_path":"/var/backups/invariantdb/my-graph-2026-07-10.tar.gz"}'

A 200 with a returned current_snapshot_seq means every file in the archive re-hashed to the value in the lineage manifest and the graph is open at that snapshot. Any drift is a hard error; the graph is not created.

3. Verify the restored graph

Step 2 already did this. The engine walked every file in the archive, re-hashed it against the lineage manifest, and refused to publish the graph if any file drifted. A 200 on /restore means integrity verified end-to-end. For an ongoing check, schedule a restore-into-scratch on a cadence that matches your RPO — the restore path is the honest verifier.

4. Verify the audit chain survived

Because the archive included the full history log, the tamper-evident audit chain over the restored graph is byte-identical to the original. Re-verify with a single procedure call:

CALL db.verifyAclChain() YIELD status, entries, brokenAtEventId, reason

status should read clean, and entries should count every ACL audit event that had been recorded when the backup was taken. brokenAtEventId and reason are null on a clean chain; on a tampered chain they carry the event where verification failed.

5. (Optional) Roundtrip a specific belief

To prove a specific decision survived the roundtrip, walk the belief lineage on both graphs and compare:

CALL db.beliefLineage("belief-99") YIELD beliefId, claim, confidence, createdAt, supersededAt, depth, evidence RETURN beliefId, claim, confidence, createdAt, supersededAt, depth, evidence

Same output on my-graph and on my-graph-verify. That is the practical demonstration that the archive preserved the full bitemporal and provenance trail.

Backup + restore is single-graph, single-node disaster recovery. Multi-region and quorum-replicated topologies for continuous availability live on the high-availability cluster page. Detailed operator procedures ship with your production contract.