Organization isolation¶
Organization scope must not depend on discipline in every query.
mic.tenancy associates context with the current execution flow and exposes a
relational view that rejects unbounded operations.
from mic.tenancy import TenantScopedRelationalStore, tenant_context
projects = TenantScopedRelationalStore(
raw_store,
tenant_field="organization_id",
)
with tenant_context(organization_id):
rows = projects.find(Project, query)
Secured-view guarantees¶
TenantScopedRelationalStore protects insert, get, find, count, and
delete:
- without
tenant_context, the operation raisesTenantLeak; - reads and counts always receive the organization filter;
- a caller-supplied filter on the organization field is rejected;
- an inserted record whose organization differs from context is rejected;
getanddeletenever see a row from another organization.
The context uses ContextVar: it follows the current asynchronous flow and is
restored when the block exits. The HTTP, gRPC, or worker transport remains
responsible for opening the context from an already authenticated identity.
Make bypass inaccessible¶
The guarantee applies to the injected view. If a repository also receives the
raw store, it can bypass that boundary. Composition must therefore construct the
raw store at bootstrap and pass only TenantScopedRelationalStore to
multi-organization modules.
For highly sensitive data, complement this application boundary with a database-level policy such as PostgreSQL Row-Level Security. MIC neither creates nor operates that policy, and it does not replace isolation tests on the real target dialect.
Intentional limits¶
- the field defaults to
organization_idand remains configurable; - engine-specific operations use the SQL escape hatch and must explicitly apply the same scope;
- MIC chooses neither partitioning strategy, organization hierarchy, nor cross-organization administration rules.
See also the explicit SQL escape hatch.