Skip to main content
Riktra holds what a company has decided and what its people are working on. That is worth protecting, and worth being specific about.

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 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; history is never rewritten.

How this is enforced rather than promised

Row-level security is the tenancy mechanism, not a layer on top of one. The consequence worth understanding: the retrieval path that assembles context for an AI does no permission filtering of its own, by design. A bug there returns too little context rather than someone else’s. 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

A security_lint() function runs against the schema and checks:
  • RLS coverage on every exposed table
  • role-scoped policies
  • search_path pinning on security-definer functions
  • PUBLIC grants on internal functions
It is asserted empty on every change. During hardening it found two live gaps — a PUBLIC grant that left the rate limiter callable, and default privileges exposing a table intended to be internal — which is the argument for having it. Tenant isolation is covered by integration tests that run against a real Postgres as genuine signed-in users, not as a privileged client. A test that passes because it used the service key proves nothing.

Authentication

Sign-in is Supabase Auth. MCP clients authenticate through OAuth 2.1 with per-user tokens; because those tokens are Supabase JWTs, row-level security applies to MCP calls automatically rather than through a parallel implementation. The service-role key never reaches the browser or any client bundle, and CI fails the build if it appears in the repository.

The anti-surveillance boundary

Riktra does not show managers what individuals are doing. There is no view, export or tool that aggregates one person’s activity for someone else to read, and adding one would contradict the product rather than extend it.

What is logged

MCP calls are logged with the user, organization, tool name and outcome — enough to answer “did the connection work” and “who called what”. Private context never appears in a log, in analytics, or in any error payload.