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

full 20: The shell: lichen-shell folded into the site #83

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

The web terminal. The lichen-shell crate is folded into lichen-site as its shell module. On a multi-site server with the sandbox on, the shell now runs as a child of its site's process, inside that process's sandbox, instead of in one of the old per-terminal sandboxes (bubblewrap, systemd-run, an incus VM); those are deleted. The front server opens each terminal's PTY pair for a site process, so the sandbox grants no terminal device.


AST-verified changes

  • PER-SITE-PROCESS P2c-1 shell fold (317941bd): crates/lichen-shell/src/** became lichen-site's src/shell.rs and src/shell/ (21 files). Map machine-docs/reviews/P2C1-SHELL-MOVE-MAP-2026-10-09.json. Checked by the builder (run id not recorded): "Shell move check (rn6, b33796d7..317941bd) ok: 3683 items, 21 renamed, 145 carried, 0 visibility". The adversary's rerun: "317941bd equal=true 21-renamed 145-carried 0-vis". Allowed: the scoped rewrites lichen_shell → lichen_site::shell, and crate:: → crate::shell:: inside the folded files.

Roughly 112 of this PR's 1,833 changed lines are code the fold carried verbatim. In git diff -M, git pairs pty.rs, router.rs and session.rs with their lichen-shell originals, so only their later changes show. Most of the rest is the deleted sandbox backends (−1,116 in lichen-shell), which have no new home: they were removed, not moved.

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


What changed and why:

  • The fold (P2c-1). The crate became a module, with no change of behaviour in the fold commit itself (above).
  • The terminal runs in its site's process (P4-4b, design note §18 A10; shell/sandbox.rs, new). portable-pty starts the shell through the pre-main hook's nest mode (/proc/self/exe nest -- <shell>), which gives it a nested Landlock domain of its own inside the site's. Its environment is the base list, TERM, and a HOME in the site's .lichen. In local mode the shell runs unwrapped with the user's own HOME, USER, LOGNAME and SHELL. The old backends (lichen-shell/src/sandbox/{bwrap,systemd,incus,unsandboxed}.rs, incus.rs's VM pool, ShellState::with_incus) are deleted; the module docs say they are archived at lichen-code-archive 1f01c136.
  • The PTY pair comes from the front server (STC-PTS1; shell/peer_pty.rs, new). A sandboxed process that opened its own terminals needed all of /dev/pts, which let one site's terminal open another's. A site process now asks the front server for each pair; outside a site process the pair is opened locally. The child end is opened from the pair, never by path.
  • Closing a terminal hangs it up (pty.rs, SITE-FRONT-BOUNDARY decision 6). In a site process the nest mode is the session's reaper: closing sends SIGHUP, the nest mode passes it to the shell (and kills a shell that ignores it), and it ends once nothing of the session is left, a nohup'd job included. Local mode keeps portable-pty's kill.
  • The routes are a resource under the site's API, in the site's WRITES group (router.rs): POST /cms/api/shell, GET /cms/api/shell/{id}/ws and DELETE /cms/api/shell/{id}. session.rs keeps the idle and hard timeouts.

Files (crates/lichen-site/src/ unless noted)

  • shell/sandbox.rs — new: what a shell is started as, in a site process and in local mode. Read this first.
  • shell/peer_pty.rs — new: the PTY pair from the front server (STC-PTS1), and since FULL-SITE-PROCESS 6g the shell's own child-routes socket at its descriptor (ROUTES_FD), where it may ask for a publish or a push (#40).
  • shell/pty.rs (from lichen-shell/src/pty.rs) — spawning, and the hangup and reaping on close; bash is found on the PATH without running which as a child (SECURITY-DEBT FSP-NESTLESS1).
  • shell/session.rs (from lichen-shell/src/session.rs, −146) — sessions and timeouts, without the backend choice.
  • shell/router.rs (from lichen-shell/src/router.rs), shell.rs — the routes and the module root.
  • crates/lichen-shell/ (Cargo.toml, lib.rs, incus.rs, sandbox/*.rs) — deleted.

Lives elsewhere

  • The nest mode itself and the nested domain (prepare_ordinary): #17. The front server's side of the PTY request (front_pty, ToFront::Pty): #19.
  • Who may open a terminal (the routes' groups and Permissions): #18.
  • Not in any PR (base commit): shell/README.md, the built-in editor's terminal (xterm), and the tests (tests/terminal_ws_tests.rs and the shell's #[path] tests).

Category: The shell: lichen-shell folded into the site Size: 14 files, +484/−1,349 Rounds: round 0 at stack ae42e3c0, unchanged at 29a7c15b and 6b650b2a; round 1 at b66397c0 (FULL-SITE-PROCESS): shell/peer_pty.rs gives the shell its own child-routes socket, where it may ask for a publish or a push (6g, #40), and shell/pty.rs finds bash on the PATH itself instead of running which as a child with no domain of its own (FSP-NESTLESS1). Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/36/round/1/interdiff Plans and ledger: FULL-SITE-PROCESS-2026-10-25.md (6g); PER-SITE-PROCESS-2026-10-09.md (P2c-1; P4-4b, design note PER-SITE-PROCESS-P0-DESIGN-2026-10-09.md §18 and amendments A7, A10), CHILD-ENV-CLEAR-2026-10-08.md (E1, E2: the shell's environment), SITE-FRONT-BOUNDARY-2026-10-17.md (decision 6), the fix STC-PTS1. Inline #[cfg(test)] modules: none. 3 files point to a test file with #[path] (shell/pty.rs, shell/sandbox.rs, shell/session.rs), and shell/peer_pty.rs has test-only items. Bucket moves: none from round 1, which put the shell in site core. Split out here because the lichen-shell fold and the backends' removal make it a subject of its own. Round 1: #13 (Site crate core) covered shell.rs, shell/peer_pty.rs, shell/pty.rs and shell/router.rs. The crate deletion, shell/sandbox.rs and shell/session.rs are new to the set in this range.

Commits 0