Publishing
The manual checklist for publishing the origin-* crates and the two npm packages, in dependency order.
Products scaffolded with cargo xtask new depend on the origin-* crates by registry
version, not by path (see ADR-0026 and crates/origin-xtask/src/scaffold.rs). Pass
--local only when a generated product should use the current Origin checkout.
This document is the checklist for the real thing. Publishing itself is a manual,
deliberate act (scripts/publish-crates.sh --execute); nothing here runs on its own.
Why these 29, in this order
Every workspace crate is published except the demo and the repository’s own xtask
binary. The first 21 are what the template’s own dependencies pull in, transitively; the
last 8 (origin-ai, origin-mcp-core, origin-auth-loopback, origin-mcp-stdio,
origin-mcp-http, origin-process-std, origin-workspace-fs, origin-workspace-watch)
are optional crates a product adds when it needs them.
The MCP boundary is published as origin-mcp-core: the name origin-mcp on crates.io
belongs to an unrelated project.
The order matters because cargo publish verifies a crate by resolving its
dependencies against the registry, not against local paths: a crate cannot be
published before every origin-* crate it depends on already exists on crates.io.
origin-domain, origin-manifest
origin-events, origin-platform, origin-secrets, origin-storage, origin-http,
origin-telemetry, origin-connector
origin-settings, origin-auth, origin-http-reqwest, origin-secrets-system,
origin-storage-sqlite, origin-notifications-tauri, origin-jobs, origin-sync
origin-accounts
origin-app
origin-tauri
origin-xtask
origin-ai, origin-mcp-core, origin-auth-loopback, origin-process-std,
origin-workspace-fs, origin-workspace-watch
origin-mcp-stdio, origin-mcp-http
(Crates on the same line don’t depend on each other and could in principle publish in any order relative to one another; the script still does them one at a time, in a fixed order, so a partial run is easy to reason about.)
Prerequisites
- A crates.io account with publish rights for the
casoonnamespace, and eithercargo loginrun locally orCARGO_REGISTRY_TOKENset. - Crate name availability: run
scripts/publish-crates.sh --check-namesbefore the first real publish (needs network access to crates.io; degrades to “could not check” per name rather than failing if that access is unavailable). - All 29 crates share the workspace version; the script publishes that version.
Running it
scripts/publish-crates.sh # metadata check only, publishes nothing
scripts/publish-crates.sh --check-names # + live crates.io name availability
scripts/publish-crates.sh --execute # publishes for real, one crate at a time
The metadata check verifies every crate has description, license, version,
readme (with a matching README.md in the crate directory), keywords and
categories set and valid (checked against the crates.io category slug list embedded
in the script), and is not marked publish = false — everything crates.io itself would
otherwise reject one crate at a time, ten minutes apart. None of this needs network
access; --check-names is the one step that does.
--execute does, for each crate in order:
- Skip it if
<name>@<version>is already on crates.io (see below). cargo publish --dry-run -p <name>— a real build-and-package pass against the crates.io registry, using whichever of itsorigin-*dependencies are already published. This is the per-crate dry run; it cannot run meaningfully for every crate ahead of time, only immediately before that crate’s own turn, because it needs its dependencies to already be live.cargo publish -p <name>for real.- Poll
cargo info <name>@<version>(the exact version, not just the crate) until the crates.io index has caught up — the next crate’s dry run and build both depend on this one being resolvable there, not merely accepted.
crates.io limits how quickly brand-new package names may be created. If that limit is reached, the script waits ten minutes and retries the same crate automatically.
If a run fails partway through
Crates already published stay published (crates.io has no unpublish for a used
version). Fix the failure and re-run the same --execute command: step 1 above skips
anything already published at the current version, so a partial run resumes on its own
rather than needing the crates array trimmed by hand.
Publishing the frontend packages
Scaffolded products also depend on @casoon/origin-client and @casoon/origin-ui by
registry version (^<version> in the template’s ui/package.json). They are published
under the @casoon npm scope: @origin on npm belongs to an unrelated project.
- Needs an npm account with publish rights for
@casoon(npm whoami). - Use
pnpm publish, nevernpm publish: only pnpm rewrites@casoon/origin-ui’sworkspace:*dependency on@casoon/origin-clientto the real version. - Publish
@casoon/origin-clientfirst —@casoon/origin-uidepends on it. - Both packages share the workspace version; bump them together with the crates.
pnpm --filter @casoon/origin-client publish --dry-run # inspect the tarball
pnpm --filter @casoon/origin-client publish
pnpm --filter @casoon/origin-ui publish
publishConfig.access is public in both manifests; a scoped package is otherwise
published as restricted.