lichen.page.review from the reviewed point 0b92cb61 to stack eb5a1653, as one PR per subsystem (review set)

full 04: Admin area: the hub repository, migrations, the share password and the storage allowance #67

targeting full/03-site-process
Open opened by and assigned to notplants-bot.bsky.social
targeting full/03-site-process

Each site has an admin area of its own, outside its folder and out of its own code's reach: <store root>/.lichen/sites/<site>/. It holds the site's hub (which began as its history's backup), the record of the migrations run on the site, the share password's hash, which moved out of the site folder where a script could read or change it, and the area's own settings. Since FULL-SITE-PROCESS the site process's admin thread holds the area read-write and runs its hub and migrations, the front server writes only the share password and the owner's key copy there, and the owner's storage allowance sits beside the area rather than in it.


AST-verified changes

  • lichen-server/src/site_area/backup.rs → site_area/hub_repo.rs, paths rewritten and names kept, with its test file (SFB 4c, 21fc608d). Checked by notify-bf00a6335 (recorded with 9 hex digits): "product move ok (1590 items, 53 carried, 1 file renamed, 1750 comments compared, 119 module declarations)"; adversary A11603: "equal, 1590 items, 1537 stayed, 53 carried, 1 renamed". Allowed: the file move, the derived path rewrite, mod lines, doc text rewrites.

  • SECURITY-FOLDER-AND-PRINCIPALS unit 2 (b603dbbe): site_area.rs, site_area/allowance.rs and site_area/hub_repo.rs → admin_area.rs, admin_area/allowance.rs and admin_area/hub_repo.rs, part of site_area/ → admin_area/ (13 files renamed, crate::site_area → crate::admin_area in all lichen-server Rust). Checked by the builder: "AST equal three ways (8da73502 → b603dbbe, → bdaa795b, → 067dbe37), plants caught"; adversary rerun notify-020eb199: "all equal=True (2471/2471, renamed 13, carried 426, visibility 0, errors 0)". The non-move edits (d85a66d2: include_str! paths, literals) and the rustfmt (bdaa795b) are separate commits.

  • SECURITY-FOLDER-AND-PRINCIPALS unit 1 (46529b05): site_area/share.rs (as security/share.rs) into lichen-server's security/, part of seven product files moved there with their test files (12 files renamed; crate:: paths rewritten in lichen-server). Checked by notify-eebdd32e: "items 2471/2471, moved 0, stayed 2343, carried 128, files_renamed 12, visibility_changes 0, comments_compared 2429, module_declarations_checked 147, errors 0"; adversary rerun (19:08Z, its checker built from ast-checker/): both runs "equal=True (2471/2471, renamed 12, visibility 0, errors 0)". The rustfmt (d6c25912, proven by cargo fmt) and the non-move edits (bd48e739: literals) are separate commits.

  • FULL-SITE-PROCESS 5b (#1649 on lichen.page.review, 26779036 → 0e8871fe, map machine-docs/reports/fsp-5b/move-map.json): admin_area/hub_repo.rs renamed whole from lichen-server into lichen-site, one of 34 files (19 modules and their 15 #[path] test files) moved lichen-server → lichen-site, each re-exported where it was; the others here are in #21, #23 and #25. Run notify-981f7470: "equal=True; items 5557/5557, stayed 4752, carried 805 in the 34 renamed files; visibility_changes 99, every one listed; comments compared 4991; module declarations checked 352"; plants item_edited and visibility_unlisted caught, a dropped declaration gate not caught (covered by the PR's gate table); adversary reruns equal (notify-c53fbd02). The rustfmt (eedd7e97, ad03def8) and the pub widenings for what the front calls (6a2b574d, c04bebac, 0e8871fe, each in the map) are separate commits. None of this PR's 3,757 changed lines can be skipped for these moves: backup.rs was created after 09-23, so hub_repo.rs reads as new code in this diff, wherever it moved. The moves only tell you that renaming and moving it changed no code.


What the area holds, and why:

  • The site's history repository, now its hub (SITE-HISTORY-BACKUPS, then SITE-FRONT-BOUNDARY unit 4). A script, or anything it starts, can write the site's .git, and a crash mid-commit can damage it, so lichen keeps an append-only copy (the site process's admin thread, since FULL-SITE-PROCESS 6c): backup.git, updated by one git fetch with no + and no --prune, transfer.fsckObjects on, the site's upload-pack run inside the site's sandbox with its pack settings pinned, the fetch bounded to ten minutes. SITE-FRONT-BOUNDARY then made this repository the hub every push lands in. The receive is #21; this PR is the repository's lifecycle: create, migrate, rewrite after an owner's act (squashed, move_main, put_main_back), restore a site from it, and record why an update failed for the owner to see (STC-BACKUP1).
  • Migrations, an ordered list in code (lichen-site/src/admin_area/migrations.rs since FULL-SITE-PROCESS 6d), run by the site process's admin thread before its main runtime is handed anything, under the area's opening, which a cancelled import's removal waits for. Each is idempotent; the record lists applied ids. This PR has 0001-backup, 0002-share-password, 0003-hub, 0004-hub-head and 0009-allowance-out; the remote, sync, publish, records and known-hosts migrations live with their modules in #23 and #25.
  • The storage allowance (allowance.json, operator note N8; since FULL-SITE-PROCESS unit 3 in <store root>/.lichen/allowance/<site>/, beside the area, migration 0009-allowance-out): the owner's limit and what the owner's other sites weigh, written by the front server before a site process starts and after events that change another site's weight (backup updates, at least every ten minutes; rewrites; a process exit; a deletion). The site adds its own weight when it checks, instead of asking the front server (SHB-STORAGE1): its admin thread bounds each push into the hub with it, and its main runtime its own writes. The staleness bound is SECURITY-DEBT ALLOWANCE-STALE1.
  • The share password's hash. It was .lichen/share-password in the site folder, handled by lichen-cms/src/share.rs (deleted here). Now it is share-password in the area; migration 0002-share-password moves it (lichen-site/src/admin_area/share_password.rs, run by the admin thread since 6d); the share panel's routes and the guest's login check are the front server's (security/share.rs), and the site's main runtime and children never see the hash.

Since FULL-SITE-PROCESS 6c the site process's admin thread holds the whole area read-write; its main runtime and children hold of it only the hub, read-only, unless the owner opts in to full-permissions-for-child-processes (in the area's settings.toml, admin_area/settings.rs). In local and single-site mode nothing is sandboxed, so there the area is placement, not protection (PSP-LOCAL1).

Files

  • crates/lichen-server/src/admin_area.rs — what the area holds and who may reach it; what the front server still does there (readies it before a launch, marks a first start, copies the owner's key, the storage budget), and re-exports of the moved modules. Read this first.
  • crates/lichen-site/src/admin_area.rs — new: the site's admin-area module list.
  • crates/lichen-site/src/admin_area/migrations.rs — new: the migration runner (MIGRATIONS, open), moved from the front server (6d), and 0009-allowance-out.
  • crates/lichen-site/src/admin_area/hub_repo.rs (1,971 lines; moved from lichen-server in 5b) — the hub repository: the confined fetch and the sandboxed upload-pack, the hub migrations, the rewrite helpers #23 uses, restore, the failure the owner is shown.
  • crates/lichen-server/src/admin_area/allowance.rs — when the allowance is written, and how stale it may be.
  • crates/lichen-site/src/storage.rs — the site's side: read the allowance and its backup's weight, add its own usage.
  • crates/lichen-server/src/security/share.rs — the share password's routes and the guest login check, at the front.
  • crates/lichen-site/src/admin_area/share_password.rs — new: the share password's file in the area and its migration.
  • crates/lichen-site/src/admin_area/settings.rs — new: the area's own settings.toml, and the opt-in full-permissions-for-child-processes the front server reads at launch.
  • crates/lichen-cms/src/share.rs — deleted: the site's own share-password code.

Moved in: lichen-cms/src/share.rs (a deletion git does not pair with any file) is placed here, beside the code that replaced it.

Lives elsewhere

  • The hub's receive (lichen-site/src/admin_area/hub.rs): #21. The git remote record (lichen-site/src/admin_area/remote.rs) and the owner's key copy (owner_key.rs): #23. The publish record (lichen-site/src/admin_area/publish.rs): #25.
  • The site process's start-up that gives its admin thread the area: #38. The spawn that grants it (site_spec): #39.
  • lichen-flat-file's tree_private_area, which makes the folder: #17. Where the backup's weight is measured (state.rs): #31.
  • SANDBOX.md ("A site's own area and its history's backup") and docs/SECURITY.md are in the base commit.

Category: Admin area: the hub repository, migrations, the share password and the storage allowance Size: 10 files, +3,567/−190 Rounds: round 0 at stack ae42e3c0; round 1 at 6b650b2a (SECURITY-FOLDER-AND-PRINCIPALS): the area is now the admin area: site_area.rs, allowance.rs and hub_repo.rs renamed to admin_area* (unit 2), and the share password's module to security/share.rs (unit 1); admin_area.rs adds an area's Opening, held by its migrations, so a cancelled import's migrations end before its area is removed or never run (SFP fix-up 4b, 6032b15e). The share password stays in this PR, its subject, though its file is now in security/ beside files #18 and #30 hold. Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/20/round/1/interdiff. Round 2 at b66397c0 (FULL-SITE-PROCESS): hub_repo.rs moved into lichen-site (5b, AST-checked) and its git runs on the site process's admin thread; the migrations, the share password's file and the area's settings come to lichen-site (6c, 6d); the allowance moves beside the area (unit 3); the front's admin_area.rs keeps what the front still does there. Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/20/round/2/interdiff Plans and ledger: SITE-HISTORY-BACKUPS-2026-10-11.md (M1 area and migrations, M2 share password, B1–B4 backup), SITE-FRONT-BOUNDARY-2026-10-17.md ("The hub", unit 4) with SITE-FRONT-BOUNDARY-U1-DESIGN-2026-10-19.md, OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N8), FULL-SITE-PROCESS-2026-10-25.md (units 3, 5b, 6c, 6d; the operator's Q3); security items STC-BACKUP1, SHB-STORAGE1; builder ledger SFB 4c (line 2904). Inline #[cfg(test)] modules: none. 4 files point to a test file with #[path] (the moved modules' tests came back to lichen-server in 5b's tests PR, through test-support accessors). Bucket moves: crates/lichen-cms/src/share.rs (new to this range) placed here by an explicit rule, with its replacement. In round 4, hub_repo.rs stays here with its subject though it moved crates, and the new lichen-site/src/admin_area.rs, migrations.rs, settings.rs and share_password.rs are placed here by an explicit rule. Otherwise as round 1. Round 1: #4 (Site area) covered these files from 0b92cb61, except lichen-cms/src/share.rs.

This pull request has no versions submitted yet.