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

# How Cloud secures your data

> How Cloud protects your database credentials and agent tokens, and isolates customers and regions.

Cloud holds two kinds of secret for you: your database credentials and your agent tokens. This page is how each is protected, and how customers and regions are isolated from one another. The [policy engine](/docs/how-it-works) is the runtime guard; this is the storage and isolation model underneath it.

You sign in to the dashboard with email and password, or Google.

## Managed credential encryption

When you paste a Postgres URL, Cloud encrypts it before it touches the database. In production, credentials are encrypted with **AES-256-GCM using a per-region AWS KMS key** — one CMK per region, not one per tenant — via envelope encryption: KMS generates a data key, the DSN is sealed under it.

The ciphertext is bound to your account and region with an **encryption context of `{customerId, region}`**, enforced on both encryption and decryption. A decrypt request for the wrong customer or the wrong region fails closed — the credential can't be decrypted out of context, even with access to the ciphertext.

The plaintext DSN is **never persisted**. It's encrypted on the way in and only ever decrypted in-region to spawn the engine for a request.

## Tokens stored as HMAC digests

[Machine tokens](/docs/cloud/projects#machine-tokens) are never stored in plaintext. Each token is hashed with **HMAC-SHA256 under a per-region secret** and only the digest is kept. When an agent presents a token, Cloud hashes the presented value and compares digests in constant time.

This is why a token's full value can be shown only once, at mint time: there's no stored plaintext to show you later — only the digest and a short prefix/suffix for recognition.

## Per-agent least-privilege scope

Each agent credential reaches only the databases you grant it, at the access level you set — **read** or **read/write**. An agent never sees a database it wasn't granted, and a credential carrying no grant **fails closed**: an agent that connects over OAuth with no database access is rejected, not silently given the whole project. This is least privilege at the credential, on top of the per-database [policy](/docs/policies/overview) the engine enforces on every query.

## Roles

Sensitive dashboard actions are restricted to owners and admins; members connect agents and query. See [roles](/docs/cloud/projects#team-roles).

## Row-level security for audit

The hosted [audit log](/docs/concepts/audit-trail) is shared infrastructure, isolated per customer with **Postgres row-level security**. Every audit query is scoped to the requesting account's rows by the database itself — isolation is enforced at the row level, not only in application code, and the policy is forced so even a privileged database role can't read across customers.

## Region locality of secrets

Your [region](/docs/cloud/overview#regions-and-data-residency) choice is a hard boundary for your secrets:

* Credentials are encrypted under that region's KMS key.
* Tokens are hashed under that region's HMAC secret.
* Audit rows live in that region's database.

The keys are physically separated per region, and the KMS encryption context pins each decrypt to its region. **A compromise of one region cannot decrypt or forge the other region's secrets** — a US-region incident leaves EU credentials and tokens cryptographically out of reach, and vice versa. This is why the region choice is immutable: it's a real isolation boundary, not a label.

## Browser hardening headers

The dashboard sends a set of security response headers on every route — landing, sign-in, and the authenticated app alike:

* **`Content-Security-Policy: frame-ancestors 'none'`** and **`X-Frame-Options: DENY`** — the origin can't be embedded in a cross-site iframe, so the dashboard can't be clickjacked.
* **`X-Content-Type-Options: nosniff`** — no MIME sniffing.
* **`Referrer-Policy: strict-origin-when-cross-origin`** — referrers don't leak full URLs cross-origin.
* **`Permissions-Policy: camera=(), microphone=(), geolocation=()`** — the page asks for no device APIs.
* **`Strict-Transport-Security`** with a two-year `max-age`, `includeSubDomains`, and `preload` — browsers stay on HTTPS.

## Defense in depth

Cloud's encryption and isolation sit on top of the same enforcement self-hosters get — they don't replace your database's own permissions, so give each project's databases a least-privilege database role as well. See the [threat model](/docs/security/threat-model) for what Midplane does and doesn't claim, and what's in and out of scope.

<Columns cols={2}>
  <Card title="Threat model" icon="crosshair" href="/docs/security/threat-model">
    What Midplane claims and doesn't, and what's in and out of scope.
  </Card>

  <Card title="Adversarial corpus" icon="shield-check" href="/docs/security/adversarial-corpus">
    The bypass attempts pinned as permanent regression tests.
  </Card>
</Columns>
