ggui keys register
read as.md ggui keys register ships your publisher Ed25519 public key to the marketplace registry’s POST /author-keys endpoint. After this, every subsequent ggui gadget publish / ggui blueprint publish whose bundle is signed by the matching private key validates against the registry’s stored row.
When to use it
Section titled “When to use it”You run ggui keys register after ggui gadget publish (or ggui blueprint publish) has auto-generated a keypair on disk for a given scope. The publish flow does the keypair generation automatically on first run, then signs the artifact, then POSTs to /publish. The registry rejects a signed publish whose publicKeyId isn’t already registered for the caller’s identity — that’s the moment to run this command.
The typical bootstrap sequence for a new publisher:
# 1. First publish under @my-org auto-generates# ~/.ggui/keys/@my-org/{private,public}.key and signs the bundle.ggui gadget publish# → Error: unknown_key — public key not registered for this publisher.
# 2. Register the public half with the registry.ggui keys register --scope @my-org
# 3. Re-run the publish. The signature now verifies.ggui gadget publishAfter step 2, every future publish under @my-org from this machine (and any other machine you copy ~/.ggui/keys/@my-org/private.key to) validates without a second register call.
ggui keys register --scope <@scope> [--registry <url>] [--auth=bearer [--token <token>]]| Flag | Required | Purpose |
|---|---|---|
--scope | yes | npm scope the key was generated under, prefixed with @ (e.g. --scope @my-org). The CLI reads ~/.ggui/keys/<scope>/public.key — must already exist (see “When to use it” above). |
--registry | no | Override the registry URL. Same resolution chain as ggui gadget publish: --registry flag, then GGUI_REGISTRY env var, then ggui.json#registry walking up from CWD. No hard-coded default — pick a registry deliberately so a typo doesn’t accidentally register against prod. |
--auth=bearer | no | Send an explicit bearer token instead of the stored ggui login session — for self-hosted registries that authenticate with a static publish token. Pair with --token <token> or set GGUI_REGISTRY_TOKEN. Same flags the publish verbs take. |
--token | no | The bearer token for --auth=bearer (overrides GGUI_REGISTRY_TOKEN). |
ggui keys register uses your stored ggui login session by default — the same credential ggui gadget publish sends to /publish, and the one the hosted registry’s /author-keys route authenticates (see Marketplace § Auth). The CLI reads ~/.ggui/auth.json, refreshes the access token automatically when it has expired, and sends it as Authorization: Bearer <token> — the only identity surface; the request body carries only publicKeyBase64, never the caller’s user id. The server derives the publisher subject from the verified credential and the keyId from the raw public-key bytes.
Self-hosted operators running their own @ggui-ai/registry-server deployments pass --auth=bearer --token <token> (or set GGUI_REGISTRY_TOKEN) — the same escape hatch the publish flow takes (see ggui marketplace for the parallel).
Output
Section titled “Output”Success — first-write (HTTP 201):
$ ggui keys register --scope @my-orgRegistered publisher key for @my-org. registry: https://registry.ggui.ai subject: <your-user-id> keyId: a1b2c3d4e5f60718Idempotent re-register (HTTP 200) — same public-key bytes for (subject, keyId) already on file:
$ ggui keys register --scope @my-orgAlready registered publisher key for @my-org. registry: https://registry.ggui.ai subject: <your-user-id> keyId: a1b2c3d4e5f60718Both exit 0. Safe to run unconditionally in CI bootstrap scripts.
Errors and exit codes
Section titled “Errors and exit codes”| Code | Exit | Meaning |
|---|---|---|
no-registry | 1 | No registry URL resolved. Pass --registry, set GGUI_REGISTRY, or add registry to ggui.json. |
invalid-registry | 1 | Resolved URL is malformed. |
no-keypair | 1 | No ~/.ggui/keys/<scope>/public.key on disk. Run ggui gadget publish or ggui blueprint publish under this scope first — first-publish generates the keypair as a side-effect. |
auth_failed | 1 | The stored login session expired (or its refresh was rejected). Run ggui login again. |
auth_config_missing | 1 | No stored login session (~/.ggui/auth.json missing or unreadable) — run ggui login first. Or --auth=bearer was passed without --token / GGUI_REGISTRY_TOKEN. |
network-error | 1 | fetch threw — DNS, TLS, or connection refused. |
unauthorized | 1 | Registry rejected the bearer credential (HTTP 401). Re-run ggui login (the session may have been revoked) — or, for self-hosted registries, check the --auth=bearer token. |
invalid_request | 1 | Registry refused the body (HTTP 400). The public-key file is corrupted or the wrong length — regenerate by deleting ~/.ggui/keys/<scope>/ and re-running ggui gadget publish. |
key_conflict | 3 | Registry holds a different public key for the same (subject, keyId) tuple (HTTP 409). Vanishingly rare — a 64-bit keyId SHA-256 truncation collision OR a stale row from a previous owner. Exit 3 is distinct so scripts can detect it without parsing the message. |
http-error | 1 | Any other non-2xx response (typically 5xx). Check the message for the registry’s error string; retry transient failures. |
bad-response | 1 | Registry returned a 2xx with a malformed body, or any status with invalid JSON. Almost always a registry-side bug — re-run; if persistent, the registry is misconfigured. |
The structured error field of the registry’s response body (closed enum: unauthorized / invalid_request / key_conflict / server_error) is preferred over status-code mapping when the response carries a well-formed body — key_conflict returned with a non-409 status still surfaces as key_conflict on the CLI side.
Trust model
Section titled “Trust model”The on-disk private key under ~/.ggui/keys/<scope>/private.key is mode 0o600 — treat it like a long-lived password. Copying it between machines lets you publish from CI without re-registering. Losing it means generating a new keypair under the same scope: when the new public key gets registered, both old + new keys are valid (publish flow pins the signing key onto each ArtifactVersionRow at publish time, so historical versions still verify under the previous key). To rotate out the old key entirely, register the new one + remove the old row server-side (operator-only).
See Marketplace § Trust model for the full per-author-key + per-version pinning design and the install-time two-leg verification (SHA-384 + Ed25519).