#[non_exhaustive]pub enum TableScope {
Tenant,
TenantKeyed {
key: String,
},
Unscoped,
TenantOrSession,
TenantOrBase,
}Expand description
How the host scopes one table under a project’s TenancySchema (PLAN-tenancy-principal,
Decision A / D2 / D3). A table’s scope is a fact of the data model, declared per project (the
guiding principle — the app configures its own concepts — not baked into a component). New
variants (the R3 session disjunct, the R4 target public-subset) land in later stages; the enum is
#[non_exhaustive] so adding them is not a breaking change for downstream crates.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Tenant
Scope on the schema’s default_tenant_key = the resolved
tenant (the common case).
TenantKeyed
The identity table (tenant/org/account), keyed by its own PK: scope on key = the
resolved tenant instead of the default column (R2). key MUST be unique — a non-unique key
would match other tenants’ rows — validated at schema load.
Unscoped
Global reference/enum data (countries): reads are unscoped (reachable even by a
principal-less request — fail-closed is per table-scope, not per invocation); writes are
deny-by-default (a shared-data write is a cross-tenant blast). Host-declared, never
guest-inferred (a guest can’t mark a sensitive table global).
TenantOrSession
An anonymous-first table (R3): rows are owned EITHER by a resolved tenant
(default_tenant_key = <Tenant fact>) OR by an anonymous session
(session_key = <Session fact>, on default_tenant_key IS NULL rows). A read lowers to the disjunction Or([tenant_key = T, session_key = S]) over
whichever axis facts the request carries; the disjoint columns confine a cheap anon session
to tenant IS NULL rows structurally (never tenant-owned rows). Requires the schema to set
session_key; a TenantOrSession table with no session_key is refused (deny-by-default).
The confinement rests on the invariant a session-owned row has default_tenant_key IS NULL — the host write path enforces it (an anon write stamps only session_key, leaving
the tenant key NULL; promote is the sole cross-partition move, guarded by tenant_key IS NULL). An app should add a DB CHECK (<tenant_key> IS NULL OR <session_key> IS NULL) as
belt-and-suspenders: a raw-SQL migration or an all-grant write that set BOTH columns on one
row would let a session reader match a row a tenant also owns.
TenantOrBase
A base-inclusive table: rows with default_tenant_key IS NULL are shared base data
(global reference rows — e.g. base EP-vocabulary packs), readable by every tenant, while
non-NULL rows are the usual per-tenant rows. A READ confines to
(default_tenant_key = <resolved own/target> OR default_tenant_key IS NULL) (AND the public
subset on the target axis) on BOTH the own and target axes — the shared base is folded in
regardless of the field’s scope, so a mixed-table field can fold the base into only the
tables that declare it (unlike the field-level own_or_null/target_or_null, which widen
every table the field reads). A WRITE stamps the resolved tenant exactly like [Tenant] — a
guest can never create or update a NULL-tenant base row (base rows are operator-seeded via a
privileged path). This is the per-table analog of the field-level NULL-base modes, and the
base-partition sibling of [TenantOrSession] (whose NULL partition is anonymous-session rows
instead of shared base). Unlike [Unscoped], the per-tenant (non-NULL) rows keep the
tenant = <resolved> boundary — only the NULL rows are shared — so a tenant can never read
another tenant’s owned rows.
Trait Implementations§
Source§impl Clone for TableScope
impl Clone for TableScope
Source§fn clone(&self) -> TableScope
fn clone(&self) -> TableScope
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TableScope
impl Debug for TableScope
Source§impl<'de> Deserialize<'de> for TableScope
impl<'de> Deserialize<'de> for TableScope
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<TableScope, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<TableScope, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
impl Eq for TableScope
Source§impl PartialEq for TableScope
impl PartialEq for TableScope
Source§impl Serialize for TableScope
impl Serialize for TableScope
Source§fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
impl StructuralPartialEq for TableScope
Auto Trait Implementations§
impl Freeze for TableScope
impl RefUnwindSafe for TableScope
impl Send for TableScope
impl Sync for TableScope
impl Unpin for TableScope
impl UnsafeUnpin for TableScope
impl UnwindSafe for TableScope
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.