Marketplace Registry
read as.md The marketplace registry is where authors publish ggui artifacts and consumers install them. It’s HTTP-only — distinct from the WebSocket protocol — and hosts immutable, signed, content-addressable bundles + manifests. The registry is for published, signed artifacts; to move your own deployment’s cached blueprints between servers, use ggui export-pool and load the pool on the other server with ggui serve --seed-pool <dir>.
Three nouns, three roles
Section titled “Three nouns, three roles”Marketplace docs and registry wire shapes use exactly three terms. Do not say “plugin” / “library” / “package” — see the glossary for the canonical vocabulary.
| Noun | Role | Where it lives |
|---|---|---|
| gadget | ingredient — composed into a UI | clientCapabilities.gadgets (client-side hook) |
| tool | action — invoked by the agent | agentCapabilities.tools (agent-side function) |
| blueprint | recipe — returned directly as a cached UI | TSX source + optional contract + variance hints |
A gadget is a GadgetDescriptor (see Gadgets SDK) packaged for distribution. A blueprint is a (source, contract?, fixtureProps?, variance?) quad. Both ride ArtifactManifest — a discriminated union on kind: "gadget" | "blueprint".
Registry resolution
Section titled “Registry resolution”The hosted registry lives at registry.ggui.ai; self-hosted registries work identically (see Self-Hosted Registry).
The CLI resolves the registry URL per verb (highest precedence first):
- publish / search —
--registry <url>flag →GGUI_REGISTRYenv →ggui.json#registry. Errors if none is set. - install —
--registry <url>flag →ggui.json#registry→GGUI_REGISTRYenv → built-in defaulthttps://registry.ggui.ai.
Note the orders differ: with both the env var and ggui.json#registry set, publishes follow the env var while installs follow ggui.json.
CLI surface — two sibling verbs
Section titled “CLI surface — two sibling verbs”Two parallel namespaces, one per artifact kind. Each verb hard-enforces its kind from the manifest — running a gadget verb in a blueprint repo (or vice versa) emits a friendly redirect and exits non-zero.
| Verb | What it does |
|---|---|
ggui gadget create <id> | Scaffold ggui.gadget.json + src/index.ts hook stub |
ggui gadget publish | Bundle (esbuild), sign, upload from CWD |
ggui gadget search [query] | List gadgets (--kind=gadget hard-locked) |
ggui gadget install <id>@<v> | Verify + register into ggui.json#app.gadgets[] |
ggui blueprint create <id> | Scaffold ggui.blueprint.json + src/blueprint.tsx + optional contract stub |
ggui blueprint publish | Sign + upload from CWD (no bundling — TSX rides inline on the manifest) |
ggui blueprint search [query] | List blueprints (--kind=blueprint hard-locked) |
ggui blueprint install <id>@<v> | Materialize to .ggui/installed-blueprints/<scope>__<name>__<version>/ for discoverLocalUis to pick up |
ggui blueprint uninstall <id>@<v> | Remove a locally-installed blueprint (deletes the materialized directory; when it was the last installed blueprint, also strips the install-managed glob from ggui.json#blueprints.include) |
The two namespaces are deliberately separate sibling verbs — NOT an umbrella ggui artifact that auto-detects kind. Gadgets and blueprints share a registry but mean different things to the install flow, so the kind discriminator is surfaced at the verb level.
Author workflow
Section titled “Author workflow”# === Gadget ===
# 1. Scaffoldggui gadget create @my-org/weather-cardcd weather-card
# 2. Implement the hook + edit ggui.gadget.json (description, usage,# example, gotchas — these are required and shape the LLM's prompt# at code-gen time, so write them carefully).
# 3. Authenticate (the publish flow reuses the session `ggui login`# stores at ~/.ggui/auth.json, refreshing it automatically).ggui login
# 4. Dry-run validates bundle + conformance preflight without# uploading. Recommended on every change.ggui gadget publish --dry-run
# 5. Upload. First publish auto-generates an Ed25519 keypair and# registers your public key with the registry.ggui gadget publish
# === Blueprint ===
# 1. Scaffold (different template — TSX source + contract stub)ggui blueprint create @my-org/login-formcd login-form
# 2. Edit src/blueprint.tsx + the contract; manifest carries `variance`# hints that prime the matcher.
# 3. Same auth setup as gadgets.
# 4. Publish — no bundling step; TSX rides inline on the manifest.ggui blueprint publishThe publish CLIs run a shared internal pipeline that asserts manifest.kind === verb-kind, runs the registry’s conformance gate, signs (gadget: sha384(bundleBytes) of the compiled bundle; blueprint: canonical JSON of the manifest), and POSTs {manifest, bundle?, signature} to /publish.
Consumer workflow
Section titled “Consumer workflow”# Search the registry — each namespace hard-locks its kind filter.ggui gadget search "weather"ggui gadget search --hook=useWeatherCardggui blueprint search "login"
# Install. Verifies signature + SRI before writing anything.ggui gadget install @my-org/weather-card@0.1.0 # → ggui.json#app.gadgetsggui blueprint install @my-org/login-form@0.1.0 # → .ggui/installed-blueprints/Install always runs both verification legs (bundleSha384 recompute + Ed25519). If the registry didn’t pin a public key on the version (legacy / pre-key-pinning artifacts), the default behavior is warn-and-continue; pass --strict to turn that into a hard exit 1 instead.
publish (always) and private-row reads accept --auth=bearer --token=<value> (or GGUI_REGISTRY_TOKEN) for self-hosted / local-development flows. Without the flag, the CLI sends the access token from your stored ggui login session (~/.ggui/auth.json), refreshing it automatically when expired. The hosted registry’s authenticated endpoints (/publish, /conformance/check, /author-keys) accept exactly this credential — run ggui login once and the default auth path works end-to-end. Third parties running their own @ggui-ai/registry-server deployments pass --auth=bearer.
Trust model
Section titled “Trust model”The signing posture is determined by manifest.visibility. Both posters share the same bundleSha384-recompute fast leg; they differ only on the cryptographic verify leg.
visibility | Algorithm | Trust root | Key/identity binding |
|---|---|---|---|
public | sigstore-cosign | Fulcio cert + Rekor inclusion proof | OIDC subject embedded in the Fulcio leaf cert |
private | Ed25519 | AuthorKeys table (per-publisher pinned key) | publicKeyId = base64(sha256(publicKey))[:16] (16-char fingerprint) |
Per-author Ed25519 keys (private gadgets). On first publish, the CLI generates a 32-byte private key at ~/.ggui/keys/<scope>/private.key. The public key (base64(pk)) is registered with the registry under the publisher’s server-side account subject — see ggui keys register for the explicit verb and the registration error matrix. The registry copies the publishing key onto every ArtifactVersionRow it writes (field: authorPublicKey, base64-encoded raw Ed25519 public key bytes). A later key rotation does NOT invalidate historical versions — each version verifies against the exact key that signed it.
Sigstore (public gadgets). Public artifacts sign via the keyless sigstore flow — the CLI obtains a short-lived Fulcio certificate bound to your OIDC identity and submits the signature to Rekor for a public transparency-log entry. There is no long-lived author key for the public path; trust roots in Fulcio + Rekor.
Two-leg verification on install. Both must pass before any disk write:
- bundleSha384 recompute. Compute
sha384(payload), compare againstsignature.bundleSha384. Fast tamper detection. Runs for both algorithms. - Cryptographic verify. Dispatches on
signature.algorithm:ed25519→derivePublicKeyId(returnedPubkey) === signature.publicKeyId(rejects key-swap attacks), thenverifyBundleEd25519. If the registry didn’t pin a public key for the version, the CLI warns and continues by default;--strictturns the warn into a hard fail.sigstore-cosign→verifyBundleSigstorevalidates the Fulcio cert chain + Rekor inclusion. By default any valid OIDC identity is accepted; pass--verify-identity <subject-or-/regex/>to additionally pin the Fulcio leaf’s subject claim.
Subresource integrity (bundleSri)
Section titled “Subresource integrity (bundleSri)”Publish computes sha384-<base64(sha384(bundle))> server-side. The read endpoint returns it; install writes it onto GadgetDescriptor.bundleSri; the iframe runtime emits a <link rel="modulepreload" integrity="..."> tag before dynamic-importing the bundle. A CDN compromise can’t silently swap the bundle without breaking SRI.
CSP script-src auto-derives from each entry’s bundleUrl origin — no per-registry allowlist needed.
Versioning
Section titled “Versioning”- Semver.
1.2.3-alpha.1+build.42form.parseArtifactManifestrejects ranges,latest, leading zeros (applies to gadgets and blueprints alike). - Immutable.
POST /publishof an existing(scope, name, version)triple returns409 version_exists. - Yank, not delete. Yanked versions return
410 Gonewith the manifest still in the body for audit. Hard delete is intentionally NOT supported.
HTTP API surface (core routes)
Section titled “HTTP API surface (core routes)”The core routes (version-listing GET /pkg/{scope}/{name} and the /bundles/* fetch routes ride alongside):
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /search?q=&kind=&hook=&tag=&author=&… | public | List artifacts. AND-semantics across filters. |
GET | /pkg/{scope}/{name}/{version} | mixed1 | Read a single version’s manifest + fetchable URLs + signing metadata. |
POST | /publish | CLI session | Upload {manifest, bundle?, bundleSha384?, signature}. |
POST | /conformance/check | CLI session | Dry-run the conformance gate without publishing. |
Wire shapes are defined in @ggui-ai/registry-core (shared by the hosted cloud and the self-hosted server).
What’s in ggui.json after install
Section titled “What’s in ggui.json after install”Gadgets land on app.gadgets[]:
{ "app": { "gadgets": [ { "package": "@my-org/weather-card", "version": "0.1.0", "bundleUrl": "https://registry.ggui.ai/bundles/...", "bundleSri": "sha384-Wj1...", "exports": [ { "hook": "useWeatherCard", "description": "Renders a weather card.", "usage": "Use to surface current weather.", "example": { "city": "Berlin" } } ] } ] }}Blueprints materialize to disk under .ggui/installed-blueprints/<scope>__<name>__<version>/:
.ggui/installed-blueprints/└── my-org__weather-card__0.1.0/ ├── index.tsx # the published TSX source └── ggui.ui.json # generated UI manifest (id, contract, entryPoint)The install adds .ggui/installed-blueprints/**/ggui.ui.json to ggui.json#blueprints.include automatically. The existing discoverLocalUis glob loader picks them up at boot — no special-case install path on the server side.
Agent-facing MCP tools
Section titled “Agent-facing MCP tools”The marketplace surface for the agent (not the publisher CLI) lives on the same MCP endpoint as everything else. Three tools enumerate and materialize blueprints from the registry-backed catalog:
| Tool | Purpose |
|---|---|
ggui_list_featured_blueprints | Enumerate builder-curated featured blueprints (no filters; takes empty input). |
ggui_search_blueprints | Semantic search across the app’s blueprints (manifest + cached generations). |
ggui_render_blueprint | Resolve a registered blueprint by id to its compiled JS bundle — returns {blueprintId, blueprintName, code, contentType}; no session is created. |
Full wire shapes + agent-side usage: MCP Protocol Reference. Gadgets are advertised via ggui_list_gadgets instead — the catalog rides on the contract, not as a separate “render” call.
Roadmap
Section titled “Roadmap”Tracked-but-not-yet-shipped surfaces, in rough priority order:
- Per-org private artifacts.
visibility: "private"today rejects only on missing JWT; per-org group enforcement on the read handler is the next step. - Yank UX. No
ggui gadget yanksubcommand yet — yanking goes through registry admin tooling. The read + install sides already handle410 Gonecorrectly (install exits 1 with a “yanked” message). - Cross-registry federation / mirror. Single-registry installs only; federated lookup is a separate slice.
- Per-user authentication. Author identity is the hosted account’s subject today; team workflows share one account. Per-user identity within a team is future work.
Reference
Section titled “Reference”- Protocol spec. Protocol overview
- Self-hosting. Self-hosted registry — byte-compatible
@ggui-ai/registry-serverfor air-gapped or local-dev use. - Glossary. gadget / tool / blueprint — the three-noun vocabulary used throughout.
- Author manifest.
@ggui-ai/artifact-manifest—gadgetManifestSchema/blueprintManifestSchema. - Signing.
@ggui-ai/gadget-signing— universal Ed25519 (browser + Node). - Publisher key registration.
ggui keys register.