---
title: "Security & isolation"
description: "How Guuey isolates hosted agents and MCP servers, guards credentials, and stores and deletes user data — the mechanisms, not the marketing."
---

Guuey runs other people's agents on shared infrastructure, so isolation is not a feature bolted on afterwards — it is the shape of the runtime. This page describes the mechanisms as they exist today: how workloads are sandboxed, what the network lets them reach, where credentials live, and what happens to stored data when someone asks for it to be gone. No compliance badges, no adjectives — just what the platform does.

This page is the one-screen inventory. The why underneath each section has its own page: [How hosting works](/concepts-hosting/) for the runtime, [The identity model](/concepts-identity/) for tokens and verification, [BYOK & credential custody](/concepts-byok/) for key sealing, and [The MCP proxy](/concepts-mcp-proxy/) for brokered tool calls.

## Runtime isolation

Every deployed app gets its own long-running pod in its own Kubernetes namespace. Workloads that run **your code** — a code-mode agent's image, an MCP server you host on Guuey — are additionally sandboxed with [gVisor](https://gvisor.dev): system calls from your process hit a user-space kernel, not the host's, and each hosted MCP server gets its own gVisor-sandboxed pod, separate from every agent and every other server. A no-code agent's pod runs Guuey's own runtime image and is not a gVisor workload today; its isolation is the namespace, the default-deny network below, and the jail around the worker described next.

Resource limits are structural, not advisory. Each pod size (`xs`–`xl`) maps to a fixed CPU and memory allocation, and how many pods an app may run is capped by its plan — see [Pod sizes and scaling](/hosting/#pod-sizes-and-scaling). A runaway workload exhausts its own pod, not its neighbours'.

Inside every agent pod there is a second wall: the worker that runs your turns — Guuey's own runtime worker for a no-code agent, your program for a code-mode one — runs in a jail with a cleared, allowlisted environment. The allowlist carries paths and configuration — never a real key — and the jailed process holds no Kubernetes service-account token, so even code that escapes your own logic has nothing to authenticate with.

## Network posture

Agent namespaces are **default-deny in both directions**, with explicit allows for exactly what an agent needs:

- **DNS** to the cluster resolver.
- **Inbound** traffic only from Guuey's ingress stack — nothing else in the cluster can connect to your pod.
- **Outbound** TCP to the public internet, with private address ranges and link-local addresses excluded.

The exclusions are the point. A compromised agent pod cannot reach other tenants' pods, in-cluster services, the cloud provider's instance-metadata endpoint (so no node-credential theft), or the Kubernetes control plane — with one deliberate, narrowly scoped exception: a pod may annotate _its own_ pod object so the autoscaler doesn't disrupt a conversation mid-turn, and RBAC bounds that door to exactly that one operation in the pod's own namespace. What's left is an agent that can talk to the internet under its own identity and to nothing of yours or anyone else's.

The hosted-MCP data plane uses the same model: its namespaces are default-deny with explicit allows for the specific flows the design calls for, and hosted MCP servers accept traffic only from Guuey's gateway.

## Identity & verification

End-user identity is whatever your app declares, verified the same way on every surface:

- **Bring your own auth**: you point the app at your OIDC issuer, and Guuey verifies your users' ID tokens against your provider's published keys. Guuey never holds your signing key.
- **Guuey's per-app issuer**: tokens are bound to your app's own issuer and audience, and both are checked on every verification — a token minted for one app is structurally useless at another. The app secret your backend uses to mint tokens is printed once at creation; only its hash reaches storage, and there is deliberately no route to read it back. The key that actually signs the tokens is a per-app keypair sealed under Guuey's KMS — never exposed by any API, unsealed only inside Guuey's signer for the moment of a mint.
- **Anonymous guests** are supported where the app allows them: guest turns are capped per app, and guest storage is deliberately ephemeral.

Internally, when an agent pod calls a platform service — the MCP gateway, thread history, hosted state — it authenticates with a short-lived, KMS-signed federation token scoped to one user and one resource. A stolen token is minutes from useless and was never good for anything beyond the one thing it named.

Gateway logging is redacted by design: request lines carry the method name, never the payload, and OAuth callback parameters are dropped from logs before they ship anywhere.

## Credential custody

**Bring-your-own model keys** (added in the console under **Behavior → Model** or the workspace **Provider keys** panel) are encrypted with AWS KMS before they are stored; the ciphertext is bound to an encryption context naming its owner, so a blob can't be replayed for another account, app, or workspace. A key is decrypted only to be placed where your own app uses it — never into another tenant's reach.

**Managed model keys** never enter your agent's code at all. The model call leaves the jail through a loopback proxy; the credential lives with the supervisor on the other side of that proxy, and the jailed environment's allowlist structurally excludes it.

**Hosted MCP server secrets** (`guuey mcp secrets set`) are stored as KMS ciphertext. The plaintext never persists, is never baked into a server image, and is never returned by any API — the list endpoint returns names only. The gateway decrypts and injects a secret per invocation, for that server's own upstream calls.

**OAuth connections** to third-party MCP servers go through Guuey's credential broker. The authorization dance happens at the provider — Guuey receives tokens, never your users' passwords. Tokens are KMS-sealed at rest, and the plaintext exists only inside the gateway process for the duration of a forwarded call:

- Your agent's pod never holds a third-party token. It carries only a short-lived, KMS-signed Guuey federation token addressed to the gateway; the gateway checks the per-app grant and injects the real credential upstream.
- A user authorizes a server once; each app's access to that connection is granted per app, explicitly, via a consent card in chat.
- Disconnecting deletes the stored credential (the KMS ciphertext is gone), drains every app grant on the connection, and makes a best-effort revocation call to the provider.

## Data storage & deletion

What the platform persists, and where:

| Data                 | Where                                       | Scope                                           |
| -------------------- | ------------------------------------------- | ----------------------------------------------- |
| Conversation history | DynamoDB                                    | Per app, per user                               |
| Key-value state      | Hosted KV                                   | Per (user, MCP server), hard caps, TTL required |
| Home directory files | Per-user filesystem (durable where enabled) | Per (app, user), quota-capped                   |

Every primitive is scoped per user and per app, and reads are confined to that scope: a token minted for one app is refused at another, and an agent can never read another user's threads or another server's keys. Durable KV and the durable home are enabled per environment — [State & memory](/state-and-memory/) states the current posture and the caps.

Deletion is built as a first-class path, not a support ticket:

- **End users** can see and delete what an app has stored about them; a user clearing their data is a fresh start, not an error.
- **Builders** get per-user portability and erasure verbs — `guuey mcp state export` and `guuey mcp state wipe` operate on one user's entries for one server ([State & memory](/state-and-memory/#operating-on-user-data)).
- **Account deletion** is queued with a cancellation window, then a cascade deletes every user-keyed record across the platform's tables, with an independent hourly sweeper as the fallback path. File erasure runs as an asynchronous wipe job per user directory, and a wipe that stalls raises an internal alarm rather than silently lingering.

## What Guuey keeps out of reach

A short list worth stating plainly:

- Your users' third-party **passwords** — OAuth happens at the provider; only tokens ever reach Guuey.
- Third-party **OAuth tokens** — never in your agent's pod, plaintext only inside the gateway during a forward.
- **Hosted-MCP secret values** — write-only from your side; no API returns them.
- **Managed model keys** — structurally absent from the environment your agent's code runs in.
- **Other tenants** — not reachable from your pod's network, filesystem, or identity, and yours not from theirs.

Isolation claims on this page describe the running platform. If you're evaluating Guuey for a workload with specific security requirements, the runtime details above are the right starting point — and the same sandbox applies to the hardest case, [compute-class MCP servers](/hosting/#isolated-long-running-sandboxes), because that is what the platform was built to host.