`wanderbooks update` reads a release manifest (default: GitHub Releases'
fixed address, D-26), refuses anything not higher than this version,
downloads the platform's file and accepts it only if its sha256 matches
the manifest AND its minisign signature verifies against the release key
compiled into the program (WANDERBOOKS_RELEASE_PUBKEY at build time;
a build without one updates from nothing but a file:// feed whose key the
owner names with --trusted-key, so a local release can be tried). The
verified file is staged under <root>/updates/<version>/, the running
version is kept under updates/previous/ with the journal format it
understood, and the program file is replaced by a rename of a complete
copy made beside it (a reader sees the old file or the new, never a
part). The new program must answer --help; if not, the old one is put
back. Nothing tells the books to stop: the host already notices a
replaced executable and restarts onto it, finishing calls in progress
(host.rs INVARIANT 7).
`wanderbooks rollback` swaps the kept version back, and refuses while any
book declares a journal format or feature the kept version does not know
(plan 3.9: after the owner consents to a newer format the only way back is
the pre-upgrade restore). A package-managed install (a package ships
share/wanderbooks/install-method beside bin/) refuses both and says to use
the package manager. An AppImage replaces the file named by $APPIMAGE.
Tests (crates/ledger-cli/tests/update.rs): update_is_atomic,
failed_update_rolls_back, unsigned_update_refused (another key, other
bytes, a sha that differs, no signature, not a signature),
bridge_survives_update, update_waits_for_a_running_call,
package_managed_install_refuses_update, the kept-version and format-gate
tests; unit tests in ledger-host/src/update.rs.
Dependencies: minisign-verify 0.3 in ledger-host (what cargo-packager's
updater uses to check release files; no dependencies of its own) and
minisign 0.10 as a ledger-cli dev-dependency (the tests sign with a key
they make). sha2 and the two minisign crates are built optimized in debug
and test builds: the tests hash a debug build's program. The release
profile strips symbols, for the packages (P11a step 3).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
D-63: the 0700 data root is the trust boundary already (I-1: hidden, not
locked), so the OS keyring added no wall and cost headless servers a D-Bus
session, CI a knob and every OS its own store. The file keeper is the only
store: run/tokens/, 0600, written atomically. The three keyring stores, the
file_keyring knob and their tests are removed; an unreadable token file is an
error, never no token.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
The agent path was Linux-only because tokens had a keyring store only
there (every other OS hit NOT_YET, so `connect` and the bridge failed).
D-56's destination is one installation for macOS, Windows and Linux.
Both new stores sit behind the same Items trait as the Secret Service,
so INVARIANT 4 (one account, one item, locked is an error) is kept once
and tested once through the file keyring. Windows credentials have a
unique target name, so a probe stands in for a search.
New dependencies (ledger-host only, target-gated): apple-native-keyring-store
(feature keychain) and windows-native-keyring-store, the stores the
keyring-core 1 API we already use is published with. No ledger-core change.
The `portable` job builds and tests the workspace on macos-latest and
windows-latest. Windows is allowed to fail: the rest of the host is
Unix-only (inventory in docs/packaging.md). Not verified locally: no
macOS or Windows target is installed; CI is the evidence.
Also pins cargo-packager, cargo-packager-updater and cargo-generate-rpm
in mise.toml for the trial, and records that the trial is not yet run.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
Files reach the books through the server: uploads by hash and size,
then filed; exports (the CPA package, the 1099 worksheet, W-9s) handed
over as downloads from the server, never read off disk by the agent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
Agent tokens (wb_ and 64 hex digits), one book and one name each, kept
only as SHA-256 in a chain log and in the OS keyring; made, listed and
revoked only through the admin socket (`wanderbooks token ...`). The
door checks them, scopes cover every tool, every call is in the access
log (fail closed), and the owner passphrase is an Argon2id hash.
Setup's terminal test waits for its prompts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
One wanderbooks executable. The books are open while the viewer, an
agent session or a terminal holds them: a host process takes leases on
a socket, starts the server (the hidden mode `wanderbooks server`) with
a pipe on its input, and closes it 10 s after the last lease ends. No
background service. One host even in a race (run/host.lock).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj