This document covers the GitHub and npm setup required for the current Paperclip release model:
- canaries: manual
workflow_dispatch(channelcanary) or nightly schedule — not on everymasterpush - stable: manual promotion from a chosen source ref
- npm auth:
NPM_TOKENin GitHub Environmentsnpm-canaryandnpm-stable(required for CI publish today) - optional future: npm trusted publishing (OIDC) once every package is configured on npmjs.com
Repo-side files that depend on this setup:
.github/workflows/release.yml.github/CODEOWNERS
-
Secret
NPM_TOKEN— Create an npm automation token with publish access to@paperclipai/*(all 15 packages including@paperclipai/cli). Add it asNPM_TOKENin GitHub Environmentsnpm-canaryandnpm-stable..github/workflows/release.ymlsetsNODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}and runsnpm whoamibeforepnpm installon publish jobs. -
setup-noderegistry URL — Publish jobs useactions/setup-nodewith:registry-url: https://registry.npmjs.orgalways-auth: true
Without (1), npm whoami fails immediately. Without (2), the CLI may not use the token correctly.
Note:
- the release workflows intentionally use
pnpm install --no-frozen-lockfile - this matches the repo's current policy where
pnpm-lock.yamlis refreshed by GitHub automation after manifest changes land onmaster - the publish jobs then restore
pnpm-lock.yamlbefore runningscripts/release.sh, so the release script still sees a clean worktree
Before touching GitHub or npm settings, merge the release automation code so the referenced workflow filenames already exist on the default branch.
Required files:
.github/workflows/release.yml.github/CODEOWNERS
Do this for every public package that Paperclip publishes.
At minimum that includes:
@paperclipai/cli@paperclipai/server@paperclipai/ui- public packages under
packages/
For each package:
- open npm as an owner of the package
- go to the package settings / publishing access area
- add a trusted publisher for the GitHub repository
Viraforge/paperclip
npm currently allows one trusted publisher configuration per package.
Configure:
- workflow:
.github/workflows/release.yml
Repository:
Viraforge/paperclip
Environment name:
- leave the npm trusted-publisher environment field blank
Why:
- the single
release.ymlworkflow handles both canary and stable publishing - GitHub environments
npm-canaryandnpm-stablestill enforce different approval rules on the GitHub side
Current production path: publish jobs use NPM_TOKEN in npm-canary / npm-stable (see prerequisites above).
If you later configure OIDC trusted publishing for every package and want to drop the long-lived token:
- run a canary publish with
NPM_TOKENremoved from the environment (test in a fork first) - confirm
npm whoamiandpnpm publishsucceed via OIDC only - run a stable dry-run, then one real stable publish
- only then revoke automation tokens
After trusted publishing works:
- revoke any repository or organization
NPM_TOKENsecrets used for publish - revoke any personal automation token that used to publish Paperclip
- if npm offers a package-level setting to restrict publishing to trusted publishers, enable it
Goal:
- no long-lived npm publishing token should remain in GitHub Actions
Create two environments in the GitHub repository:
npm-canarynpm-stable
Path:
- GitHub repository
SettingsEnvironmentsNew environment
Recommended settings for npm-canary:
- environment name:
npm-canary - required reviewers: none
- wait timer: none
- deployment branches and tags:
- selected branches only
- allow
master
Reasoning:
- canary publishes run via workflow_dispatch (channel
canary) or schedule; they should not require human reviewers on the environment - restrict which branches can trigger the workflow in GitHub Actions settings if desired
Recommended settings for npm-stable:
- environment name:
npm-stable - required reviewers: at least one maintainer other than the person triggering the workflow when possible
- prevent self-review: enabled
- admin bypass: disabled if your team can tolerate it
- wait timer: optional
- deployment branches and tags:
- selected branches only
- allow
master
Reasoning:
- stable publishes should require an explicit human approval gate
- the workflow is manual, but the environment should still be the real control point
Open the branch protection settings for master.
Recommended rules:
- require pull requests before merging
- require status checks to pass before merging
- require review from code owners
- dismiss stale approvals when new commits are pushed
- restrict who can push directly to
master
At minimum, make sure workflow and release script changes cannot land without review.
This repo now includes .github/CODEOWNERS, but GitHub only enforces it if branch protection requires code owner reviews.
In branch protection for master, enable:
Require review from Code Owners
Then verify the owner entries are correct for your actual maintainer set.
Current file:
.github/CODEOWNERS
If @cryppadotta is not the right reviewer identity in the public repo, change it before enabling enforcement.
These files should always trigger code owner review:
.github/workflows/release.ymlscripts/release.shscripts/release-lib.shscripts/release-package-map.mjsscripts/create-github-release.shscripts/rollback-latest.shdoc/RELEASING.mddoc/PUBLISHING.md
If you want stronger controls, add a repository ruleset that explicitly blocks direct pushes to:
.github/workflows/**scripts/release*
Do not add a personal Claude or Anthropic token for automatic changelog generation.
Recommended policy:
- stable changelog generation happens locally from a trusted maintainer machine
- canaries never generate changelogs
This keeps LLM spending intentional and avoids a high-value token sitting in Actions.
After setup:
- merge a harmless commit to
master - open the
Releaseworkflow run triggered by that push - confirm it passes verification
- confirm publish succeeds under the
npm-canaryenvironment - confirm npm now shows a new
canaryrelease - confirm a git tag named
canary/vYYYY.MDD.P-canary.Nwas pushed
Install-path check:
npx @paperclipai/cli@canary onboardAfter at least one good canary exists:
- resolve the target stable version with
./scripts/release.sh stable --date YYYY-MM-DD --print-version - prepare
releases/vYYYY.MDD.P.mdon the source commit you want to promote - open
Actions->Release - run it with:
source_ref: the tested commit SHA or canary tag source commitstable_date: leave blank or set the intended UTC date like2026-03-18do not enter a version like2026.318.0; the workflow computes that from the datedry_run:true
- confirm the dry-run succeeds
- rerun with
dry_run: false - approve the
npm-stableenvironment when prompted - confirm npm
latestpoints to the new stable version - confirm git tag
vYYYY.MDD.Pexists - confirm the GitHub Release was created
Implementation note:
- the GitHub Actions stable workflow calls
create-github-release.shwithPUBLISH_REMOTE=origin - local maintainer usage can still pass
PUBLISH_REMOTE=public-ghexplicitly when needed
Use this policy going forward:
- canaries are automatic and cheap
- stables are manual and approved
- only stables get public notes and announcements
- release notes are committed before stable publish
- rollback uses
npm dist-tag, not unpublish
Check:
- the workflow filename on GitHub exactly matches the filename configured in npm
- the package has the trusted publisher entry for the correct repository
- the job has
id-token: write - the job is running from the expected repository, not a fork
Check:
- the
publishjob uses environmentnpm-stable - the environment actually has required reviewers configured
- the workflow is running in the canonical repository, not a fork
Check:
.github/CODEOWNERSis on the default branch- branch protection on
masterrequires code owner review - the owner identities in the file are valid reviewers with repository access