Skip to content

hash‐github‐cache

Eugene Lazutkin edited this page Jul 8, 2026 · 1 revision

hash-github-cache (since 1.7.0) records the SHA-256 of each published binary artifact into your addon's package.json, so install-from-cache can verify that a downloaded binary is exactly the one you published. It is the release-side companion to save-to-github-cache — it hashes the very assets that utility uploaded. See Verifying artifacts for the concept and the threat it addresses.

Like the other two utilities it has no dependencies (node:crypto does the hashing).

How to use

Add a single prepublishOnly hook to your addon's package.json:

{
  "scripts": {
    "prepublishOnly": "hash-github-cache --write"
  }
}

Now a plain npm publish — run after the release's binaries have been built and uploaded — hashes the release assets, stamps an artifactHashes map into package.json, and packs the updated file. Because the write happens on every publish, the bag can never go stale; if the release is missing or incomplete for the version being published, the command fails and aborts the publish rather than shipping a partial bag.

The stamped package.json is left in your working tree — commit it after the release.

An explicit alternative

If you would rather not have package.json machine-edited during npm publish, generate and publish in two steps and keep the hook as a guard:

{
  "scripts": {
    "release": "hash-github-cache --write && npm publish",
    "prepublishOnly": "hash-github-cache --check"
  }
}

npm run release writes the bag, then publishes; --check fails a bare npm publish whose bag is stale, missing, or wrong.

Monitoring after publish

The same --check mode doubles as a tamper monitor. Run it any time against the live release:

hash-github-cache --check --from-release 1.2.3

It re-hashes the release's assets and fails if any of them no longer matches the hash bag your published package recorded — i.e. if an asset was swapped after publish.

What it produces

For each artifact it recovers the slot from the file name (${prefix}${platform}-${arch}-${abiSlot}${suffix}, minus any .br / .gz), decompresses it, and records sha256:<hex> of the resulting binary. One entry per slot — all compression formats of a slot decode to the same bytes. The result is written to package.json as:

{
  "artifactHashes": {
    "darwin-arm64-137": "sha256:fe4fe40a...",
    "linux-x64-137": "sha256:9e68bb76..."
  }
}

Command-line parameters

Exactly one of --write / --check is required.

  • --write — compute the bag and write (or refresh) artifactHashes in package.json.
  • --check — compute the bag and compare it to package.json. Exits non-zero with a per-slot diff (missing: / mismatch: / stale:) if it differs; useful as a publish guard, in CI, and as a post-publish monitor.
  • --from-release [tag] — hash the assets attached to a GitHub release. This is the default source. The tag defaults to the package.json version; pass a value to override. The repository is read from the package.json github / repository.url field, or from GITHUB_REPOSITORY.
  • --from dir — hash slot-named artifacts in a local directory instead of a release (for single-host builds or network-free CI).
  • --prefix prefix / --suffix suffix — artifact name decoration. Must match the install and save sides.
  • --package path — the package.json to read and stamp. Default: ./package.json.

Environment variables

  • GITHUB_API_URL — base URL of the GitHub REST API for --from-release. Default: https://api.github.com.
  • GITHUB_TOKEN / PERSONAL_TOKEN — used to read a private repository's release. Public repositories need no token. Authorization is dropped when a download follows a redirect to the asset CDN, so a token is never sent to a third-party host.

Example

A realistic setup lives in uhop/node-re2:

  • package.json wires the prepublishOnly hook and carries the artifactHashes bag.
  • build.yml builds and uploads the per-platform binaries the bag is computed from.

Clone this wiki locally