Sovereignty
The L0–L4 sovereignty ladder, the endpoint facts behind it (compute region, zero data retention, training), and how workspace and router policy enforce them on every request.
"Is the data in Europe?" and "can a third-country government compel disclosure?" are different questions. Stav answers the second with an ordinal ladder, and the first with facts recorded on each Provider Endpoint.
The ladder
| Level | Name | What it means |
|---|---|---|
| L4 | Full sovereignty | EU/EEA ownership and control, a sovereignty qualification (not security alone), and no third-country dependency that can be revoked |
| L3 | EU/EEA ownership & control | The serving operator is EU/EEA-owned and controlled |
| L2 | EU/EEA operation + certification | EU/EEA operation, independently audited security certification |
| L1 | EU/EEA location | Inference runs on EU/EEA soil |
| L0 | Third-country operator | Operated under a non-EU/EEA legal system, outside the EU/EEA guarantees |
| — | UNCLASSIFIED | Not assessed yet |
The rungs follow the Union assurance levels in the EU's proposed Cloud and AI Development Act (CADA), with L0 added below. CADA is a proposal, not law: the levels are Stav's dated assessment, not a regulatory recognition.
- Ordinal.
L0 < L1 < L2 < L3 < L4, so "at least L3" is enforceable. - UNCLASSIFIED is not a rung. It never satisfies a floor of L1 or above.
How a level is assigned
A level belongs to the serving endpoint:
- an override set on that endpoint, else
- the Inference Provider's assessed level, else
UNCLASSIFIED.
Each resolved level is logged with a registry version, so a level recorded today stays interpretable after the criteria change.
Reading the level per request
Chat Completions responses (streaming and non-streaming) carry the level of the endpoint that served them:
X-Stav-Sovereignty-Level: L3
The same model served by two Inference Providers can return two different levels.
raw = client.chat.completions.with_raw_response.create(
model="auto",
messages=[{"role": "user", "content": "…"}],
)
level = raw.headers.get("X-Stav-Sovereignty-Level")
if level in (None, "L0", "UNCLASSIFIED"):
log.warning("request %s served at %s", raw.headers["X-Stav-Request-Id"], level)
X-Stav-Sovereignty-Badge may also appear. It is deprecated; read X-Stav-Sovereignty-Level.
The level is also written to the request log (Your workspace → Monitor → Request log).
Endpoint facts
Three more facts are recorded per Provider Endpoint, each with its source:
| Fact | Values |
|---|---|
| Compute country | ISO country code, or unknown |
| Compute region | eu, eea, us, global, or unknown |
| Zero data retention (ZDR) | yes, no, or unknown |
| Trains on input | no, yes, or unknown |
Rules:
- Unknown never passes. A ZDR requirement admits only endpoints marked ZDR. "No training" admits only endpoints marked does not train. A region requirement admits only endpoints whose country or region is inside it.
- A region never proves a country. An EU requirement admits an EU-state country or region
eu. An EEA requirement also admits regioneeaand Norway, Iceland, Liechtenstein.globalsatisfies nothing. - Compute region is where inference runs. It says nothing about sub-processors. ZDR is the provider's own statement.
The serving endpoint's country, region, ZDR and training facts are stamped on the request log row when the request runs and never recomputed.
Where you set policy
Workspace: Your workspace → Govern → Key policies → Reach. Applies to every key in the workspace.
| Control | Effect |
|---|---|
| Sovereignty floor | Minimum ladder level (L0 = no floor) |
| Compute region | Any, EU + EEA, or EU only |
| Zero data retention only | Only ZDR-confirmed endpoints |
| No training on our data | Only endpoints that do not train on input. On by default |
| Intelligence floor | Minimum model score, 0–100 |
Router: Your workspace → Connect → Routers → a named router. A router can set its own floor, region, ZDR and no-training, plus allowed and blocked models.
Blocked provider countries: Your workspace → Connect → Routers → default router settings. Models whose Inference Provider is based in a listed country are excluded for the workspace.
Tighten-only. Workspace and router combine to the stricter value. A router can raise the floor, narrow the region, or add a requirement. It can never loosen the workspace's.
workspace floor L1 + router floor L3 → L3
workspace floor L3 + router floor L1 → L3
How enforcement works
- Routed requests (
model="auto",@workspace/router): policy is applied to the candidate pool before scoring. Cost, latency and quality compete only inside the admissible set. - The serving endpoint is checked too, not just the model. A model with one compliant and one non-compliant endpoint is served only from the compliant one.
- Pinned models and endpoints are held to the same wall. Nothing falls back.
| Situation | Response |
|---|---|
| No candidate survives routing policy | 404 no_eligible_models |
| Intelligence floor removed every candidate | 400 quality_floor_unsatisfied |
| Pinned model has no endpoint at the workspace floor | 403 model_below_sovereignty_floor |
| Pinned endpoint fails region, ZDR or country policy | 403 endpoint_not_admissible |
| Pinned model excluded by workspace config | 403 model_not_permitted |
| Workspace policy could not be read | 503 policy_unavailable — fails closed, nothing charged, retry with backoff |
Refusals are logged with the rule that blocked them, e.g. Blocked by workspace policy: sovereignty floor L3.
Fails closed. Policy is read from configuration behind a 60-second cache. If that read fails, the request is refused with 503 policy_unavailable and nothing is charged — the floor, region, ZDR and no-training requirements never degrade to none. The per-request header and log, not the settings page, are still the evidence to check.
The classifier does not move policy
Stav classifies routed prompts, including whether they touch a sensitive domain. That signal is logged. It never changes where a request may be served. Where traffic may go is set once, in configuration.
Blocking an Inference Provider
Sometimes the requirement is not a rung or a region but a name: never send our traffic to that company. Your workspace → Govern → Key policies → Reach → Blocked Inference Providers is that switch.
- Every surface. It applies to
/v1/chat/completions,/v1/messages,/v1/responsesand/v1/embeddings, on every key in the workspace. - Routed requests route around it. With
model="auto"or a router, an endpoint on a blocked provider is never a candidate. A model that another provider also serves stays available and is served there. - Named and pinned requests are refused by name. A pin to an endpoint on a blocked provider, or a model that only blocked providers serve, returns
403with the codeprovider_blocked. Nothing falls through to the blocked provider. - Workspace-wide and tighten-only. A router cannot unblock a provider the workspace blocked.
{
"error": {
"message": "'mistral/mistral-large/azure' is served by the Inference Provider 'azure', which your workspace has blocked. …",
"type": "permission_error",
"param": "model",
"code": "provider_blocked"
}
}
A block list is not a jurisdiction list. Blocked provider countries judge where inference runs; a blocked provider judges who runs it. Use both when both matter.
Choosing a floor
| Situation | Setting |
|---|---|
| General traffic, no regulated data | No floor or L1 |
| Personal data of EU residents | L1, compute region EU or EEA |
| Third-country disclosure is a live risk | L3 |
| Provider must keep nothing | Zero data retention only |
| Data must not train models | Leave No training on our data on |
| One named company must never serve your traffic | Blocked Inference Providers |
A higher floor narrows the pool, usually at some cost in price or frontier quality.