Questions to ask about the step between a project and your machine
This note compares how two package registries and their install tools handle that step. It does not rank them.
| Document | what-can-you-check-about-a-package.md |
|---|---|
| Version | 0.1 — peer review draft |
| Audience | People who depend on software built from many packages, including wallets: read Start here. Technical reviewers: read the full note. |
| Method | Follow one package from its maintainer’s publish step to the installer on your machine. At each step, put the same question to two registries and record what each registry’s own documentation says. Tie each statement to a pinned source. |
| Changes | See the changelog. |
| Scope | The Python Package Index (PyPI) with its installer pip, and the npm registry with the npm command-line tool, as documented on 14 October 2026. This is not a finding about any project published there, a review of any registry’s operations, or a survey of attacks. |
This section is for anyone who uses software that is built from other people’s packages. Most wallets are. You do not need to read code to use it. The rest of the note is the record behind each question.
What a package registry is. A registry is a public service that stores packages: small pieces of software that other software is built from. A project’s maintainer uploads a package. An install tool downloads it onto a developer’s machine, together with the packages it depends on. A wallet can depend on hundreds of packages this way.
Why it matters. The release note asks what a project publishes. This note asks what happens between that project and your machine. The package you install passed through an account, a registry, and an install tool. Each one is a place where a package could change, or where a different package could take its place.
The questions that matter.
How to judge an answer. A good answer shows you something you can check: a lockfile with hashes, a provenance record, a registry setting. A weak answer repeats the claim in different words. A weak answer does not prove a problem. It shows that the claim is not proved yet.
What this note does not do. It does not rank the registries or say which to use. It does not say whether any package is safe. A signed record shows where a package came from, not whether to trust what it does.
A claim about an installed package needs a defined object: one file, at one version, from one registry, installed by one tool with one set of options. “Published by the maintainer”, “built from source”, and “verified” describe different properties. None follows from a package’s name or download count.
This note uses each registry’s own documentation and the documentation of its install tool, read in their source repositories and pinned to a commit. It does not inspect any package, account, or registry operation. The registries’ mechanisms change, so a later reading can differ.
Each record in §3 has: claim; object and date; public source; what was read; result; unresolved. The fixed entries “Not applicable” and “Not read for this note” have the meanings in the release note §1.1. A statement by a registry about its own service is recorded as that registry’s statement.
Terms from the release note §1.2 keep their meaning. This note adds the following.
| Term | Meaning here |
|---|---|
| Registry | A public service that stores packages and serves them to install tools. |
| Install tool | The program that resolves, downloads, and installs packages: pip for PyPI, the npm command-line tool for the npm registry. |
| Dependency | A package that another package needs. A transitive dependency is a dependency of a dependency. |
| Long-lived token | A secret, saved by a maintainer or a build system, that lets its holder publish until someone revokes it. |
| Trusted publishing | A registry feature in which an automated build proves its identity with a short-lived OpenID Connect (OIDC) token, and the registry accepts a publish from that build without a saved token. |
| Provenance record | A signed statement that ties a published file to the source and the build that produced it. PyPI calls these attestations; npm calls them provenance statements. |
| Install-time code | Code from a package that runs while the package is installed, before anyone uses it. |
| Lockfile | A file that records the exact version, and a hash, of every package in a project’s dependency tree. |
| Pinning | Requiring one exact version. A hash pin also requires the file’s hash. |
| Step | The question | What would answer it |
|---|---|---|
| 1. Publish | Who could publish this version? | The credential the registry accepted: an interactive login with a second factor, a long-lived token, or a short-lived build identity |
| 2. Provenance | What ties this file to its source? | A provenance record naming a source commit and a build, signed and logged publicly |
| 3. Storage | Can this file be replaced after it was published? | The registry’s rule on replacing a file at an existing version and name |
| 4. Install | What runs when the file is installed? | The install tool’s default for install-time code, and the option that changes it |
| 5. Pinning | Will the same file be installed next time? | A lockfile or requirements file with exact versions and hashes for the whole tree |
| 6. Withdrawal | What happens after a bad release? | The registry’s rules on withdrawing a version, and on reusing a version or a name |
The release note’s three checks cover the project’s side of step 2. This note adds the registry and the install tool around it.
Claim. PyPI offers trusted publishing and attestations, requires a second factor on accounts, and keeps file names unique even after deletion. pip, by default, does not check files against tampering and can run code from source packages; options change both.
Object and date. PyPI’s user documentation and help page in the pypi/warehouse repository at commit 8a4943e; pip’s documentation in the pypa/pip repository at commit c889090; PEP 592 and PEP 740 in the python/peps repository at commit 145bee8. All read 14 October 2026.
Public source. Listed in §8.
What was read. PyPI’s pages on trusted publishers, attestations and their security model, consuming attestations, and yanking. The help page sections on two-factor authentication, deletion, and file name reuse. pip’s page on secure installs. The status lines of the two PEPs.
Result.
== or ===).” PEP 592, which defines yanking, has the status Final.--require-hashes, and disallowing source distributions, with --only-binary :all:. In hash-checking mode, “Hashes are required for all dependencies”, and requirements must be pinned.Unresolved. The note did not read PyPI’s pages on API tokens or organization accounts, or any PyPI feature for approving a release before it is public. It did not read whether pip or any other Python installer verifies attestations by default. It did not read the build-backend standard in detail; “running arbitrary code from distributions” is pip’s own summary.
Claim. The npm registry offers trusted publishing, provenance statements, and a staged publishing step, and it never reuses a name and version. Since npm 12.0.0, the command-line tool blocks dependency install scripts unless the project allows them.
Object and date. npm’s documentation in the npm/documentation repository at commit f8a3253; the npm command-line documentation and changelog in the npm/cli repository at commit 83ea1a1, where the latest release listed is 12.2.0. All read 14 October 2026.
Public source. Listed in §8.
What was read. npm’s pages on trusted publishing, generating and viewing provenance, requiring two-factor authentication, staged publishing, and the unpublish policy. The command-line pages on lifecycle scripts, the lockfile, npm audit signatures, and script approval. The 12.0.0 changelog entry.
Result.
npm audit signatures “checks the registry signatures and provenance attestations” of installed packages. It is a separate command. The registry’s website also shows a mark for packages published with provenance, and warns if the linked source can no longer be found.package@version has been used, you can never use it again.” A version can be unpublished within 72 hours if no other package depends on it. After 72 hours, unpublishing requires no dependents, fewer than 300 downloads in the last week, and a single maintainer. Otherwise npm recommends deprecation, which keeps the package installable and shows a warning.allowScripts policy.” The command-line documentation says install commands “silently skip lifecycle scripts for any dependency that does not have a matching entry in allowScripts, and end with a list of the packages whose scripts were skipped”. Approvals are pinned to the reviewed version by default. The same release changed the defaults for installing from git repositories and from user-supplied tarball URLs to “none”.package-lock.json “describes the exact tree that was generated”. For each package it records resolved, where the package came from, and integrity, “A sha512 or sha1 Standard Subresource Integrity string for the artifact”.Unresolved. The note did not read how many projects have moved to npm 12, or how earlier versions behave beyond the changelog entry. It did not read whether npm install verifies provenance by default; the documentation read describes npm audit signatures as the check. It did not read npm’s rules on reusing a package name after a whole package is unpublished.
Each row puts one question to both registries. The entries come from §3. A cell is a summary; the record in §3 has the source.
| Question | PyPI and pip | npm registry and npm |
|---|---|---|
| Short-lived build identity for publishing | Yes, trusted publishing. Saved tokens remain available. | Yes, trusted publishing. Tokens remain available unless a package-level setting disallows them. |
| Second factor on accounts | Required | Required for publishing, unless a granular token with bypass is used |
| Review step before a version is public | Not read for this note | Staged publishing, with maintainer approval under a second factor |
| Provenance record | Attestations under PEP 740: publish attestation and SLSA provenance | Provenance attestation and publish attestation |
| Public log of provenance | Recorded in Sigstore’s public transparency log; PyPI does not treat an attestation as verified without an inclusion proof | Logged in a public transparency ledger |
| Install tool checks provenance by default | Not described in the pip documentation read | Not described; npm audit signatures is a separate command |
| A file at a published name can be replaced | No. Deleted files cannot be re-uploaded. | No. A name and version can never be used again. |
| Name released after deletion | Yes. Deleting a project releases the name to any user. | Not read for this note |
| Install-time code by default | pip can run code from source distributions. --only-binary :all: disallows them. |
Since 12.0.0, dependency install scripts are blocked unless the project allows them |
| Hash pinning | Opt-in hash-checking mode, covering every dependency | Recorded for every package in the lockfile |
| Withdrawing a release | Yanking: ignored unless pinned with == or ===. Deletion is permanent. |
Unpublish under limited conditions; otherwise deprecation with a warning |
Five differences explain most of the table.
Both registries say the same thing about their own provenance records. PyPI: an attestation tells you “where a PyPI package came from, but not whether you should trust it.” npm: provenance “does not guarantee the package has no malicious code.”
In the terms of the release note’s §5, a registry provenance record is the build platform’s statement about its own build. It ties a file to a source commit and a workflow. It answers the signed check for the build identity. It does not answer the rebuilt check, because no second party built the file. See What Can You Check About a Software Release? §5.
PyPI’s security model page names a use that needs no trust decision about the code: watching for change. A reader who records which identity published past versions can notice when a new version comes from a different identity, or from none. That turns a provenance record into a check that runs over time.
| If the claim is | Request | If the artifact is missing |
|---|---|---|
| It’s published by the maintainer | Which credential published this version: an interactive login, a saved token, or a build identity | A maintainer’s name on the package page is not a record of who published each version |
| It has provenance | The provenance record for the exact file you installed, and the source commit it names | A mark on a package page covers one version, not every version or every dependency |
| We verify our dependencies | The command that checks provenance or signatures, and where it runs | Provenance that no tool reads is a record, not a check |
| Our builds are reproducible | A lockfile or hashed requirements file covering every transitive dependency | A version range installs whatever the registry serves on the day |
| Installing it is safe | Which packages ran install-time code, and who approved each one | A default that runs every package’s install code approves all of them |
| That bad version was removed | Whether it was yanked, deprecated, unpublished, or deleted, and whether your lockfile still names it | A withdrawn version can remain in lockfiles and caches |
| It’s the same package as before | The publishing identity of this version compared with earlier versions | A familiar name is not a familiar publisher |
Each source is listed once with the version consulted and the sections that cite it. The registries publish these pages on their websites; the links below point to the source files at the commit read.
PyPI, at pypi/warehouse commit 8a4943e.
| Reference | What it defines | Cited in |
|---|---|---|
| Trusted publishers: getting started | Trusted publishing and its short-lived tokens | §3.1, §4 |
| Attestations: introduction | Attestations and the kinds PyPI accepts | §3.1, §4 |
| Attestations: security model | What an attestation does and does not show, and the transparency-log requirement | §3.1, §4, §5 |
| Attestations: producing | Producing attestations, and the public transparency log | §3.1 |
| Attestations: consuming | How attestations are served and verified | §3.1 |
| Yanking | Yanking a release | §3.1, §4 |
| Help page | Two-factor requirement, deletion, and file name reuse | §3.1, §4 |
pip, at pypa/pip commit c889090.
| Reference | What it defines | Cited in |
|---|---|---|
| Secure installs | pip’s defaults, hash-checking mode, and disallowing source distributions | §3.1, §4 |
Python Enhancement Proposals, at python/peps commit 145bee8.
| Reference | What it defines | Cited in |
|---|---|---|
| PEP 592 | Adding “Yank” Support to the Simple API | §3.1 |
| PEP 740 | Index support for digital attestations | §3.1, §4 |
npm registry, at npm/documentation commit f8a3253.
| Reference | What it defines | Cited in |
|---|---|---|
| Trusted publishing | Trusted publishing and supported build services | §3.2, §4 |
| Generating provenance statements | Provenance and its limits | §3.2, §4, §5 |
| Viewing package provenance | Provenance on the website, and npm audit signatures |
§3.2 |
| Requiring 2FA | Two-factor requirements and token settings | §3.2, §4 |
| Staged publishing | Review before a version is public | §3.2, §4 |
| Unpublish policy | Unpublishing, version reuse, and deprecation | §3.2, §4 |
npm command-line tool, at npm/cli commit 83ea1a1.
| Reference | What it defines | Cited in |
|---|---|---|
| Changelog | The 12.0.0 change to dependency install scripts | §3.2, §4 |
| Script approval | allowScripts and blocked scripts |
§3.2 |
| Scripts | Lifecycle scripts during install | §3.2 |
| package-lock.json | The lockfile, resolved, and integrity |
§3.2, §4 |
| npm audit | npm audit signatures |
§3.2, §4 |
This repository
| Reference | What it defines | Cited in |
|---|---|---|
| What Can You Check About a Software Release? | The three checks, the fixed entries, and provenance records | §1.1, §1.2, §2, §5 |
Newest first. Versioning follows STYLE.md. Version tags use the prefix package-v, and each version links to its tagged text.
| Version | Change |
|---|---|
| 0.1 — 14 October 2026 | First draft. Six steps from maintainer to machine. Records for PyPI with pip and for the npm registry with the npm command-line tool, from their own documentation pinned to commits. A side-by-side table, what a provenance record answers, and reviewable claims. |