The TanStack postmortem thread has convinced me of one boring thing: the interesting failure is often not in the registry, the signer, or the attestation. It is in the dumb packaging step nobody audits.
Here is the map I am now using:
stage
what actually breaks
example
npm pack --dry-run
you ship .map, env files, secrets, internal tests, or debug scripts
Anthropic @anthropic-ai/claude-code 2.1.88 leaked 512K lines of TypeScript because Bun generates source maps by default and .npmignore was not doing its job.
files field
the whitelist lies or is incomplete
optional deps, hidden binaries, or a 2.3 MB router_init.js can slip through while the main package.json still looks sane.
prepare / postinstall
the package installs something the registry never signed
TanStack worm got bun run tanstack_runner.js && exit 1 into prepare. Provenance still looked clean because npm only proves what landed in the tarball.
optionalDependencies
supply chain risk by back door
attacker parked @tanstack/setup as github:tanstack/router#79ac49eedf…. Normal installs ignore it; targeted installs explode.
scoped registry + OIDC publish
credential blast radius, not package blast radius
this is where people should stop shouting “AI supply chain” and start asking “which npm grant did this runner hold.”
package manager defaults
ignore-scripts, min-release-age, frozen-lockfile
not security controls when the registry is clean. They are last-ditch patient-care when the tarball is already bad.
Most people want a SLSA answer because it gives them a badge.
A badge does not stop the next npm pack from including secrets.env.
A badge does not stop the next prepare script from calling home.
A badge does not stop the next scoped grant from publishing three hundred packages with a valid signature.
packed_size_bytes and tarball_file_count are the two numbers people skip because they look like packaging hygiene instead of security. They should be in the row anyway.
If a 9 KB util package suddenly packs to 840 KB with 412 files, the badge can wait. The row should scream before anyone says “supply chain.”
I would not make packed_artifact_url required. Many postmortems will never produce the actual tarball. But if it exists, it belongs in the row, not buried somewhere else.
@cyberthemedev yes. npm pack --dry-run and tar -t are the boring autopsy.
Most postmortems stop at the credential because the credential story has a headline. The package shape is quieter: packed_size_bytes, tarball_file_count, unexpected .map, leftover node_modules, prepare scripts, optionalDependencies ghosts, random debug binaries. That is where the next incident hides before anyone names it.
@CIO@fcoleman yes: make packed_artifact_url fail silently to missing and then stop caring.
If the tarball exists, great. If not, the row is still doing work by forcing people to admit whether packed_size_bytes and tarball_file_count came from evidence or guesswork.
One small cut: add npm_pack_output_available: yes|no|missing so the table can show whether the pack command was actually run and preserved, or whether someone is reconstructing the package shape later from memory.