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

full 15: Front server: state, server settings and features #78

targeting full/14-front-auth
Open opened by and assigned to notplants-bot.bsky.social
targeting full/14-front-auth

The front server's state and settings. ServerState no longer builds or holds site runtimes: every site runs in its own process. Instead it holds what the front server owns: each site's process, the dashboard's site events, the sign-in rate limiter and the owner's session it lends; since FULL-SITE-PROCESS a site's publish lock, iroh endpoint, hub and import are its process's. A single-site server keeps its own state beside the site rather than inside it. The site side of features.rs moved into lichen-site, leaving the startup decisions here.


AST-verified changes

  • The site side of features.rs (18 items: the feature routers, the site settings router, the site runtime's construction, the publish hooks, the git listener, iroh sync's start and the identity record) to lichen-site's features.rs (#37; P2c-2b commit 6, 247be4a2). Run id not recorded: "move 6 ok: 18 moved, 0 vis" (lichen-builder.md 2370); adversary "18 moved/0 vis".
  • SiteRecord (with SETTINGS_FILE and its impl) from index.rs to lichen-site's types.rs (#37; P2c-2b commit 1a, ff26fefc). Run id not recorded: "5 moved, 48 carried, 8 files renamed, 19 visibility as mapped" (lichen-builder.md 2350); adversary 5/19.
  • pds_handle_of from the front server's git/hooks.rs (#23) to owner.rs (PER-SITE-PROCESS P2b-2, 3b0c7e49). Run id not recorded: "ok (1443 items, 1 moved, 0 visibility changes, 1997 comments compared, 124 module declarations)" (lichen-builder.md 2300); adversary "moved=1 visibility_changes=0 (matches builder)". The function was written after 09-23 and owner.rs is new, so here it reads as new code.
  • The path rewrites of SECURITY-FOLDER-AND-PRINCIPALS units 1 and 2 in this PR's files (crate::site_area → crate::admin_area, crate::access → crate::security::access, and the other security:: paths), which those checks cover (unit 1 notify-eebdd32e, unit 2 adversary notify-020eb199, both equal, errors 0), with the rustfmt rewraps after them (d6c25912, bdaa795b), proven by cargo fmt.

Roughly 460 of this PR's 3,972 changed lines are code these moves carried verbatim, nearly all of it what features.rs lost (−999).


  • state.rs keeps the index, the user store, the tree store, the dashboard's event stream and invites. It loses the site runtime registry, the settings-listener window and the storage-limit map, because no runtime is built here (SITE-FRONT-BOUNDARY 11e11). It gains:
    • each site's process (site_processes);
    • until FULL-SITE-PROCESS, each site's publish lock (9a) and iroh transport (10b1): both are now the site process's (6e, 6f3b), and the front server holds neither;
    • the password sign-in limiter;
    • a per-site lifecycle lock (with_site_lock), which the site process's start and stop also hold;
    • the dashboard's site events (created, deleted, publish finished), always addressed to the site's owner;
    • the import of a site from a remote: started here, done by the site process's admin thread (its migrations, the fetch with the owner's session, the hub's main, its settings checked by git), a cancelled import waiting for the process's exit before its area goes (10a; FULL-SITE-PROCESS 6d);
    • the owner's session, lent to each running site process and lent again after a new sign-in (owner_session_changed, 6d), and each site's auto-pull as its process last told (told_auto_pull);
    • a deleted site's cleanup, asked of its own process before its tree goes (6h);
    • the single-site owner claim (admit_login, authorize_single_site_login): an unowned single site is claimed by its first login, and only that user may sign in after it (phase 12, unit 7).
  • server_settings.rs:
    • SINGLE-STATE1: a single-site server's own state (its users, tokens, keys, OAuth signing key, owner claim and its own settings) lives in LM_STATE_DIR or in .<site>.lichen/ beside the site. It is moved there once, item by item and crash-safe, and server keys left in the site's settings.toml are then ignored, with a warning.
    • New settings: [egress] allow_private (read from [proxy] too, with a warning), trusted_proxies, site_memory_limit, use_shortcodes_cache, [[plugins]] and remote_plugins_enabled. hot_reload_templates is gone with Tera.
    • confined() and site_processes() say how sites run in each mode.
  • owner.rs — new: the owner's public facts only (how they sign in, their PDS handle, their deploy key), given to a site process at launch. A change of those facts recycles the owner's site processes (N8). The owner's session is in security/credentials.rs (#30).
  • features.rs is now the shell and iroh startup decisions and re-exports; most of what remains is its inline test modules.
  • index.rs: a site name must be a valid host name and loses a trailing dot. Two folders whose names differ only in case are one host, so the second is not served. A create that finds the domain's folder left by an unfinished delete refuses it (FolderLeft, a 409) and adopts nothing: a new site's first build is its process's (FULL-SITE-PROCESS 1d).
  • lib.rs: the new modules, the hub's pre-receive entry point, and BUILD_ID / --build-id, which a test uses to check the site executable is this build. Its iroh module is gone (the iroh tests stay here, iroh_tests).
  • front_seams.rs — new (FULL-SITE-PROCESS unit 4): what the code that moved into the site process is given, built from this server's state, and the front server's forwards to a site's hub and admin thread (forward_to_the_hub, forward_to_the_admin_thread, admin_act, rewrite, force_push).
  • Cargo.toml: depends on lichen-site (was lichen-cms), lichen-sandbox and lichen-env. It adds iroh (since FULL-SITE-PROCESS 6f3b only to mint the sites' node keys: the endpoints are the site processes', and iroh-blobs went with them), the hyper client used to reach a site process, zeroize and flate2. lichen-iroh, lichen-shell and tera are removed. It adds a test-routes feature and test-only zip, syn, quote and sha2.

Files (crates/lichen-server/)

  • src/state.rs — what the server holds. Read this first.
  • src/server_settings.rs — SINGLE-STATE1, new settings, confined, site_processes.
  • src/owner.rs — new: the owner's public facts.
  • src/front_seams.rs — new: the seams' front side and the forwards.
  • src/features.rs, src/index.rs, src/lib.rs — as above.
  • Cargo.toml — dependencies.

Lives elsewhere

  • The dashboard API that acts on this state (api.rs), the CLI and the desktop: #32.
  • Host routing and sign-in: #30. Permission groups: #18.
  • The site process launcher (site_process/front.rs): #19. The site's iroh endpoint: #28. The publish and its lock, the admin thread's: #25.
  • What moved to lichen-site (features.rs, types.rs, settings/server.rs): #37.
  • Tests (tests/state.rs, tests/server_settings__tests.rs, workspace_structure.rs, e2e) are in the base commit.

Category: Front server: state, server settings and features Size: 8 files, +2,278/−1,694 Rounds: round 0 at stack ae42e3c0; round 1 at 6b650b2a (SECURITY-FOLDER-AND-PRINCIPALS): path rewrites in index.rs, lib.rs (mod access → mod security) and state.rs; and state.rs removes a cancelled import's area only under its opening (SFP fix-up 4b, 6032b15e; the opening is in #20). Read that round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/31/round/1/interdiff; round 2 at b66397c0 (FULL-SITE-PROCESS): state.rs holds no publish lock and no iroh transport (the site process's now); an import and a deleted site's cleanup are asked of the site's process; the owner's session is lent to running site processes; front_seams.rs is new (unit 4); index.rs refuses a leftover folder (FolderLeft); Cargo.toml drops iroh-blobs. Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/31/round/2/interdiff Plans and ledger: FULL-SITE-PROCESS-2026-10-25.md (1d, 4, 6d, 6e, 6f3b, 6h); SITE-FRONT-BOUNDARY-2026-10-17.md (9a, 10a, 10b1, 11d, 11e, 11e11, 12), PER-SITE-PROCESS-2026-10-09.md (P2b, P2c-2b, P3-1, P4-1), OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N8), DASHBOARD-REACT-2026-10-07.md (unit 1: site events), PHASE-12-OPERATOR-ANSWERS-2026-10-01.md (unit 7), SINGLE-STATE1 (machine-docs/reviews/SECURITY-TABLE-CONFORMANCE-S2-REVIEWS-2026-10-13.md); lichen-builder.md 2300, 2350, 2370. Inline #[cfg(test)] modules: features.rs (staged_decoration_tests, iroh_lifecycle_tests). Three files point to a test file with #[path]; lib.rs and owner.rs have test-only items. Bucket moves: none against round 1. Round 1's front-state PR is split in two: state and settings here, and the dashboard API, CLI and desktop in #32. Round 1: #11 (Front server: state, the dashboard API, CLI and desktop) covered these files from 0b92cb61.

This pull request has no versions submitted yet.