Last week we took the count. An ordinary install brings several hundred megabytes, a hundred thousand files and a fifteen-hundred-package transitive closure to a project that has not yet run a line, and none of it is a price the architecture forces. The count was the first of the four breaks. Today, the second, and the one the count quietly sets up. A node_modules directory has no responsibility line, and the security cost of that absence is one the architecture cannot fix.
The instinct, when this is pointed out, is to reach for the audit. Read the dependencies. Scan them. Pin them. Sign them. The instinct is understandable and, as a primary defence, mistaken, because it answers a question nobody can answer at this scale. You will not read fifteen hundred packages, and neither will anyone else. Security here was never going to come from reading the code.
It comes from somewhere else. It comes from a responsibility line: knowing where a component lives, who is accountable for it, and how it arrives on your disk. The clearest way to see what that means is to look at a piece of software nobody reads and everybody trusts.
Libc, the Component Nobody Reads
Every program on a Unix system links against the C library, libc. It is some of the most security-critical code on the machine: every string copy, every memory allocation, every system call passes through it. Almost no application engineer has read it. And almost none worry about it, which is the interesting part.
The reason is not that the code is open source. So is the typical npm package. The reason is that libc has a line, and the line is drawn in four places at once.
It lives in a known place: the base system source tree of the operating system. On FreeBSD it is src/lib/libc, shipped as one coherent snapshot with the kernel and the userland, by one project, as a single release. The GNU C Library, glibc, is maintained by the GNU project under a public stewardship committee.
It has named maintainers answerable in public: a project or a foundation with a roadmap, a mailing list and a release schedule, and people whose stated job is to be accountable for what ships.
It arrives on a cadence: numbered releases on a published schedule, not a new version pushed to the world at three in the morning by whoever currently holds a publish key.
And it can be checked: the Reproducible Builds effort lets an independent party rebuild the binary from the source and confirm, byte for byte, that they match, and releases are signed, so the artifact on your disk is demonstrably the one the maintainers cut.
You do not read libc. You know where it lives, who is accountable for it, how it arrives, and how to confirm it arrived intact. That is a responsibility line. It is precisely what node_modules does not have, and the absence is not a matter of the packages being bad. It is a matter of the architecture never having drawn the line at all.
node_modules, the Component With No Line
Set the same four questions against an ordinary install and each one comes back empty. Where does it live? In a registry any of millions of accounts can publish to, under names any of them can claim. Who is accountable? Whoever currently holds the publish key for each of the fifteen hundred packages, an identity that can change overnight and answers to no schedule. How does it arrive? On the next npm install, as whatever the latest tag points at in the moment you run it, which can be re-pointed retroactively. And how do you confirm it is what you expected? You mostly cannot, because the thing you would confirm it against does not exist.
Underneath all four is the absence of a boundary in the running model. Your application code and the fifteen hundred packages it pulls in execute with the same permissions. A package's post-install script runs as you, with your shell, your environment variables, your SSH keys and your cloud tokens, the moment the install completes and before you have run anything yourself. Any package can read and modify the files of any other. There is no sandbox, no capability list, no fence between the code you wrote and the code a stranger published last night. The trust model is total and implicit: everything you install is trusted with everything you have.
libc you do not read either. But you know where it lives, who is accountable for it, and how it gets in. node_modules has none of those, and that is the boundary that was never drawn.
The Register of 2026
This is not a theoretical exposure. The first half of this year alone supplies a register, and it is worth reading in order, because the mechanisms differ and the shape never does.
31 March, axios. The HTTP client, over a hundred million weekly downloads. Two malicious versions were published from a compromised maintainer account, with both the latest tag and a legacy tag pointed at them, so fresh installs pulled a backdoor by default; an injected dependency carried a post-install script that was a cross-platform remote-access trojan, and CISA issued an alert.
14 May, node-ipc. An inter-process-communication library, around eight hundred and twenty-two thousand weekly downloads. Three malicious versions went out from a maintainer account whose recovery email lived on the domain atlantis-software.net, a domain that had expired in January 2025 and was re-registered by the attacker in May 2026 for the price of a registration. A standard password reset followed, and a credential stealer reached 822,000 weekly installs. The line that protected a near-million-download package ran through a lapsed domain renewal that nobody was watching.
28 May, a typosquat run. A single actor under a fresh alias published fourteen malicious packages inside four hours, several of them spoofing the upstream OpenSearch repository address in their manifest so as to read as legitimate.
29 May, dependency confusion. A dependency confusion campaign: thirty-three packages registered under organisational scopes that mirrored real internal corporate namespaces, across nine organisations, so that a build resolving an internal name would reach for the public impostor instead.
1 June, Miasma. Thirty-two packages under the @redhat-cloud-services scope, published through a hijacked continuous-integration pipeline identity. This one is the sharpest, because the packages carried valid signed provenance: a genuine SLSA attestation that a known pipeline had built them. The signature was real. The package stole credentials anyway. The line had been drawn at the artifact and not at the identity permitted to publish it.
Five incidents in three months. A stolen account, a lapsed domain, a typosquat, a dependency confusion, a hijacked pipeline: five different mechanisms, and one shape underneath all of them. There was no responsibility line, so there was nothing to cross. And the list is not closed; it never is, because the thing producing it is structural and not a run of bad luck.
The Industry That Sells the Cure
A market has grown up to sell the boundary back, package by package. It is not worthless, and it is not the boundary.
The advisory-driven tools, Dependabot and its kind, are reactive by construction: they alert once an advisory exists. For a freshly published malicious version there is a window, hours to days wide, in which the package installs clean by every tool that waits for a CVE. node-ipc was caught by behavioural detection, not by a feed that was still waiting for an identifier.
Snyk's default scan tests production dependencies and, by default, leaves devDependencies out, on the reasonable-sounding ground that they are "noise" that does not reach production. But devDependencies are the majority of what a typical install puts on disk, and they run with the same permissions as everything else during development, which is exactly when a post-install script fires. The default scan looks past most of the directory it is pointed at, and does so by design.
The behavioural scanners that do better, Socket among them, are themselves installed through npm, into the same environment they are asked to audit. The watchman lives in the house he is watching.
And Miasma is the cleanest illustration of the limit. The thing the audit industry increasingly sells, signed provenance, was present and correct, and the package was malicious all the same, because provenance certifies that a known pipeline built an artifact, not that the pipeline was still in the right hands. Signing did not draw the line. It moved the line to a place the attacker had already reached.
None of this makes the tools useless. Dependabot closes known holes; Snyk finds real vulnerabilities; Socket's behavioural reading caught node-ipc when the feeds had not. The narrower and harder point is the one the first piece named: a market sells the cure for a disease the registry ships as a feature, and the cure, sold as a boundary, is a set of partial filters bolted onto an architecture that still has no boundary beneath them.
The Point
The responsibility line is the architecture's job, and node_modules never drew one. No scanner added afterwards can draw it retroactively, because the thing it would have to bound, the standing right of any of fifteen hundred strangers to run code as you on the next install, is the very thing the registry sells as convenience. You cannot bolt a boundary onto a model whose selling point is the absence of one.
libc is the proof that the line is not a fantasy. It is ordinary, in every operating system, for code that nobody reads and everybody depends on: a known home, a named owner, a delivery you can verify. The difference between libc and node_modules is not that one is audited and the other is not. It is that one has a boundary and the other was built, and sold, without one.
Security here was never a reading problem. It was a boundary problem, and the boundary was never drawn.
Next Wednesday: the workaround that became architecture.