GitHub Releases are canonical. A protected stable tag produces one verified package archive, GitHub provenance, and an immutable Release, then the same workflow publishes those exact bytes to npm through trusted publishing. npm is a downstream mirror. The new @hraness/slopcamera package has no dual-use content declaration or classification-based disclosure requirement. Its npm publisher must be configured for this exact new identity; settings on historical @hraness/atet do not carry over.
Start with canonical GitHub publication. The npm publication follows automatically in the same workflow. Slopcamera v3.2.4 is the first canonical Slopcamera release; these procedures publish each later version the same way, and no archive is advertised before its live acceptance.
Keep the source candidate separate from a verified public release. apps/web/published-release.json names the verified canonical Slopcamera release that the site and README advertise. Keep source installation as the public default until a Slopcamera archive passes live verification. Then update the installation surface and its verified release datum together.
-
In the version bump pull request, rename the
## Unreleasedheading inCHANGELOG.mdto the new version, such as## 3.4.1 - 2026-10-01, and keep its summary paragraph and change bullets. The release page copies that section.scripts/push-release-tag.ts, the verification job and the publisher each stop when the section is missing, empty, still says Unreleased, or lacks a summary followed by bullets. -
Merge the new stable source through the current-head Required gate and complete the repository's local and native acceptance. From clean current
main, runbun run ./scripts/push-release-tag.ts <exact-stable-version>. Its sole annotated-tag push follows owner, repository, protected-main, exact CI run/attempt/Required job, both live tag rulesets, immutable GitHub latest and monotonic remote-tag checks. It never publishes npm, moves a tag, or deletes a remote ref. -
The protected tag workflow repeats the owner (
894119) or tagger bot (337004703) actor and sender ID, repository ID1310516748, annotated tag, source ancestry and current-main workflow/helper closure checks. The read-only job runs the complete source gate, builds onenpm pack --ignore-scriptsarchive, independently validates its bounded USTAR inventory and metadata, and exercises the exact archive in isolated Bun and npm consumers. Five official VTracer targets must pass before attestation. -
A checkout-free job reauthorizes the current run and loads only the byte-identical current-main/tagged release helper. It verifies the four-file handoff against the verification job's digests before requesting OIDC. Pinned
actions/attestsigns the archive,npm-pack.json,release-manifest.jsonandSHA256SUMS.provenance.jsonlcarries the returned bundle. No package or product code runs with attestation or release credentials. -
The dependent publisher independently verifies the five exact files, cryptographic provenance and hosted source/run/attempt identity. It creates an Actions-authored draft, uploads only missing matching assets, checks all provider names, sizes and digests, rechecks live source/authority and version ordering, then publishes immutable Latest. It never overwrites an asset or deletes/recreates a release. Discover existing drafts through the bounded authenticated release list, require one exact-tag match, and use its positive release ID for readback; GitHub may return 404 for a draft at the tag endpoint. Matching state from the same attempt is reconciled; a prior attempt's different manifest or bundle stops with its exact state rather than relabeling historical provenance.
-
Verify the live release, download its five assets into a fresh directory, and verify the archive before installation:
gh release verify v<version> --repo hraness/slopcamera gh release download v<version> --repo hraness/slopcamera --dir <fresh-directory> gh release verify-asset v<version> <fresh-directory>/hraness-slopcamera-<version>.tgz --repo hraness/slopcamera gh attestation verify <fresh-directory>/hraness-slopcamera-<version>.tgz --repo hraness/slopcamera --signer-workflow hraness/slopcamera/.github/workflows/release.yml --signer-digest <source-sha> --source-digest <source-sha> --source-ref refs/tags/v<version> --deny-self-hosted-runners --bundle <fresh-directory>/provenance.jsonl
Check every checksum against the downloaded bytes and bind the manifest to the verified source, tag and release attempt. Run
scripts/package-smoke.ts --archive <archive> --pack-json <npm-pack.json>against the exact tagged source in an isolated consumer. A checksum or unsigned manifest alone is not provenance. -
After live asset and installation acceptance, update the public release datum and installation references in a normal checked change, then verify Production HTML, Markdown and download targets. The one-command CLI install and update is
bun add --global https://github.com/hraness/slopcamera/releases/download/v<version>/hraness-slopcamera-<version>.tgz; omit--globalfor an SDK dependency. Package and command names stay@hraness/slopcameraandslopcamera. Resolve Latest discovery to an immutable version before verification; do not install a mutable latest URL or pipe a downloaded script into a shell.If an update from a previous local archive reports Bun's
DependencyLoop, use its named tarball syntax: prefix that same verified archive URL with@hraness/slopcamera@in thebun add --globalcommand. Checkslopcamera --versionafter the update.
The release title is Slopcamera v<version>. The body is the changelog summary, ## Changes with its bullets, ## Install with the exact versioned archive command and the npm mirror command, and ## Verify with the SHA256SUMS asset, the full source commit and a link to this guide at the tag. The machine identity record <!-- hraness-github-release-v1 ... --> follows as the final bytes. Reruns and npm admission parse the identity from the last marker, require the body to end with -->, and require the notes above it to match the rendered changelog section and generated sections byte for byte, so a hand edit to a published page stops the rerun.
The manifest has exactly schema hraness-github-release-v1, repository/name and numeric repository ID, package, stable version/tag, source SHA, release workflow/current authority SHA, numeric run ID/attempt, and archive name/bytes/SHA-256/SHA-512. Every file is bounded and regular; unexpected files, symlinks, traversal, unsafe packed configuration and inconsistent bytes reject. Native binaries are not added to this distribution without separately enumerated asset and installation proof. The capture shell and its release gates were removed in PR #111; the current companion's macOS source build was added in PR #116.
npm publication is automatic. The same tag Release workflow that publishes the
immutable GitHub Release then runs publish_npm, which publishes the exact
canonical archive bytes to @hraness/slopcamera through npm trusted publishing
(OIDC) with npm provenance, and admit_npm, which verifies the public registry
copy against the canonical asset. No dispatch, staged approval, two-factor
prompt, or token is involved; Slopcamera is ordinary software and carries no
content-policy classification. Preserve account two-factor authentication and
the exact configured publisher; never substitute a long-lived publishing token.
publish_npm runs only after publish has verified immutable Latest. It checks
out no product source and installs no dependencies. It re-runs the owner and
current-main authority checks, loads the byte-identical current-main release
helper, downloads the attested five-file handoff by numeric artifact ID (so a
rerun of only the failed job still publishes the identical bytes), and admits it
through github-release.ts npm-admit: exact handoff bytes and trusted digests,
this run's source and tag, cryptographic provenance, and the published immutable
Latest release whose assets carry the same digests. The checkout-free USTAR and
packed-manifest reader then rejects any packed tag, non-canonical
publishConfig, or unsafe archive before the job requests OIDC.
Immediately before mutation the job scrubs ambient npm_config_tag, proves that
npm 11.19.0's clean default tag is latest, rehashes the archive, and reads the
registry version. An absent version must be newer than public latest; a
version that already publishes the identical SHA-512 integrity is an earlier
attempt of the same publication and completes without a second write; a
different integrity fails closed and is never overwritten. The only registry
mutation is equivalent to:
npm publish <attested-archive> \
--@hraness:registry=https://registry.npmjs.org \
--access public \
--ignore-scripts \
--provenance \
--registry=https://registry.npmjs.orgIt runs from a clean directory with separate empty user and global npm
configuration files and deliberately omits --tag, so npm keeps its built-in
monotonic-latest guard. The returned identity must match the archive
integrity exactly.
admit_npm checks out the verified tag read-only, downloads the immutable
GitHub asset and the registry archive, and compares them with
npm-package-identity.ts (complete content inventory, entry types, modes,
sizes and hashes; npm may re-encode transport bytes). Using the verified registry
archive's integrity, it waits for npm's install metadata, latest tag, and
provenance subject to agree. It refreshes the registry view and checks it against
the retained archives, then installs the exact version in an isolated consumer, runs
npm audit signatures --json --include-attestations --omit=dev, and verifies
registry signatures plus the npm publish and SLSA attestations with
npm-publish-authority.ts, which binds the provenance to this repository, the
tag ref, the push event and the Release workflow. The isolated package smoke
runs against the registry archive. Those npm proofs are mirror acceptance; they
are not GitHub release authority. An npm write can succeed before its runner
reports failure, so rerun only the failed jobs and let the registry readback
reconcile the state; never republish a version or move latest backward.
The packed manifest must not contain a top-level tag, because npm gives that
field precedence over the command's explicit dist-tag. Its publishConfig must
contain exactly access and registry, with values public and
https://registry.npmjs.org. A scoped registry, proxy, authentication, tag,
provenance-file, or any other packed npm configuration is forbidden because npm
otherwise lets package metadata override its network and publication options.
The checkout-free publication parser and source/release identity parser both
require the exact eight-byte USTAR signature (ustar\0 plus 00) and
npm/node-tar's byte-475 prefix discriminator (zero means 130 prefix bytes;
nonzero means 155). Shared hostile fixtures keep both tar consumers
behaviorally aligned.
Keep two active repository rulesets matching refs/tags/v*. Immutable
version tags restricts update and deletion with an empty bypass list.
Release tag creation restricts creation only and gives immutable owner
User ID 894119 and the hraness-release-tagger App the only always-bypass
entries. .github/workflows/auto-tag.yml uses that App to create the
annotated v<version> tag once a version bump passes CI on main, and the
release accepts its hraness-release-tagger[bot] user (Bot ID 337004703)
as the tag pusher. Do not grant the generic GitHub Actions integration, an
administrator, a repository role, a team, or any other integration this
bypass, and never combine creation with update or
deletion. This one-time provider setup lets the already-authenticated owner
create the exact release tag under standing task authority without a routine
GitHub sudo approval. Never create probe tags or move a version tag. The canonical GitHub gate is independent of optional npm mirroring.
See npm's documentation for trusted publishing.
@hraness/slopcamera publishes only through one GitHub Actions trusted
publisher with this exact identity:
- organization or owner:
hraness - repository:
slopcamera - workflow filename:
release.yml - environment:
npm-release - allowed action:
npm publish
The one-time owner setup (passkey or two-factor prompt) is:
npm trust github @hraness/slopcamera --repo hraness/slopcamera --file release.yml --environment npm-release --allow-publish --yes
npm trust list @hraness/slopcameraRevoke any earlier relationship that names another workflow file or
environment with npm trust revoke @hraness/slopcamera --id <trust-id>.
The GitHub environment npm-release has administrator bypass disabled, the
sole protection rule branch_policy, and the sole deployment policy tag v*.
It has no required reviewers and no secrets. Only publish_npm may reference
this environment or request the npm OIDC token; attest holds the only other
id-token: write. Set package publishing access to Require two-factor
authentication and disallow tokens, remove traditional publishing tokens, and
never add an npm token to GitHub. Do not copy historical Atet classification
metadata or its disclosure requirement into the new package.
Atet's one-time npm bootstrap published @hraness/atet@3.1.1 as
hraness-atet-3.1.1.tgz. That package used a dual-use declaration and a
classification-specific disclosure. The retained publication record
describes the original commands and checks. Those requirements and artifacts
belong to the historical package; they do not describe Slopcamera's package or
prove that its registry publisher has been configured.
Preserve the historical tags, archives, package versions, attestations and receipts unchanged. A GitHub repository redirect does not rename their package contents or archive filenames. Never recreate an old artifact under a new name and call it the original publication.