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

full 03: Site process and the front-site link #66

targeting full/02-permissions
Open opened by and assigned to notplants-bot.bsky.social
targeting full/02-permissions

Each site now runs in a process of its own. The front server launches it sandboxed (lichen sandbox -- lichen site serve), forwards each request to it over a passed socketpair along with who it admitted, stops it when idle, and replaces it when it crashes. This PR is that link in both crates: the launch and lifecycle, the control channel, the forwarded identity, the requests a site may make of the front server (a push into its hub, and its iroh endpoint's asks of its hub), and the asks the front server makes of a site. Since FULL-SITE-PROCESS the site process holds everything that is its site's: the front lends it the owner's session and node key over the control channel, sends its admin acts on connections the admin thread serves, and relays the site's own requests back to that admin thread.


AST-verified changes

  • site_process/{serve,host}.rs from lichen-server to lichen-site (PER-SITE-PROCESS P2c-2b commit 7d, 62dee6fa). Checked (run id not recorded): "moves 7b and 7d ok, plants refused"; adversary equal.

  • site_process/{control,identity,probe}.rs (and their tests) from lichen-server to lichen-site, with token_scope.rs and runtime_registry.rs (P2c-2b commit 1a, ff26fefc). Checked (run id not recorded): "5 moved, 48 carried, 8 files renamed, 19 visibility as mapped"; adversary 5/19. The token_scope.rs and runtime_registry.rs halves are in #37.

  • site_process/link.rs renamed into lichen-site with the owner seam (P2c-2b commit 2a, 02255939). Checked (run id not recorded): "2a recheck ok (12 moved, 5 carried, 1 renamed, 20 visibility; plant refused)". The credentials.rs and owner.rs halves are in #37.

  • peer_pid from the front server's requests.rs to lichen-site/src/site_process.rs (P2c-2b commit 0, 97e70426). Adversary: "moved4/vis1" (builder count not recorded). The commit's other items are in #37.

  • The front server's site_process/requests.rs → ipc/router.rs and site_process/site_credentials.rs → ipc/credentials.rs; children, children_of, storage and owner into ipc/ (ORR R8a, 67ad8a56). Checked by notify-772dd5a4: "PASS (AST EQUAL 4 moved, controls RED)"; adversary notify-dbcdad0f: "Move AST EQUAL 1324=1324". Allowed: pub(super) on 3 functions, scoped path rewrites. (The same unit's split of iroh routes into ipc/iroh.rs and the transport into ipc/transport.rs, d7061892, was checked by dedent, not by the AST checker.)

  • 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.

  • FULL-SITE-PROCESS 5a2 (#1647 on lichen.page.review, 765209c7 → 8d7bb2b4, map machine-docs/reports/fsp-5a2/move-map.json): SITE_MAX_PROCESSES, hub_push_spec, wisp_spec and on_path left the front server's site_process/front.rs for lichen-site/src/sandbox/spawns.rs (#39), visibility pub. Run notify-73fbe4f9: "equal=True, items 5557/5557, moved 4, stayed 5553, visibility 4", both plants caught; adversary rerun "equal=True, 5557/5557, moved 4" (notify-4a18fbec).

  • FULL-SITE-PROCESS 6a (#1651, deef05fc → 0b71076c, map machine-docs/reports/fsp-6a/move-map.json): site_spec, upload_pack_spec, restore_spec, backup_fetch_spec, hub_receive_spec and hub_fetch_spec left site_process/front.rs for sandbox/spawns.rs (#39), with three items from elsewhere. Run notify-82fbca10: "equal=True: items 5581/5581: moved 9, stayed 5572; visibility_changes 8, every one listed; comments compared 4995"; adversary rerun equal=True, 5581/5581, plants caught (notify-ec60785b). None of this PR's 5,395 changed lines can be skipped for these moves: every file here was created after 09-23 (+5,395/−0), so in this diff it reads as new code. The moves only tell you that the later reshuffles did not change what they carried.


What changed and why:

  • A process per site (PER-SITE-PROCESS). The front server keeps sign-in, the dashboard, the site index and the users. For a site's request it starts the site's process under the domain's lifecycle lock, forwards over a fresh socketpair (an upgrade is relayed byte for byte), stops it after an idle period (drain, then kill), and replaces a crashed one at the next request, at most once per back-off. Since FULL-SITE-PROCESS 6d an imported site's process is started first, to fetch the remote into its new hub with the owner's session; a cancelled start kills its process and waits for its exit before anything is removed. It also answers the site process's connects, the other half of lichen-sandbox's egress.
  • Who is asking (identity.rs). The front server signs the client in and checks the site's ACL, then sets one header on the private socket, after removing any copy the client sent: the identity, the role and the token's scope, no credential. The site process takes it out before any handler runs. Since FULL-SITE-PROCESS 3e it also carries the host's role (Role: Admin for the dashboard's acts and the front's own, Site otherwise, the default); the site's own push and its iroh endpoint's asks get roles of their own (own_push, iroh), admitted to those routes alone.
  • What crosses (SITE-FRONT-BOUNDARY, decision 9, unit 11a; OPERATOR-REVIEW-REVISIONS N8). The site's requests to the front server are POST /hub/git-receive-pack and, with iroh, /hub/iroh/... (ipc/router.rs), on a socketpair whose maker is checked by SO_PEERCRED; since FULL-SITE-PROCESS 6c and 6f3a the front relays both back to the same site process's admin thread, which holds the hub. There is no storage, owner or credentials question: the site reads its allowance from its area, and the owner's facts come with the launch. What the front server asks of a site (a revert, auto-save, publish settings, iroh peers and rooms, rewrite holds, a merge from the hub) is a route on the site's ordinary router (acts.rs, and the site's git routes), each named once in asks.rs (unit 12; since FULL-SITE-PROCESS 4b in lichen-site/src/site_seams/asks.rs, which lichen-server re-exports). Since FULL-SITE-PROCESS 6b the dashboard's admin acts come on connections of their own (ToSite::Admin), served by the admin thread's admin router (#38); the control channel also carries the owner's lent session (Credentials, 6d), the node key (NodeKey, 6f2), and what the admin thread tells the front of its hub (Told: the hub's weight, auto-pull, a publish finished).
  • Lifecycle by process basics (unit 11c). Readiness is GET /cms/api/health, a stop is SIGTERM, a recycle is exit status 75; two recycles within 10 s count as a crash. A watchdog recycles a process whose interpreter call is stuck in native code.
  • One shape in every mode (unit 11d): local mode, a single site and the desktop run site processes too, unconfined.
  • Build identity (unit 11e): lichen-server/build.rs hashes the sources into LICHEN_BUILD_ID, so a test that starts site processes from a built lichen refuses a stale one (use_built_site_executable); in production the site process is /proc/self/exe.

Files

Front server (crates/lichen-server/):

  • src/site_process/front.rs (1,823 lines) — launch, start, forwarding, idle stop, crash restart, recycles, imports, the owner's session lent and renewed, the egress answers (a datagram socket's too, with iroh). Read this first.
  • src/site_process.rs — the module's rule (what crosses) and re-exports.
  • src/site_process/asks.rs — re-exports the asks, now in lichen-site (below).
  • src/ipc.rs, src/ipc/router.rs, src/ipc/transport.rs — what the front server answers a site process (the relays to its admin thread), and the SO_PEERCRED check.
  • build.rs — the build id.

Site (crates/lichen-site/src/):

  • site_process/serve.rs — lichen site serve: preamble, handshake, the runtime, serving passed connections, health first, exit status.
  • site_process/control.rs — the control channel's frames and descriptor passing (Admin, Credentials, NodeKey, Told among them); HEALTH, EXIT_RECYCLE.
  • site_process/identity.rs — the forwarded identity, taken out of the request, and the host's Role.
  • site_process/link.rs — the site's side of its requests to the front server.
  • site_process/host.rs — the site process as its site's host (SiteHost), from the launch handshake; with iroh, its node key and the start of its iroh runtime.
  • site_process/session.rs — new: the owner's atproto session as the admin thread holds it, in memory alone, answered only while unexpired (6d).
  • site_seams/asks.rs — new here: every ask of a site process and of its main runtime by its admin thread, method and route named once, for SANDBOX.md's table and its check.
  • site_process/watchdog.rs — interpreter calls watched; a stuck one recycles the process.
  • site_process/probe.rs — test-only probe routes (test-routes feature), in a permission group, with the admin thread's half on its admin router (6b).
  • site_process.rs — the modules, and peer_pid (SO_PEERCRED; a macOS version that has never been built: no macOS venue).
  • acts.rs, ipc.rs, ipc/git.rs, ipc/publish.rs, ipc/settings.rs — the asks as ordinary routes, and their thin handlers.

Lives elsewhere

  • The sandbox a site process enters, its egress filter, call groups, reaper and memory bound: #17. Its spawn permissions (SITE_PROCESS): #18.
  • The hub receive that /hub/git-receive-pack reaches: #21. The area the allowance is read from: #20.
  • The site's iroh (its runtime, node key and hub asks; ipc/iroh.rs, whose room bridge is gone): #28. The terminal pair the front server opens: #36.
  • The start-up order and the admin thread with its admin router (site_process/start.rs, admin.rs): #38. Every spawn's sandbox, site_spec included: #39. The child-routes socket: #40. The site process's hub routes (site_process/hub.rs): #21.
  • SANDBOX.md (what crosses, and why) is in the base commit, as are the tests.

Category: Site process and the front-site link Size: 22 files, +5,395/−0 Rounds: round 0 at stack ae42e3c0; round 1 at 29a7c15b (SFB fix-up 13b: acts.rs, site_process/host.rs, +4/−2); round 2 at 6b650b2a (SECURITY-FOLDER-AND-PRINCIPALS): path rewrites in ipc/router.rs and site_process/front.rs; site_process/serve.rs layers api::files::read_refused_bodies, so a body a route refuses is read to its end and its 413 reaches the front server instead of a closed connection (SFP fix-up 4b, 128873c6). Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/19/round/2/interdiff. Round 3 at b66397c0 (FULL-SITE-PROCESS): the admin acts on admin connections (6b), the owner's session lent and renewed (6d), the node key (6f2), the site's push and iroh asks relayed to its admin thread (6c, 6f3a), the host role (3e), imports started in the site process (6d), the asks moved to site_seams/asks.rs (4b), and nine spawn builders moved out to #39 (5a2, 6a, AST-checked). Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/19/round/3/interdiff Plans and ledger: PER-SITE-PROCESS-2026-10-09.md with PER-SITE-PROCESS-P0-DESIGN-2026-10-09.md (§1–§4, §13, §15–§18; P2c-2b, P4-5), OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N8, R8a), SITE-FRONT-BOUNDARY-2026-10-17.md (decision 9; units 11a–11e, 12) with SITE-FRONT-BOUNDARY-U10-DESIGN-2026-10-23.md, -U11-DESIGN-2026-10-24.md and -U12-DESIGN-2026-10-24.md; FULL-SITE-PROCESS-2026-10-25.md with -U0-DESIGN-2026-10-26.md (units 3e, 4b, 5a2, 6a–6f). Inline #[cfg(test)] modules: none. 9 files point to a test file with #[path]; 1 has test-only items. Bucket moves: none from round 1. site_process/asks.rs is new since round 1 (SFB unit 12) and joins its module here; in round 4 the new site_process/session.rs and site_seams/asks.rs (the asks' new home) are placed here by an explicit rule. Round 1: #3 (Site process and the front-site link) covered these files from 0b92cb61, except asks.rs.

This pull request has no versions submitted yet.