Internal repo. This is where Socket's @socketaddon/* npm packages are published from — the GitHub Actions workflow in this repo is the one authorized to push new versions to npm. The packages themselves (currently just iocraft, more later) are built elsewhere; this repo only takes finished binaries, double-checks them, and publishes them.
The npm registry has a feature called trusted publishing: instead of storing a long-lived npm token in CI secrets (which can leak), you tell npm "the GitHub Actions workflow at <owner>/<repo> is allowed to publish package X." When that workflow runs, it asks GitHub for a short-lived OIDC token, presents it to npm, and npm verifies it before accepting the publish.
Because trust is bound to one repo, you have to pick: which repo publishes? Two options for native-binary packages like iocraft:
-
The build repo also publishes. Simple, but the build repo ends up with npm-publishing privileges. A bug in the build pipeline can accidentally publish broken artifacts. That's how socket-cli used to do it.
-
Split the build from the publish. The build repo (
socket-btm) uploads its compiled binaries to a GitHub Release, signed and hashed. A separate repo (this one) downloads those binaries, verifies the hashes, and is the only repo allowed to push to npm.
This repo is option 2. Its only job is "pick up artifacts from socket-btm's GH Release, verify, publish."
pnpm installThis installs dependencies and runs the husky setup. End users don't install this repo — they install the published @socketaddon/<name> packages from npm.
End users:
npm install @socketaddon/iocraftnpm automatically picks the right pre-built native binary for the user's OS+CPU.
-
socket-btm finishes a build and cuts a GitHub Release like
iocraft-20260424-18f0f46. The release contains eight.nodebinaries (one per platform) plus achecksums.txtlisting the SHA-256 of each. -
Someone here updates
packages/build-infra/release-assets.jsonwith the new tag and the new per-asset SHA-256s. (The$schemapointer in that file makes editors autocomplete + flag typos.) -
Someone triggers the GitHub Actions workflow at
.github/workflows/provenance.yml. The workflow runsscripts/publish.mts, which:- Reads the embedded SHA-256s.
- Downloads each
.nodefrom socket-btm's GH Release. - Hashes the downloaded file and compares against the embedded SHA-256. Mismatch = abort the whole run, no packages published.
- Stages the per-platform package in
os.tmpdir()(so the working tree is never mutated), drops the verified.nodeinto the stage, and runspnpm publishfrom there. - Repeats for all eight, then publishes the umbrella package last (its
optionalDependenciesreferences the per-platforms by exact version, so they have to land on npm first).
If a checksum doesn't match, nothing publishes — that's the fail-loudly story.
packages/
build-infra/ # shared helpers
lib/release-checksums/
core.mts # parse + hash + verify
consumer.mts # download from sibling GH releases
release-assets.json # which release tag we're on + per-asset SHA-256
release-assets.schema.json # JSON Schema validating the .json above
iocraft/ # umbrella package on npm
iocraft-darwin-arm64/ # 8 per-platform shims, one per OS+CPU
iocraft-darwin-x64/
iocraft-linux-arm64/
iocraft-linux-arm64-musl/
iocraft-linux-x64/
iocraft-linux-x64-musl/
iocraft-win32-arm64/
iocraft-win32-x64/
scripts/
publish.mts # the actual orchestrator
The umbrella + 8 per-platform packages are how Node-native modules are published on npm. The umbrella declares the eight others as optionalDependencies with os + cpu constraints, and npm installs only the matching one. Consumers npm install @socketaddon/iocraft and get exactly the right binary, automatically.
release-checksums/ is the consumer half of a fleet-wide helper trio (core + consumer + producer). The producer half lives in socket-btm — it's what generates checksums.txt at build time. We only need the consumer half here.
Contributor commands
pnpm install # install dependencies + run husky setup
pnpm run check # lint + type check
pnpm run lint # lint files modified vs HEAD
pnpm run lint --all # lint the whole workspace
pnpm run fix # auto-fix lint + format
pnpm run test # run vitest scoped to changes
pnpm run test --all # full vitest suite
pnpm run cover # vitest with coverage
pnpm run security # AgentShield + zizmor scans
pnpm run setup # download zizmor + sfw with sha256 verification
pnpm run update # bump dependencies (taze)
pnpm run publish:dry # stage + verify, but don't actually publish
pnpm run publish:ci # full publish — CI only, requires OIDC
pnpm run clean # remove cachesMIT (per published package).