The guarantees
Tenant isolation
Every exposed table has row-level security. A query from one company cannot return another’s rows — on the web path and the MCP path alike.
No admin bypass on private context
personal_context and focus_items are owner-scoped. No application role reads around it, and private records never enter org-scoped retrieval, logs or analytics.AI cannot change shared truth
Company writes go through proposals and human approval. Direct writes are private-scope only.
Versioned history
Shared-truth writes create a version row in the same transaction, enforced by a database trigger. Restoring creates a new version; normal editing appends history. Account/company closure has a separate documented erasure scope.
How this is enforced rather than promised
Row-level security restricts authenticated queries, together with verified membership and role checks. The same boundaries apply when context is retrieved by an authorised AI client. These controls reduce risk; they do not make every application bug harmless. Versioning is a trigger rather than a convention, so no write path can forget it. Roles are checked in the database rather than in the description a model reads before calling a tool.Asserted in CI, not documented in a checklist
Asecurity_lint() function runs against the schema and checks:
- RLS coverage on every exposed table
- role-scoped policies
search_pathpinning on security-definer functionsPUBLICgrants on internal functions