|
| 1 | +# Backup, restore, and moving to another machine |
| 2 | + |
| 3 | +Slice 034. One archive holds a Trinity; a fresh install takes it back. Losing the machine is a recoverable |
| 4 | +event, not a total one. |
| 5 | + |
| 6 | +## What the archive holds |
| 7 | + |
| 8 | +`mix trinity.export --out <file.tar.gz>`, or the Export button on `/settings`, writes a gzip tarball with: |
| 9 | + |
| 10 | +| Entry | What it is | Always | |
| 11 | +|---|---|---| |
| 12 | +| `manifest.json` | the format (`trinity-archive/1`), this build's version, when it was made, the migrated schema versions of each database, whether private keys are inside, and one line per file with its size and SHA-256 | yes | |
| 13 | +| `trinity.db` | the primary database: personas, sessions, messages, the search index, memories and their change log, approvals and rules | yes | |
| 14 | +| `receipts.db` | the receipt chains and their checkpoints | yes | |
| 15 | +| `keys/registry.json` | the key registry: public keys, algorithms, statuses (a stranger verifies receipts with this) | yes | |
| 16 | +| `keys/receipts-<algorithm>.key` | the private signing key | only with `--keys` (or `?keys=1` on the page) | |
| 17 | +| `skills/…`, `personas/…` | files under those directories, when present (skills arrive at slice 040) | when present | |
| 18 | + |
| 19 | +Each database is snapshotted with `VACUUM INTO`: a consistent copy including what the write-ahead log held, |
| 20 | +taken while Trinity runs. The manifest says `keys_included` either way, so an archive's holder knows what it |
| 21 | +carries. |
| 22 | + |
| 23 | +## What it does not hold |
| 24 | + |
| 25 | +- Model caches, the desktop shell's state, the `LOCK` file, `RESTORED` markers from earlier restores. |
| 26 | +- The private key, unless asked for. An archive without it verifies every receipt ever written (the registry |
| 27 | + carries the public keys) and cannot write a new one as you: a fresh install without the key generates its |
| 28 | + own on first boot and appends it to the registry as a new key id; the old chain stays verifiable under the |
| 29 | + old key's row. Hand an archive to someone else without `--keys`. |
| 30 | +- Anything from other machines: this is one data directory, not a sync. |
| 31 | + |
| 32 | +## Restoring |
| 33 | + |
| 34 | +On a fresh install, with Trinity stopped: |
| 35 | + |
| 36 | +``` |
| 37 | +mix trinity.import trinity-2026-09-21.tar.gz |
| 38 | +``` |
| 39 | + |
| 40 | +The task reads the manifest, checks the schema versions against this binary's migrations (an archive from a |
| 41 | +newer Trinity refuses by name; an older one migrates on the next boot), verifies every file's digest against |
| 42 | +the tarball's bytes, and only then writes the files. A data directory that is not empty is refused with the |
| 43 | +paths it found; `--force` replaces them and prints each one. A directory held by a live Trinity is refused |
| 44 | +until that process stops. A `RESTORED` file in the data directory names the archive and the time. |
| 45 | + |
| 46 | +Then start Trinity. The receipt chains verify under the restored registry (`mix trinity.receipts.verify --scope |
| 47 | +<scope>`, or the Verify button on a session's receipts page), which is the sharpest check that the copy is |
| 48 | +faithful. The search index is inside the database on SQLite; on Postgres it is a generated column. |
| 49 | + |
| 50 | +`--data-dir <dir>` points either task at a directory other than the configured one. |
| 51 | + |
| 52 | +## Postgres |
| 53 | + |
| 54 | +The archive is the SQLite data directory's. A deployment on Postgres (`TRINITY_DB=postgres`) keeps its rows in |
| 55 | +that database and backs it up with Postgres's own tools (`pg_dump`); the export there carries the key registry |
| 56 | +and the directories, not the databases, and says so in the manifest's file list. |
| 57 | + |
| 58 | +## Size |
| 59 | + |
| 60 | +On the machine this was written on (2026-09-21), 18 sessions and 8,029 messages made a 713 KB archive (3.1 MB |
| 61 | +of database before compression). Vectors (slice 032) and any model cache are not measured yet; the slice that |
| 62 | +adds them reads this line. |
0 commit comments