Docs
Home GitHub npm

Incremental Snapshots

Like git — only captures what changed. Full version history, minimal storage.

How It Works

Instead of storing a full copy every time, incremental snapshots compute a delta against the previous snapshot and store only changed files.

                       Full chain:
base (full)delta-1delta-2delta-3base (full)delta-4 → ...
                                               
  chain depth 0                              auto-forced at depth 10

The delta process

  1. Hash current state: SHA-256 hash every file in the new snapshot
  2. Load parent hashes: Read the content hashes from the previous snapshot's delta manifest (or compute them for full snapshots)
  3. Compare: Identify which files are added, modified, or removed
  4. Store delta: Only save the changed and added files, plus a delta manifest listing all changes
Incremental snapshot output
$ savestate snapshot
 Platform detected: clawdbot
 Incremental snapshot created!
  Changes: +3 added, ~5 modified, 42 unchanged
  Saved:   847 KB vs full snapshot
 Encrypted & stored (12.4 KB)

Auto-Detection

Incremental snapshots are automatic. When you run savestate snapshot:

  1. SaveState checks if a previous snapshot exists
  2. If yes, it loads the parent's content hashes and computes a delta
  3. If no previous snapshot exists (first run), it creates a full snapshot

You don't need to do anything special — incrementals are the default behavior.

Forcing a Full Snapshot

Use the --full flag to bypass incremental detection and create a complete snapshot:

$ savestate snapshot --full

Full snapshots are also automatically forced when:

Chain Depth

ConstantValueMeaning
MAX_CHAIN_DEPTH10Maximum deltas before forcing a full snapshot
FULL_SNAPSHOT_THRESHOLD0.7 (70%)If this fraction of files changed, force full

This ensures that restoring any snapshot never requires walking more than 10 chain links — keeping restore time fast even with hundreds of snapshots.

Delta Manifest

Incremental archives contain a meta/delta-manifest.json with full change tracking:

{
  "parentId": "ss-2026-01-26T09-30-00-b7c1m4",
  "baseId": "ss-2026-01-20T00-00-00-x1y2z3",
  "chainDepth": 3,
  "resultHashes": {
    "files": {
      "identity/personality.md": "sha256:a1b2c3...",
      "memory/core.json": "sha256:d4e5f6..."
    },
    "count": 47,
    "rootHash": "sha256:..."
  },
  "entries": [
    { "path": "memory/core.json", "type": "modified", "hash": "sha256:...", "size": 4096 },
    { "path": "identity/skills.json", "type": "added", "hash": "sha256:...", "size": 1024 },
    { "path": "identity/old-config.json", "type": "removed" }
  ],
  "stats": {
    "added": 1,
    "modified": 1,
    "removed": 1,
    "unchanged": 44,
    "totalFiles": 46,
    "bytesSaved": 867328
  }
}

Delta entry types

TypeMeaningFile in archive?
addedNew file not in parentYes — full content included
modifiedFile exists but content changed (different SHA-256)Yes — full new content included
removedFile was in parent but not in currentNo — just the path in the manifest

Reconstruction

When restoring from an incremental snapshot, SaveState reconstructs the full state by walking the chain:

Reconstruction process:

1. Walk backwards from target snapshot to find the base (full) snapshot
2. Load the base snapshot → full file map
3. Apply each delta in order:
      a. Add/overwrite files from the delta
      b. Remove files marked as 'removed'
4. Result: complete reconstructed state

Example chain walk:
basedelta-1delta-2target
                                
47 files  +2, ~3     ~1, -1    +1
                                   = 49 files

This is transparent — you just run savestate restore and it handles the chain walking automatically.

Content Hashing

Every file is hashed with SHA-256 for change detection:

interface ContentHashes {
  files: Record<string, string>;   // path → SHA-256
  count: number;                    // total file count
  rootHash: string;                 // hash of all hashes (quick comparison)
}

The rootHash is computed by sorting all path:hash pairs and hashing the result — a quick way to check if anything changed at all before computing the full delta.

💡 The resultHashes field in the delta manifest stores the hashes of the full state after applying the delta. This means the next incremental snapshot can compare directly without needing to reconstruct.