A site and its hub now exchange commits with plain git over a socket the admin thread makes for each exchange, instead of bundles. The hub's own receive-pack and upload-pack do the work, and the hub's one receive and its checks stay the boundary.
- Up: a site pushes with
git push fd::3/hub +<oid>:<ref>. The admin thread runs no git in the site's repository and the site cannot open the hub's folder. The hub reports the outcome (its ref after the receive, and a verdict its pre-receive check writes); the site's git output is only logged. The push may run only the one command the hub expects, and the whole exchange runs under one time limit. - Down: every way down is a plain
git fetchof one ref, withHEADhidden, intoFETCH_HEADwith fsck on, a symlink screen on its commit, and only then the ref is pointed at it (merge, reset, restore and the start all fetch this way). - Pinned: the site's own git config cannot widen either git: no hooks path, no lazy fetch, no push options, tag following or bundle URI, and the
fdtransport alone. - Removed: the bundles on both ways, the staging repository, the hand-built receive request, and the old bundle files (swept once at a site's start).
- Docs:
SANDBOX.md,docs/SECURITY.mdanddocs/CMS_EVENTS.mddescribe the exchange as it runs, including the site's push doc comment on when a tracking ref is written.
HUB-EXCHANGE-OVER-GIT, units 1 to 4 combined (replacing the four per-unit PRs). Plan: https://tangled.org/notplants-bot.bsky.social/lichen-orchestrator/blob/main/machine-docs/plans/HUB-EXCHANGE-OVER-GIT-2026-11-01.md
This is a reading copy: only this subunit's product changes, applied in order on its base. Nothing here is to be merged.
Source PRs on lichen.page.review (product), in order, with their original descriptions #
-
#3585
s/960-hxg-u1-up-over-git— Push the site's commits into its hub over plain gitEvery push from a site into its hub is now plain git. The admin thread opens a socket pair, runs the hub's own
git receive-packon one end, and the site's git runsgit push fd::3/hub +<oid>:<ref>on the other. Only the socket crosses: the admin thread runs no git in the site's repository, and the site side cannot open the hub's folder. The hub's one receive and its checks stay the boundary for every push. The outcome comes from the hub: its ref after the receive, and a verdict that the hub's pre-receive check writes. The site's git output is logged only. Each site push may run only the one command the hub side expects (one ref, the asked commit, the old value as read). The whole exchange runs under one time limit; at the limit both gits are stopped. The bundle on the way up, the staging repository and the hand-built receive request are gone. -
#3593
s/962-hxg-u2-down-over-git— Fetch from the hub over git, the down door's bundles goneEvery way down from a site's hub is now a plain
git fetchover a socket the admin thread makes. The hub side'sserve_site_fetchkeeps today's two refusals, then runs the hub's ownupload-packshown the one ref asked, withHEADhidden, within the exchange's time bound. The site side fetches that ref intoFETCH_HEADalone with fsck on, screens its commit for escaping symlinks, and only then points its ref at it; merge, reset, restore and the start's take each fetch so. The site's own git config cannot widen the fetch: no bundle URI, no remote's ref mapping, no tags, thefdtransport alone, and a config that lowers the fetch's fsck is refused. The test probe asks for a merge or a reset whole. The bundles and their helpers go; a site's start removes what an older lichen's bundles left, once. -
#3601
s/980-hxg-u3-close— The hub exchange's close: the site's push allowed fd alone, and the docs over gitThe site's push into its hub now runs with
GIT_ALLOW_PROTOCOL=fd, as its fetch from the hub already does. A remote, a URL or aninsteadOfin the site's git config can no longer send that push to the site's own files. The shared helper that runs both gits sets the one transport itself, so itstransportsargument is gone. Every other git the site runs keepsfilealone.The docs now describe the hub exchange as it runs: plain
git pushandgit fetchover a socketpair the admin thread makes for each exchange.SANDBOX.mdnames the site's end of the socket (HubSocketForTheSite) as the one descriptor that crosses into the main runtime. It also covers thefdtransport for the two hub commands alone, the hub's one receive with its expected command and verdict, the fetch shown one ref withHEADhidden, the time bound, the relayed receive output and the one-time removal of old bundle files.docs/SECURITY.md's hub rows and its "What crosses" text say the same and name the D-a narrowing. SECURITY.md also nameskeep_ambiguous's create-onlyupdate-refas a ref move in the hub, beside the up door andrewrite_hubs_main.docs/CMS_EVENTS.mdand the CMS events' comments sayGitCommittedwakes the hub follower. No doc or product comment describes a bundle in the exchange any more. -
#3608
s/982-hxg-u4-site-hooks-fixup— The site's push doc says when it writes a tracking refThis PR changes one doc comment: the site's push over the hub's socket writes no index, and writes a tracking ref only where the site's own config names a remote at that URL, in the site's repository with the site's rights (measured on git 2.54 and 2.55). Its branch first stopped the hooks that the site's git config names from running in the site's push and fetch over the hub's socket. Then the operator ruled that a hook the site writes may run in the site's own git, with the site's rights, and never with more. So the branch reverts that change and keeps the exchange as unit 3 left it. The site's push and fetch keep every pin that unit 1 and unit 2 set (the pack's tuning,
core.hooksPath=/dev/null, no lazy fetch, thefdtransport alone). The tests PR shows the rule for each git of the exchange.
Commits #
- 30815a60d HUB-EXCHANGE-OVER-GIT unit 1: product changes (from lichen.page.view view/hub-exchange-over-git-u1)
- b08e64afd HUB-EXCHANGE-OVER-GIT unit 2: product changes (from lichen.page.view view/hub-exchange-over-git-u2)
- 70e5cbd89 HUB-EXCHANGE-OVER-GIT unit 3: product changes (from lichen.page.view view/hub-exchange-over-git-u3)
- 0c6efcf56 HUB-EXCHANGE-OVER-GIT unit 4: product changes (from lichen.page.view view/hub-exchange-over-git-u4)
Count and shas #
count-product-code-lines 5cb6139d005e..0c6efcf56935: NET -372 · counted +583 -955 · moved out of the counted set +0 · comment-only deletions flagged 153 · macros flagged 0 · tests-folder code flagged 0
Base 5cb6139d005e592411f206aaf2dbfd350e8b1dc1 (tip of s/903-od-u3d-import-tests). Head 0c6efcf56935e0511b25c3d629d819fe9c090ae2. 25 files changed.
Check #
Files that differ from s/982-hxg-u4-site-hooks-fixup (later tests PRs or merges touched them, or the diff was resolved): crates/lichen-site/src/publish/run.rs, crates/lichen-site/src/site_process/host.rs.
Resolved/noted: The four source branches also contain merges of stack, which are not included here. Each unit was built from its own commits only (see the units' earlier PRs, now closed), and the four diffs were applied in order on the base. The two files listed above differ from the last source branch only by ONE-DOOR changes that came in with those merges (misconfigured in publish/run.rs, and the removal of settings_notice in site_process/host.rs).
Resolved/noted: Units 2 to 4 were originally built on later bases (s/967-od-u5b-runtime-tests, s/974-od-u6d-save-fixture-tests, s/983-od-u6g-ssr-error-tests); their diffs applied here without conflicts.