EU compliance
How the gateway helps you run LLM workloads with EU data residency, durable records, and exportable evidence — and exactly where its guarantees end.
This page describes technical controls. It is not legal advice; map the controls to your own obligations with your counsel.
Residency: where your traffic and data live
Section titled “Residency: where your traffic and data live”The production gateway is EU-resident by default:
| Component | Location |
|---|---|
| Gateway (request processing) | Fly.io Frankfurt (fra) |
| Usage WAL (durable accounting buffer) | Volume in the same Frankfurt region |
| Database (usage records, ledger, audit log) | Supabase eu-central-1 (Frankfurt) |
| TLS termination | Fly edge; DNS is unproxied (no third-party in the request path) |
Two honest caveats:
- Anycast TCP termination can occur at the Fly edge nearest to your client before being routed to the Frankfurt machine — request processing happens only in Frankfurt; treat edge hops as transit.
- Residency of the gateway does not change where the model provider runs. That is what region routing (below) is for.
Region routing: eu_only
Section titled “Region routing: eu_only”Every configured provider carries a region (eu, us, or global), and every
organization has a routing policy:
| Policy | Effect |
|---|---|
any (default) | All providers available |
prefer_eu | Declarative preference; not enforced in v1 (no automatic upstream switching) |
eu_only | Requests to non-EU providers are rejected with 403 {"error":{"type":"region_policy",…}} |
Ask your operator to set eu_only if you need a hard guarantee. Enforcement
happens before the request body is read or forwarded — nothing leaves the
gateway toward a non-EU upstream.
Every request additionally records the provider’s region in your usage data
(per-request attestation): it appears in the evidence export
and in GET /gw/stats?group_by=region.
Provider EU routes — current state, honestly
Section titled “Provider EU routes — current state, honestly”| Provider | EU route today |
|---|---|
| OpenAI | ✅ EU residency endpoint (eu.api.openai.com) can be configured as a region: eu provider |
| Mistral | ⚠️ Aspirational — La Plateforme is EU-resident, but the gateway ships no Mistral provider: it exists only as a commented-out example in config.example.yaml and is not configured in any deployment. Using it requires the operator to add a mistral-eu provider (region: eu) first |
| Anthropic (Claude) | ✅ via AWS Bedrock eu-central-1 or Google Vertex AI EU — no first-party EU inference endpoint exists (the first-party API is US-routed and is blocked for eu_only organizations), but the gateway supports SigV4/OAuth-signed Bedrock and Vertex providers; available when your operator configures the corresponding region: eu provider. Requests use the Bedrock/Vertex-native paths and request bodies through the provider prefix; note that usage metering for Bedrock’s invoke-with-response-stream (AWS eventstream framing) is not parsed in v1 — non-streaming responses are metered normally |
| Other aggregators / APAC providers | region: global — blocked for eu_only organizations |
If a provider is not listed with region: eu in your deployment, assume it is
not EU-resident.
Current state, plainly. The routes above are what the gateway supports; they are not active until an operator configures a
region: euprovider. As shipped, the production deployment runs onlyus(OpenAI, Anthropic first-party) andglobal(Alibaba International and other non-EU routes) providers — noregion: euprovider is configured yet. So aneu_onlypolicy today would reject all inference (every provider is non-EU). If you need EU-only routing, ask your operator to add an EU route (e.g.openai-eu, Anthropic via Bedrockeu-central-1/ Vertex EU) first. The gateway’s own data plane (processing, WAL, database) is Frankfurt-resident regardless — this caveat is strictly about upstream model providers.
Record-keeping and retention
Section titled “Record-keeping and retention”- Every proxied request is recorded as metadata (model, provider, region, cost,
tokens, status, latency). Request and response bodies are not stored by
default (
usage.store_payloads: false; the EU profile keeps payload storage off), and the evidence export never includes bodies regardless of that setting. - Retention is configurable. The shipped EU production deployment runs
retention enabled at 185 days (≥ 6 months, aligned with the record-keeping
direction of EU AI Act Art. 26(6); note the 2025 Omnibus shifted application
timelines — verify the current state with counsel rather than relying on a date
here), with a 1-hour sweep interval. The 185-day window is the production
contract for this page.
- The open-source code ships with the sweep disabled by default
(
retention.enabled: false): a self-hosted gateway deletes nothing until an operator turns retention on. The Sociaro-operated EU production gateway setsretention.enabled: true, so the 185-day sweep is active there.
- The open-source code ships with the sweep disabled by default
(
- Expired rows are deleted by a background sweeper; every deletion pass is itself journaled (what, how many rows, which cutoff) and the journal is never swept — proof that erasure followed policy.
- The financial async-job ledger is retained independently (billing-grade records are not subject to the usage sweep).
Details: Evidence export & retention.
Evidence for audits and procurement
Section titled “Evidence for audits and procurement”GET /gw/evidence exports your organization’s request metadata and the
operator actions that targeted your organization as tamper-evident NDJSON:
each line carries a SHA-256 of the exact bytes of the previous line, so any
modification breaks the chain. Verify offline with
scripts/verify_evidence.py
(stdlib Python, no dependencies).
The field mapping table relates each exported field to EU AI Act articles and ISO/IEC 42001 control themes — aligned with, not certified against.
Article 50 transparency helper
Section titled “Article 50 transparency helper”Providers can be configured to add X-AI-Generated: true to successful (2xx)
LLM responses. This is plumbing for your own Article 50 disclosure flows — the
disclosure obligation itself remains yours; the header just makes AI-generated
responses machine-detectable in your stack.
Contracts and certifications
Section titled “Contracts and certifications”- DPA and SCC templates, the sub-processor list, and the data-flow diagram are being prepared on the organizational track — request the current versions from your operator.
- ISO 27001 certification is in progress; SOC 2 Type II and ISO/IEC 42001 are planned based on demand. We will not claim a certification before it is issued.
Quick checklist for your security questionnaire
Section titled “Quick checklist for your security questionnaire”| Question | Answer |
|---|---|
| Where is traffic processed? | Fly.io Frankfurt (EU) |
| Where is usage data stored? | Supabase Frankfurt (EU), plus a WAL volume in the same region |
| Are prompts/completions stored? | Not by default (store_payloads: false, kept off in the EU profile); the evidence export never includes bodies |
| Can non-EU model providers be blocked? | Yes — routing_policy=eu_only, enforced with 403 region_policy |
| Is there per-request proof of execution region? | Yes — region on every usage record, in stats and in the evidence export |
| How long are records kept? | EU production runs retention enabled at 185 days (1-hour sweep) with a journaled deletion process; configurable (the open-source default ships with the sweep off) |
| Can we get an auditable export? | Yes — hash-chained NDJSON via GET /gw/evidence, verifiable offline |