---
title: Architecture
description: How ggui is wired — three channels, a symmetric capability model, a generation pipeline, and a four-tier artifact registry. Protocol-level; implementation-agnostic.
---

This page is the **protocol's architecture** — what every ggui implementation must do, independent of how it's deployed. For deployment shapes, see [Self-Hosted](/self-hosted/pairing/) and [Reference deploys](/self-hosted/reference-deploys/).

## Three actors

```
Agent (LLM-driven)
   │
   │ MCP
   ▼
Server  ◀───── WebSocket (live) ──────▶  Renderer (iframe / standalone page)
   │                                            │
   │           bootstrap (bundle fetch)         │
   └────────────────────────────────────────────┘
```

- **Agent** — your code with an LLM in the loop. Speaks to the server over MCP.
- **Server** — speaks MCP outward, orchestrates generation, routes events. Hosts the artifact registry the renderer pulls from. Runs at your URL via `ggui serve` (hosted `mcp.ggui.ai` coming soon).
- **Renderer** — an iframe (or standalone page) hosting the generated component. Sends user actions back to the server through the host's `tools/call` relay.

## Three channels

ggui's wire is split across three orthogonal channels. Each has one job.

| Channel       | Direction         | Purpose                                                                                                                                                                                                           |
| ------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Bootstrap** | Server → Renderer | One-shot fetch of the compiled component bundle when the iframe first loads. Any gadgets bound to the session load behind a `<link rel="modulepreload" integrity>` SRI gate.                                      |
| **MCP**       | Agent ↔ Server    | Control plane. `ggui_handshake`, `ggui_render`, `ggui_update`, `ggui_consume`, `ggui_emit`, `ggui_get_session`.                                                                                                   |
| **Live**      | Renderer ↔ Server | WebSocket at `ws://127.0.0.1:6781/ws` (self-hosted default; hosted `wss://mcp.ggui.ai/ws` coming soon). Server deliveries outbound (`StreamEnvelope`, props updates, drain acks); contract violations on an inbound action are answered with a typed `error` frame (`CONTRACT_VIOLATION`, code -32020) on this channel — nothing lands on the consume buffer. |

The rendered view's user actions reach the server through the host's MCP-Apps `tools/call` relay to `ggui_runtime_submit_action` — the spec-canonical dispatch path; the server appends the gesture to the pipe `ggui_consume` drains. The live WebSocket's job is server → renderer delivery (stream emits, props updates, drain acks).

The channels are independent. The renderer can drop and reconnect the live channel without disturbing an agent's MCP turn; the agent can `ggui_render` repeatedly without ever touching the live channel.

:::note[Multiple MCP services on one server]
Beyond the audience-filtered `/mcp`, `/protocol`, `/ops` routes, additional standalone services can be mounted at their own paths (e.g. `https://your-server/docs`, `https://your-server/playground/todos`). See [MCP services](/architecture/mcp-services/) and [Audience routes](/architecture/audience-routes/).
:::

→ See [Protocol overview](/protocol/overview/) for the formal three-channel spec.

## Capability model

ggui has two **symmetric** capability surfaces — one for what the agent can do, one for what the renderer can render. Both are operator-bounded and declared per-app.

|                 | Renderer side                                                                           | Agent side                                                                    |
| --------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Unit**        | **gadget**                                                                              | **tool**                                                                      |
| **Catalog**     | `clientCapabilities.gadgets`                                                            | `agentCapabilities.tools`                                                     |
| **Role**        | Wraps a 3rd-party browser library (Leaflet, Mapbox, Chart.js) into an LLM-callable hook | Gives the agent a function to invoke (e.g. `searchContacts`, `createInvoice`) |
| **Authored as** | `ggui.gadget.json` manifest                                                             | MCP tool code                                                                 |
| **Bound at**    | iframe boot (SRI-verified)                                                              | session start                                                                 |

Plus a third primitive — **blueprints** — which aren't capabilities but **cached recipes** (pre-composed UIs). A blueprint hit short-circuits fresh generation.

→ See [Gadgets SDK](/sdk/gadgets/), [MCP Protocol](/api/mcp-protocol/), [Marketplace](/sdk/marketplace/).

## Generation pipeline

Rendering is a two-call flow — `ggui_handshake` negotiates, `ggui_render` commits:

```
0. NEGOTIATE  ggui_handshake({intent, blueprintDraft}) searches the blueprint cache by
              contract shape (exact contractHash hit short-circuits; semantic similarity
              otherwise) and returns a suggestion
1. COMMIT     ggui_render({handshakeId, props}) consumes the handshake; a cache hit
              reuses the stored blueprint (~100ms)
2. GENERATE   Otherwise, run the server's UI generator (@ggui-ai/ui-gen):
              workflow → impl → check → derive
3. COMPILE    TSX → JS via esbuild (~20-50ms)
4. DELIVER    Renderer fetches the compiled bundle on the bootstrap channel
```

Published artifacts (gadgets, blueprints) on the marketplace registry pick up an author signature at _publish_ time — Ed25519 (publisher keypair) for private artifacts, sigstore keyless (OIDC) for public ones — see [Marketplace](/sdk/marketplace/). Fresh generations are session-scoped and skip that step.

The generator (step 2) is a bounded harness: pick a workflow (`single_pass`, `staged`, `staged-concurrent`), run the LLM-driven impl phase, run a check leg (typecheck, render-smoke, per-axis assertions), and on failure derive a revised harness and retry up to `maxIterations`. Output: a TypeScript-typed contract plus a compiled component module.

→ See [UI Generator](/architecture/ui-generator/) for the harness internals.

## Artifact registry

Gadgets and blueprints resolve through a four-tier waterfall on every push:

```
1. App-local        ggui.json#app.gadgets, ggui.json#blueprints.include
                    (plus installed artifacts under .ggui/installed-blueprints/)
2. Per-org private  operator's private registry (artifacts with visibility:"private")
3. Public           registry.ggui.ai (marketplace)
4. Fall back        fresh generation
```

A blueprint is keyed by a stable `blueprintId`; its `contractHash` — the RFC 8785 canonical-JSON hash of its `DataContract`, scoped per `(appId, contractHash)` — groups variants and is the cache lookup key. An exact contractHash hit short-circuits to score 1.0; otherwise a multi-axis semantic search (contract embedding, structural fingerprint, variance tags, intent) ranks candidates. Install the same `(scope, name, version)` on two different servers and the matcher returns the byte-identical UI on both.

→ See [Marketplace](/sdk/marketplace/), [Self-Hosted Registry](/sdk/self-hosted-registry/).

## Deployment shapes

The same protocol runs in two shapes:

|                  | Self-hosted                                                         | Hosted (coming soon)              |
| ---------------- | ------------------------------------------------------------------- | --------------------------------- |
| **MCP endpoint** | your URL via `ggui serve`                                           | `mcp.ggui.ai`                     |
| **WS endpoint**  | `ws://127.0.0.1:6781/ws` (or your URL)                              | `wss://mcp.ggui.ai/ws`            |
| **Auth**         | pluggable `AuthAdapter` + optional OAuth 2.1 (`ggui serve --oauth`) | OAuth                             |
| **Registry**     | your registry (or none)                                             | `registry.ggui.ai`                |
| **Generation**   | in-process                                                          | managed                           |
| **Pick if**      | "I need data residency, custom auth, or air-gapped"                 | "I just want to ship an agent UI" |

Both speak the same wire. Switching between them is configuration, not code.

→ See [OSS Quick Start](/oss-quickstart/) to run it yourself. A managed hosted path at `mcp.ggui.ai` is coming soon.

## Where to next

- [Protocol overview](/protocol/overview/) — formal spec
- [How ggui works](/how-it-works/) — narrative walk-through of the four moments
- [Event System](/architecture/event-system/) — live-channel event flow in detail
- [UI Generator](/architecture/ui-generator/) — the generator harness