npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@extension.dev/artifact-integrity

v0.5.1

Published

Trust what you ship: download and verify browser extension artifacts (zip structure, manifest, metadata) and emit a deterministic JSON report for CI release gates.

Readme

@extension.dev/artifact-integrity Version Downloads Discord

The release gate for browser extensions. Download an artifact, verify it, and fail CI on tampered bytes before they ship.

npm install @extension.dev/artifact-integrity

Runs as a library call or a one-line CLI in any CI. Exit 0 ships, exit 1 does not.

extension.dev · Templates · Discord

Why a release gate

An extension artifact is the exact bytes your users install. Between the build that produced it and the store that publishes it sit a registry, a CDN, and a release pipeline, any of which can serve the wrong bytes. This package downloads the artifact your release is about to promote and verifies it end to end:

  • Download the packaged artifact and its co-published metadata over a token-aware fetch with a hard timeout and a size cap, so private and unlisted projects gate the same way public ones do and a hostile origin cannot exhaust the runner
  • Structure the archive as a valid zip, inspected in memory (never extracted to disk), with a root manifest.json that parses and declares manifest_version 2 or 3
  • Integrity: hash the downloaded bytes with SHA-256 and compare them against a declared digest
  • Report every check as a deterministic JSON file your CI can gate on, archive, or diff release over release

Content integrity is the check that turns this from a well-formedness lint into a trust boundary, but be precise about which threat each digest source defends against. The digest is resolved, in order, from an explicit expectedSha256 you pin in CI, then the artifact manifest's files.zip.sha256, then a sha256 or SRI integrity field in the co-published metadata.

  • The manifest and metadata digests come from the same origin that serves the bytes. They catch accidental corruption and partial tampering, but a fully compromised registry can serve tampered bytes together with a matching digest, and the check would pass.
  • Only a pinned expectedSha256 (established out of band, never fetched) defends against a compromised registry. Set it in CI whenever the guarantee matters, and set requireDigest to fail closed when no digest can be resolved at all, so an unverifiable artifact never slips through green.

Verification tooling is only worth trusting when you can read it; this package is open source for exactly that reason.

Usage

Library

import { runArtifacts } from "@extension.dev/artifact-integrity";

const result = await runArtifacts({
  artifactsBaseUrl: "https://registry.extension.land",
  owner: "my-org",
  repo: "my-extension",
  sha: "abc123",
  browser: "chrome",
  // Optional. Pin the expected package digest to make content integrity a hard
  // gate. When set it is the source of truth; a malformed value is a hard error,
  // never a silent fallback to a registry-declared digest.
  expectedSha256:
    "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  // Optional. Fail closed when no digest can be resolved from any source,
  // instead of passing the integrity check informationally.
  requireDigest: true,
  // Optional bearer token for token-gated (private/unlisted) projects. Sent
  // only over HTTPS; a non-HTTPS base URL with a token is refused.
  token: process.env.EXTENSION_DEV_TOKEN,
});

CLI

extension-artifact-integrity \
  --base-url https://registry.extension.land \
  --owner my-org \
  --repo my-extension \
  --sha abc123 \
  --browser chrome \
  --expected-sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \
  --require-digest \
  --token "$EXTENSION_DEV_TOKEN" \
  --out /abs/path/to/artifact-integrity.json

--expected-sha256, --require-digest, and --token are optional. --expected-sha256 and --token also read from the EXTENSIONDEV_EXPECTED_SHA256 and EXTENSION_DEV_TOKEN environment variables, and --require-digest from EXTENSIONDEV_REQUIRE_DIGEST=1.

Output

  • stdout: Wrote <OUTPUT_PATH>
  • side effect: writes a JSON result file (default: ./artifact-integrity.json)
  • exit code: 0 when the gate passes, 1 on failure or error

The gate is defined by severity, not by a raw boolean: a run is ok: false only when a fail-level check fails. info and warn checks are reported but never block, so a warning can never silently become a hard failure.

Schema

Top-level shape (public-safe):

type ArtifactIntegrityReport = {
  ok: boolean;
  browser: string;
  // SHA-256 (hex) of the downloaded package archive, when bytes were retrieved.
  // Always reported so you can record or pin it even if no digest was declared.
  sha256?: string;
  urls: {
    package: string;
    metadata: string;
    manifest?: string;
  };
  checks: Array<{
    id:
      | "download-package"
      | "zip-structure"
      | "manifest-present"
      | "download-metadata"
      | "package-integrity";
    ok: boolean;
    detail?: string;
    title?: string;
    // Severity class the check carries WHEN IT FAILS, not its current status:
    // a passing check can still read `level: "fail"`. Only a failing
    // `fail`-level check blocks the gate.
    level?: "info" | "warn" | "fail";
    summary?: string;
    remediation?: string;
    expected?: string;
    actual?: string;
  }>;
};

Checks may include optional metadata fields (title, level, summary, remediation, expected, actual) to make reports more actionable.

Maxed-out JSON example:

{
  "ok": false,
  "browser": "chrome",
  "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "urls": {
    "package": "https://artifacts.extension.land/my-org/my-extension/abc123/chrome.zip",
    "metadata": "https://artifacts.extension.land/my-org/my-extension/abc123/chrome.json",
    "manifest": "https://artifacts.extension.land/my-org/my-extension/abc123/artifact-manifest/chrome.json"
  },
  "checks": [
    {
      "id": "download-package",
      "ok": true,
      "title": "Download package",
      "level": "fail",
      "summary": "Package archive is reachable and downloadable.",
      "remediation": "Ensure the build artifact exists and the URL is correct.",
      "expected": "HTTP 200 and valid bytes",
      "actual": "Downloaded"
    },
    {
      "id": "zip-structure",
      "ok": true,
      "title": "Zip structure",
      "level": "fail",
      "summary": "Package is a valid zip archive.",
      "remediation": "Ensure the artifact is a valid zip file.",
      "expected": "Valid zip archive",
      "actual": "Zip parsed"
    },
    {
      "id": "manifest-present",
      "ok": false,
      "detail": "manifest_version must be 2 or 3 (found 1)",
      "title": "Manifest present",
      "level": "fail",
      "summary": "manifest.json exists at the archive root and declares manifest_version 2 or 3.",
      "remediation": "Place a valid manifest.json (manifest_version 2 or 3) at the root of the extension package.",
      "expected": "Valid /manifest.json at zip root",
      "actual": "manifest_version must be 2 or 3 (found 1)"
    },
    {
      "id": "download-metadata",
      "ok": true,
      "title": "Download metadata",
      "level": "fail",
      "summary": "Browser metadata JSON is reachable and valid JSON.",
      "remediation": "Publish <browser>.json with build metadata.",
      "expected": "HTTP 200 and valid JSON",
      "actual": "Downloaded"
    },
    {
      "id": "package-integrity",
      "ok": false,
      "title": "Package integrity",
      "level": "fail",
      "summary": "Package bytes match the declared SHA-256.",
      "remediation": "Republish the artifact, or fix the declared digest in metadata.",
      "expected": "abc0...def",
      "actual": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "detail": "Package digest mismatch: expected abc0...def, got e3b0c442...b855."
    }
  ]
}

The extension.dev stack

| Package | Use it to | | --- | --- | | @extension.dev/mcp | Give AI agents tools to build, run, debug, and publish extensions | | @extension.dev/skill | Teach AI agents the judgment half: cross-browser rules, gotchas, playbooks |

Community

  • Join the Discord for help and feedback
  • Report a bug or request a feature on GitHub

License

MIT (c) Cezar Augusto and the extension.dev collaborators