Most inference platforms treat sovereignty as a binary: EU region or not. That is not how the question is actually asked. "Is the data in Europe" and "can a third-country government compel disclosure" are different questions with different answers, and a company subject to the CLOUD Act answers the second one badly no matter where its servers sit.
So Stav grades sovereignty on an ordinal ladder rather than a flag.
The ladder
| Level | Name | What it means |
|---|---|---|
| L4 | Full sovereignty | EU/EEA ownership and control without derogation, a qualification certifying sovereignty rather than security alone (SecNumCloud today; EUCS at high once that scheme exists), and no third-country dependency that can be unilaterally revoked |
| L3 | Sovereign | EU/EEA ownership and control of the serving operator, with privileged production access confined to EU/EEA-based personnel |
| L2 | European operation | EU/EEA operation plus certification — personnel in the Union, an independently audited security certification (ISO 27001 or equivalent), a maintained SBOM, and third-country components identified and risk-assessed |
| L1 | European-hosted | EU/EEA and adequacy location — inference and data stay on EU/EEA soil |
| L0 | Foreign jurisdiction | Served under third-country law, outside the EU/EEA guarantees |
| — | UNCLASSIFIED | Not yet assessed |
These are Stav's customer-facing labels for the four CADA Union assurance levels, plus an explicit L0 floor. The same labels appear on stav.ai/providers and on every provider detail page.
Two properties matter more than the labels:
It is ordinal. L0 < L1 < L2 < L3 < L4. That is what makes a floor possible: "at least L3" is a statement the router can enforce, where "sovereign or EU-hosted, but not commercial" was a list you had to maintain by hand.
UNCLASSIFIED is not a rung. It is a data-quality state meaning we have not assessed this yet. It is never a floor value, and an unassessed endpoint fails any floor of L1 or above. An unverified provider does not get the benefit of the doubt.
The rungs are aligned to the four Union assurance levels in the EU's proposed Cloud and AI Development Act (CADA), with L0 added as an explicit floor for everything outside that scope. CADA is a legislative proposal, not law — Stav's levels are an indicative, versioned assessment, not a regulatory recognition. Stav reads "Union" as EU/EEA, which is the reading that matters for a Norwegian operator and for EEA customers generally.
Reading the level per request
Every inference response carries the effective level of the endpoint that actually served it:
X-Stav-Sovereignty-Level: L3
The header is on every successful inference response. Its value is resolved per endpoint, not per model — the same model served from two providers can come back at two different levels, which is precisely the distinction a compliance argument turns on.
raw = client.chat.completions.with_raw_response.create(
model="auto",
messages=[{"role": "user", "content": "…"}],
)
level = raw.headers["X-Stav-Sovereignty-Level"]
if level in ("L0", "UNCLASSIFIED"):
log.warning("request %s served at %s", raw.headers["X-Stav-Request-Id"], level)
The same level is written to the request log, so Monitor → Requests in the portal gives you a per-request ledger rather than a policy document. When an auditor asks "how do you know", the answer is a row per call.
X-Stav-Sovereignty-Badgeis also emitted on some responses. It is the legacy three-bucket value and is deprecated — readX-Stav-Sovereignty-Level.
How a level is assigned today
A level resolves in this order:
- An override set on the specific endpoint
- The provider's assessed level
- A deterministic mapping from the legacy three-bucket classification
UNCLASSIFIEDif none of the above resolves
Every resolution is stamped with a registry version, so a level recorded last month remains interpretable after the criteria change. The current registry is the initial seed: levels are assigned per provider rather than per individual criterion, and the fine-grained criterion grid (which will let a provider be held at L1 by a single failed L2 criterion) is still being built. Treat published levels as indicative and dated — because that is what they are.
A level is a property of the serving endpoint, resolved from the provider record behind it. Where that record is incomplete the level reads UNCLASSIFIED rather than being guessed. Treat an unexpected UNCLASSIFIED on a response as a data-quality alert — it means an endpoint reached you that has not been assessed.
Setting a floor
A floor is a minimum level the router may select. It is set in the Customer Portal, in two places:
- Team → Routing — the team-wide floor, applied to every routed request
- On a named router — a per-router floor
The two combine with a maximum, never a minimum. A named router can raise the floor above the team's; it can never resolve below it. This is deliberate: adding a router must not be a way to widen access.
team floor = L1
router floor = L3
effective floor = L3
team floor = L3
router floor = L1
effective floor = L3
The floor is applied to the whole candidate pool before any scoring happens, so quality, cost and latency optimisation all take place inside the permitted set. A cheaper sub-floor model cannot win on score — it was never in the pool.
One deliberate exception is worth knowing about: the floor is read from your team's routing configuration behind a short cache, and if that lookup fails it degrades to no floor rather than failing the request. That is an availability trade Stav makes on your behalf. It is also why the per-request header, not the configuration page, is the thing to assert on.
When nothing satisfies the floor
The request fails. It does not quietly fall back.
{
"error": {
"message": "Smart Router could not select a model: … There are 78 active model(s) in the catalogue but none are eligible.",
"type": "invalid_request_error",
"param": "model",
"code": "no_eligible_models"
}
}
HTTP 404. If you see this after raising a floor, the cause is almost always that the floor plus another constraint (a capability requirement, a model allowlist) has emptied the pool between them.
Floors and pinned models
The floor governs routing — auto and named routers. A request that pins a model by name is governed instead by your team's blocked provider jurisdictions, which are enforced on every request:
{
"error": {
"message": "The model 'gpt-4o-mini' is not available for your team's configuration.",
"type": "permission_error",
"param": "model",
"code": "model_not_permitted"
}
}
So the two controls do different jobs. Blocked provider jurisdictions are the wall: they apply to every request, including a pinned one, and nothing your developers write can step over them. The floor shapes routing inside that wall. If the requirement is absolute, set both — a floor alone leaves a pinned model to the jurisdiction list.
Sovereignty is a setting, not a guess
Stav classifies every routed request before choosing a model, and one of the classifier's outputs is whether the prompt appears to touch a sensitive domain — the categories that carry heightened obligations under the EU AI Act. That signal is recorded: it appears in the request log and in compliance reporting. It does not move your sovereignty level.
That is deliberate. Where a request may be served is a decision your team makes once, in configuration, on the L0–L4 ladder. It is never inferred per request from what the prompt looks like. A router that silently upgraded a request to a sovereign pool because the text mentioned a diagnosis would make your effective policy a function of prompt wording — unpredictable for you, and impossible to evidence to an auditor.
So the floor is the whole story. The team floor, raised where you want it by a named router's floor, applied to the candidate pool before any scoring happens. A workload that must be served at L3 is served at L3 because you set L3 — on every request, whatever the prompt happens to say.
Choosing a floor
| Your situation | Floor |
|---|---|
| General product traffic, no regulated data | none, or L1 |
| Personal data of EU residents, standard processing | L1 |
| Regulated sector, third-country disclosure is a live risk | L3 |
| Public-sector procurement with a sovereignty clause | L3, with the jurisdiction list locked to match |
Setting a floor narrows the pool, which usually costs you something on price or on frontier quality. That trade is the point — make it explicitly, at the team level, once, rather than implicitly in each service.
Next steps
- Enforce a sovereignty floor — the end-to-end setup
- Smart Router — how the floor interacts with scoring
- Response headers
- stav.ai/sovereignty — the full public explainer, including the CADA and SEAL background