fix(ssh): wire git core.sshCommand to the writable sidecar, and assert it
~/.ssh is commonly bind-mounted READ-ONLY from the host, so a per-host `ControlPath ~/.ssh/cm/%r@%h:%p` — the standard CGNAT multiplexing recipe, and correct on the host — resolves inside an unwritable dir in the container. Every push dies `unix_listener: cannot bind to path ...: Read-only file system`, behind git's misleading "make sure you have the correct access rights". setup-lan-access.sh already writes the fix: ~/.ssh-local/config overrides ControlPath into the writable ~/.ssh-local/cm BEFORE `Include ~/.ssh/config`, so -F repairs the socket path and keeps every per-host User/Port/IdentityFile. entrypoint-user.sh now points git at it, guarded on the sidecar existing — setup-lan-access.sh writes none on native Linux Docker, where -F at a missing file would break every git-over-ssh call instead of fixing one. An existing core.sshCommand is left alone (first-wins, as for the three git settings above). Why code and not another doc line: the remedy was already in the global AGENTS.md, in pi-devbox-environment SKILL.md §3, in 24 MemPalace drawers from three devices, and printed verbatim by recreate-sanity-check.sh — and an agent that had run that script two hours earlier still hit the failure and reinvented a /tmp/sshcm workaround. A fifth copy was not the missing piece. Assertions, each where it can actually pass: - smoke-test.sh: two STATIC greps (wiring line + its [ -r ] guard). `run` uses --entrypoint="", so asserting the runtime value there would repeat the v1.8.0 mistake of an assertion that cannot pass, unvalidated until the next tag. - smoke-test.sh runtime phase: a BICONDITIONAL — sidecar present => must route through it; absent => must be unset. The absent arm is the one CI exercises (native Linux runner), so "is set" would have failed CI for a correct image. - recreate-sanity-check.sh: the runtime assertion, plus an explicit fail for the inverted state (set while the sidecar is missing). The permanent "default ssh precedence" warning keeps its severity but now states that it is structural and can never reach zero, and whether git is wired, unwired, or has no sidecar. All five arms exercised against the real script before commit; that caught a defect in the first draft, which reported "git IS wired ... unaffected" about a state where the sidecar was gone and every git-over-ssh call failed. Host ~/.ssh/config needs no change: the same line is right on the host and unusable through a read-only mount, so the fix belongs in the container layer.
This commit is contained in:
@@ -319,6 +319,35 @@ fi
|
||||
if [ -f "$HOME/.gitignore_global" ] && ! git config --global core.excludesFile &>/dev/null; then
|
||||
git config --global core.excludesFile "$HOME/.gitignore_global"
|
||||
fi
|
||||
# Route git-over-ssh through the WRITABLE ssh sidecar. ~/.ssh is commonly
|
||||
# bind-mounted read-only from the host, and a per-host
|
||||
# ControlPath ~/.ssh/cm/%r@%h:%p
|
||||
# inherited from that config (the standard CGNAT multiplexing recipe) kills every
|
||||
# push with
|
||||
# unix_listener: cannot bind to path ~/.ssh/cm/...: Read-only file system
|
||||
# hidden behind git's misleading "Please make sure you have the correct access
|
||||
# rights", which sends the reader hunting for a key problem that does not exist.
|
||||
# setup-lan-access.sh (run near the top of this script) already wrote
|
||||
# ~/.ssh-local/config, whose leading `Host *` block overrides ControlPath into
|
||||
# the writable ~/.ssh-local/cm and only THEN `Include`s the user's own config —
|
||||
# so -F repairs the socket path while keeping every per-host User/Port/
|
||||
# IdentityFile. Wiring it here means no caller has to know any of that.
|
||||
#
|
||||
# WHY THIS IS NOT LEFT TO DOCUMENTATION: measured 2026-09-22 on tor-ms22, an
|
||||
# agent with the remedy in its system prompt, in a loaded skill, in 24 palace
|
||||
# drawers, AND printed verbatim by recreate-sanity-check.sh two hours earlier
|
||||
# still hit this and reinvented a /tmp/sshcm workaround. The knowledge was
|
||||
# available four times over, so a fifth copy is not the fix — removing the need
|
||||
# to know is.
|
||||
#
|
||||
# The [ -r ] guard is load-bearing, not decoration: setup-lan-access.sh only
|
||||
# writes the sidecar on VM-backed hosts (OrbStack / Docker Desktop). On native
|
||||
# Linux Docker there is none, and pointing -F at a missing file would break EVERY
|
||||
# git-over-ssh operation instead of fixing one. Respect a value the user already
|
||||
# set — same first-wins convention as the three settings above.
|
||||
if [ -r "$HOME/.ssh-local/config" ] && ! git config --global core.sshCommand &>/dev/null; then
|
||||
git config --global core.sshCommand "ssh -F $HOME/.ssh-local/config"
|
||||
fi
|
||||
|
||||
# ── pi: deploy toolkit + extensions + mempalace bridge ─────────────
|
||||
# pi is always installed in pi-devbox; no INSTALL_PI guard needed.
|
||||
|
||||
Reference in New Issue
Block a user