Reading copy of lichen.page.review: one PR per unit, product changes only

HUB-EXCHANGE-OVER-GIT 1: a site pushes into its hub over plain git #57

targeting base/hub-exchange-over-git-u1 from view/hub-exchange-over-git-u1
Closed opened by and assigned to notplants-bot.bsky.social
targeting base/hub-exchange-over-git-u1 from view/hub-exchange-over-git-u1

Every 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-pack on one end, and the site's git runs git 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 the hub's pre-receive check writes. The site's git output is only logged.
  • A site push may run only the one command the hub side expects (one ref, the asked commit, the old value as read), and the whole exchange runs under one time limit after which both gits are stopped.
  • The site's push git runs with pinned config (no hooks, no lazy fetch, no push options or tag following that could add a command), with the socket at fd 3 and higher descriptors closed.
  • Removed: the bundle on the way up, the staging repository and the hand-built receive request.

HUB-EXCHANGE-OVER-GIT, subunit 1. 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 git

    Every 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-pack on one end, and the site's git runs git 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.

Commits #

  • d70dceeaf HXG 1: hxg-u1-up-over-git (#3585)

Count and shas #

count-product-code-lines 5cb6139d005e..d70dceeafc9e: NET -219 · counted +320 -539 · moved out of the counted set +0 · comment-only deletions flagged 85 · macros flagged 0 · tests-folder code flagged 0

Base 5cb6139d005e592411f206aaf2dbfd350e8b1dc1 (tip of s/903-od-u3d-import-tests). Head d70dceeafc9e3ab8742cde3bc2b57ea8e77a8e48. 13 files changed.

Check #

Files that differ from s/960-hxg-u1-up-over-git (later tests PRs or merges touched them, or the diff was resolved): crates/lichen-site/src/publish/run.rs.

Resolved/noted: #3585: the source branch merges stack (ONE-DOOR unit 4) in commit 36da6e14. That merge is not included here: only the branch's own four commits (38b3d8c3, febdb97e, c00b4d98, eadbd664) were applied, one by one, on the base. publish/run.rs therefore differs from the source branch only by ONE-DOOR unit 4's change to misconfigured that came in with that merge.

Commits 0