Semaphor
Security

Security Overview

Unified Security for multi-tenant data isolation in Semaphor

Unified Security is Semaphor's model for controlling data access across the platform. It lets you define security policies on a connection, then assign them to specific actors -- tenants, tenant users, or organization users -- so each person sees only the data they should.

Three policy primitives cover the most common multi-tenant isolation patterns:

PolicyFull NameWhat It Controls
CLSConnection Level SecurityParameterize database connection strings or file paths per actor
SLSSchema Level SecurityRoute actors to specific database schemas
RLSRow Level SecurityFilter rows with WHERE clause predicates

You can use these individually or combine them. A tenant might connect to a dedicated database (CLS), operate within a specific schema (SLS), and still have row-level filtering applied (RLS).


How It Works

Unified Security follows a three-step model: define, assign, resolve.

1. Define policies on connections

An admin creates policy definitions on a connection. A policy definition is a reusable template with placeholders for values that vary per actor.

Example RLS policy definition
tenant_id = {{ tenant_id }}
Example CLS policy definition
postgresql://app:{{ password }}@db.company.com:5432/{{ tenant_db }}

A single policy definition can include CLS, SLS, and RLS rules together, or any subset.

2. Assign policies to actors

An admin creates assignments that bind a policy definition to a specific actor scope and fill in the placeholder values.

For example, assigning the RLS policy definition above to tenant "Acme Corp" with tenant_id = "acme" means every Acme user will have WHERE tenant_id = 'acme' applied to their queries.

3. Resolve at runtime

When a user runs a query, Semaphor resolves their effective security context by collecting all applicable assignments, merging them according to each policy type's rules, and applying the result to the query before execution.

Resolution flow
Actor identity + Applicable assignments + securityParams = Effective security context

The resolved CLS configures the connection, SLS pins the schema, and RLS predicates are injected as WHERE clauses -- all before the query reaches the database.


Actor Model

Unified Security supports four assignment scopes that target different levels of your user hierarchy:

ScopeTargetUse Case
ALL_TENANTSEvery tenant in the projectShared baseline policy (e.g., a common RLS rule)
TENANTA specific tenantTenant-specific connection params or schema
TENANT_USERA specific end user within a tenantUser-level row filtering or access narrowing
ORG_USERAn organization userInternal user policies (independent, no inheritance)

Tenant inheritance

For tenant users, policies inherit and narrow through three layers:

ALL_TENANTS --> TENANT --> TENANT_USER

Each layer can only restrict further, never widen access. If a tenant-level policy sets tenant_id = 'acme', a tenant-user policy cannot change it to tenant_id IN ('acme', 'beta'). It can only add additional restrictions like region = 'west'.

This means the tenant boundary is always preserved. Admins define the tenant-wide default, and tenant-user assignments narrow within that boundary.

Organization users

Org-user assignments are independent -- they do not participate in the tenant inheritance chain. If an org user has no assignment, they receive unrestricted access to the connection.


Connection Security Mode

Each connection operates in one of two security modes:

ModeBehavior
legacyUses the existing token-based CLS, RCLS, and SLS model
unifiedUses Unified Security policy definitions and assignments

This is a per-connection setting, not project-wide. You can migrate connections one at a time while the rest continue using legacy security.

Migration path

  1. Start with all connections in legacy mode.
  2. Author Unified Security policy definitions and assignments (this has no runtime effect while in legacy).
  3. Preview the resolved security for different actors to verify correctness.
  4. Switch the connection to unified mode.
  5. If issues arise, switch back to legacy -- policy definitions and assignments are preserved.

Policy definitions are inert until assigned

Creating a policy definition does not enforce anything on its own. Enforcement begins only when an assignment binds a policy definition to an actor and the connection is in unified mode. Unassigned actors on a unified connection use the base connection with no Unified Security restrictions.


When to Use What

Choose your policy type based on how your data is organized:

Data ArchitectureRecommended Policy
Separate databases per tenantCLS -- parameterize the connection string
Separate schemas per tenantSLS -- route to the correct schema
Shared tables with a tenant identifier columnRLS -- filter rows by tenant
Separate databases with shared tables insideCLS + RLS
Separate schemas with user-level filteringSLS + RLS
File-based sources (S3) with tenant foldersCLS -- parameterize file paths

Most multi-tenant applications need at least one of CLS or SLS for tenant isolation, often combined with RLS for finer-grained access control within the tenant boundary.


Key Concepts

Before diving into configuration, here are the terms you will encounter throughout the security documentation:

  • Policy Definition -- A reusable security policy template attached to a connection. Contains CLS, SLS, and/or RLS configuration with placeholder parameters.
  • Assignment -- Binds a policy definition to an actor scope (ALL_TENANTS, TENANT, TENANT_USER, or ORG_USER) with concrete parameter values.
  • Resolution -- The runtime process that collects applicable assignments for the current actor and merges them into an effective security context.
  • Security mode -- The per-connection setting (legacy or unified) that controls which security model is active.

Next Steps

Legacy Security

For the legacy security model using token-based CLS, RCLS, and SLS policies, see Multi-Tenancy (Legacy).

On this page