@projectpac/packages-npm
v0.6.0
Published
Part of PAC: @projectpac/packages-npm.
Readme
@projectpac/packages-npm
The npm package source: where a registry package's bytes come from, and what npm says vouches for them.
A source brings bytes onto the machine and reports what backs them. It decides nothing about installing: reading the manifest, what the principal is shown, the policy gate, and the tree row all live above the seam in the host, which is what lets this package be one source among several rather than the only way a node can install anything.
- Into the node's one module directory. The host hands every source the same
place to fetch into --
<data dir>/modules, an npm prefix whosenode_modulesholds what setup seeded and what any source fetched -- and both node processes resolve a row's module from it. This source keeps no store of its own, so what it brought is the same kind of thing to the node as anything else there, and outlives this source's own row. Every fetch is--saved into the prefix's own manifest, because what npm has no record of it prunes on its next install. - A registry name or a path, and nothing else. npm's own grammar accepts far
more -- git urls, remote tarballs,
npm:aliases,user/reposhorthand -- and every one of those makes the fetch reach a host of the spec's choosing. The fetch happens before the principal has been asked anything, because the manifest they will be shown comes out of the package, so where a fetch may reach is this source's decision and never the asker's. Reaching a git host is what@projectpac/packages-gitis for, and enabling that one is something an operator does on purpose. --ignore-scriptsis load-bearing. npm would otherwise run whatever the package put in its install hooks, which is code the principal has not agreed to run and has not even been shown yet. Downloading a package is only downloading.- Nothing is imported to describe a package. The host reads the manifest out
of the package's own
package.jsonwhere this source put it, which is JSON, so describing a plugin costs nothing and executing it is a separate decision. A package that declares none is refused there, after the fetch and before anything of it runs. - npm's chain, not one of PAC's. A signature scheme invented here would
verify only packages published by people who had adopted it -- which is nobody,
and would make installing a stranger's flow impossible rather than merely
unproven. npm signs every tarball it serves, and a package published with
--provenancecarries a SLSA attestation binding those bytes to a repository and a commit.npm audit signatureschecks both, and one verdict covers them. npm reports only what failed, soverifiedis concluded rather than read: the package is in the audited tree, npm resolved it from the registry, and a well-formed report names it in neither list. Anything short of that -- the audit failing to run included -- answersmissing, neververified: this is the one place where failing to check must not read as having checked. What the verdict is allowed to mean is the host'srequireVouched. - Bounded. The module directory has a byte ceiling, and a fetch that would
cross it is undone before the host reads anything -- because the fetch runs
before any authorization exists, so an unbounded one lets anything that can
ask fill the disk with no principal involved. What is counted is what the
directory actually holds: a symlink is skipped rather than followed, so a path
install -- which npm implements as a
file:link into somebody's checkout, and which downloads nothing -- is not bounded by the size of a working tree it never fetched. Following one also did not terminate, because a checkout's ownnode_moduleshas cycles in it.
Why this one is seeded
Every other plugin arrives by being fetched. This one cannot: a source fetched through itself does not exist. So setup puts it into the module directory and names its row in the init file, the daemon enrolls that row on the first boot, and after that everything is ordinary -- a git source is installed by this one. The daemon itself depends on no source.
Removing it is allowed, and a node left with no source runs every plugin whose module it already has and refuses to fetch a new one, the way a node with no transport reaches no peer. What it fetched stays: the module directory is the node's, not this source's, and a plugin does not stop being loadable because the thing that fetched it is gone.
