# Data residency

**The operator processes and stores everything it receives in the EU, and says so in a signed record you can check.** What it receives is commitments, signatures and governance data. Trade values never reach it, because the raw-data wall refuses them.

## Where things are

| Data | Where it is stored | Where it is processed |
|---|---|---|
| Trade values, quantities, bands, corridor secrets, documents, customer identities | **Inside each member bank only.** Never sent to the operator, enforced by the raw-data wall. | Inside the bank (the bander, which has no network) |
| Commitments, signatures, governance events, the verdict ledger, webhook state | Supabase Postgres, **AWS eu-central-1 (Frankfurt)** | **A Cloudflare Durable Object restricted to the EU jurisdiction** |
| This site's static files | Cloudflare Pages | Cloudflare's network |

### How processing is pinned

- **Every request goes to one object.** The Worker at the edge forwards each API request to a single Durable Object created with Cloudflare's `eu` jurisdiction. Cloudflare guarantees that such an object runs and persists only in EU data centres.
- **The edge does not read the request.** It forwards the body unread; only the rate limit and the size check happen there.
- **The object checks its own jurisdiction.** It reads the jurisdiction from its own id at runtime. It does not rely on configuration saying so.
- **It fails closed.** If the EU object cannot be reached, the request is answered `503 unavailable` and nothing is processed anywhere else.

## Check it yourself

`GET /residency` returns a signed `mirror.residency` record:

```json
{
  "processing": { "platform": "cloudflare durable object", "jurisdiction": "eu", "evidence": "runtime", "observed_colo": "FRA" },
  "storage":    { "platform": "supabase postgres on aws", "region": "eu-central-1 (Frankfurt)", "jurisdiction": "eu", "evidence": "declared" },
  "edge":       { "colo": "MXP", "body_read_at_edge": false, "note": "..." },
  "commitment": "Processing and storage of everything this operator receives are in the EU jurisdiction; ...",
  "not_covered": ["the first edge hop ...", "..."]
}
```

Verify it against `/jwks` like any other record. Then run the fail-closed check, which ships in the verification kit and in every [SDK](/sdk.md):

```js
MirrorKit.residency.meets(record, "eu")   // { ok: true, reasons: [] }
```

- **Processing** passes only if the jurisdiction was observed at runtime. A jurisdiction that is merely declared does not pass.
- **Storage** must be in the same jurisdiction.
- **Anything missing, unknown or malformed** fails, with the reason.

A Node host, or the Supabase Edge Function, reports its processing as declared or not pinned, and so it does not meet `eu`.

`GET /health` also says, in plain words, whether processing is pinned.

## What the commitment does not cover

The record lists these itself, under `not_covered`:

- **The first edge hop.** The Cloudflare data centre that first receives a request can be anywhere. TLS ends there, and the request is forwarded unread. Pinning that hop too needs Cloudflare's Regional Services, a commercial product this rail does not use.
- **Durable Object ids.** Cloudflare logs them outside the jurisdiction for billing and debugging.
- **Storage is declared, not observed.** The operator cannot ask its database where it lives. The region is in the [sub-processor register](/subprocessors.md).
- **Anything a member sends elsewhere.** A member's own systems, and the bander, are outside the commitment.

## Commitments

- **The EU commitment can be a contract term:** [Schedule C](/contract-schedules.md). It is a draft for counsel, which no bank has signed.
- **A member's own data never leaves the member.** That is a property of the design, not a residency promise that could change.
- **Other jurisdictions.** The same mechanism supports `us` and `fedramp` objects. No such deployment exists, and none is offered until one does.

What the operator can possibly hold is the [refusal list](/cannot.json).
