Rust npm Publishing
Distribute Rust binaries to npm using the
platform package pattern.
When to Use This Skill
Activate when any of the following are true:
- Publishing Rust binaries to npm or debugging publish pipeline failures
- Setting up , , or platform package manifests
- Managing version sync across workspace packages and
- Working with protocol or prepare/restore scripts
- Scripts matching , , , or exist
Decision Tree
What does the user need?
Setting up publishing for the first time?
→ The Pattern section + Configuration + Templates
Running the publish pipeline?
→ Publish Pipeline section
→ Full details: references/publish-pipeline.md
Adding a new platform?
→ Adding a New Platform section
→ Platform reference: references/platform-matrix.md
Version sync / bump?
→ Versioning section
→ Full strategy: references/version-strategy.md
→ workspace:* details: references/workspace-protocol.md
Debugging a failure?
→ references/troubleshooting.md
The Pattern
@scope/my-tool ← main package (thin JS wrapper + bin.js)
├── optionalDependencies:
│ ├── @scope/my-tool-darwin-arm64 ← macOS ARM (M-series)
│ ├── @scope/my-tool-darwin-x64 ← macOS Intel
│ ├── @scope/my-tool-linux-x64 ← Linux x86_64
│ └── @scope/my-tool-windows-x64 ← Windows x86_64
Each platform package contains only the pre-compiled binary for that target.
npm installs only the one matching the user's OS/CPU at install time.
Same approach used by SWC, Turbopack, esbuild, and similar tools.
Configuration
Each repo provides a
(see
examples/):
typescript
export default {
scope: '@myorg',
binaries: [{ name: 'my-cli', scope: 'cli', cargoPackage: 'my-cli-rs' }],
platforms: ['darwin-x64', 'darwin-arm64', 'linux-x64', 'windows-x64'],
mainPackages: [{ path: 'packages/cli', name: 'my-cli' }],
cargoWorkspace: 'Cargo.toml',
repositoryUrl: 'https://github.com/myorg/my-project',
};
Publish Pipeline
sync-versions → generate-manifests → add-platform-deps → copy-binaries
→ validate-binaries → prepare-publish → validate-workspace → publish-platforms
→ wait-propagation → publish-main → restore-packages
Critical ordering: Platform packages MUST be published and propagated before
main packages, because main packages reference them as
.
See references/publish-pipeline.md for step-by-step details.
Main Package Wrapper ()
The main npm package is a
thin JS wrapper —
resolves the platform binary and spawns it.
See
templates/wrapper/ for the template.
Key details:
- returns (not ) — map →
- Use
require.resolve('pkg/package.json')
to find platform package, then read for binary name
- Use and forward exit codes from the Rust process
- Main package has NO / fields — it installs everywhere. Only platform packages use those.
Platform Package Manifests
Each platform package needs
/
fields and a
for chmod:
json
{
"name": "@scope/cli-darwin-arm64",
"os": ["darwin"],
"cpu": ["arm64"],
"main": "my-cli"
}
Platform Matrix
| Platform | Rust Target | | | Binary Extension |
|---|
| | | | (none) |
| | | | (none) |
| | | | (none) |
| | | | |
See references/platform-matrix.md for adding new platforms.
Adding a New Platform
- Add to array in
- Add Rust target:
rustup target add <target-triple>
- Add to CI matrix in publish workflow
- Regenerate manifests:
pnpm tsx scripts/generate-platform-manifests.ts
Versioning
Root
is the
single source of truth. Never manually edit version elsewhere.
root package.json (version: "0.2.15")
├── packages/cli/package.json → 0.2.15 (via sync-versions.ts)
├── Cargo.toml → 0.2.15
└── platform-packages/*/ → 0.2.15 (via generate-manifests)
Version Sync Flow
bash
npm version patch # Bump root: 0.2.15 → 0.2.16
pnpm tsx scripts/sync-versions.ts # Propagate to all packages + Cargo.toml
Dev Builds
CI computes:
→
0.2.16-dev.{github_run_id}
Published with
. Install:
Cargo.toml stays at base version (
) for dev builds — Cargo pre-release handling differs from npm.
Version Bump Guide
| Change | Command | Example |
|---|
| Breaking API | | 1.0.0 → 2.0.0 |
| New feature | | 0.2.0 → 0.3.0 |
| Bug fix | | 0.2.15 → 0.2.16 |
| CI/testing | Automatic (CI) | 0.2.15 → 0.2.16-dev.123 |
workspace:* Protocol
pnpm uses
for internal deps during development. Replace before publish, restore after:
bash
pnpm tsx scripts/prepare-publish.ts # Replace workspace:* → real versions
pnpm tsx scripts/validate-no-workspace-protocol.ts # Safety gate
npm publish
pnpm tsx scripts/restore-packages.ts # Restore workspace:*
See references/workspace-protocol.md for details.
See references/version-strategy.md for the full strategy.
Troubleshooting Quick Reference
| Problem | Likely Cause | Fix |
|---|
| Missing platform in bin.js | Add platform key mapping |
| Platform pkg not found on npm | Registry propagation delay | Wait; check publish logs |
| in published pkg | prepare-publish didn't run | Run prepare-publish.ts |
| Binary not executable | postinstall didn't run | the binary |
| Version mismatch | Forgot to sync | Run sync-versions.ts |
See references/troubleshooting.md for detailed diagnostics.
Templates
| Directory | Contents |
|---|
| templates/scripts/ | All 11 publish pipeline scripts |
| templates/wrapper/ | + main package |
| examples/ | Real from consuming projects |
Setup & Activation
bash
npx skills add -g onsager-ai/dev-skills --skill rust-npm-publish -a claude-code -y
Auto-activates when:
present, scripts matching
or
exist, user mentions "publish", "platform packages", or "version sync".