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-1 → delta-2 → delta-3 → base (full) → delta-4 → ...
↑ ↑
chain depth 0 auto-forced at depth 10
The delta process
- Hash current state: SHA-256 hash every file in the new snapshot
- Load parent hashes: Read the content hashes from the previous snapshot's delta manifest (or compute them for full snapshots)
- Compare: Identify which files are added, modified, or removed
- Store delta: Only save the changed and added files, plus a delta manifest listing all changes
$ 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:
- SaveState checks if a previous snapshot exists
- If yes, it loads the parent's content hashes and computes a delta
- 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 reaches 10: After 10 consecutive deltas, a full snapshot is created to keep the chain short and restore fast
- Change ratio exceeds 70%: If more than 70% of files changed, a full snapshot is more efficient than a delta
- No parent found: First snapshot or parent can't be loaded
Chain Depth
| Constant | Value | Meaning |
|---|---|---|
MAX_CHAIN_DEPTH | 10 | Maximum deltas before forcing a full snapshot |
FULL_SNAPSHOT_THRESHOLD | 0.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
| Type | Meaning | File in archive? |
|---|---|---|
added | New file not in parent | Yes — full content included |
modified | File exists but content changed (different SHA-256) | Yes — full new content included |
removed | File was in parent but not in current | No — 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:
base → delta-1 → delta-2 → target
↓ ↓ ↓ ↓
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.
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.