Your Build Path Has One Owner And No Fallback
Monday's coupled degradation and npm's coming install-script default hit the same surface from opposite directions: the pipeline nobody can rebuild once its remote dependencies stop answering.
The row nobody priced
The interesting casualty here was not Actions. It was identity. SAML, OIDC and SCIM degraded in the same window as the API, which produces two failures at once. The responders who need to fix the pipeline cannot authenticate. User provisioning and deprovisioning freeze, so a compromised account cannot be offboarded while the incident runs. That is a security control failure wearing an availability costume, and most incident plans treat source control, CI and the identity provider as three independent vendors.
The two accounts of the cause diverge, and the divergence is instructive. GitHub identified a faulty component and restored service the same day. That is good MTTR. Risky Business's read is blunter: a mid-tier hacktivist crew with commodity DDoS kept a build-critical dependency down for hours. Both readings land on the same engineering conclusion. The trigger is outside your control and the coupling is not. A deploy freeze should not be someone else's decision.
npm 12 removes the other unowned write
Here is what actually happens today. npm install grants arbitrary code execution to every package in the transitive tree, at the privilege of whoever ran it. In CI that is usually a runner holding registry credentials and cloud metadata access. npm 12 disables install scripts by default, which moves the trust boundary to the right place and breaks builds in predictable spots: node-gyp consumers that compile from source (bcrypt, canvas, older sqlite bindings), browser downloaders (playwright, puppeteer), binary fetchers (esbuild, swc, cypress), codegen hooks (prisma generate) and git hook installers (husky's prepare). This detail reaches us through a Socket-sponsored segment, so verify scope and version timing against npm's own release notes before booking sprint capacity.
The reflex failure mode is the one to plan against: red CI on release day, every failing package into a blanket allowlist, ship, move on. That restores the entire attack surface and hands back a migration nobody performed. The migration that holds up relocates binary acquisition and codegen out of install-time into an explicit build step. A Makefile target, a Bazel rule, a discrete CI stage. Somewhere versioned, cacheable and reviewable.
| Dependency | Failure observed or coming | Mitigation | Effort |
|---|---|---|---|
| Raw repo / archive downloads | ~50% error rate; nondeterministic build failures masked by retries | Pull-through artifact cache, digest-pinned deps | Days |
| Actions / CI | No builds, no deploys, including hotfixes | Self-hosted runner pool, warm and drilled | Weeks |
| SAML / OIDC / SCIM | Responder lockout plus frozen deprovisioning | Break-glass accounts bound to hardware keys, audited | Days |
| Install scripts (npm 12) | Native modules, binary fetchers and codegen hooks stop running | Explicit build stage per package; no blanket allowlist | Days to weeks |
The order that actually pays
Digest pinning comes first. It is hours of work and it makes everything downstream deterministic: pin third-party actions and container base images by SHA, not tag. That kills a class of supply-chain surprise, and it is what makes a pull-through cache trustworthy rather than merely present. Mirrors and self-hosted runners come second. They are weeks of work, and they only pay off if someone has rehearsed them.
Then rehearse. Blackhole github.com at the egress layer and attempt a real hotfix. Two things surface within minutes: pipeline steps that silently depend on codeload endpoints, and runbook steps that begin with the words "clone the repo."
Source control, build system and identity provider are one failure domain until proven otherwise. In this incident, nobody proved otherwise.
What to do
Blackhole github.com at your egress layer this week and attempt a production hotfix end to end, targeting under 60 minutes to ship with it unreachable.
Open a CI branch with ignore-scripts=true in .npmrc this sprint, run a clean install and full build, and catalog every failing package before the npm 12 default lands.
Pin every third-party action and container base image by SHA digest this sprint, then stand up a pull-through cache and read-only repo mirrors this quarter.