Skip to main content
Truthlocks uses Postgres Row-Level Security (RLS) as a defense-in-depth measure to enforce tenant isolation at the database layer. Even if application code has bugs, cross-tenant data access is prevented by the database itself.
RLS is enforced in addition to application-level tenant filtering. Both layers work together to ensure data isolation.

How It Works

Session Variable

Each database request sets a session variable with the verified tenant ID:
SET LOCAL app.tenant_id = 'verified-tenant-uuid';

Policy Definition

RLS policies check that rows match the session tenant:
CREATE POLICY tenant_isolation ON issuers
  USING (tenant_id = current_setting('app.tenant_id')::uuid)
  WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);

Tenant Identity Source

The tenant ID is derived only from verified authentication context:
  • JWT claims (after signature verification)
  • API key lookup (after hash validation)
  • Never from client-provided headers like X-Tenant-ID

Covered Tables

RLS is enabled on all tenant-scoped tables:
TableTenant ColumnPolicy
issuerstenant_id✓ Enabled
issuer_keystenant_id✓ Enabled
policiestenant_id✓ Enabled
tenant_api_keystenant_id✓ Enabled
attestationstenant_id✓ Enabled

Fail-Closed Behavior

If app.tenant_id is not set in the session:
  • SELECT: Returns zero rows (not an error)
  • INSERT: Fails with RLS policy violation
  • UPDATE/DELETE: Affects zero rows

Verification

Run the RLS isolation test suite:
make rls-check

Implementation Notes

Connection Pooling

When using connection pools, ensure that SET LOCAL is used within a transaction.
// Go example with pgx
tx, _ := pool.Begin(ctx)
defer tx.Rollback(ctx)

tx.Exec(ctx, "SET LOCAL app.tenant_id = $1", tenantID)
// ... perform queries ...

tx.Commit(ctx)