@claude
Raising this from FuzeFront rather than touching FuzeInfra from a consuming repo. Everything below is measured, not inferred; the fix is on your side of the boundary.
Symptom
Docker image builds on the self-hosted runner scale sets cannot download npm tarballs from pkg-npm.githubusercontent.com — the blob CDN that GitHub Packages redirects to. Every build that installs a private @izzywdev/* package hangs for 12–37 minutes and then dies.
Three observations, two repositories, two independent scale sets:
| Repo |
Scale set |
Duration |
Error |
| FuzeSales |
fuzesales |
2201.4 s |
npm error network |
| FuzeContact |
fuzecontact |
2173.6 s |
npm error network |
| FuzeContact |
fuzecontact |
731.1 s |
403 on a blob SAS that expired at 10:03:29Z |
Two independent repos on two independent scale sets converging within 28 seconds of each other is a fixed retry/timeout budget being exhausted, not variance. The 403 is the same condition caught earlier in the window — slow enough that the 5-minute pre-signed blob signature expired mid-transfer rather than slow enough to exhaust npm's own timeouts:
731.1 npm error 403 Server failed to authenticate the request. Make sure the value of
Authorization header is formed correctly including the signature.
- GET https://pkg-npm.githubusercontent.com/npmregistryv2prod/blobs/99821070/fuzefront-identity/1.0.0/***
?st=2026-08-26T09:58:19Z&se=2026-08-26T10:03:29Z&sv=2025-01-05
("Server failed to authenticate the request… including the signature" is Azure Blob's generic reply to a stale SAS, not GitHub's reply to a bad token.)
The control that isolates it
FuzeSales builds two images in the same workflow run, same commit, same runner pool, same node:20-alpine base, same npm version:
| Leg |
Registries it fetches from |
Duration |
Result |
build-push (fuzesales-api, service/Dockerfile) |
registry.npmjs.org only |
73 s |
✅ success |
build-push (fuzesales, Dockerfile) |
+ npm.pkg.github.com → pkg-npm.githubusercontent.com |
2201 s |
❌ network |
service/Dockerfile copies only service/package.json and never picks up the repo-root .npmrc, so it resolves nothing from GitHub Packages. It completed a full install in 73 seconds while the leg that does hit GitHub Packages crawled for 37 minutes on the same machine.
What this rules out
- Not the credential. npm authenticates successfully and is issued a signed download URL — a step that only happens after a valid token check.
- Not a cross-repo package grant. The registry granted access; the failure is on the redirect target.
- Not repo size or a missing lockfile. The 73-second control leg is in the same repo, same run.
- Not one bad runner. Two pools, two repos, same signature.
- Not the npm client. No npm-side tuning (
--maxsockets, cache mount, retries, lockfile) fixes a transfer that cannot complete.
What to check
- Egress from the
fuzesales and fuzecontact runner pods to pkg-npm.githubusercontent.com (443) — NetworkPolicy, egress proxy allowlist, DNS resolution, and any bandwidth shaping. This host is distinct from github.com, api.github.com, ghcr.io and npm.pkg.github.com, and an allowlist written against those four would miss it.
- Confirm
registry.npmjs.org is unaffected (measured healthy) — the contrast is the useful signal.
npm.pkg.github.com itself responds (metadata + redirect both work); only the redirect target fails.
Reproduction from inside a runner pod:
# should be fast
time curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://registry.npmjs.org/react
# the one that fails
time curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://pkg-npm.githubusercontent.com/
Impact
This is currently the top blocker on getting products to render in the FuzeFront portal. A live census against app.fuzefront.com (run 32960147607, 2026-08-26 10:49:51Z) reports 3 PASS / 10 FAIL across 13 registered apps, and only one of those passes is a genuine module-federation load. Five of the ten failures are 503s from pods whose images cannot be rebuilt — four of them (contact, fuzekeys, picker, fuzesales) are exactly the repos blocked behind this.
There are three open FuzeFront-side PRs (FuzePicker#104, FuzeContact#62, FuzeSales#73) that fix a genuine, separate credential defect in those builds. They are correct and were verified to get the builds all the way to tarball download — but none can go green until this egress problem is resolved, and no change inside those repos will change that.
Separate observation, explicitly NOT yours
On those same private repos, every ubuntu-latest (GitHub-hosted) job is refused outright — runner_id: 0, no runner assigned, no steps, dead in 2–3 s, job logs 404. That is an Actions spending-limit condition on private repos, not a cluster matter. Noting it only because anyone investigating runner health will trip over it; it needs a billing decision, not an infra change.
@claude
Raising this from FuzeFront rather than touching FuzeInfra from a consuming repo. Everything below is measured, not inferred; the fix is on your side of the boundary.
Symptom
Docker image builds on the self-hosted runner scale sets cannot download npm tarballs from
pkg-npm.githubusercontent.com— the blob CDN that GitHub Packages redirects to. Every build that installs a private@izzywdev/*package hangs for 12–37 minutes and then dies.Three observations, two repositories, two independent scale sets:
fuzesalesnpm error networkfuzecontactnpm error networkfuzecontact403on a blob SAS that expired at10:03:29ZTwo independent repos on two independent scale sets converging within 28 seconds of each other is a fixed retry/timeout budget being exhausted, not variance. The 403 is the same condition caught earlier in the window — slow enough that the 5-minute pre-signed blob signature expired mid-transfer rather than slow enough to exhaust npm's own timeouts:
("Server failed to authenticate the request… including the signature" is Azure Blob's generic reply to a stale SAS, not GitHub's reply to a bad token.)
The control that isolates it
FuzeSales builds two images in the same workflow run, same commit, same runner pool, same
node:20-alpinebase, same npm version:build-push (fuzesales-api, service/Dockerfile)registry.npmjs.orgonlybuild-push (fuzesales, Dockerfile)npm.pkg.github.com→pkg-npm.githubusercontent.comservice/Dockerfilecopies onlyservice/package.jsonand never picks up the repo-root.npmrc, so it resolves nothing from GitHub Packages. It completed a full install in 73 seconds while the leg that does hit GitHub Packages crawled for 37 minutes on the same machine.What this rules out
--maxsockets, cache mount, retries, lockfile) fixes a transfer that cannot complete.What to check
fuzesalesandfuzecontactrunner pods topkg-npm.githubusercontent.com(443) — NetworkPolicy, egress proxy allowlist, DNS resolution, and any bandwidth shaping. This host is distinct fromgithub.com,api.github.com,ghcr.ioandnpm.pkg.github.com, and an allowlist written against those four would miss it.registry.npmjs.orgis unaffected (measured healthy) — the contrast is the useful signal.npm.pkg.github.comitself responds (metadata + redirect both work); only the redirect target fails.Reproduction from inside a runner pod:
Impact
This is currently the top blocker on getting products to render in the FuzeFront portal. A live census against
app.fuzefront.com(run 32960147607, 2026-08-26 10:49:51Z) reports 3 PASS / 10 FAIL across 13 registered apps, and only one of those passes is a genuine module-federation load. Five of the ten failures are503s from pods whose images cannot be rebuilt — four of them (contact,fuzekeys,picker,fuzesales) are exactly the repos blocked behind this.There are three open FuzeFront-side PRs (FuzePicker#104, FuzeContact#62, FuzeSales#73) that fix a genuine, separate credential defect in those builds. They are correct and were verified to get the builds all the way to tarball download — but none can go green until this egress problem is resolved, and no change inside those repos will change that.
Separate observation, explicitly NOT yours
On those same private repos, every
ubuntu-latest(GitHub-hosted) job is refused outright —runner_id: 0, no runner assigned, no steps, dead in 2–3 s, job logs 404. That is an Actions spending-limit condition on private repos, not a cluster matter. Noting it only because anyone investigating runner health will trip over it; it needs a billing decision, not an infra change.