From b46dc71b3c852c20ef75c93f36b345fcba5e9226 Mon Sep 17 00:00:00 2001 From: James Yong Date: Tue, 13 Oct 2026 15:58:48 -0700 Subject: [PATCH] Enable npm provenance and prepare OIDC activation release --- .github/workflows/npm-release.yml | 4 +++- README.md | 19 +++++++++++-------- clients/app/package.json | 4 ++-- clients/room/package.json | 4 ++-- clients/ts/package.json | 2 +- package-lock.json | 10 +++++----- tools/npm/publish.mjs | 2 +- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 0857a60b..64183708 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -84,6 +84,8 @@ jobs: name: npm-packages path: dist/npm/ # npm exchanges GitHub's identity for a short-lived publish token. - # No NPM_TOKEN or NODE_AUTH_TOKEN. Private source repos cannot attest provenance. + # Public source and packages support attestations without an npm token secret. - name: Publish verified artifacts in dependency order + env: + NPM_CONFIG_PROVENANCE: "true" run: node tools/npm/publish.mjs diff --git a/README.md b/README.md index bf710f04..aaafd9e5 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ with `--allow-host-execution` on a disposable research machine. ## Development and delivery -The canonical repository is the private [`vibefield/rosetta`](https://github.com/vibefield/rosetta). +The canonical repository is the public [`vibefield/rosetta`](https://github.com/vibefield/rosetta). Use a feature branch and pull request, then squash merge after **CI passed** succeeds. CI runs on pull requests and `main`, and can be started manually. It checks workflow syntax, the committed ground-truth artifacts, Python tests, Rust formatting, Clippy @@ -131,8 +131,11 @@ npm trust github @vibefield/rosetta-protocol --repo vibefield/rosetta --file npm npm trust github @vibefield/rosetta-room --repo vibefield/rosetta --file npm-release.yml --allow-publish --yes ``` -Trust setup may require account 2FA. Future releases use GitHub-hosted OIDC without -an npm token secret. The source repository is private, so npm provenance is disabled. +Trust setup may require account 2FA. A new trusted publisher must complete its first +successful publish within two days or it expires; a manual dry run does not activate +it. See [npm's trusted-publisher requirements](https://docs.npmjs.com/trusted-publishers/#trusted-publisher-configuration-expiry). +Future releases use GitHub-hosted OIDC without an npm token secret. The public source +repository supports npm provenance, which the publish job explicitly enables. The **npm release** workflow publishes only on `npm-v*` tag pushes; a manual run is a complete dry run. It checks main ancestry, stable tag/version agreement, all CI, and the exact tarballs before publishing protocol first and room second. Reruns skip @@ -144,8 +147,8 @@ through **CI passed**. Tag that main commit, using its version (for example): ```sh -git tag -a npm-v0.1.1 -m 'Rosetta npm 0.1.1' -git push origin npm-v0.1.1 +git tag -a npm-v0.1.2 -m 'Rosetta npm 0.1.2' +git push origin npm-v0.1.2 ``` Use `gh workflow run npm-release.yml --ref main` to verify a release without publishing. @@ -185,9 +188,9 @@ if a draft needs correction, delete only that unpublished draft before rerunning Manual workflow runs never publish, even when dispatched against a tag. No cloud account, deployment secrets, or automatic desktop updater is configured here. -Private-repository branch protection requires GitHub Team for the organization. -On the current Free plan, **CI passed is visible but not enforced**: users with write -access can still push or merge directly. After upgrading, apply the prepared policy: +The public repository enforces **CI passed** and pull requests on `main`, including +for administrators. The policy is recorded in `.github/main-protection.json`; to +reapply it: ```sh gh api --method PUT repos/vibefield/rosetta/branches/main/protection \ diff --git a/clients/app/package.json b/clients/app/package.json index b15eef71..e18b8a5d 100644 --- a/clients/app/package.json +++ b/clients/app/package.json @@ -39,8 +39,8 @@ "@vibecook/mille-ui": "^0.3.4", "react": "^19.2.6", "react-dom": "^19.2.6", - "@vibefield/rosetta-protocol": "^0.1.0", - "@vibefield/rosetta-room": "^0.1.0" + "@vibefield/rosetta-protocol": "^0.1.1", + "@vibefield/rosetta-room": "^0.1.1" }, "devDependencies": { "@types/react": "^19.2.14", diff --git a/clients/room/package.json b/clients/room/package.json index bcc138b8..f1221e73 100644 --- a/clients/room/package.json +++ b/clients/room/package.json @@ -1,6 +1,6 @@ { "name": "@vibefield/rosetta-room", - "version": "0.1.0", + "version": "0.1.1", "type": "module", "description": "rosetta's History room as a package: the projects, sessions and worktree columns, the panels and charts, and the transcript, behind injected ports. It dials nothing.", "main": "./dist/index.js", @@ -35,7 +35,7 @@ "@radix-ui/react-context-menu": "^2.3.7", "@radix-ui/react-dialog": "^1.1.23", "@radix-ui/react-tooltip": "^1.2.16", - "@vibefield/rosetta-protocol": "^0.1.0", + "@vibefield/rosetta-protocol": "^0.1.1", "@tanstack/react-virtual": "^3.14.10", "@vibecook/mille": "^0.3.4", "@vibecook/mille-ui": "^0.3.4" diff --git a/clients/ts/package.json b/clients/ts/package.json index 1963c1aa..9fb79701 100644 --- a/clients/ts/package.json +++ b/clients/ts/package.json @@ -1,6 +1,6 @@ { "name": "@vibefield/rosetta-protocol", - "version": "0.1.0", + "version": "0.1.1", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/package-lock.json b/package-lock.json index 83f2a438..c9125822 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,8 @@ "@tanstack/react-virtual": "^3.14.10", "@vibecook/mille": "^0.3.4", "@vibecook/mille-ui": "^0.3.4", - "@vibefield/rosetta-protocol": "^0.1.0", - "@vibefield/rosetta-room": "^0.1.0", + "@vibefield/rosetta-protocol": "^0.1.1", + "@vibefield/rosetta-room": "^0.1.1", "react": "^19.2.6", "react-dom": "^19.2.6" }, @@ -43,7 +43,7 @@ }, "clients/room": { "name": "@vibefield/rosetta-room", - "version": "0.1.0", + "version": "0.1.1", "license": "UNLICENSED", "dependencies": { "@radix-ui/react-context-menu": "^2.3.7", @@ -52,7 +52,7 @@ "@tanstack/react-virtual": "^3.14.10", "@vibecook/mille": "^0.3.4", "@vibecook/mille-ui": "^0.3.4", - "@vibefield/rosetta-protocol": "^0.1.0" + "@vibefield/rosetta-protocol": "^0.1.1" }, "devDependencies": { "@types/react": "19.2.17", @@ -114,7 +114,7 @@ }, "clients/ts": { "name": "@vibefield/rosetta-protocol", - "version": "0.1.0", + "version": "0.1.1", "license": "UNLICENSED", "devDependencies": { "typescript": "5.9.3" diff --git a/tools/npm/publish.mjs b/tools/npm/publish.mjs index 9812ebd7..16331c88 100644 --- a/tools/npm/publish.mjs +++ b/tools/npm/publish.mjs @@ -41,7 +41,7 @@ for (const pkg of packages) { } } for (const pkg of pending) { - execFileSync("npm", ["publish", pkg.tarball, "--access=public", "--ignore-scripts", "--workspaces=false", "--provenance=false", registry], { stdio: "inherit" }); + execFileSync("npm", ["publish", pkg.tarball, "--access=public", "--ignore-scripts", "--workspaces=false", registry], { stdio: "inherit" }); let verified = false; for (let attempt = 0; attempt < 60; attempt++) { const integrity = await publishedIntegrity(pkg);