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

product: 03 Site process and the front-site link #50

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

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

description: Review set for lichen.page.review, 0b92cb61 (reviewed) → eb5a1653 (stack). PR 3 of 16. 22 files, +2140/−1206. One commit, ebb307a3aa44, which sets these files to their content at eb5a1653. Targets #2 (oct/02-permissions). #4 stacks on this one. Tests and generated files are not in any PR; they are in main.

The front server and a site process now talk in fewer ways. The operator's 2026-10-17 rule (SITE-FRONT-BOUNDARY): the site process holds its own tree and nothing else, and the two talk only by ordinary authenticated HTTP requests and by git, plus the terminal pair the front server opens and passes over.

What that changes here:

  • No private router. The site's /ipc/ and /acts/ routes, which only the front server could open on an "admin" connection, are gone. What the front server asks of a site (auto-save, publish settings, iroh peers and rooms, a revert) is now ordinary routes under /cms/api/ in ordinary permission groups (lichen-site/src/acts.rs), on the same connection kind as a client's request. The front server forwards who it admitted (the identity header); the site never sees a client's cookie or token (STC-FWDCRED1).
  • The site asks the front server for one thing: a push into its own hub (POST /hub/git-receive-pack, lichen-server/src/ipc/router.rs). The owner-session route (/credentials), the storage question and the owner question are gone: the site holds no credential, reads its storage allowance from its area, and gets the owner's public facts at launch; a change of those facts recycles the process (N8).
  • Lifecycle by process basics, not messages (unit 11c): readiness is a health route (GET /cms/api/health), a stop is SIGTERM, and a recycle is exit status 75; the Admin, Shutdown, Ready, Failed and Recycle frames are gone. Two recycles within 10 s count as a crash.
  • One shape in every mode (decision 15, unit 11d): local mode, a single site and the desktop also run each site in its own process, only unconfined. confined() says which.
  • Build identity (unit 11e): lichen-server/build.rs hashes the sources into LICHEN_BUILD_ID, so a test or a server refuses to launch site processes from a stale lichen executable.

info: Plans: SITE-FRONT-BOUNDARY-2026-10-17.md (decisions 9 and 15; units 11a–11e; "Lifecycle by process basics"), its notes SITE-FRONT-BOUNDARY-U1-DESIGN-2026-10-19.md and -U10-DESIGN-2026-10-23.md; OPERATOR-REVIEW-REVISIONS-2026-10-13.md (N8 and its refinements: the site owns its locks and runtime, reads its allowance and backup directly, minimal cross-process actions). Earlier background: PER-SITE-PROCESS-2026-10-09.md.

Files

Front server (crates/lichen-server/src/):

  • site_process/front.rs (+1012/−194) — launch, start under the runtime lock, forwarding, idle stop, crash restart. New: the health check in place of Ready, exit-status recycles, unconfined processes for local/single/desktop, use_built_site_executable and its build-id check, the drain bound after an upgrade error. Read this first.
  • site_process.rs — the module's rule (what crosses) and its re-exports; memory and reaper now come from lichen-sandbox (#1).
  • ipc.rs, ipc/router.rs, ipc/transport.rs — new home of what the front server answers a site process: the passed socketpair, checked by SO_PEERCRED, served with a router that has the hub push and nothing else.
  • site_process/requests.rs, site_process/site_credentials.rs — deleted (the old router with /storage, /credentials, /owner).
  • site_process/reaper.rs — deleted here; it moved to lichen-sandbox (#1).
  • ../build.rs — the build id.

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

  • acts.rs — new: the front server's asks as ordinary routes, in groups.
  • ipc.rs, ipc/git.rs, ipc/publish.rs, ipc/settings.rs — the handlers behind acts.rs, now thin; the git module keeps only revert (the rewrites are the front server's, #6).
  • site_process/serve.rs — lichen site serve: preamble, launch handshake, serving every passed connection; health first; exit status says why it stopped.
  • site_process/control.rs — the frame protocol, smaller: Admin, Shutdown, Ready, Failed and Recycle go; HEALTH and EXIT_RECYCLE come.
  • site_process/link.rs — the site's side of its requests to the front server; the children list and credentials asks go, the owner's facts are kept from the launch.
  • site_process/host.rs — the site process as its site's host: its area, owner facts and public iroh identity from the launch; a test can answer its own hub pushes.
  • site_process/watchdog.rs — a stuck interpreter call makes the process drain and exit 75 itself.
  • site_process/probe.rs — test-only probe routes (built with test-routes), now in a permission group, with switches for the 11c/11d fail-closed tests.
  • site_process/identity.rs, site_process.rs — the forwarded identity marks the request as admitted; peer_pid for macOS (unbuilt: no macOS venue, a named evidence gap).

Moved in: lichen-server/build.rs and lichen-server/src/ipc.rs (front server core, by path) and lichen-site/src/acts.rs and lichen-site/src/ipc.rs (site core): each is part of the link between the two processes.

Moved out

  • lichen-site/src/ipc/iroh.rs is the room bridge's site half, so it is in #9.

Lives elsewhere

  • The hub's receive that /hub/git-receive-pack reaches: #5.
  • The terminal pair the front server opens (shell/peer_pty.rs): #13.
  • The tests of all this (crates/lichen-cli/tests/site_process_tests.rs, lichen-server/src/tests/site_process__front.rs, ipc__*.rs, …) are in main.
Commits 0