---
title: Trust & security
description: What hosted ggui stores, which of it reaches a language model, how keys and the render iframe are protected, and how to delete an account.
---

Hosted ggui sits between your agent and your user, so it necessarily handles both sides' data. This page says what it holds, for how long, and which parts a language model sees. Where the honest answer is a limit rather than a guarantee — the host controls the sandbox, a signature is not an audit, one contract field can carry data into the prompt — it says that instead of reassuring you.

## Your data in hosted ggui

A render is one generated interface. Everything below is scoped to a render, an app, or your account.

| Data                                                                                          | Stored where                                                                                                                    | Reaches a language model?                                                                |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| The intent and contract your agent sends                                                      | The render row in DynamoDB, and — if the render becomes a reusable blueprint — a blueprint row                                  | **Yes.** This is the generation prompt                                                   |
| The generated component code                                                                  | Inline on the render row; durable blueprints go to S3 keyed by content hash; a copy of the TSX lands in a per-user render cache | It _is_ the model's output                                                               |
| The props your agent renders with                                                             | On the render row, next to the code                                                                                             | **No.** Props values are not part of the generation prompt                               |
| What your user does in the UI — action payloads and the context snapshot taken at that moment | Buffered on the render row until the agent consumes them, and appended to a per-render event log                                | **Not by ggui.** They are handed back to your agent, which is itself a model — see below |
| The end-user identity your auth adapter supplies                                              | An `endUserIdentity` column on the render row                                                                                   | No                                                                                       |
| Connector keys (`ggui_user_*`)                                                                | A SHA-256 hash plus an 8-character prefix. Plaintext is returned once at mint time and never written                            | No                                                                                       |
| Your own provider keys (BYOK)                                                                 | KMS ciphertext, decrypted into memory for the duration of one call                                                              | No — the key authenticates the provider call, it is never prompt content                 |

### What generation actually sees

The generation prompt is built from your agent's intent, the contract's **schemas** (field names, JSON-Schema types, and any `default` / `example` values you declare on them — so treat contract examples as prompt content), the variance signals, the rendering context the host reports (device, shell, viewport), the gadget catalogue, and ggui's own design-system documentation. Runtime props are carried alongside the generation input and deliberately not spread into it.

Blueprint matching has two tiers. An exact contract-key match is deterministic and calls no model at all. When that misses, a reranking model is asked to choose among cached candidates — and it is shown the intent plus **summaries** of the contracts (`name:type` pairs), never props or event payloads.

`ggui_amend` performs no generation. It swaps props and pushes them down the live channel; no model is involved. `ggui_update` is the same swap plus a new history card (it also mints no generation, but does advance the epoch).

:::caution[Two contract fields carry values into the prompt]
`variance` is meant for design-shaping signals only — persona, aesthetic, mood — and `variance.context` is serialized verbatim into the generation prompt. Separately, the `default` and `example` values you declare on a spec entry are rendered into the prompt as comments, so the model sees the exact sample data you wrote. Neither is a bug: examples exist to show the model the shape it must handle. But the rule that per-user runtime data belongs in `props` and `contextSpec` is documented guidance to your agent, not something the server structurally enforces. Keep real user data out of `variance`, `default`, and `example` — put it in `props`, which never enters the prompt.
:::

### The event loop is a model, by design

When your user submits a form, that payload travels back to your agent through `ggui_consume`. Your agent is a language model. That is the entire point of the protocol — the user's answer becomes structured input to the next turn — but it means "does my user's form input reach an LLM?" has the honest answer **yes, yours**. ggui's own generation and matching models never see it.

### What changes with your own key

If you store a provider key and the app is configured to use it, the generation call is made with your key, so the prompt goes to your provider account under your agreement with that provider rather than through ggui's pool. Storing a key alone does not do this — the app's generation billing has to select it, via the **Generation billing** card on the app's Keys or Settings tab in the console. See [Credits, billing & BYOK](/hosted/billing/#bring-your-own-key-byok) for that gate and what it does to billing.

## Keys

**Connector keys.** A key is 32 bytes of randomness prefixed `ggui_user_`. What gets stored is a SHA-256 hash of the whole string plus the first 8 characters, which is what the console lists. The plaintext is handed to you exactly once and is not recoverable afterwards — if you lose it, mint a new one. Revocation is a soft flag: the row survives for audit and later requests with that key are rejected. Full mechanics in [Connector keys](/hosted/connector-keys/).

**Provider keys.** Keys you bring are validated, then encrypted with AWS KMS under an encryption context that binds them to their scope — `{userId, provider}` for an account-wide key, `{appId, provider}` for a per-app one. The plaintext is never persisted — only the KMS ciphertext, a hash, and the last four characters; decryption happens in memory for the call. Because the encryption context is cryptographically bound to the ciphertext, a key encrypted for one scope fails to decrypt in another rather than silently working. The console only ever displays the last four characters.

**Publisher keys.** Signing keys for the marketplace are Ed25519 and stay on your machine; the registry holds the public half. See [`ggui keys register`](/cli/keys-register/).

## Sandboxing

Generated code is code a model wrote. It runs in an iframe, and it never runs with access to the host document.

When ggui mounts the iframe itself, the sandbox grants `allow-scripts allow-forms` and deliberately withholds `allow-same-origin`, so the frame cannot read the page that embeds it. Camera, microphone, geolocation, and clipboard-write are only enabled when the render's contract asked for them. Component bundles are content-addressed and served with their SHA-256 alongside; gadgets load behind an SRI integrity gate at iframe boot.

Three honest limits:

- **In a third-party host, the host owns the sandbox.** When your interface renders inside Claude Desktop or claude.ai, that host sets the iframe's sandbox tokens and Content-Security-Policy. ggui declares which origins the frame needs to reach; enforcement is the host's, and that surface is opaque to ggui.
- **Marketplace gadgets are signed, not audited.** Signature verification proves a bundle came from the author who published it and has not been altered since. It says nothing about what the bundle does. Public gadgets verify through sigstore, private ones through a pinned Ed25519 key — the [marketplace](/hosted/marketplace/) page has both postures.
- **The integrity gate is only as strong as the descriptor.** A gadget installed from the registry carries a SHA-384 SRI hash that the runtime enforces. A hand-authored gadget entry with no `bundleSri` falls back to an integrity-less dynamic import, and installing a legacy artifact whose version has no pinned public key warns and continues unless you pass `--strict`. See [Gadgets](/sdk/gadgets/).

The channel model and where the SRI gate sits are in the [architecture overview](/architecture/overview/).

## Operator access

A small operator group exists for support and monitoring, and it can read render records — the contract, the generated code, and event payloads — across accounts, through the same access-controlled admin surface that runs the service. Three facts bound it:

- **Membership is manual.** The group has no self-join path; an operator is added by an infrastructure command, not a sign-up flow.
- **It cannot read provider keys.** The BYOK key table deliberately carries no operator grant — an operator never sees your provider keys, even in encrypted form.
- **Every read is individually audit-logged.** Operator reads of render data go through a single audited query — the underlying models carry no direct operator grant, so a read that skips the log is structurally impossible. Each entry records the operator's identity and the target keys, and lands in a dedicated log group retained for two years.

## Deletion & retention

**Renders and their event logs.** By default a render is kept indefinitely — reopening an old card from chat history always finds it, which is the point of the protocol's rehydration story. An agent that wants a bounded lifetime sets a per-render TTL; bounded renders are purged by DynamoDB at that TTL plus a seven-day grace window, and activity on a render slides the expiry forward. Deletion of unbounded renders happens through the paths below, not a schedule.

**Blueprints.** A blueprint is the durable artefact — a render promoted to something reusable. Blueprints carry no TTL. Their bodies are content-addressed and reclaimed only by an operator-invoked purge (takedown, app retirement, or collection of bodies that have been unreferenced long enough to be safely orphaned), never on a schedule.

**Your account.** `/account` in the console has a type-to-confirm **Delete account** control. It is blockers-first: the request is refused, with the reason stated and nothing deleted, if you own a platform-managed app or if you are the last owner of an org that still has other members. If it is accepted, the account is frozen immediately — global sign-out plus disable — and an asynchronous worker then walks the deletion in order: org memberships, then each app with its keys, blueprints and renders, then your connector keys, provider keys, render cache, credit balance, renders, coupon attribution, and status subscription, then your user row. Cognito sign-in is deleted last, and only after a verification pass re-runs every finder query and finds nothing left. If anything remains, the erasure is marked failed and sign-in is deliberately preserved so the leftover can be traced.

Two things survive on purpose, both stated in the console's own confirmation: billing records required for accounting are kept with your personal details scrubbed rather than deleted, and CLI session tokens are left to their own expiry (they are hashed and time-bounded) instead of being swept.

One gap worth naming: if you were an **end user** of somebody else's hosted app, your identity data on their render rows is not reachable by your own account erasure. It persists as long as those renders do — until the app's per-render TTL fires, the app owner deletes their account, or an operator takedown removes the rows.

## Policies and status

- [Privacy policy](https://ggui.ai/privacy) — the binding statement, including subprocessors and analytics.
- [Terms of service](https://ggui.ai/terms)
- [status.ggui.ai](https://status.ggui.ai) — live service status.

For anything this page does not answer, the privacy policy lists a contact address.