---
title: Marketplace Registry
description: Publish and install gadgets + blueprints from the ggui marketplace. Versioned, Ed25519-signed, content-addressable distribution for ggui artifacts.
---

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

Marketplace docs and registry wire shapes use exactly three terms. Do not say "plugin" / "library" / "package" — see the [glossary](/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](/sdk/gadgets/)) packaged for distribution. A blueprint is a `(source, contract?, fixtureProps?, variance?)` quad. Both ride `ArtifactManifest` — a discriminated union on `kind: "gadget" | "blueprint"`.

## Browse the registry

[hub.ggui.ai](https://hub.ggui.ai) is the public, unauthenticated browser over the same registry — no account, no CLI. It lists recently published artifacts, has a search page (`/search`) with filters for kind (blueprint / gadget) and MCP binding (`tool=`, `server=` — find every artifact that works with a given tool or server), and a per-version page for each artifact showing the manifest, signing metadata (algorithm, verified-publisher badge where applicable), and a ready-to-copy install command. It reads only the registry's public CORS-open endpoints, so it shows exactly what `ggui gadget search` / `ggui blueprint search` show — same catalog, browsable form.

## Registry resolution

The hosted registry lives at `registry.ggui.ai`; self-hosted registries work identically (see [Self-Hosted Registry](/sdk/self-hosted-registry/)).

Every verb resolves the registry URL through the same chain (highest precedence first): `--registry <url>` flag → `GGUI_REGISTRY` env → `ggui.json#registry` (walking up from CWD). The verbs differ only on a full miss, following the npm model:

- **Read verbs** (`install`, `search`) fall back to the public default `https://registry.ggui.ai` — zero config works out of the box.
- **Write verbs** (`publish`, `keys register`) error if none is set — a publish target must be opted into explicitly.

## 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 gadget uninstall <id>@<v>`    | Remove the installed `(package, version)` entry from `ggui.json#app.gadgets[]` (reverse of install; the emptied array is dropped)                                                             |
| `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

```bash
# === Gadget ===

# 1. Scaffold
ggui gadget create @my-org/weather-card
cd 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-form
cd 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 publish
```

The 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

```bash
# Search the registry — each namespace hard-locks its kind filter.
ggui gadget search "weather"
ggui gadget search --hook=useWeatherCard
ggui blueprint search "login"

# Install. Verifies signature + SRI before writing anything.
ggui gadget install @my-org/weather-card@0.1.0      # → ggui.json#app.gadgets
ggui 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.

## Installing gadgets from the ggui console

The CLI is the config-managed path (`ggui.json#app.gadgets[]`, versioned alongside your deployment). On hosted ggui there's also a per-app web path: an app's **Marketplace** tab in the ggui console has a Blueprints / Gadgets toggle over the same registry search — pick Gadgets, find a package, and Install. It writes the latest version onto that app's row in a separate provenance-split table, distinct from the CLI-managed `ggui.json` column. The app's **Gadgets** tab shows the resolved catalog an agent actually gets from `ggui_list_gadgets`, and lets you remove a web-installed gadget or pin it to a different version (re-installing at a picked version is the pin operation).

Resolution when both paths touch the same package follows a fixed precedence, stdlib floor < console-installed < ggui.json-declared:

- A package declared in `ggui.json` always wins, and the web install path never writes that column — attempting to install a package that's already declared there is rejected with `package_declared`, pointing you back to the config-managed path instead.
- A console-installed package overrides the stdlib floor but is itself overridden the moment the same package is declared in `ggui.json`.
- Nothing installed anywhere falls through to the stdlib floor.

Use the console path for exploration and per-app changes without a deploy; use `ggui gadget install` when the gadget set needs to be versioned with the rest of your app's config.

## Auth

`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`](/cli/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`](/sdk/self-hosted-registry/) deployments pass `--auth=bearer`.

## 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) |

The pairing is enforced **server-side at publish**, not just by the CLI: `POST /publish` rejects a mismatched pair with `400 visibility_algorithm_mismatch`. `public` requires `sigstore-cosign` so every publicly listable artifact has a transparency-log entry; `private` requires an Ed25519 author key. A hand-rolled request can't opt a public artifact out of the transparency log by signing it with a private key.

**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`](/cli/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:

1. **bundleSha384 recompute.** Compute `sha384(payload)`, compare against `signature.bundleSha384`. Fast tamper detection. Runs for both algorithms.
2. **Cryptographic verify.** Dispatches on `signature.algorithm`:
   - `ed25519` → `derivePublicKeyId(returnedPubkey) === signature.publicKeyId` (rejects key-swap attacks), then `verifyBundleEd25519`. If the registry didn't pin a public key for the version, the CLI warns and continues by default; `--strict` turns the warn into a hard fail.
   - `sigstore-cosign` → `verifyBundleSigstore` validates 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`)

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

- **Semver.** `1.2.3-alpha.1+build.42` form. `parseArtifactManifest` rejects ranges, `latest`, leading zeros (applies to gadgets and blueprints alike).
- **Immutable.** `POST /publish` of an existing `(scope, name, version)` triple returns `409 version_exists`.
- **Yank, not delete.** Yanked versions return `410 Gone` with the manifest still in the body for audit. Hard delete is intentionally NOT supported.

## 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=&tool=&server=&…` | public      | List artifacts. AND-semantics across filters. `tool` / `server` filter on an artifact's MCP tool bindings (find everything that works with a given tool or server). |
| `GET`  | `/pkg/{scope}/{name}/{version}`                       | mixed[^1]   | 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.                                                                                                                    |

[^1]: Public for `visibility: "public"` artifacts. A `visibility: "private"` version is served only to the verified account that published it or that owns the artifact's scope; every other request — anonymous or authenticated — receives the same `not_found` response as a genuinely missing artifact (the version list likewise filters versions you cannot read), so the API never confirms a private artifact exists. "CLI session" = the `ggui login` session token, verified at the hosted deployment's gateway (see [Auth](#auth)).

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

**Gadgets** land on `app.gadgets[]`:

```json
{
  "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

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](/api/mcp-protocol/#ggui_list_featured_blueprints). Gadgets are advertised via [`ggui_list_gadgets`](/api/mcp-protocol/#ggui_list_gadgets) instead — the catalog rides on the contract, not as a separate "render" call.

## Roadmap

Tracked-but-not-yet-shipped surfaces, in rough priority order:

- **Per-org private artifacts.** `visibility: "private"` reads are enforced per-subject today — the publisher or the scope owner reads a private version; everyone else gets an opaque `not_found`. Org-membership admission (a teammate reading an org-mate's private versions) is the next step.
- **Yank UX.** No `ggui gadget yank` subcommand yet — yanking goes through registry admin tooling. The read + install sides already handle `410 Gone` correctly (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

- **Browse.** [hub.ggui.ai](https://hub.ggui.ai) — the public registry browser (search, per-version manifests, install commands).
- **Protocol spec.** [Protocol overview](/protocol/overview/)
- **Self-hosting.** [Self-hosted registry](/sdk/self-hosted-registry/) — byte-compatible `@ggui-ai/registry-server` for air-gapped or local-dev use.
- **Glossary.** [gadget / tool / blueprint](/glossary/) — the three-noun vocabulary used throughout.
- **Author manifest.** [`@ggui-ai/artifact-manifest`](/sdk/gadgets/) — `gadgetManifestSchema` / `blueprintManifestSchema`.
- **Signing.** [`@ggui-ai/gadget-signing`](/sdk/gadgets/) — universal Ed25519 (browser + Node).
- **Publisher key registration.** [`ggui keys register`](/cli/keys-register/).