Ship the CLI on npm, and prune the lock so --locked passes - #6
Merged
Conversation
@reachpad/cli plus four platform packages, published from the same cli-v* tag as the GitHub release and carrying the same binaries. The launcher resolves the one platform package npm installed and execs it — no second implementation of any verb, so there is nothing to keep in parity. npm is here because our binaries are ad-hoc signed, not notarized: any download that stamps com.apple.quarantine (a browser download from the releases page; the Homebrew cask we shipped until 2026-08-13) meets Gatekeeper, and npm never sets that attribute. release.yml now also builds x86_64-apple-darwin — Intel Macs previously had no binary at all — and carries a Developer ID sign + notarize step that warns loudly and skips while the six secrets are absent. The lock is the other half. assemble-cli-repo.sh copied the private monorepo's Cargo.lock verbatim onto a generated three-member manifest, so cargo had to rewrite it: 'cargo test --workspace --locked' failed on pristine main, which is this repo's own greentree gate, and the committed lock was never the one a release built from. Pruned to 265 packages from 532, with no version changed and nothing added. Greentree-Change-Id: b5b7dc593c8a5054d9076f415fd90957
A browser download from the releases page is the one install path that stamps com.apple.quarantine, so it is the one that meets Gatekeeper while the binaries are only ad-hoc signed. Someone who hits "Apple could not verify reachpad is free of malware" with no explanation next to the download concludes the binary is malware rather than unsigned. The release body now leads with the three install commands that avoid the tag, and gives the checksum-then-xattr pair for the tarball itself. Greentree-Change-Id: a4f6e540b26185556c82353e2250920e
npm signs provenance on every OIDC publish and refuses the upload when package.json does not name the repository it was built from — after the tarball is built and the attestation is already in the transparency log. The check covered the four platform packages and skipped @reachpad/cli, which is the one published LAST: a missing field there fails with four packages already public and a version npm will not accept again. Greentree-Change-Id: cdd9dfd0b14b83eafd89b9970533937d
Trusted Publishing can only be configured on a package that already exists, so the first version of all five must be pushed by hand — and until then the publish dies with ENEEDAUTH next to a release that otherwise succeeded, which reads as a credentials problem rather than a setup step nobody has done yet. The failure path now checks whether the package exists at all and, when it does not, emits an annotation with the bootstrap commands and the per-package trusted-publisher settings. Greentree-Change-Id: 4128d9db73fdace724aba6623606bc16
Pruning the lock made it correct; nothing was using it. The release build and CI both re-resolved on the runner, which means the committed lock described nothing that was ever built, two builds of one tag could differ, and a transitive dependency publishing a semver-compatible release between them would be picked up with nobody deciding to. The provenance claim at the top of release.yml — tag, readable source, workflow run, SHA256SUMS — needs the source to determine the binary. --locked is the step that makes that true rather than nearly true. CI gets it too, so the lock is checked on every PR instead of first discovered at release time, and so CI stops being green on trees the repo's own greentree check refuses. Verified: cargo build --release --locked -p reach succeeds and the binary reports reachpad 0.3.0. Greentree-Change-Id: 921271c61caa36784a769b292483d5a9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
@reachpad/cliplus four platform packages (-darwin-arm64,-darwin-x64,-linux-x64,-linux-arm64), published from the samecli-v*tag as the GitHub release and carrying the same binaries.The launcher (
npm/cli/bin/reachpad.js) resolves the one platform package npm installed for the machine and execs it. There is no second implementation of any verb, so parity is by construction — same binary, same flags, same exit codes.Why npm
Our release binaries are ad-hoc signed by the linker, not Developer ID signed and notarized. I checked the shipped 0.3.0 Mach-O: one CodeDirectory slot, identifier
reachpad-c5850a1ca39ad196, no CMS blob. Ad-hoc is enough to execute on Apple silicon, which is whybrewand the curl installer work. It is not enough for anything that stampscom.apple.quarantine— a browser download from the releases page, or the Homebrew cask we shipped until 2026-08-13. npm never sets that attribute.Also:
npx @reachpad/clineeds no install, and Node is already on every machine that runs coding agents.The lock
assemble-cli-repo.shcopies the private monorepo'sCargo.lockverbatim onto a generated three-memberCargo.toml. The lock therefore describes a different workspace than the manifest, and cargo has to rewrite it. Consequences, both live onmaintoday:cargo test --workspace --locked— this repo's own greentreetestcheck — fails on pristinemain.release.ymlruns without--lockedand re-resolves on the runner.Pruned here: 532 to 265 packages. Every surviving
(name, version)was already in the lock; nothing added, no version changed (the apparentdigest/sha2changes are duplicate majors losing the copy no public crate references). The companion fix inreachpad-infraprunes it in the sync script, and produces a byte-identical lock to this one.Release workflow
x86_64-apple-darwin, cross-compiled on the same arm64 runner. Intel Macs previously had no binary at all —install.shrefused them and the formula rejected them on an arch requirement.release.ymlfor the exact secrets.Reachpad/reachpad-mcp. Idempotent (skips a version already on npm), platform packages first.Tests
npm/test/packages.test.mjs, 16 tests, no network and no build. Two kinds:release.yml's build matrix (parsed, not restated),npm/prepare.mjs's staging table, the launcher's lookup, and@reachpad/cli'soptionalDependencies. A target added to the matrix and nowhere else publishes tarballsnpm installcan never reach.reachpad runreturns the guest's status), signals (a SIGTERMed guest must make the launcher die of SIGTERM, notexit 1), and stdio inheritance (a pipe would handreachpad attacha non-tty).Wired into both
ci.ymlandgreentree.yaml.Verified by hand
Staged the real 0.3.0 linux tarball, packed both tarballs,
npm install -ginto a prefix, and ran against the live control plane:npx reachpadandnpm install --ignore-scriptsboth work — the reason the binary is an optional dependency rather than a postinstall download.Not in this PR
The first publish of each of the five packages must be manual: npm Trusted Publishing can only be configured on a package that already exists.
npm/README.mdhas the exact commands and the per-package settings.