spade publish
The spade publish command submits the current block collection to the cloud registry, which screens the source, builds the artifact, signs it, and makes it available to workers.
It does not upload an artifact. It submits a reference — (repo_url, commit_sha, subdir, collection, version, language) — for a commit you have already pushed. The registry clones your repository at exactly that commit and does the rest.
That indirection is the point. The registry screens source and then builds from the same commit SHA, so the artifact it signs is provably the result of building the exact code that was screened. Publishing local, unpushed changes would let the screened source and the deployed artifact diverge, which would defeat screening entirely.
Usage🔗
cd my-collection
spade publish # private: only you can see, use, or install it
spade publish --public # anyone can discover, use, and install it
spade publish --clone-secret github-ro # the registry clones a private repository with this secret
spade publish --clone-secret gl --clone-username deploy-tokenNo positional arguments. The collection is determined by your working directory, so a monorepo holding several collections publishes the right one based on where you run the command.
Requires a spade login session.
Flags🔗
| Flag | Meaning |
|---|---|
--public | Make the collection public, so anyone can discover, use, and install it. Collections are private by default. On a later publish the flag is optional: omitting it leaves visibility as it is, and it never makes a public collection private again — that is done from the web UI's Collections page. |
--clone-secret <name> | The name of a cloud secret (stored with spade secret set --remote) holding a read-only token the registry uses to clone a private repository. The binding is remembered on the collection, so the flag is needed once, or whenever it changes. |
--clone-username <user> | The username presented with the clone secret. The default, x-access-token, is what GitHub expects for a personal access token; GitLab deploy tokens carry their own username. Needs --clone-secret. |
See Visibility and sharing for what private and public mean.
What it does🔗
1. Validate the collection🔗
Runs the same manifest and structural checks as spade check. Errors are printed and nothing is submitted.
2. Refuse to publish what the registry cannot reproduce🔗
- You must be inside a git repository.
- The working tree must be clean — no uncommitted changes.
HEADmust be reachable on the configured remote — no unpushed commits.
Each of these fails locally, before anything is sent.
3. Resolve the reference🔗
| Field | Source |
|---|---|
collection, version | the language's own manifest (Cargo.toml, go.mod, pyproject.toml, package.json, DESCRIPTION) |
commit_sha | git rev-parse HEAD |
subdir | git rev-parse --show-prefix — empty when the repository root is the collection |
repo_url | the current branch's remote (origin by default), normalized |
An SSH remote (git@github.com:org/repo.git) is rewritten to its https:// equivalent, because the registry's builder containers have git but no ssh client. The rewritten URL is printed so you can see what was submitted:
Publishing from https://github.com/org/repo (remote "origin" is git@github.com:org/repo.git)A remote using a transport the registry cannot clone fails here rather than in the build queue.
4. Submit and track🔗
The reference is POSTed to the registry with your login session. The status endpoint is a bearer-authenticated JSON API rather than a page a browser can open, so spade publish tracks progress in the terminal instead of handing you a URL you cannot authenticate to:
Collection gdal is private: only you can see, use, or install it.
Share it with named users from the web UI's Collections page, or publish with --public.
Submitted gdal@1.2.0. Tracking progress (Ctrl-C to stop):
• submitted
• screening source
• screening passed
• building artifact
• available
gdal@1.2.0 is published and available.
linux/amd64 sha256:a1b2c3d4e5f6…
linux/arm64 sha256:9876fedcba01…The visibility line is printed first, so a collection you meant to be public is not discovered private by a colleague who cannot see it.
Polling stops at a terminal state, or after 20 minutes with a note that the build continues on the registry. Ctrl-C stops watching, not the build. A failed publish exits non-zero and prints which screener rejected it, or why the clone failed:
Publish failed for gdal@1.2.0.
screener source-scan: disallowed syscall in src/lib.rsPublishing from a private repository🔗
The registry clones your repository itself, so a private repository needs a credential it can use. That credential is a cloud secret bound to the collection: the registry never stores the token, only the secret's name, and resolves the value under your account at build time.
Create a read-only token for the repository. On GitHub, a fine-grained personal access token with Contents: read scoped to just the collection's repository; on GitLab, a deploy token with
read_repository.Store it as a cloud secret, from the terminal or on the web UI's Secrets page:
spade secret set --remote github-ro Enter secret value: ················Publish with the secret's name. The binding is remembered, so later publishes of the same collection need no flag:
spade publish --clone-secret github-roFor a host that expects a specific username with the token, add
--clone-username.
The CLI cannot tell a private repository from a missing one — asked anonymously, they look the same — so a publish from a private repository with no secret bound fails at the registry's clone step, with a message that names the flag:
Publish failed for gdal@1.2.0.
cloning https://github.com/org/gdal: authentication failed; for a private
repository bind a read-only token with `spade publish --clone-secret <name>`Changing the collection's owner clears the binding, since the secret belonged to the previous owner's account; the new owner binds one of their own.
Version lifecycle🔗
A published version moves through these states:
| State | Meaning |
|---|---|
submitted | Request received, waiting in the screening queue |
screening | Screening is running against the cloned source |
screened | Screening passed; awaiting build |
building | The registry is building the artifact in the bundler image |
available | Built, signed, and stored — workers may install and execute it |
failed | Screening, build, or signing failed. Terminal |
Three more are reached by owner or operator action rather than by the pipeline: deprecated (hidden from browsing, still installable), yanked (blocks new installs), and recalled (the operator-only hard security switch, reachable from any state and irreversible).
What publishing enables🔗
A published version is what workers install: they fetch the signed artifact by reference and verify it before executing. That is how a cloud run gets its blocks. The same artifact is what spade install <collection>@<version> fetches for a laptop — for a public collection, anyone's; for a private one, only the owner's.
Configuration🔗
The registry is addressed as <server>/registry, derived from the server URL stored by spade login. Set SPADE_REGISTRY_URL to point at a different registry.
See also🔗
spade checkfor running the same validation independentlyspade installfor building and registering a collection locallyspade loginfor authenticating, which publishing requires