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

full 07: Git with a site's remote: pushes, fetches and rewrites, on the site process's admin thread #70

targeting full/06-site-git
Open opened by and assigned to notplants-bot.bsky.social
targeting full/06-site-git

Git with a site's remote, and the rewrites of its history: the push of a site's hub to its remote, the fetch of the remote into the hub, the compact, force-pull and force push, the editor's git panel, the remote record and the SSH with the owner's deploy key. Since FULL-SITE-PROCESS this code is lichen-site's and runs on the site process's admin thread, which holds the hub, the area's copy of the deploy key and the owner's session the front server lends it; the front server admits these requests and forwards them, and pushes and fetches nothing itself. On 09-23 the front server ran the sites' git against each site's working tree; that code went to lichen-site's main runtime (#22), and this is what replaced it.


AST-verified changes

  • push_worker.rs moved from lichen-site/src/git/ back to lichen-server/src/git/ (SITE-FRONT-BOUNDARY 6b, 0dcf0cc9). Checked (run id not recorded on the move line): "the push worker AST-moved, checker ok, 3/3 plants CAUGHT"; adversary rerun (A11631): "AST move c379c890→0dcf0cc9 rerun: equal". The site's copy existed only inside this range (P2c-2b commit 4 had moved it there), so here the file reads against its 09-23 self.
  • What git/hooks.rs and git/router.rs gave lichen-site in P2c-2b commit 4 (9fe049e0), item by item; both now re-export lichen_site::git::hooks and ::router. Checked (run id not recorded): "moves 4 ok (56 moved, 189 carried, 6 renamed, 33 vis)"; adversary rerun (A11334): "equal=true 56 moved/6 renamed/189 carried/33 vis 0 errs". The site's half is in #22.
  • pds_handle_of from git/hooks.rs to owner.rs (PER-SITE-PROCESS P2b-2, 3b0c7e49). Checked (run id not recorded): "ok (1443 items, 1 moved, 0 visibility changes, 1997 comments compared, 124 module declarations)"; adversary rerun (A11322) matched. owner.rs is in #31.
  • try_refresh_token from git/hooks.rs to credentials.rs (P2c-2a seam 2, 6ec79159). Checked: "move 6ec79159 ok (1 moved, 0 visibility), plant refused"; adversary rerun (A11326): "equal=true, 1452 items, 1 moved, 0 visibility". credentials.rs is in #30.
  • git/mod.rs renamed to git.rs, one of the 19 foo/mod.rs → foo.rs renames of OPERATOR-REVIEW-FIXES U1 (N8, f6a10c32). Checked over 8b30050a..f6a10c32 together with U1's moves (run id not recorded): "3004 items, 48 moved, 69 carried, 19 renamed, 2 visibility as mapped, 233 module declarations"; adversary rerun notify-abab368a, the same counts. In this diff the two halves are separate entries (git.rs is new, git/mod.rs deleted), because the file changed a great deal after.
  • PdsScope and its impl from lichen-site/src/owner.rs to site_area/remote.rs (SITE-FRONT-BOUNDARY 7b). The landed commit, 5670cd85, mixes the move with deletions, so the move was checked on a rebuilt pure move (on e3950a4b): run notify-9a972a6c, "equal, 2 moved, 4970 stayed, no errors"; adversary rerun notify-222a9edd: "equal, 2 moved, 0 errors; 3 plants refused". Allowed: the path lichen_site::owner::PdsScope → crate::site_area::remote::PdsScope in code and docs. The site's half is in #37.
  • SECURITY-FOLDER-AND-PRINCIPALS unit 2 (b603dbbe): site_area/remote.rs → admin_area/remote.rs, part of site_area/ → admin_area/ (13 files renamed, crate::site_area → crate::admin_area in all lichen-server Rust). Checked by the builder: "AST equal three ways (8da73502 → b603dbbe, → bdaa795b, → 067dbe37), plants caught"; adversary rerun notify-020eb199: "all equal=True (2471/2471, renamed 13, carried 426, visibility 0, errors 0)". The non-move edits (d85a66d2: include_str! paths, literals) and the rustfmt (bdaa795b) are separate commits.
  • 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 5b (#1649 on lichen.page.review, 26779036 → 0e8871fe, map machine-docs/reports/fsp-5b/move-map.json): one move commit (85d6303c) renamed 34 files whole from lichen-server to lichen-site (19 modules and their 15 #[path] test files); lichen-server re-exports each where it was. Here: git/{outbound, inbound, rewrite, editor, push_worker, seams, ssh_relay}.rs and admin_area/remote.rs, now under crates/lichen-site/src/, and the re-exports and mod lines in lichen-server's git.rs. Checked by run notify-981f7470: "equal=True; items 5557/5557, stayed 4752, carried 805 in the 34 renamed files; visibility_changes 99, every one listed; comments compared 4991; module declarations checked 352"; plants item_edited and visibility_unlisted caught (a dropped declaration gate is not caught, because the checker lets declarations change; the PR's gate table covers it); adversary reruns equal (notify-c53fbd02). Allowed: the 99 listed visibility changes, pub for what the front server calls (6a2b574d, c04bebac, 0e8871fe, inside the checked range). Outside the check: 26779036 (path-only, before the move), e0dc7411 (FSP-NOMDBOOK1), the for_tests accessors and test hooks, ad03def8 (rustfmt).

Roughly 330 of this PR's 5,251 changed lines are code these moves carried verbatim. The push, the fetch, the rewrites, the editor's panel, the remote record and the SSH were all written after 09-23, so in this diff they read as new code; the moves only tell you that unit 5b's crossing into lichen-site changed no item.

On this forge the renamed file here (git/push_worker.rs) shows as a whole deletion and a whole addition, so its diff reads 5,427 changed lines; with rename detection (git diff -M full/06-site-git full/07-git-sync) it is 5,251.


What this code does (SITE-FRONT-BOUNDARY units 6, 7, 8, 11b1; FULL-SITE-PROCESS units 4, 5b, 6d):

  • Who runs it (FULL-SITE-PROCESS 6d): the site process's admin thread, which holds the site's area (the hub, the remote record, the deploy key's copy) and the owner's atproto session the front server lends over the control channel (#19). The front server admits each request in its group and forwards it (editor_forwards in server.rs, front_seams); lichen-server/src/git.rs says it pushes and fetches nothing. Unit 4 first made this code take explicit inputs rather than the front server's state (git/seams.rs: Sites, Owners, SiteHandle), so that unit 5b could move it unchanged.
  • Push (git/outbound.rs, unit 6): after each move of the hub's main (a site's push, a client's, a rewrite), one coalescing worker per site (git/push_worker.rs) pushes it to the remote, when the site's sync says auto_push. A failed push is retried after 5, 10, 20, 40, 60 and 300 seconds, for at most an hour. Git runs from the hub with no system or global config, what a push could take from the hub's config pinned on the command line (no hooks, no credential helper, a fixed list of protocols), with the owner's PDS session piped to git-remote-pds or the area's copy of the deploy key. A failed push is shown to the owner, redacted, until one succeeds.
  • Where to (admin_area/remote.rs): remote.toml in the site's area, which the admin thread reads and writes and its migrations make: the URL the owner confirmed on the dashboard, the URL found in the site's own .git/config (a proposal, never pushed to or fetched from), the sync toggles, and site_key (a key the site's settings named, which lichen does not take).
  • Fetch (git/inbound.rs, unit 7): the dashboard's auto-pull, the editor's pull and remote status fetch into the hub's hidden refs/remotes/origin/*, through a staging repository bounded by the owner's remaining storage and screened with the hub's tree rules; a tip that would bring back history a compact replaced is not taken, and the site then merges from the hub. The same file fetches each collaborator's git_remote into the hub (fetch_a_collaborator, unit 10c) and takes an iroh peer's pack (take_a_peers_pack, #28).
  • Rewrites (git/rewrite.rs, unit 8): the site's main runtime holds and pushes its pending work, the admin thread moves the hub's main by compare-and-swap, the main runtime resets to it, and only then is anything irreversible done (the old objects pruned, the forced push). A rewrite left unfinished is settled when the site next opens (the admin thread checks at start, #38). The dashboard's compact, force-pull and force push reach it as /hub/compact, /hub/force-pull and /hub/force-push on the admin router; a single site's /cms/api/git/compact, force-pull and force-push go the same way. take_a_client_push takes a full token's force push (#21).
  • The editor's git panel (git/editor.rs, unit 11b1, FULL-SITE-PROCESS 6d): /cms/api/git/status, remote-status, push and pull on the site's host, served by the admin thread on the hub in the groups and answer shapes the site's routes had; what it needs of the site it asks the main runtime as the client admitted.
  • SSH (git/ssh_relay.rs, owner_key.rs; PER-SITE-PROCESS P3-4, FULL-SITE-PROCESS 4f1): the admin thread's own SSH with the owner's deploy key. The front server keeps the key in the user store and copies it into the site's area before the site's process launches, and rewrites or removes every copy when the key or the site's owner changes; owner_key.rs writes and reads that copy without following a link. For each run of git the key is written to a 0600 file in the area's git-keys, outside the site, and removed after; host keys are learned once and held to. The main runtime and its children never reach the key, unless the owner opts them in. The relay a site's git once ran ssh through is gone.
  • What stays in the front server (lichen-server/src/git.rs, git/hooks.rs, git/router.rs): a new site's first commit, the word of a rewrite a site undid, the dashboard's auto-pull hint (auto_pull_sites), and re-exports of the moved modules.

Files

Moved into crates/lichen-site/src/ (FULL-SITE-PROCESS 5b), run by the admin thread:

  • git/outbound.rs — the push. Read this first.
  • git/inbound.rs — the fetch into the hub, collaborators' remotes, a peer's pack.
  • git/rewrite.rs — compact, force-pull, force push under the site's hold.
  • git/editor.rs — the editor's git panel.
  • git/push_worker.rs (+4/−47 against its 09-23 self in lichen-server) — the scheduling loop (run, run_with_wait) that outbound.rs drives.
  • git/seams.rs — what a git route is given of its site (unit 4).
  • git/ssh_relay.rs — the admin thread's SSH. The front server's old lichen-server/src/git/ssh_relay.rs is deleted; it existed only inside this range, so this diff shows only the new file.
  • admin_area/remote.rs — the remote record and the sync toggles; PdsScope.
  • owner_key.rs — new: the area's copy of the owner's deploy key.

Still in crates/lichen-server/src/:

  • git/hooks.rs (+30/−363) — auto_pull_sites; re-exports the site's hooks.
  • git/router.rs (+11/−420) — re-exports the site's git router; the old push, pull and rewrite handlers are gone.
  • git.rs — a new site's first commit, and re-exports of the moved modules. git/mod.rs (−38), the 09-23 root it was renamed from, is deleted here.

Moved in: git/mod.rs's deletion, with the git.rs it became (git pairs them only below its default similarity); admin_area/remote.rs (round 1's starting map put it under Site area: it is the remote record this code reads); owner_key.rs (new this round: the key this code's SSH reads).

Lives elsewhere

  • The site's own git (commits, recovery, the push into the hub): #22. The hub's receive and the site process's hub routes: #21. The hub repository's rewrite helpers: admin_area/hub_repo.rs in #20.
  • The dashboard routes that start these acts: api.rs in #32. The owner's session as the front server holds it: security/credentials.rs in #30; as the site process holds it (site_process/session.rs): #19. The front server's side of the seams (front_seams.rs): #31.
  • The admin thread and the site process's start: #38.
  • A publish's push of the source and its Pages deploy: #25.
  • iroh's git sync, which hands a peer's pack to take_a_peers_pack: #28.
  • Tests (git/tests/*.rs, lichen-server's tests/moved/*_tested.rs, lifecycle_tests.rs, e2e git tests) are in the base commit.

Category: Git with a site's remote: pushes, fetches and rewrites, on the site process's admin thread Size: 13 files, +4,383/−868 Rounds: round 0 at stack ae42e3c0; round 1 at 6b650b2a (SECURITY-FOLDER-AND-PRINCIPALS): remote.rs renamed into admin_area/ (unit 2), and path rewrites and rustfmt in git/{editor,hooks,inbound,outbound,rewrite}.rs; round 2 at b66397c0 (FULL-SITE-PROCESS units 4, 5b, 6d, 7): the push, fetch, rewrites, editor's panel, remote record and push worker moved into lichen-site (5b, AST-checked) and now run on the site process's admin thread with the owner's lent session and the area's copy of the deploy key; the SSH is the admin thread's (git/ssh_relay.rs anew, the front server's deleted); new git/seams.rs and owner_key.rs; the front server's git.rs, hooks.rs keep only what is the front's. Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/23/round/2/interdiff Plans and ledger: FULL-SITE-PROCESS-2026-10-25.md (parts 4–7; units 4, 4f1, 5b, 6d, 7) with FULL-SITE-PROCESS-U0-DESIGN-2026-10-26.md; SITE-FRONT-BOUNDARY-2026-10-17.md (units 6, 7, 8, 10c, 11b1; decision 7 as revised 2026-10-21) with SITE-FRONT-BOUNDARY-U6-DESIGN-2026-10-21.md, -U7-DESIGN-2026-10-22.md, -U8-DESIGN-2026-10-22.md; PER-SITE-PROCESS-2026-10-09.md (P2b-2, P2c-2a, P2c-2b, P3-4); OPERATOR-REVIEW-FIXES-2026-10-07.md (U1, N8). Ledger: lichen-builder B2087, B2300, B2339, B2367, B2931, B3111; lichen-adversary A11224, A11322, A11326, A11334, A11631, A11639. Inline #[cfg(test)] modules: none inline; 5 files point to a test file with #[path] (the front-needing tests came back to lichen-server's tests/moved/, in the base commit). Bucket moves: round 1's Git PR is split in two: git with the site's remote here (the front server's until FULL-SITE-PROCESS, the admin thread's now), the site's own repository in #22. git/mod.rs's deletion is here with git.rs. Every file kept its PR when FULL-SITE-PROCESS moved it into lichen-site: the subject did not change, its home did. Round 1: #6 (Git: the front server pushes, fetches and rewrites) covered these files from 0b92cb61.

This pull request has no versions submitted yet.