diff --git a/plan/served.md b/plan/served.md index c200c54..d5d1bf9 100644 --- a/plan/served.md +++ b/plan/served.md @@ -1480,7 +1480,14 @@ Each unit follows the 8.0.4 template. "A-n" is an owner answer numbered in 8.3.1 5. **Files:** owns `deploy/`, `.github/workflows/two-user.yml`, `crates/ledger-cli/tests/two_user.rs`, `handbook/self-host.md`; touches `books.start`'s `isolation` (`tools/kept.rs`), `crates/ledger-host/src/commands.rs` (the flag). 6. **Tests and proofs:** `agent_uid_cannot_read_tenant`, `self_host_adopt_same_head`; `isolation_label_is_enforced_under_the_unit`. 7. **Done when:** (1) the CI job passes; (2) the recipe, followed on a clean VM by someone other than its author, reaches `books.start` from a desktop Claude Code in under 30 minutes (recorded in the As-built block); (3) `just check` green. -8. **As built:** none yet. +8. **As built** (2026-10-26, `a5ec3056`): + - **`--isolation` and `--http` had to be built, not just wired.** Build item 1's unit file names `--http` for the tunnel (3.9), but the server had only Unix sockets, and `isolation` was a literal in `tools/kept.rs::start_out`. Both are server flags now (`local::run`): `--isolation same_user|enforced` sets `server::Isolation` (reported by `books.start`), and `--http [ADDR]` binds the same router on loopback (default `127.0.0.1:7411`) before the ready line; a failed bind is said and is not fatal (3.9). `serve` carries both to the server through the host (`Opener::starting_with`, `start::start_host`, `host::Args`), and validates `--isolation` itself so a typo fails before a host starts. A host already running keeps the flags it was started with; on a self-host machine `serve` is the only holder. + - **The move's head check needed a source at this version.** `books adopt` writes the copy in this version's journal format (3.9), so adopting a copy written by an older version appends `journal.format_set` and the source's head is one event behind. The CI job therefore makes the desktop source with this version first (adopt the repository fixture there), closes it, and copies that book; the source's head and the moved books' are then equal, and the test compares them. The preview still says when a copy was upgraded ("The copy is in this version's journal format"). + - **`token create` on a headless server.** It keeps the token in the OS keyring and revokes it if it cannot, so a server with no Secret Service cannot mint one (tokens.rs INVARIANT 4, as built P2 slice 4). The recipe mints inside `dbus-run-session` with `gnome-keyring-daemon` and copies the text once; the CI job uses the `file_keyring` test knob. A "print it without keeping it" flag was considered and not added: it is new surface, and the text still lands in a terminal. + - **The remote bearer on the client.** 3.3's "header helper `wanderbooks token print`" prints the token *this* machine's keyring holds for its own data root; there is no command that keeps a remote token on a client. The recipe puts the bearer in Claude Code's `--header` (its config is the owner's, mode 0600) or in Codex's `bearer_token_env_var`. No new tool or flag was invented; this is the one place the recipe does not follow 3.3 literally, and it is in `deploy/README.md` and `docs/testing.md`. + - **`IPAddressDeny=any` blocks the server's own backup push.** The unit has the plan's lines; a book whose backup target is a remote repository needs that host allowed in an `IPAddressAllow` drop-in (README step 4). A target on the same machine needs nothing. + - **The write checks use a second book.** `agent_uid_cannot_read_tenant` must write to prove the door, and a write advances the moved books' head; it writes to a `tunnel` book the job makes, so `acme`'s head is left for `self_host_adopt_same_head`. + - **Unverified here (owner/EM):** Done when 1, the GitHub Actions job (no run from this worktree) and Done when 2, the recipe walked on a clean VM by someone other than its author in under 30 minutes. Verified locally: `isolation_label_is_enforced_under_the_unit`, and both two-account tests against a stand-in deployment whose client ran in a user namespace as host uid 100000 (the server as 1000), over `--http` on 127.0.0.1, with the tenant root 0700 and refused to the client; the moved books reported the source's seq, hash and event count. `just check`'s steps pass (format, clippy, rustdoc, the whole workspace's tests, and `server-only`), run one by one rather than through the recipe: its temp sweep would delete other engineers' `/tmp/wb-*` dirs on a shared machine. No kernel, `Ledger` command, Kani harness or Lean theorem changed, so `just prove` is not touched. **P9b (planned, not scheduled; after Q-6):** claude.ai and ChatGPT need OAuth 2.1. The recommendation is D-7 (b): an external identity provider (Authentik, Ory or Keycloak) issues tokens and the server only validates them (audience = the book URI, `iss`, expiry), so no authorization server is written in-house and no external security review is needed; the Origin allowlist gains `https://claude.ai`; `books.job` and the 240 s rule come here if a call is measured over the limit. **P9c (not planned, D-51):** the remote viewer replica (`/replica/*`, `Snapshot::from_lines`, the blob LRU), `books.invite`, the `/h/` pages for remote people, the MCP App upload widget and `openai/fileParams`. The viewer on another machine reads a backup clone (D-30, D-31) read-only instead.