Closes #239. Replaces #276, which reimplemented actions/checkout in the runner; this keeps the real action and moves only where its git goes.
Mechanism
The runner Worker intercepts the sandbox's HTTPS to g1t.sh itself. A sandbox already passes its traffic through the Worker once any of it is intercepted (interceptHttps, the abuse host), and it already trusts the re-signing certificate, so nothing inside the sandbox changes:
- Jobs whose other requests go straight out get a new
gitoutbound handler forg1t.sh(setOutboundByHost); brokered jobs reach the same code throughegress. - A signed-in
git-upload-packrequest (info/refs?service=git-upload-packor the POST) is handed to the repos service over its binding, exactly as the website's front door does (proxyGit), and the answer streams back, never buffered (unlikeforward's 8 MB read of a replayable answer). The repos service authorizes it as any clone and answers from the git edge. originstayshttps://g1t.sh/<owner>/<repo>and the action'shttp.https://g1t.sh/.extraheaderis sent as it is, so the token reaching the repos service is the action's own, and latergit fetch/git pushin the job work as before. NoinsteadOf, no extra config, no token injected by the Worker.
I chose this over url.insteadOf to an internal host because the rewritten URL would no longer match the action's extraheader, and putting the credential back would mean the Worker injecting the job's token (a bypass of what the job itself sent).
What stays on the public path
- Pushes (
git-receive-pack): the pack is streamed and cannot be replayed for a fallback. - Anonymous clones: the front door counts them by address.
- Pages, packages and anything else at g1t.sh, unchanged (
fetch, as the containers library did;brokered/forwardfor brokered jobs). - A brokered request carrying a stand-in, so it is still filled in.
Fallback
The fetch's small request body (wants and haves) is held; when the repos service throws or answers 5xx, the same request is sent the public way. A 4xx (e.g. 401) is the answer, as it would be on the public path. If the host cannot be routed (setOutboundByHost fails), the job's git goes out as before.
Expected gain
Recent Deploy runs' Run actions/checkout@v5 step: Plan (fetch-depth: 0) 6.6 s on #483, 27.6 s on #481, 34.6 s on #478 (26.5 s of it the fetch); shallow checkouts 3 to 16 s. The full-history variance is the public round trip through the front door plus the egress buffering; on the internal path the pack comes straight from the git edge over the service binding. I could not measure it on a real job since this is not deployed; compare the Plan job's checkout on the first Deploy after merge.
Checked
node --test services/runner/src/*.test.ts: 118 pass, includinginternal-git.test.ts(adapted from #276): what takes the path, the request as handed on, refusals passed through, redirects, fallback on a throw and on 503 with the body intact, and everything else never reaching the repos service.tscin services/runner andprettier --checkon the touched files: clean.- Docs: the
actions/checkoutrow in guides/actions.md and the Egress section of architecture/compute.