P2 slice 2: the data root (plan 3.4, 5.5)

ledger_store::root: one private folder for every book, found without the
environment (--root, else the root setup recorded in a file only this
account can write, else the platform default under the home from the
password database). Every folder made 0700, files 0600; a root that is a
link, another user's, open to others, unmarked, or under a folder another
user could swap is refused; no error names it. Setup keeps a recorded
root, never makes a second root or moves books while one holds any, and
leaves a folder holding other things untouched. Books are created and
adopted one at a time; adoption reads the source under its writer lock
(changing nothing in it), copies into staging, re-hashes every stored
file against its name, checks every named file is there, opens and
verifies the copy with the source's head, imports settings for books
that keep none, syncs, and only then renames it into place.
wanderbooks setup makes the root and never prints where it is. Three
review rounds; mutation on the diff (docs/testing.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-18 14:42:32 -06:00
1 parent e863bfee74
commit b47331e9d5
11 files changed
+2629 -10

No files matched your search

+3 -3
View File
@@ -216,7 +216,7 @@ No scope unwraps the vault. Unwrap happens only inside an `/h/` request that has
archive.git/ bare; journal.jsonl + blobs/ + vault ciphertext; age-encrypted on push; destination Q-1
```
The server, the CLI and the GUI resolve the data root with one shared function (`ledger-store::paths::data_root()`: the platform data directory, overridable with `--root`), never from the working directory, a project directory or a toml file. The root is created with an explicit `set_permissions(0o700)`, not the umask. Extracted text is stored as a content-addressed blob, recorded in the journal by `attachment.text_extracted`, and archived with the books. Page rasters are disposable. Deleted assumptions: `books_dir = data_dir.parent()` (S:1155, `ledger-gui/src/books.rs:1474`, `ledger-mcp/src/main.rs:102`), the `.gitignore` check (S:531-543), and the `$XDG_RUNTIME_DIR` viewer file (`ledger-store/src/viewer.rs:98-130`), which `ops/show.jsonl` replaces.
The server, the CLI and the GUI find the data root with one shared function (`ledger_store::root::Root::find`, built in P2 slice 2): `--root` if given, else the root `wanderbooks setup` recorded in `wanderbooks.location` beside the default, else the platform default under the account's home, taken from the password database on Unix, never `$HOME`. Finding it never reads the environment, the working directory, a project directory or a toml file; `$XDG_DATA_HOME` is honoured once, by `setup`, which records the result. The root and every folder made in it are created with an explicit 0700, not the umask, and a root that is a symlink, belongs to another user or that others can enter is refused (`setup` tightens one of ours). `setup` leaves a `.wanderbooks-root` marker, and a folder without one is never opened as a root nor, if it holds anything, made into one; every folder above the root must be one no other user can swap it in (owned by this account or the system, not writable by others unless sticky). Run again, `setup` keeps the root it recorded, and it never makes a second root or moves books while the current one holds any. Neither `setup` nor any refusal prints the root's location: nothing an agent is shown names it. Books are created and adopted one at a time, under `run/books.lock`. Windows (P11): `%LOCALAPPDATA%` from the known-folder API and an owner-only DACL; until then the Windows paths are read from the profile and carry no ACL. Extracted text is stored as a content-addressed blob, recorded in the journal by `attachment.text_extracted`, and archived with the books. Page rasters are disposable. Deleted assumptions: `books_dir = data_dir.parent()` (S:1155, `ledger-gui/src/books.rs:1474`, `ledger-mcp/src/main.rs:102`), the `.gitignore` check (S:531-543), and the `$XDG_RUNTIME_DIR` viewer file (`ledger-store/src/viewer.rs:98-130`), which `ops/show.jsonl` replaces.
### 3.5 Keys
@@ -560,9 +560,9 @@ The `WANDERBOOKS_TOML` template (`ledger-cli/src/main.rs:237-347`) becomes the e
- `wanderbooks books adopt <data-dir> --id X [--toml FILE] [--config-json FILE]` runs as the human, with no sudo, through the admin socket. The server, which also runs as the human, does the copy:
1. Takes the source flock, so it refuses while a stdio server holds it.
2. Verifies the chain and records head H.
3. Copies `journal.jsonl` and `blobs/`, re-hashes every blob, rebuilds the projection, and checks that the head equals H.
3. Copies `journal.jsonl` and `blobs/` into a staging folder (a cut-off last line or a link among the blobs is refused), opens the copy (rebuilding the projection), checks that the head equals H, verifies it end to end and re-hashes every blob; only then is it renamed into `books/X/`, and on any failure the staging folder is removed and nothing appears. A source inside the root, or holding it, is refused.
4. Imports settings from the given texts (preview printed, confirmation at the TTY) unless the journal already has `settings.set` events.
5. Moves `vault/*.bin` into `books/X/vault/`, and the v1 key into `keys/legacy.key`.
5. Copies the vault's records into `books/X/vault/`, and the v1 key into `keys/legacy.key` (0600). Copied, not moved (P2 slice 2): adoption never changes the source, so a refused or abandoned adoption leaves it exactly as it was; step 7 is what moves the old copies away.
6. Seeds `archive.git` from the source's git history, filtered to the journal and blob paths, and records the old repository's last commit hash. The push destination is the owner's answer to Q-1.
7. If the owner agrees (Q-3), moves the source data dir, its vault and the project folder's git history into `<root>/legacy/X-<date>/`. Nothing is deleted. If the owner says no, the source stays where it is, read-only, and Needs attention lists it as an exposed old copy.
- `ledger-cli` `init`/`new` stop scaffolding and call `books create`.