Knowledge Base Persistence
Learn how to save and restore your knowledge base across server restarts using snapshots and automatic journal recovery.
Overview
zpm persists your knowledge base using two mechanisms:
- Write-Ahead Journal (WAL) — Every fact assertion or retraction is logged before being written to memory, ensuring durability
- Point-in-Time Snapshots — Named snapshots capture the full knowledge base state at a moment in time
The server automatically recovers from the most recent snapshot plus subsequent journal entries on startup, preventing data loss.
Create a Snapshot
Use save_snapshot to create a named checkpoint of your knowledge base:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "save_snapshot",
"arguments": {
"name": "backup_2026_04_13"
}
}
}Before publication, zpm writes the checkpoint to a temporary file and loads that closed file in a fresh Prolog engine. The snapshot is renamed into place only after it passes this validation. After a successful snapshot:
- The journal is rotated and eligible archived journals are cleaned up
- You can restore to this exact state later
- The snapshot uses human-readable Prolog source format
If serialization or fresh-engine validation fails, zpm removes only the temporary candidate. The previous checkpoint and active journal remain in place, so the failed save does not destroy the last recoverable state.
List Available Snapshots
Query all available snapshots to see what restore points exist:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list_snapshots",
"arguments": {}
}
}Response includes snapshot names, timestamps, and sizes to help you choose which to restore.
Restore from a Snapshot
Use restore_snapshot to return to a previous state:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "restore_snapshot",
"arguments": {
"name": "backup_2026_04_13"
}
}
}The restore process:
- Loads the snapshot, replacing the current knowledge base
- Asserts all facts from the snapshot as immediately queryable predicates
- Replays all journal entries recorded after the snapshot
- Returns to the exact state including any facts added since the snapshot
Important: All predicates in the snapshot are loaded as dynamic, so they are fully accessible via query_logic and reflected in get_knowledge_schema counts immediately after restore. The snapshot mechanism uses human-readable Prolog source format with explicit :- dynamic(F/A). directives to ensure reliable round-trip behavior.
This enables point-in-time recovery without losing recent changes.
Monitor Persistence Status
Check the status of your persistence layer (journal size, last snapshot, operational mode):
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "get_persistence_status",
"arguments": {}
}
}Status includes:
journal_size— Current write-ahead log size in byteslast_snapshot— Name and timestamp of the most recent snapshotmode— “normal” (persistence active) or “degraded” (persistence unavailable, in-memory only)
Degraded Mode
If the persistence directory is not writable, zpm starts in degraded mode:
- The server runs normally with all tools available
- Facts and rules are held in memory only
- No snapshots or journal entries are created
- On restart, the knowledge base starts empty
Once the persistence directory is writable again, the server automatically returns to normal mode.
Manual Inspection
Snapshots and journal entries use human-readable Prolog source format, so you can inspect them manually:
% Example snapshot (Prolog code)
fact(a).
fact(b).
rule(X) :- fact(X), other_fact(X).
% Example journal entry
%% 1712973249000 assert new_fact(value).
%% 1712973250000 retract old_fact(value).This human-readable format enables:
- Easy debugging and auditing
- Manual fact correction if needed (after making a byte-for-byte backup)
- Integration with standard Prolog tools
Recover an Invalid Snapshot Safely
ZPM v0.4.1 could write an invalid :- dynamic(...) declaration when a fact
argument contained : or :-. A later process may fail to mount that memory
with LoadFailed. The fix protects newly generated snapshots; it does not
rewrite snapshots already on disk.
Stop all zpm processes using the project before attempting a repair. Copy the complete affected memory directory to a safe location and verify the copy:
memory_dir=".zpm/kb/feature_auth" # Replace with the affected memory path
backup_dir="/safe/recovery/location/feature_auth-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
cp -a "$memory_dir/." "$backup_dir/"
diff -qr "$memory_dir" "$backup_dir"Inspect the affected snapshot for malformed :- dynamic(...) declarations
and compare them with the fact heads. For example, a snapshot containing
adr(boundary, 'Domain', 'Keep std::fs outside domain'). needs the declaration
:- dynamic(adr/3)..
Repair only the generated declaration in a separate copy of the project.
Leave the fact clauses, other snapshots, knowledge.pl, and WAL unchanged.
Start zpm in that copy, query each affected predicate, and check that the
expected facts appear exactly once. If the copy mounts successfully, apply
the same declaration-only repair to the original snapshot while zpm remains
stopped, then restart and verify again. Keep the byte-for-byte backup for
rollback. Do not delete the knowledge-base directory: the active journal may
contain mutations made after the snapshot.