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

product: 04 Site area: the hub repository, storage allowance and share password #51

targeting oct/03-site-process from oct/04-site-area
Closed opened by and assigned to notplants-bot.bsky.social
targeting oct/03-site-process from oct/04-site-area

superseded by the full set from 09-23, #17–#37

description: Review set for lichen.page.review, 0b92cb61 (reviewed) → eb5a1653 (stack). PR 4 of 16. 7 files, +2962/−196. One commit, 43bf923efe8b, which sets these files to their content at eb5a1653. Targets #3 (oct/03-site-process). #5 stacks on this one. Tests and generated files are not in any PR; they are in main.

Each site now has an area of its own, outside its folder and out of its reach: <store root>/.lichen/sites/<site>/. Only the front server writes it. A site process may read two things in it, its history repository and its storage allowance, and nothing else.

What the area holds, and why it exists:

  • The site's history repository, backup.git (SITE-HISTORY-BACKUPS). A script, or anything a script starts, can write the site's .git, and a crash mid-commit can damage it. So the front server keeps an append-only copy: one git fetch with no + and no --prune, transfer.fsckObjects on, the site's upload-pack run inside the site's sandbox, and the fetch itself confined to the backup. SITE-FRONT-BOUNDARY then made this repository the hub (unit 4): every push lands here, and the front server works on it. The receive is #5; this PR is the repository's lifecycle: create, migrate, rewrite after an owner's act, restore a site from it, and tell the owner when its update failed (STC-BACKUP1).
  • Migrations, an ordered list in code that runs when a site opens, under its locks: 0001-backup, 0002-share-password, 0003-hub, 0004-hub-head, then the remote, sync, publish and records migrations of later PRs. An unknown id in a site's record refuses to open it (no downgrade).
  • The storage allowance (allowance/allowance.json, operator note N8): the owner's limit and what their other sites weigh, written by the front server. The site adds its own weight when it checks, instead of asking the front server. Over the limit, no site code runs, no build writes are committed and no peer is merged (SHB-STORAGE1).
  • The share password's hash. It was .lichen/share-password in the site folder, where a script could read or change it. Now the front server checks it at login, manages it on the share panel, and the site never sees it. A guest is let go when the password changes (STC-GUEST1).

info: Plans: SITE-HISTORY-BACKUPS-2026-10-11.md (M1 the area and migrations, M2 the share password, B1–B4 the backup), SITE-FRONT-BOUNDARY-2026-10-17.md ("The hub", unit 4), OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N8: "read storage allowance and backup directly"), and the security fixes named in the code (STC-BACKUP1, STC-GUEST1, SHB-STORAGE1, SHB-FWD-REWRITE1).

Files

  • crates/lichen-server/src/site_area.rs — new: what the area holds, who may reach it, and the migration runner (MIGRATIONS, open). Read this first.
  • crates/lichen-server/src/site_area/hub_repo.rs — new (1905 lines): the history repository (BACKUP), its confined fetch and the relay between the sandboxed upload-pack and the confined fetch; the hub migrations; squashed, move_main, put_main_back and the rewrite marks that #6's rewrites use; restore; the failure the owner is shown. It began as the backup module and was renamed when it became the hub.
  • crates/lichen-server/src/site_area/allowance.rs — new: when the allowance is written and how stale it may be (SECURITY-DEBT ALLOWANCE-STALE1).
  • crates/lichen-site/src/storage.rs — new: the site's side: read the allowance and the backup's weight, add its own usage.
  • crates/lichen-server/src/site_area/share.rs — new: the share password in the area, its migration, its routes and the guest login check.
  • crates/lichen-site/src/share.rs, crates/lichen-site/src/api/share.rs — deleted: the site's own share-password code and routes.

Moved in: lichen-site/src/storage.rs (the site's reader of the allowance) from the site-core bucket, so the allowance's two halves are reviewed together.

Lives elsewhere

  • The other files in the area: the git remote record (site_area/remote.rs) in #6, the publish record and keys (site_area/publish.rs) in #7; the hub's receive (site_area/hub.rs) in #5.
  • lichen-flat-file's tree_private_area, which makes the folder: #1.
  • How the area is described for users and operators: SANDBOX.md ("A site's own area and its history's backup") and docs/SECURITY.md, #16.
  • Tests (lichen-server/src/tests/site_area*.rs, e2e) are in main.
Commits 0