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,modlines, doc text rewrites. -
SECURITY-FOLDER-AND-PRINCIPALS unit 2 (b603dbbe):
site_area.rs,site_area/allowance.rsandsite_area/hub_repo.rs→admin_area.rs,admin_area/allowance.rsandadmin_area/hub_repo.rs, part ofsite_area/→admin_area/(13 files renamed,crate::site_area→crate::admin_areain 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(assecurity/share.rs) into lichen-server'ssecurity/, 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 fromast-checker/): both runs "equal=True (2471/2471, renamed 12, visibility 0, errors 0)". The rustfmt (d6c25912, proven bycargo 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.rsrenamed 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 thepubwidenings 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.rswas created after 09-23, sohub_repo.rsreads 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 onegit fetchwith no+and no--prune,transfer.fsckObjectson, the site'supload-packrun 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.rssince 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 has0001-backup,0002-share-password,0003-hub,0004-hub-headand0009-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, migration0009-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-DEBTALLOWANCE-STALE1. - The share password's hash. It was
.lichen/share-passwordin the site folder, handled bylichen-cms/src/share.rs(deleted here). Now it isshare-passwordin the area; migration0002-share-passwordmoves 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), and0009-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 sandboxedupload-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 ownsettings.toml, and the opt-infull-permissions-for-child-processesthe 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'stree_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") anddocs/SECURITY.mdare 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.