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

full 21: Site crate core: settings, runtime, features and the crate itself #84

targeting full/20-shell from full/21-site-core
Open opened by and assigned to notplants-bot.bsky.social
targeting full/20-shell from full/21-site-core

The core of the lichen-site crate (lichen-cms renamed): its manifest and module list, settings, the runtime and how it is built, and what a site's work may ask of its owner and of the server it runs in. Much of it moved from lichen-server into the site crate (PER-SITE-PROCESS P2c-2b), so a site can run in a process of its own. Later SITE-FRONT-BOUNDARY units took the owner's session and the server's in-process host back out, so the site's main runtime sees only its owner's public facts; since FULL-SITE-PROCESS the site process's admin thread holds the session the front server lends it, and the admin-area code is given what it needs through the seams defined here.


AST-verified changes

  • The crate rename lichen-cms → lichen-site (P2c-1, d7c4a668). Map machine-docs/reviews/P2C1-RENAME-MOVE-MAP-2026-10-09.json. Builder (run id not recorded): "Rename move check with rn6 at f3e7470b..d7c4a668: ok (4365 items, 113 renamed, 1874 carried, 0 visibility)". Adversary: "d7c4a668 equal=true 0-err 113-renamed 1874-carried 0-vis". Allowed: lichen_cms → lichen_site rewrites, Cargo.toml and static files as other files, rustfmt rewraps. String literals were changed in a separate commit, 8eef92b4.
  • P2c-2b moves from lichen-server into this crate (2026-10-10; maps in machine-docs/reviews/P2C2B-maps/, ast-checker 9a9c4144 with --crate lichen-server --crate lichen-site; run ids not recorded; "vis" means pub(crate) → pub widenings declared in the map):
    • c0 (97e70426): AuthProvider users.rs → owner.rs, and random_hex auth/token.rs → utils.rs (sources in #30). Adversary: "moved4/vis1".
    • c1a (ff26fefc): token_scope.rs and runtime_registry.rs from the server, items into extensions.rs and types.rs; the site_process files are in #19. Builder: "5 moved, 48 carried, 8 files renamed, 19 visibility as mapped".
    • c1b (dba66e8b): SiteServerSettings into settings/server.rs. Builder: "2 moved, 3 visibility".
    • c1c (85a5e79e): the settings helpers from the server's api.rs and utils.rs into settings.rs and settings/edit.rs (sources in #32 and #33). Builder: "10 moved, 3 visibility".
    • c2a (02255939): the owner seam, credentials.rs and owner.rs, from the server. Builder: "2a recheck ok (12 moved, 5 carried, 1 renamed, 20 visibility; plant refused)".
    • c2b (0f6f6f57): the SiteHost trait, site_host.rs → host.rs. Builder: "1 moved, 1 visibility".
    • c5 (6c06675b): the settings page's context into settings/page.rs (source in #33). Builder: "move 5 ok: 10 moved, 4 carried, 1 renamed, 4 vis".
    • c6 (247be4a2): the site's side of the server's features.rs into features.rs (source in #31). Builder: "move 6 ok: 18 moved, 0 vis".
    • c7b (66a7caa9): runtime_build.rs from the server. Builder: "moves 7b and 7d ok, plants refused". The adversary's reruns got the same counts (A11333–11336).
  • ORR R7 rename by back-substitution (d52675cd), here in features.rs, runtime.rs and types.rs: Made::Published → DistInstalled, PublishHook/Attempt/Record → Publication* and their kin. Builder notify-392a0b96: "names substituted back AST EQUAL, control RED". Adversary: "rename AST-equal (control RED)". The rest is in #24 and #27.
  • Checked on a rebuilt pure move (SFB 7b, inside 5670cd85): enum PdsScope and its impl left owner.rs for the front server's site_area/remote.rs (#23). Builder notify-9a972a6c: "equal, 2 moved, 4970 stayed, no errors". Adversary notify-222a9edd: "equal, 2 moved, 0 errors; 3 plants refused".

Roughly 1,120 of this PR's 3,727 changed lines are code these moves carried verbatim. A move from the front server shows here as added lines, and its other half as deleted lines in that server file's PR. What changed after the moves is the rest.

On this forge the 9 renamed files here show as a whole deletion and a whole addition, so its diff reads 7,339 changed lines; with rename detection (git diff -M full/20-shell full/21-site-core) it is 3,727.


What changed and why:

  • lichen-cms became lichen-site (P2c-1). lib.rs is the module list, and Cargo.toml is the new crate's manifest; lichen-cms's is deleted. The lichen-iroh and lichen-shell folds are in #28 and #36.
  • A site's work stops reaching into the front server (P2c-2a seams, then P2c-2b moves):
    • owner.rs: SiteOwner and OwnerFacts. A site process's owner is LinkOwner, whose facts came with the launch; a change of them recycles the process (N8).
    • host.rs: SiteHost, what a site needs of its server. The site process's implementation is its only one now, since the front server's in-process site_host.rs went (SFB 11e11).
    • credentials.rs: keeps the session's type, SiteCredentials. The front server lends it to the site process's admin thread, which holds it in memory alone (FULL-SITE-PROCESS 6d) for its pds:// pushes and its records; the main runtime and its children never hold it.
  • Settings (settings.rs, new at 511 lines; lichen-cms's own settings.rs is deleted):
    • SiteSections reads [git] and [publish] once, and a section that does not resolve is that feature's error, not an unreadable site (P2c-3).
    • CmsSettings holds the editor's own look; FrontendSettings the [frontend] choice.
    • settings/edit.rs makes edits inside the tree's transaction, settings/server.rs is the server's settings as a site reads them (from the launch in a site process), and settings/page.rs gives the settings view's JSON values.
  • The runtime (runtime.rs, runtime_build.rs):
    • The runtime knows its hub (HubSlot), its dashboard URL ("" on the desktop, none for a single site), the server's plugin defaults, and whether the shortcode cache is on.
    • rebuild() answers what the build installed or kept.
    • runtime_build.rs builds a site's runtime the same way for every host (design note §16).
  • The seams (site_seams.rs, new, FULL-SITE-PROCESS unit 4): the plain data and narrow traits (SiteArea, SiteFacts, SiteAsks, MainMoved, OwnerCredentials, …) the admin-area code is given instead of the front server's state, defined in this crate so that code could move in unchanged (unit 5); the site process's admin thread builds them (#21's site_process/hub.rs).
  • features.rs: the site's feature wiring: shell and iroh startup decisions, the git listener, the settings router, build_site_runtime.
  • types.rs: StorageLimitState::over_now asks now rather than trusting the cached flag (SHB-STORAGE1).
  • ssrf_guard.rs: the SSRF guard. It moved here from the front server's auth/atproto/client.rs (not AST-checked: 5052d193, 2026-09-25), so the frontend loader and the git remotes share one guarded client and address check. [proxy] allow_private is renamed [egress] in its docs (SFB 12a). AllowedRange is written and read as its text, so a site process is launched with the operator's ranges (FULL-SITE-PROCESS 6d).
  • Smaller:
    • symlink.rs: the escaping-symlink check passes git an extra environment, for the hub's pre-receive.
    • export.rs: export_bytes, a zip never put in the site.
    • token_scope.rs: moved from the server.
    • atproto.rs: the module root for the PDS client.
    • events.rs, extensions.rs, save.rs, settings_cache.rs, utils.rs: renames and small follow-ups.

Files (crates/lichen-site/ unless noted)

  • src/lib.rs, Cargo.toml — the crate. Read these first for the module list. Since FULL-SITE-PROCESS lib.rs names the crate by its own name (extern crate self as lichen_site, so moved code keeps its paths) and declares admin_area, owner_key and site_seams; Cargo.toml's git takes flate2, atproto implies git, iroh takes iroh-blobs, and rustix and urlencoding come in.
  • src/owner.rs, src/host.rs, src/credentials.rs — what site work may know of its owner and its server.
  • src/settings.rs, src/settings/edit.rs, src/settings/server.rs, src/settings/page.rs, src/settings_cache.rs — settings.
  • src/runtime.rs, src/runtime_build.rs, src/runtime_registry.rs, src/features.rs — the runtime and its wiring.
  • src/site_seams.rs — new: the seams.
  • src/types.rs, src/ssrf_guard.rs, src/symlink.rs, src/token_scope.rs, src/utils.rs, src/export.rs, src/save.rs, src/events.rs, src/extensions.rs, src/atproto.rs — shared pieces.
  • crates/lichen-cms/Cargo.toml, src/lib.rs, src/settings.rs, src/types.rs — deleted.

Moved in: settings/page.rs (round 1's dashboard PR) is placed here because it is the settings view's values, not a page. The deleted lichen-cms Cargo.toml, lib.rs, settings.rs and types.rs are placed with what they became: git pairs them below its rename threshold (lib.rs since this round, when lichen-site's grew by its new modules).

Lives elsewhere

  • The site's API and its route table: #35. Frontends and plugins: #34. The shell: #36. Rooms and resync: #29. Builds and the rebuild lane: #27, #26.
  • The sources of the P2c-2b moves: #30, #31, #32, #33. The site process that implements SiteHost: #19. The PDS client under atproto.rs: #24.
  • Not in any PR (base commit): static/ (the starter sites, nginx.conf, r-lichen.service, settings.toml.example), contract/cms-api.json, and the tests.

Category: Site crate core: settings, runtime, features and the crate itself Size: 29 files, +3,163/−564 Rounds: round 0 at stack ae42e3c0, unchanged at 29a7c15b and 6b650b2a; round 1 at b66397c0 (FULL-SITE-PROCESS): site_seams.rs is new (unit 4); lib.rs names the crate by its own name and declares admin_area, owner_key and site_seams; Cargo.toml takes flate2, iroh-blobs, rustix and urlencoding; credentials.rs describes the session lent to the admin thread; ssrf_guard.rs's AllowedRange is serializable for the launch; comments in features.rs and owner.rs name the admin thread. Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/37/round/1/interdiff Plans and ledger: FULL-SITE-PROCESS-2026-10-25.md (4, 5a2, 6d); PER-SITE-PROCESS-2026-10-09.md (P2c-1, P2c-2a seams, P2c-2b commits 0–7, P2c-3; design note PER-SITE-PROCESS-P0-DESIGN-2026-10-09.md §§15–16), SITE-FRONT-BOUNDARY-2026-10-17.md (units 9b, 11e11, 12a), OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N4/R7, N8), FRONTEND-API-AND-LOADER-2026-09-24.md (the SSRF guard), the fix SHB-STORAGE1. Adversary review records machine-docs/reviews/PER-SITE-PROCESS-P2C2B-*.md. Inline #[cfg(test)] modules: none. 10 files point to a test file with #[path] (credentials.rs, export.rs, runtime.rs, runtime_registry.rs, save.rs, settings.rs, settings_cache.rs, ssrf_guard.rs, symlink.rs, utils.rs), and lib.rs has test-only items. Bucket moves: settings/page.rs from round 1's dashboard PR; the four lichen-cms deletions placed with what they became (see Moved in). Round 1's site core is split in this set into this PR, #35, #34 and #36. Round 1: #13 (Site crate core) covered most of these files; #12 (Dashboard) covered settings/page.rs. atproto.rs, export.rs, extensions.rs, runtime_registry.rs, save.rs, settings/edit.rs, settings_cache.rs, ssrf_guard.rs, token_scope.rs and utils.rs are new to the set in this range.

Commits 0