Tools
trusty-memory: back up, restore and upgrade a palace
This guide covers backing up one palace, restoring it, upgrading across a palace format change, and rolling back. The feature is tracked in #9282. The compatibility rules behind it are in ADR-0066 and ADR-0067.
Make a backup
Run trusty-memory palace backup <NAME>, where <NAME> is the palace id.
trusty-memory palace backup my-palace
trusty-memory palace backup my-palace --dest before-upgrade
The command writes a consistent backup of one palace. It goes through the running daemon when one serves this data root. The palace stays live and writers are not paused.
With no daemon, the command opens the palace directly. That open is not
read-only. It takes the exclusive redb lock on kg.redb until the backup ends,
so a daemon started meanwhile cannot open the palace. It never migrates. A
palace in an older format is refused. Start the daemon, which migrates it, and
back up through it.
An MCP client can call the tool palace_backup instead. It takes palace
(required unless the server was started with --palace) and the optional
dest described below.
Where backups go
Backups go under the palace's backup root:
<data root>/.trusty-memory/backups/manual/<data root>/backups/manual/when palaces live under<data root>/palaces(the legacy layout)
The default backup name is <palace>/<UTC timestamp>.
Choose a name with --dest
--dest <NAME> is the relative path of the backup directory to create under
the backup root. The rules:
- Use plain names only.
- An absolute path,
..,., an empty segment or a symlinked segment is refused. No backup is written and the command exits1. - The directory must not exist.
What a backup holds
A backup copies:
kg.redbchat_sessions.redbpalace.jsonidentity.txt- the deletion journals
- this palace's
drawer_counts.jsonllines
It then writes manifest.json with row counts and a SHA-256 per file.
The vector index is not backed up. A restored palace is re-embedded (see Restore a backup). Backups are never pruned. Delete old ones yourself.
Restore a backup
Restore is offline only. Stop the daemon that serves this data root first.
trusty-memory stop has no flag to pick a daemon. Without
TRUSTY_DATA_DIR_OVERRIDE, it stops every trusty-memory daemon you run,
including the one launchd supervises on macOS. Every MCP client that uses
trusty-memory loses its memory tools until the daemon is back. It spares the
per-session stdio bridges. With TRUSTY_DATA_DIR_OVERRIDE set, it stops only
the daemon serving that data root's socket.
trusty-memory stop
trusty-memory palace restore <SNAPSHOT> [--palace <ID>]
After the restore, run trusty-memory start. Under launchd on macOS, run
trusty-memory service start instead. The hint that palace restore prints
names trusty-memory start in every case.
<SNAPSHOT> is a backup directory written by palace backup. It holds
manifest.json. --palace names the palace to restore. It defaults to the id
in the backup's manifest.
Restore fails closed. It refuses when:
- a daemon is serving this data root, or the command cannot tell whether one is;
- the backup is of another palace than
--palace; - the palace id in the backup's manifest is invalid;
- the backup is in a newer palace format than this binary supports.
Restore verifies every checksum and row count before it writes anything. It
writes only into an empty palace directory, so it never replaces a palace. The
target is <data root>/palaces/<id> when <data root>/palaces exists (the
legacy layout), otherwise <data root>/<id>. TRUSTY_DATA_DIR_OVERRIDE is
honoured.
Re-embed after a restore
The backup has no vector index. Start the daemon, then call the MCP tool
palace_reembed with dry_run=false. In that call, missing is the count
before the repair, so it can still be above zero next to a matching repaired.
Read repaired. Then call palace_reembed again, with dry_run=true or
false, and confirm it reports missing: 0. Repeat until it does. Recall
finds the restored drawers only after that.
Recover from a crash during restore
A crash can leave a directory named .<id>.restore.tmp next to the target
palace directory. Restore never reuses an existing one, so the next restore
fails until you remove it. Stop the daemon, then remove that directory. Run
the restore again.
Upgrade across a palace format change
Which release writes which format
| Release | Palace format |
|---|---|
| 0.32.x and older | 0 |
| 0.33.x | 1 |
| 0.35.0 and later | 2 |
Every release from 0.35.0 reads format 2. From 0.36.0 (unreleased; ships with trusty-mpm 1.9.0), a binary migrates a palace at format 0 or 1 in one chain, with no intermediate release (ADR-0067, amendment 2026-10-15). 0.35.0 migrates only format 1: a format-0 palace needs the two-step path in Upgrading from 0.32.x or older to 0.35.0. A palace in a newer format than the binary supports is refused, not opened.
Automatic migration
The daemon migrates a palace when it makes its next writer open of it, which is at daemon start for every palace. Before the first step, it writes a verified copy of the palace's primary files and aborts the migration if that copy fails. You do not need to run anything.
A read-only open of an older palace (the CLI, trusty-memory serve --stdio) is
refused with FormatNeedsMigration until the daemon has migrated it. The
format 1 to 2 step is on derived data and rewrites no row.
Migrate by hand with palace migrate
trusty-memory palace migrate runs the same chain as the daemon, as a step you
can preview and check. It is not in trusty-memory 0.35.x. It ships in 0.36.0.
trusty-memory palace migrate my-palace --dry-run # preview; writes nothing
trusty-memory palace migrate my-palace # backup, convert, check
trusty-memory palace migrate --all # every palace
trusty-memory palace migrate --all --yes # confirm more than one change
--dry-runprints one row per palace: its status (current,needs migration,interrupted,unverifiedorrefused), the format found, the target, the size, the space the backup needs, the free space and the planned backup directory. It then prints how many palaces would change.- A plain run takes the verified backup, runs each format step, then runs the
post-checks below. It prints the backup directory, the drawer and triple
counts before and after, and
checks:orFAILED:lines. - A palace already at the current format is left alone. No backup is taken.
- An interrupted migration resumes from the format
kg.redbholds. --allprints the dry-run table first and checks the total free space. It refuses without--yeswhen more than one palace would change, and changes nothing. It continues past a palace that fails and names each one that failed or was refused.
The post-checks verify that:
- the format status is
current; - the
kg.redbmarkers (palace_formatandtriple_key) and thepalace.jsonformat_versionagree at the expected value; - no
format_migration.jsonis left; - the drawer and triple counts equal the counts before the migration.
A failed check makes the command exit non-zero. The message names the palace, the gap and the verified backup, and states that nothing was deleted.
The command goes through the running daemon when one serves this data root. With no daemon, it opens the palaces itself, offline. A daemon's answer is final: a failed call is never retried offline.
After cargo install, a daemon that was already running is the old release. It
does not know palace_migrate, and the command says so. Restart the daemon on
the new release (trusty-memory stop, then trusty-memory start), which
migrates its palaces at its next writer open. Or stop it and run the command,
which then migrates offline.
trusty-memory doctor prints a needs migration warning for each palace that
is behind. The warning names the command and the backup root.
Palaces that tm owns
The Session Manager and portfolio palaces belong to the tm daemon. Do not run
palace migrate on them. Restart the owning daemon (tm daemon run). It
migrates them at its next open.
Where the migration backups live
The backup directory is
<data root>/.trusty-memory/backups/format-migration/<palace>/<from>-to-<to>-<UTC>/,
or <data root>/backups/format-migration/<palace>/… in the legacy layout. A
chain from format 0 writes one 0-to-2-<UTC> directory, taken before its first
step.
- It holds the palace's primary files:
palace.json,identity.txt,kg.redb,chat_sessions.redband the two deletion journals. It also holds aMANIFESTfile with the length and SHA-256 of each. - The two newest complete backups per palace are kept. Only you delete a backup.
- It does not hold the vector index (
index.usearch.redb). The index is derived data, rebuilt from the drawers. After a rollback, re-embed (see Roll back). - It is not a
palace backup. It hasMANIFEST, notmanifest.json, andpalace restoredoes not read it.
Check a palace you converted by hand
trusty-memory palace migrate my-palace --verify
trusty-memory doctor
--verify writes nothing. It runs the post-checks on any palace and prints
one block per palace. It reads kg.redb read-only, or through a temp copy when
another process holds the file (the line ends (read from a copy)).
| Line | Meaning |
|---|---|
kg_schema palace_format | The format stamped in kg.redb. |
kg_schema triple_key | The triple-key layout marker. |
palace.json format_version | The mirror of the format in palace.json. |
format_migration.json | present means a migration was interrupted. |
backup | The newest complete migration backup, and whether it still verifies. A missing or failing backup is reported, not counted as a gap. |
Each marker reads present (<n>), missing, mismatched (<n>, want <m>) or
not applicable (new, empty KG). The last is a palace with no data.
The block ends with OK: every check passed or one GAP: line per failed
check. With any gap, the command exits non-zero and names the palace and the
gap. Fix the marker, or run palace migrate <palace> to finish the job.
trusty-memory doctor reads each palace without writing. In its palace format line, a palace behind the binary is a warning, one it cannot read
(held by a daemon, or in need of crash repair) is a warning, and a palace it
refuses (newer, split markers or unreadable) is a failure.
Upgrading from 0.32.x or older to 0.35.0 (FormatTooOld)
The published trusty-memory 0.35.0 migrates only format 1, so it refuses a
format-0 palace with FormatTooOld. Palaces written by 0.32.x or older are at
format 0, so they need the 0.33.0 step. Upgrade in two steps, through 0.33.0,
the newest 0.33.x release:
- Stop the daemon.
- Install 0.33.0:
cargo install trusty-memory --version 0.33.0 --locked. - Start its daemon once:
trusty-memory start. It opens each palace and migrates format 0 to format 1. Before it migrates, it writes a verified backup under<data root>/backups/format-migration/<palace>/. - Stop the daemon, then run
trusty-memory doctor. Thepalace formatcheck passes with "every palace reads at format 1". It warns if a palace still needs migration. In that case, start the daemon again and let it finish. - Install 0.35.0 (or the current release):
cargo install trusty-memory --version 0.35.0 --locked. - Start its daemon. It migrates format 1 to format 2.
trusty-memory 0.36.0, which ships with trusty-mpm 1.9.0, migrates format 0
directly and adds trusty-memory palace migrate (#10094).
Roll back
With a migration backup
Use this after palace migrate or the daemon migrated a palace and you need
the old format back. Writes made since the migration are lost.
- Stop the daemon:
trusty-memory stop. Close every MCP client first, or it restarts the daemon. - Pick the backup. It is the newest directory that holds
MANIFESTand whose name starts with<old format>-to-, for example1-to-2-<UTC>to return to format 1. A palace can hold both0-to-2-*and1-to-2-*directories, so the newest one overall can be the wrong one. Copy from it; never move or edit it. - Copy every file in that directory except
MANIFESTback into the palace directory. - Remove
format_migration.jsonfrom the palace directory, if it is there. - Delete
index.usearch.redbfrom the palace directory. The backup does not hold it, so the migrated index stays behind. It can hold chunk rows ({uuid}#{n}) that an older binary misreads, and orphan vectors for drawers written after the migration (ADR-0067 D6). - Run the release that matches the old format: 0.33.x for format 1, 0.32.x or older for format 0. Start its daemon. It creates an empty index.
- Call
palace_reembedwithdry_run=falseuntil it reportsmissing: 0. Recall finds the drawers only after that.
This procedure copies into the existing palace directory, which is why step 5 is needed. The step-by-step section below restores into a fresh directory, so no index exists there.
The detailed form for a format 2 to 1 rollback, with checksum checks, follows.
Format 2 to 0.33.0, step by step
A binary refuses a palace in a newer format than it supports. After an upgrade
to format 2, trusty-memory 0.33.0 refuses the migrated palace, and a
palace backup taken after the migration is also format 2. To go back to the
older version you need a backup in the older format. For the format 1 to 2
upgrade, that is the migration copy described above.
- Install the older version
(
cargo install trusty-memory --version 0.33.0 --locked). Close every MCP client that uses trusty-memory, then runtrusty-memory stop. A running client restarts the daemon. - In the migration backup directory, pick the newest
1-to-2-<UTC>directory that holdsMANIFEST. Never use a0-to-1-*directory or one ending in.tmp. A0-to-1-*copy loses every write made under 0.33.0. Copy from the directory you pick; never move or edit it. - Move the upgraded palace directory out of the palace root, for example to
~/trusty-memory-rollback/<id>. Any directory in the palace root that holdspalace.jsonis read as a palace. - Make a staging directory on the same filesystem, outside the palace root.
Copy every file except
MANIFESTinto it, and check each file's SHA-256 againstMANIFEST. Then rename the staging directory to the palace directory. - Start the 0.33.0 daemon and call
palace_reembedwithdry_run=falseuntil it reportsmissing: 0. In 0.33.0,dry_rundefaults totrue, which re-embeds nothing.
MANIFEST is JSON, not a shasum list. This command prints each file's
expected hash and compares it. Run it inside the staging directory, with the
MANIFEST path in place of <backup>/MANIFEST. It exits non-zero on any
mismatch:
grep -E '"(name|sha256)"' <backup>/MANIFEST | sed -E 's/.*": "([^"]*)".*/\1/' \
| paste - - | while read n h; do echo "$h $n"; done | shasum -a 256 -c -
A palace backup taken before the upgrade, with a trusty-memory release that
has the command, restores with trusty-memory palace restore instead. Restore
does not read the migration copy (ADR-0067, Consequences).
A restore never writes index.usearch.redb, so none exists in a restored
palace. Writes made after the backup are lost.
Known limits
- Backup path checks use
lstatandmkdir, which never follow a symlink at the last segment. A same-user writer that swaps a segment for a symlink between the walk and the re-check can make at most one empty directory outside the backup root, and the re-check then refuses the backup. A swap after the re-check and before the final rename commits can redirect the backup. See ADR-0066 D7, "palace_backupdest". - Two first backups that share a parent directory that does not exist yet can race, and one can fail. Retry it.
- Restore does not write
drawer_counts.jsonlback. That file belongs to the data root, and the backup keeps this palace's lines. After a rollback, this palace's lines indrawer_counts.jsonlare stale.