Multi-Tenancy
Tenant Column
Every tenant-scoped table includes a user_id column:
-- Current (single-user mode)
CREATE TABLE secrets (
id TEXT PRIMARY KEY,
realm_id TEXT NOT NULL REFERENCES realms(id),
-- realm already has user_id, so tenant isolation is through realm ownership
...
);
Store Layer Enforcement
The store layer enforces tenant isolation:
// WRONG — no tenant filter
func (s *Store) ListSecrets(ctx context.Context, realmID uuid.UUID) ([]Secret, error)
// RIGHT — realm ownership already enforced by ResolveRealm, but store validates
func (s *Store) ListSecrets(ctx context.Context, realmID uuid.UUID, env string) ([]Secret, error)
Realm Ownership as Tenant Boundary
Realm ownership is the tenant boundary. ResolveRealm() ensures a user can only access their own realms. All queries downstream use realm_id as the scope — never a direct user_id filter on data tables.
Future org_id Migration
When organizations land (future), the migration adds org_id to the realms table and adjusts ResolveRealm() to filter by org membership instead of direct user ownership.