Skip to content

Integrity

Integrity here means: "the bytes you install are the bytes the publisher shipped." This page covers what guarantees the artifact registry provides today and what is target state.

Present state

Integrity verification today is exactly what npm provides:

MechanismCoverageSource
Tarball SHA-1Computed at publish, returned in package metadata. Verified by npm/pnpm clients.npm protocol
Tarball SHA-512 (integrity field)Computed at publish, returned in package metadata. Verified by modern npm/pnpm clients.npm protocol
Lockfile pinningConsumer locks the SHA-512 in pnpm-lock.yaml / package-lock.json. Re-installs verify.npm/pnpm
TLS to registryGitea is fronted by Caddy with TLS. Tampering in transit is detected.Caddy
Gitea internal storageGitea uses content-addressed blob storage internally. Tampering on disk would break Gitea's lookup.Gitea

This is the minimum bar for any npm-compatible registry. It defends against:

  • Network tampering (TLS).
  • Accidental re-upload-mutation (Gitea's blob store rejects different content for the same package-version key).
  • Unintended dependency drift (lockfile SHA-512 mismatch fails install).

What it does NOT defend against

  • Publisher compromise. A leaked Gitea token can publish anything as the victim. Without signing, there is no out-of-band proof of who published.
  • Registry compromise. A compromised Gitea host could serve replaced tarballs. SHA values are computed by the same compromised host, so they cannot detect this.
  • Supply-chain transitive risk. Dependencies pulled from npmjs.com are trusted at npm's bar, not Matrix's. A malicious upstream package is not blocked by Matrix's integrity layer.

These are real gaps. They are accepted today because the registry is private (Gitea behind admin token), not public.

Target state

Three additions are planned but not yet implemented:

1. Sigstore-style signing

Each published tarball gets an attached signature. The signing key is bound to the publisher's identity (Gitea user → keyless OIDC / cosign). Consumers verify the signature before extracting.

2. Provenance attestations

Per SLSA attestations: each tarball has a manifest describing how it was built (which CI run, which commit, which dependencies) and that manifest is signed.

3. Reproducible builds

Tarball SHA-512 should be deterministic given the same source tree. This requires removing build-time-injected timestamps and absolute paths from artifacts. The npm publish safety audit (projects/matrix-3/packages/docs/content/security/npm-publish-safety.md) identifies the current artifact path leakage:

38 matrix-artifact.json files in dist/ directories contain full local file paths including username [...] This leaks: developer username, project structure, build machine OS, build timestamps. Permanently. For every version ever published.

That leakage must be fixed before public npm publishing. It's a target-state item for the artifact registry too — a reproducible build is required before attestations are useful.

Verifying integrity by hand

For one-off verification of a Gitea-served tarball, compute SHA-512 yourself and compare to the registry-reported integrity:

bash
# Fetch the tarball
curl -fsS https://registry.hivecast.ai/api/packages/hivecast-admin/npm/@open-matrix/chat/-/chat-0.2.3.tgz \
  -o chat-0.2.3.tgz

# Compute the local hash
shasum -a 512 chat-0.2.3.tgz | cut -d' ' -f1 | xxd -r -p | base64

# Compare against the metadata-reported integrity
curl -fsS https://registry.hivecast.ai/api/packages/hivecast-admin/npm/@open-matrix/chat \
  | jq -r '.versions["0.2.3"].dist.integrity'

A match proves the bytes are the bytes the registry has. It does not prove those bytes are what the original publisher meant to ship — that requires signing, which is target state.

See also

Source: Today's integrity story is the npm protocol. Target state lives in WORKSTREAMS/product-launch/ and the npm-publish-safety audit.