> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r5d.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# Security model

> Trust boundaries, isolation guarantees, and an honest account of their limits.

r5d.chat runs AI-generated code on your infrastructure. This page describes what
the security model guarantees, and equally importantly what it does not.

## Trust boundaries

<AccordionGroup>
  <Accordion title="Browser to control plane" icon="browser">
    Sessions are cookie-based and signed. Every API handler re-checks that the
    authenticated user owns the records it touches. The browser never receives a
    workspace token, and all workspace traffic is proxied through the control
    plane.
  </Accordion>

  <Accordion title="Control plane to workspace" icon="right-left">
    Each workspace has a random 32-byte bearer identity stored in a Kubernetes
    Secret and mounted into the pod. It maps to exactly one user, is never shown
    to users or models, and authenticates both directions of the private protocol.
  </Accordion>

  <Accordion title="Workspace to cluster" icon="ban">
    Tenant pods receive no service-account token, host path, runtime socket, or
    host namespace. `automountServiceAccountToken` is false and service links are
    disabled, so a workspace has no ambient Kubernetes credential to use.
  </Accordion>

  <Accordion title="Secrets at rest" icon="lock">
    Provider keys, custom headers, MCP headers, and OIDC client secrets are
    AES-256-GCM encrypted with `SETTINGS_ENCRYPTION_KEY` and stay in the control
    plane.
  </Accordion>

  <Accordion title="Outbound requests" icon="globe">
    Provider, MCP, and OIDC destinations pass a guard that rejects non-HTTPS
    URLs, credential-bearing URLs, private/loopback/link-local/metadata
    addresses, and unsafe redirects, unless explicitly relaxed.
  </Accordion>
</AccordionGroup>

## Workspace pod hardening

Workspace containers run as root **on purpose**, so agents can install packages
for their task. Root is constrained:

| Control                | Setting                                |
| ---------------------- | -------------------------------------- |
| Privileged mode        | Disabled                               |
| Privilege escalation   | `allowPrivilegeEscalation: false`      |
| Linux capabilities     | All dropped                            |
| Seccomp                | `RuntimeDefault`                       |
| Service-account token  | Not mounted                            |
| Host paths and sockets | None                                   |
| RuntimeClass           | Optional Kata or gVisor                |
| Network ingress        | Control plane only, port 7337          |
| Network egress         | Public internet only, RFC1918 excluded |

<Warning>
  Root in an unprivileged container is **not** a complete sandbox against
  hostile code. A container escape through a kernel vulnerability remains
  possible. For mutually untrusted tenants, use a VM boundary such as Kata or
  gVisor, Pod Security admission, resource quotas, image policy, monitoring, and
  dedicated node pools.
</Warning>

Enable a VM boundary with:

```yaml theme={null}
workspace:
  runtimeClassName: kata
```

## File access boundaries

There are two deliberately different boundaries:

* **UI file APIs** are confined to canonical paths under `/home/r5d`. Path
  traversal and symlink escapes are rejected, so the file browser cannot be used
  to read arbitrary container paths.
* **Agent tools** intentionally operate anywhere inside the container. The
  agent's boundary is the pod itself, because installing and configuring
  software requires writing outside the home directory.

## Threats to plan for

<AccordionGroup>
  <Accordion title="Prompt injection" icon="triangle-exclamation">
    Content the agent reads, whether a web page, a document, or an MCP tool
    description, can contain instructions aimed at the model. Combining broad
    web access with high-privilege MCP tools increases the blast radius. Give
    MCP servers narrowly scoped credentials.
  </Accordion>

  <Accordion title="Malicious MCP servers" icon="puzzle-piece">
    Tool definitions are model instructions. r5d.chat fingerprints them on
    approval and surfaces later drift, but the initial trust decision is yours.
    See [MCP servers](/configure/mcp).
  </Accordion>

  <Accordion title="Resource exhaustion" icon="gauge-high">
    Agents can run expensive commands. Workspace CPU and memory limits bound a
    single pod; namespace quotas bound the aggregate. Set both.
  </Accordion>

  <Accordion title="Data exfiltration through egress" icon="right-from-bracket">
    A workspace with internet egress can send file contents anywhere. Set
    `networkPolicy.workspaceInternetEgress: false` for sensitive tenants,
    accepting that agents can then no longer install packages.
  </Accordion>

  <Accordion title="Unclaimed installation takeover" icon="door-open">
    Before setup is claimed, anyone who reaches the URL can become the
    administrator. Always set `SETUP_CLAIM_TOKEN` before exposing an unclaimed
    installation.
  </Accordion>

  <Accordion title="Custom header exposure" icon="eye">
    Provider API keys are write-only, but custom headers are returned to the
    authenticated owner so they remain editable. Treat header values as
    user-visible secrets.
  </Accordion>
</AccordionGroup>

## Hardening checklist

<Steps>
  <Step title="Set a claim token before exposure">
    `SETUP_CLAIM_TOKEN` in the Secret, claimed immediately after go-live.
  </Step>

  <Step title="Keep the outbound guard strict">
    Leave `ALLOW_PRIVATE_EGRESS` and `ALLOW_INSECURE_HTTP` at `false` unless a
    specific endpoint requires otherwise.
  </Step>

  <Step title="Use a VM runtime for untrusted tenants">
    `workspace.runtimeClassName: kata` or `gvisor`.
  </Step>

  <Step title="Keep NetworkPolicy enabled">
    `networkPolicy.enabled: true`, and decide deliberately about
    `workspaceInternetEgress`.
  </Step>

  <Step title="Apply namespace quotas">
    Bound total pods, CPU, memory, and storage so one tenant cannot starve the
    rest.
  </Step>

  <Step title="Use TLS end to end">
    Terminate HTTPS at the ingress and set `publicUrl` to the `https://` URL.
  </Step>

  <Step title="Protect the encryption key">
    Store `SETTINGS_ENCRYPTION_KEY` in a secrets manager and back it up with the
    database.
  </Step>

  <Step title="Keep a local admin account">
    Even with SSO enabled, as an identity-provider outage recovery path.
  </Step>

  <Step title="Update images regularly">
    The workspace image carries a large package surface; rebuild it to pick up
    security updates.
  </Step>
</Steps>

## Multi-tenancy guidance

<Tabs>
  <Tab title="Trusted team">
    Colleagues under one policy. Default settings are appropriate: standard
    container isolation, NetworkPolicy on, internet egress enabled.
  </Tab>

  <Tab title="Semi-trusted">
    Users from different teams or customers. Add Kata or gVisor, namespace quotas,
    and monitoring of workspace resource usage.
  </Tab>

  <Tab title="Untrusted">
    Anonymous or adversarial users. Requires a VM boundary, dedicated node
    pools, strict quotas, egress restrictions, image policy, audit logging, and
    an incident response plan. Evaluate carefully before offering this.
  </Tab>
</Tabs>

## Reporting a vulnerability

Report security issues privately through the repository's security advisory
process rather than a public issue.
