symfonic.kernel.contracts.tenancy¶
tenancy ¶
SCOPE-6 / SCOPE-11 — the subject scope, and the one way it becomes a key.
Two rules meet in this module and neither is negotiable.
One serialization contract. SCOPE-11 admits exactly one way a scope becomes
a storage key or a thread key. The formula was not chosen here — it was chosen
by the engine that minted every pause token currently in flight, and
symfonic.capabilities.human.binding.hash_scope reproduces it character for
character including the _ placeholder. :attr:SubjectScope.scope_key is
that same string, so hashing a SubjectScope and hashing whatever the
legacy path passed produce the same sixteen hex digits. A second formula "for
the new path" is how two halves of one deployment stop seeing the same rows.
Exact ancestor-prefix membership. SEC-TEN-1 forbids LIKE 'prefix%',
$regex and startswith in every backend, because acme-evil is a
string prefix of nothing anyone intended. :meth:SubjectScope.narrows compares
segments, so a lookalike tenant is a different scope no matter how its name
was spelled.
This module lives in kernel.contracts because everybody needs the value and
nobody may import platform to get it (SEC-TEN-5, LAY-ADR C6): the platform
derives a scope, the backends enforce it, and neither borrows the other's job.
Standard library only, by the package rule.
SubjectScope
dataclass
¶
The immutable scope a derivation produces and nothing downstream edits.
sub_tenant_id and namespace are two names for one segment — the
shipped FrameworkTenantScope allows either, requires them to agree when
both are given, and that behaviour is preserved here rather than
reinterpreted.
child_segment
property
¶
The single second-level segment, whichever name it arrived under.
scope_key
property
¶
The one serialization (SCOPE-11). Exactly what hash_scope hashes.
segments
property
¶
The scope as an ordered path — the unit membership is tested on.
from_key
classmethod
¶
Rebuild a scope from :attr:scope_key. Round-trips exactly.
Source code in src/symfonic/kernel/contracts/tenancy.py
narrows ¶
Is this scope parent itself, or strictly inside it?
Segment-wise, never string-wise: acme-evil does not narrow acme.
Source code in src/symfonic/kernel/contracts/tenancy.py
visible_to ¶
SubjectScopeError ¶
Bases: ValueError
A scope that cannot name its subject, or names two of them.
Deliberately a ValueError subclass: a malformed scope is bad input,
and the transport maps it to 400 (EMAP-4). It is a distinct type so a
caller can tell "this scope is unusable" from "this tenant is not yours",
which is the distinction T3.4.5 asserts three separate refusals for.