-
Notifications
You must be signed in to change notification settings - Fork 0
Storage Scopes
A storage scope is a provider-neutral, opaque partition identity — typically a tenant — bound to a document-store session. Groundwork stamps it into the envelope and every dependent physical key; it is never inferred from document payload data and never read from document JSON. There is no query flag that disables isolation.
Every ordinary document store is created with exactly one explicit access context:
// Serves storage units declared TenancyPolicy.Scoped:
DocumentStoreAccess.Scoped(new StorageScope("tenant-a"))
// Serves only units deliberately declared TenancyPolicy.Global:
DocumentStoreAccess.GlobalThere is no null, ambient, or payload-derived scope. A scoped session cannot access a global unit, a global session cannot access a scoped unit, and the mismatch is rejected before provider I/O. Application authorization for acquiring either session remains outside Groundwork — Groundwork enforces the partition, your application decides who gets which session.
Scope values are opaque and compared ordinally, including case and Unicode code-unit
distinctions. For portable provider behavior they are limited to 128 UTF-16 code units, cannot
have leading or trailing whitespace, and cannot use Groundwork's reserved __groundwork_ prefix.
The explicit envelope field name is storageScope; a payload field named tenantId remains
ordinary payload with no isolation meaning.
- The same document identity and the same unique projected value can exist in independent scopes: document primary keys and unique physical indexes include the scope key.
- Wrong-scope point reads and deletes return the same not-found outcomes as absent records — a scoped session cannot even observe that another scope's document exists.
- A compare-and-swap update in the wrong scope cannot observe the other scope's version or dependent rows.
- Every query plan carries a mandatory scope field compiled from the storage route; it is not copied from caller payload and not exposed as a removable predicate. See Querying.
- An explicit unit of work inherits its store's access context, and a commit scope that mixes global and scoped units is rejected before a provider transaction opens. See Transactions-and-Unit-of-Work.
Cross-scope work requires an explicitly acquired privileged session carrying a
PrivilegedStorageAccess capability, through one of three deliberate paths:
- Targeted scoped access — a privileged session for one specific scope.
- Targeted global access — a privileged session for global storage.
- Cross-scope query access — queries spanning scopes. Cross-scope sessions cannot perform point writes, loads, or deletes, because those operations require an unambiguous target scope.
Privileged acquisition is never the result of a missing ordinary scope, and acquisition and
rejected access emit audit evidence through IStorageScopeObserver and the
Groundwork.Documents.StorageScope activity source — with low-cardinality event shapes that
contain no scope value.
- Relational providers persist
storage_scopein the document envelope; primary keys, dependent foreign keys, unique indexes, and linked projection keys include it, and synthesized scoped physical indexes place it first. - SQL Server retains exact binary-collated originals and uses persisted fixed-width SHA-256 shadow columns for native composite keys, so maximum legal values cannot exceed its 900/1700-byte index limits (see Provider-SQL-Server).
- MongoDB uses a composite
_idcontaining scope and logical id, also persistsstorage_scope, and prefixes declared native indexes with it (see Provider-MongoDB).
Each provider's conformance suite proves the same bound operations and native keys, so isolation semantics do not vary by provider.
-
Declaring-Storage —
TenancyPolicy.GlobalvsTenancyPolicy.Scopedon the unit. -
Opening-Stores — passing
DocumentStoreAccessto the factories. - Storage-scope sessions — the full design note, including observability and conformance evidence.
This wiki is generated from
docs/wiki/ in the main
repository and republished on every push to main — do not edit pages here; changes made
directly in the wiki will be overwritten. To propose a change, open a pull request against
docs/wiki/.
Using Groundwork
- Getting-Started
- Declaring-Storage
- Opening-Stores
- Querying
- Transactions-and-Unit-of-Work
- Storage-Scopes
- Schema-Evolution
- Identity-Generators
Providers
Beyond documents
Reference