symfonic.platform.scope¶
scope ¶
SCOPE-1..8 — the one place a request becomes a principal.
The behaviour is the shipped get_tenant_scope: a missing X-Tenant-ID is
401, the verifier runs before the scope is built so an unauthorised caller
never reaches a constructed scope, and a divergent sub-tenant/namespace pair is
400. What changes is ownership. Today the verifier is a module global —
set_tenant_auth_verifier — which is a per-process singleton for a
per-deployment fact: two agents mounted in one process share one verifier, and
a test that registers one leaks it into the next. Here the resolver is an
object the host constructs and injects (SCOPE-3, HOST-3), so two of them in one
process are simply two objects.
HeaderScopeResolver ¶
HeaderScopeResolver(*, verifier: TenantAuthVerifier | None = None, environment: str | None = None, allow_insecure_prod: bool = False, environ: dict[str, str] | None = None)
Registry row 16, default implementation: headers plus an injected verifier.
X-Tenant-ID on its own is untrusted input — the framework cannot assume
an auth model — so the verifier is what turns a claim into a principal. With
no verifier the header is accepted on trust, which is fine for a unit test
and never for production; :meth:posture is how that state stops being
invisible (SCOPE-5).
Source code in src/symfonic/platform/scope.py
posture ¶
A fresh reading every call — this is a gauge, not a one-shot log.
Source code in src/symfonic/platform/scope.py
resolve
async
¶
SCOPE-1: exactly once per request, and this is the once.