Multi-tenancy.
Isolation enforced by the database, not by remembering to add a WHERE clause — and nested tenants for MSSPs who resell.
The shape
Row-level security
Each request sets a Postgres session variable to the caller's tenant. Every tenant-scoped table has a policy that filters on it, and FORCE ROW LEVEL SECURITY means the policy applies even to the table owner.
ALTER TABLE <t> ENABLE ROW LEVEL SECURITY;
ALTER TABLE <t> FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <t>
USING (tenant_id = current_setting('omni.tenant_id')::uuid);14 of the 67 tables carry this. The consequence that matters: a query that forgets to filter by tenant returns nothing rather than everything. The failure mode is a missing row, not a data leak.
Sub-tenants
An MSSP parent can create nested organisations. The parent gets management surfaces — it can see that a sub-tenant exists, its plan and its member count — but not the sub-tenant's private intel, documents or keys.
- Sub-tenant creation is gated on the parent's plan, checked server-side in the action.
- Each sub-tenant is a full organisation with its own members, keys and RLS scope.
- Suspending a sub-tenant blocks its members without deleting anything.
MSSP navigation is shown to admins regardless of plan; the paid-tier limit is enforced in the create-sub-tenant action on the parent organisation. Hiding a nav item is not access control, and this codebase deliberately does not treat it as such.
Platform admins
users.is_platform_admin is a global flag, separate from any organisation role. It grants access to the platform surfaces and can enter a tenant to act on it — and every such action is audited.
It is not a magic bypass of RLS in application code: entering a tenant sets that tenant's context, so the same policies apply.
Two scopes, on purpose
Not everything in OmniIntel is tenant-private, and that is deliberate. Reference intelligence — CVEs, KEV status, EPSS scores, public indicators, actor and malware profiles — is shared corpus. Every tenant reads the same records, which is the point: a feed nobody shares is a feed with no corroboration.
What is tenant-private is anything you contribute or accumulate: indicators you submit yourself, your knowledge-base documents, your advisories, your API usage. Those rows carry your tenant_id and are unreachable from another tenant's session — enforced by the database, not by application code.
The practical rule: if OmniIntel learned it from a public source, you share it. If you told OmniIntel, you own it. Private submission is documented under the pipeline.