Publishing
A package is signed on your machine, by a key only you hold, and uploaded with a token only you hold. Anyone can then download it and check, offline, that you published it and nobody changed it.
Publishing a release
Publishing from CI
Verifying anyone's package
What a verified package means
Rotating or losing a key
When publishing fails
The official registry is live at
https://godspeed-registry.bankole-422.workers.dev, and it is the CLI's default, with its root
of trust built in. (The address is temporary: it moves to a custom domain, and the default moves with a
CLI release.) --registry, GODSPEED_REGISTRY or godspeed login
choose another. Publishers are approved by hand for now, by the registry's maintainer.
Once: your key and your token
Publishing needs two things, and they are deliberately separate. Your signing key proves which key signed a release. Your token proves to the registry who is uploading. Losing one never gives away the other.
-
Make your signing key
$ godspeed key generate Generated a signing key. public key: ed25519:b79a6e807483491aef81606ca45acfa2b4aa943522d693213b5439a26f7d0975 stored in: ~/.config/godspeed/signing-key.toml (readable by you alone)
Back that file up: a lost key cannot be recovered. Send the public key - never the file - to the registry maintainer. They list it, signed offline, as a key allowed to sign for your namespace.
-
Make your token
$ godspeed login Made a publisher token for https://godspeed-registry.bankole-422.workers.dev, stored in ~/.config/godspeed/credentials.toml (readable by you alone). Its fingerprint: sha256:7555d1395fd4b16f16db195c707335aa748c4f5c84d7c2b27db0a5f87ca2a917 Send the registry maintainer this fingerprint - never the token itself - and your account name.
Like an SSH key, the token is made on your machine and never leaves it. The maintainer approves only its fingerprint - its SHA-256 - which is all the registry ever stores. Running
loginagain is always safe: it says whether the token is still waiting, and makes a new one only if the old one can never work again. -
Check it
$ godspeed whoami alice on https://godspeed-registry.bankole-422.workers.dev may publish under: alice/ token expires: 2027-01-14T10:34:09Z token from: stored credentials fingerprint: sha256:7555d1395fd4b16f16db195c707335aa748c4f5c84d7c2b27db0a5f87ca2a917
On Windows the two files are in %APPDATA%\godspeed\.
Publishing a release
Name your namespace in godspeed.toml - publisher = "alice" makes the package
alice/hello - and publish:
$ godspeed publish Creating package... Verifying package... Signing with ed25519:b79a6e80... Authenticating with https://godspeed-registry.bankole-422.workers.dev... Reserving alice/hello@0.1.0... Reserved until 2026-10-16T10:51:53Z. Uploading contract.toml (439 bytes)... Uploading manifest.sig (243 bytes)... Uploading x86_64.elf (18952 bytes)... Uploading aarch64.elf (78760 bytes)... Uploading armv7.elf (16644 bytes)... Uploading riscv64.elf (30968 bytes)... Finalising publication... Published alice/hello@0.1.0 (2026-10-16T10:36:55Z) Install, on a Godspeed machine: godspeed fetch alice/hello godspeed install alice/hello Verify it from the registry, as a client would: godspeed fetch alice/hello@0.1.0
- Every architecture, one version.
publishbuilds all four and publishes them together as one release. - A published version never changes. To change anything, raise the version (in
Cargo.tomland the contract) and publish again. Publishing the identical package twice is a success, not an error. - Nothing half-published is ever visible. Files are staged privately and checked; the release appears only when the registry's index is updated, all at once. Two people publishing the same version at the same moment: exactly one wins, and the other is told so.
- Want to look first?
godspeed packagebuilds exactly whatpublishwould send, intotarget/package/, andgodspeed verifychecks it.
Publishing from CI
CI gets its own token, printed once and stored nowhere:
$ godspeed token new gsp_... Fingerprint: sha256:... The token above is shown once and saved nowhere. Put it in your CI's secrets as GODSPEED_TOKEN, and send the registry maintainer the fingerprint - never the token - and your account name.
Send the fingerprint to the maintainer as before, then give the job two secrets and nothing else:
.github/workflows/publish.yml (excerpt)env:
GODSPEED_REGISTRY: https://godspeed-registry.bankole-422.workers.dev
GODSPEED_TOKEN: ${{ secrets.GODSPEED_TOKEN }} # the token from `token new`
GODSPEED_SIGNING_KEY: ${{ secrets.GODSPEED_SIGNING_KEY }} # the secret line of signing-key.toml
steps:
- run: godspeed publish
Every command runs without prompts, with the same checks as on your machine.
Verifying anyone's package
Downloading needs no account. fetch checks a release the way a Godspeed machine will before
installing it, and keeps nothing it could not verify:
$ godspeed fetch godspeed/hello@0.1.0 Checking https://godspeed-registry.bankole-422.workers.dev's trust metadata... First contact with the official registry: its root 1 matches the one built into this CLI. Downloading contract.toml... Downloading x86_64.elf... ... Fetched godspeed/hello@0.1.0 into ./godspeed-hello-0.1.0 verified: root 1 -> timestamp 2 -> snapshot 2 -> namespaces 1 -> index -> manifest -> files signed by ed25519:b79a6e80... (an active key of godspeed/) requests: log_write
The official registry's root of trust is built into the CLI: the first time this machine talks to it,
its root is accepted only if its digest is
sha256:d769d1a64c3105c8da0ffcc2f54aba1c3a16ce4ae707a6d206556f08824a146d. For any other
registry, --trust-root pins its root the same way; without it, the CLI trusts the root it is
given on first contact and prints its digest for you to compare. After that, a new root is accepted only if
the old one signed it.
A package directory - downloaded any way you like - can also be checked with no registry at all:
$ godspeed verify ./godspeed-hello-0.1.0
What a verified package means
| Checked | It means | It does not mean |
|---|---|---|
| Size and SHA-256 of every file | These are exactly the bytes the manifest names | Who made them |
| The manifest's signature | The named key signed this release | That the key is the publisher's |
| The registry's signed namespaces | That key may sign for this namespace, and was not revoked | That the software is safe |
| Timestamp and snapshot | You see the registry's current state, not an old one replayed | Anything about the code |
The design follows The Update Framework: the keys that decide who may sign for whom are kept offline, so even the registry cannot change them. Details: trust metadata.
A signature is not a safety check, and installing is not a grant. A package's contract requests authority. What it is actually given is decided by the operating system on the machine it runs on.
Rotating or losing a key
- Routine rotation: make a new key and send its public half to the maintainer, who lists it and retires the old one. Releases the old key signed stay trusted; it signs nothing new.
- A key is stolen or leaked: tell the maintainer to revoke it. Nothing it ever signed is trusted any more, however old, so republish what matters under a new version with the new key.
- A token leaks:
godspeed logoutrevokes it at the registry. The maintainer can also revoke every token of an account.
When publishing fails
Every refusal says what happened, why, and what to do next, and exits with a code a script can act on (all of them):
Cannot publish alice/hello@0.1.0
Stage: upload
Reason:
alice/hello@0.1.0 was already published on 2026-10-16T10:36:55Z with a different manifest.
Published versions never change.
Next:
Increment the version (in the contract and Cargo.toml) and publish again.
If the connection drops while finalising, publish does not guess: it reads the registry's
index and reports what it finds. If it still cannot tell, it says the outcome is unknown (exit 25), and
running publish again is always safe.