npm Blog (Archive)

The npm blog has been discontinued.

Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.

new pgp machinery

If you’ve recently examined packuments in the Registry, you might have noticed a new npm-signature field in the dist section. It might look to you like a PGP signature, and that in fact is what it is! This field holds the npm registry’s PGP signature of the integrity field.

Read on to find out why we’re signing publications, what exactly we’re signing, and how you can use this signature data.

Some context: how npm verifies package-versions

Every package has a package metadata document. This is a JSON document that describes the package and what versions have been published, and what their dependencies are. We refer to this as a packument—a portmanteau of ‘package’ and 'document’.

A package-version is a specific version of a package, along with its metadata and code tarball. You can uniquely name a package-version with its package-name + version. For example, lodash@4.17.5 is a package-version.

Every package-version published to the registry has a dist structure in the package’s metadata document. Here’s the dist structure for version 1.4.3 of light-cycle:

  "integrity": "sha512-sFcuivsDZ99fY0TbvuRC6CDXB8r/ylafjJAMnbSF0y4EMM1/1DtQo40G2WKz1rBbyiz4SLAc3Wa6yZyC4XSGOQ==",
  "shasum": "c305f0113d81d880f846d84f80c7f3237f197bab",
  "tarball": "",
  "fileCount": 11,
  "unpackedSize": 25612,
  "npm-signature": "..."

This structure tells npm clients where to fetch the tarball to install the package-version, and, further, how to verify that the tarball fetched is the same as the one the npm registry intended to serve. The shasum is a SHA-1 sum of the file. We’ve supplemented this to the side with an integrity field, based on the subresource integrity specification, to allow us to migrate to different hashing algorithms. For this publication, the client had sha512 available and used it to hash the contents.

The npm CLI checks tarball integrity when a package-version arrives in your local cache, which is a content-hash addressed cache.

The problem with shasums

The shasums above are invaluable for determining that your npm client received the data it was supposed to receive over the network without corruption. They do not, however, protect against man-in-the-middle attacks.

If an attacker has interposed a proxy between you and the registry, they can tamper with both the package JSON document that advertises the shasum and the tarball itself. This attacker could create a tarball with unexpected content, generate an integrity field for it, then construct a packument advertising this poisoned tarball. An npm client would trust the packument and therefore also trust the tarball.

We offer you a way to detect this kind of tampering by signing package integrity fields along with some data that uniquely identifies the package-version.

Note that verifying this signature only establishes that a middleman has not tampered with the contents or integrity field for that package-version. It does not establish that the npm registry itself hasn’t meddled with package contents at publication time! You still have to trust us. (We intend to offer a solution to that problem in the future.)

PGP and Keybase

The npm registry uses OpenPGP to sign packages. PGP, aka Pretty Good Privacy is an encryption standard that offers ways to authenticate and verify messages. The EFF has a good introduction to public-key cryptography and PGP, which I recommend if you would like a general introduction to the topic.

We’re generating signatures using openpgpjs , a pure JavaScript implementation of OpenPGP.

Our public key is at the end of this post.

We’ve also chosen to use Keybase to publicize our PGP key and give you confidence that the npm registry you install from is the same registry that’s signing packages. Our account on Keybase is npmregistry.

Keybase offers two advantages over the core OpenPGP experience that move us to recommend it to you. First, the Keybase application and CLI provide an excellent user experience for PGP, which can be intimidating for newcomers. Second, Keybase manages and displays social proofs that the entity that controls a specific pgp key also controls accounts on social media and other places. These proofs help you determine whether you can trust an account.

We’ve established proofs on Keybase that we control @npmjs on Twitter, the domain, and the domain Verifying these proofs won’t tell you who owns those domains, but it does establish that the same entity controls them and the pgp key advertised on Keybase.

You can also observe that npm’s executive and technical team are among the people who’ve chosen to follow npmregistry on Keybase. For example, I (ceej) am following npmregistry, as are Isaac Z. Schlueter (isaacs) and Adam Baldwin (adam_baldwin).

If you install Keybase and create an account, you can follow npmregistry yourself and keep a local copy of the registry’s public key. The verification examples that follow assume you have done so.

What we’re signing

The registry signs the following data for each new package-version published:


This string uniquely identifies a package-version in the registry and associates it with a specific package integrity field.

On package publication, we first validate that the data we received matches what the client thinks it published by checking shasums and the integrity field. Next, we construct the package-version + integrity string. We then generate a detached signature for that string—that is, a signature that isn’t part of a full message structure. The detached signature is stored in the npm-signature field in dist.

Here’s an example. When light-cycle@1.4.3 was published, the npm registry created a pgp signature for this exact string:


Verifying our signatures

The presence of a signature is not enough for you to feel confident that you got the package the npm registry saw. You must verify the signature as having been made by a specific key for a specific message. This process is a little fiddly to do by hand, but I’ll step you through how, so you can do it without relying on third parties or the npm cli if you need to. This example assumes you’ve installed Keybase, but you can also use The GNU Privacy Guard.

Suppose I want to check the signature on version 1.4.3 of light-cycle. First, I fetch the signature for the version I’m interested in and save it in a file:

$ http GET | json "versions['1.4.3'].dist.npm-signature" > sig-to-check

Next, I get the integrity field for that version:

$ http GET | json "versions['1.4.3'].dist.integrity"

You can verify that this integrity field matches the tarball you have by calculating the hash yourself. npm uses the ssri module to calculate integrity fields, and it verifies that the hashes match when it adds a tarball to your local cache.

Now that I have an integrity string for my package, I construct the string that ties the two together: package@version:integrity. This is the message I pass to keybase to verify:

$ keybase pgp verify --signed-by npmregistry -d sig-to-check -m 'light-cycle@1.4.3:sha512-sFcuivsDZ99fY0TbvuRC6CDXB8r/ylafjJAMnbSF0y4EMM1/1DtQo40G2WKz1rBbyiz4SLAc3Wa6yZyC4XSGOQ=='
▶ INFO Identifying npmregistry
✔ <tracked> public key fingerprint: 0963 1802 8A2B 58C8 4929 D8E1 3D4D 5B12 0276 566A
You last followed npmregistry on 2018-04-10 21:21:57 PDT
✔ <tracked> admin of DNS zone found TXT entry keybase-site-verification=iK3pjpRBkv-CIJ4PHtWL4TTcFXMpPiwPynatKl3oWO4
✔ <tracked> "npmjs" on twitter: [cached 2018-04-12 13:18:31 PDT; but got a retryable error (API network error: Get net/http: request canceled (Client.Timeout exceeded while awaiting headers) (code=170)) this time around]
✔ <tracked> admin of DNS zone found TXT entry keybase-site-verification=Ls8jN55i6KesjiX91Ck79bUZ17eA-iohmw2jJFM16xc
Signature verified. Signed by npmregistry 7 minutes ago (2018-04-13 15:00:37 -0700 PDT).
PGP Fingerprint: 096318028a2b58c84929d8e13d4d5b120276566a.

It’s obviously inconvenient to verify by hand, and we plan to offer you easier ways to check version signatures soon. Note that it’s expensive to verify signatures fully with Keybase, since proofs are rechecked and this requires network activity. This is not intended to be an action you take many thousands of times per second. It would be appropriate action to take when you choose to verify a deploy artifact, however, or when a package is stored in your cache in the first place.

Next steps

You’ll notice that the signatures are only available on newly-published package-versions. We’ll backfill signatures for all packages published earlier than our signing launch, but we’ll do this slowly, so as not to destroy everyone’s registry caches all at once.

We also intend to follow up with verification built into the official npm CLI, so you can check more conveniently.

Finally, we’d like to take the next step and allow package authors to sign integrity fields as part of publication, so you can have a way to verify that the npm registry itself hasn’t meddled with package contents. (I believe we’re a trustworthy steward of your JavaScript code, but I’d like to be able to prove that to you.)

The npm registry public key

Our public key follows. You can also fetch it from Keybase:

http GET
Version: Keybase Go 1.0.47 (darwin)