Skip to main content

Data Model

Rampart uses PostgreSQL as its primary data store. The schema is designed for multi-tenancy, auditability, and flexibility without sacrificing query performance.

Entity-Relationship Diagram

erDiagram
organizations ||--o{ users : "has members"
organizations ||--o{ roles : "defines"
organizations ||--o{ oauth_clients : "owns"
users ||--o{ user_roles : "assigned"
roles ||--o{ user_roles : "granted to"
users ||--o{ sessions : "creates"
users ||--o{ audit_events : "triggers"
oauth_clients ||--o{ authorization_codes : "issues"
users ||--o{ authorization_codes : "authorized by"

organizations {
uuid id PK
varchar name
varchar slug UK
varchar display_name
boolean enabled
timestamp created_at
timestamp updated_at
}

users {
uuid id PK
uuid org_id FK
varchar username UK
varchar email UK
boolean email_verified
varchar given_name
varchar family_name
varchar picture
varchar phone_number
boolean phone_number_verified
bytea password_hash
boolean enabled
boolean mfa_enabled
timestamp last_login_at
timestamp created_at
timestamp updated_at
}

roles {
uuid id PK
uuid org_id FK
varchar name
varchar description
boolean builtin
timestamp created_at
timestamp updated_at
}

user_roles {
uuid user_id FK
uuid role_id FK
timestamp assigned_at
}

oauth_clients {
varchar id PK
uuid org_id FK
varchar name
varchar client_type
text[] redirect_uris
bytea client_secret_hash
varchar description
boolean enabled
timestamp created_at
timestamp updated_at
}

sessions {
uuid id PK
uuid user_id FK
bytea refresh_token_hash
timestamp expires_at
timestamp created_at
}

audit_events {
uuid id PK
uuid org_id FK
varchar event_type
uuid actor_id FK
varchar actor_name
varchar target_type
varchar target_id
varchar target_name
varchar ip_address
varchar user_agent
jsonb details
timestamp created_at
}

authorization_codes {
uuid id PK
bytea code_hash UK
varchar client_id FK
uuid user_id FK
uuid org_id FK
text redirect_uri
varchar code_challenge
varchar scope
boolean used
timestamp expires_at
timestamp created_at
}

Table Descriptions

organizations

The top-level tenant boundary. All users, roles, clients, and policies are scoped to an organization. A single Rampart instance can host many organizations with full data isolation.

ColumnTypeNotes
iduuidPrimary key, generated server-side
namevarchar(255)Internal name
slugvarchar(255)URL-safe identifier, unique across the instance
display_namevarchar(255)Human-readable display name
enabledbooleanSoft disable without deletion (default: true)
created_attimestamptzRow creation time
updated_attimestamptzLast modification time

users

User accounts scoped to an organization. Email and username are unique within an organization (enforced via composite unique indexes).

ColumnTypeNotes
iduuidPrimary key
org_iduuidFK to organizations.id
usernamevarchar(255)Unique per org
emailvarchar(255)Unique per org
email_verifiedbooleanDefaults to false
given_namevarchar(255)Optional (OIDC standard claim)
family_namevarchar(255)Optional (OIDC standard claim)
picturevarchar(2048)Profile picture URL
phone_numbervarchar(50)Optional
phone_number_verifiedbooleanDefaults to false
password_hashbyteaargon2id hash (PHC format), nullable for social-only accounts
enabledbooleanSoft disable without deletion (default: true)
mfa_enabledbooleanWhether MFA is active for this user (default: false)
last_login_attimestamptzUpdated on successful authentication
created_attimestamptzRow creation time
updated_attimestamptzLast modification time

roles

Named roles scoped to an organization. Supports a builtin flag for system-defined roles (e.g., admin, member) that cannot be deleted.

ColumnTypeNotes
iduuidPrimary key
org_iduuidFK to organizations.id
namevarchar(100)Unique per organization
descriptionvarchar(500)Human-readable description
builtinbooleanSystem-defined role (default: false)
created_attimestamptzRow creation time
updated_attimestamptzLast modification time

user_roles

Join table between users and roles. Uses a composite primary key on (user_id, role_id) to prevent duplicate assignments.

ColumnTypeNotes
user_iduuidFK to users.id (part of composite PK)
role_iduuidFK to roles.id (part of composite PK)
assigned_attimestamptzWhen the role was assigned (default: now())

oauth_clients

Registered OAuth 2.0 / OIDC clients (relying parties). Each client belongs to an organization. The id column serves as both the internal primary key and the public client_id.

ColumnTypeNotes
idvarchar(128)Primary key and public client identifier
org_iduuidFK to organizations.id
namevarchar(255)Display name
client_typevarchar(20)public or confidential
redirect_uristext[]Allowed redirect URIs (exact match, no wildcards)
client_secret_hashbyteabcrypt hash of client secret (confidential clients only)
descriptionvarchar(500)Optional description
enabledbooleanSoft disable without deletion (default: true)
created_attimestamptzRow creation time
updated_attimestamptzLast modification time

sessions

Active user sessions. Each session stores a hashed refresh token. Refresh tokens are not stored in a separate table; they are a column on the sessions table.

ColumnTypeNotes
iduuidPrimary key
user_iduuidFK to users.id
refresh_token_hashbyteaSHA-256 hash of the refresh token
expires_attimestamptzSession expiration
created_attimestamptzSession creation time

audit_events

Append-only log of security-relevant events. This table is insert-only in normal operation; rows are never updated or deleted.

ColumnTypeNotes
iduuidPrimary key
org_iduuidFK to organizations.id
event_typevarchar(50)Event category (e.g., user.login, user.login_failed, role.assigned)
actor_iduuidFK to users.id (nullable for anonymous events)
actor_namevarchar(255)Display name of the actor at event time
target_typevarchar(50)Type of the affected entity (e.g., user, role, client)
target_idvarchar(255)ID of the affected entity
target_namevarchar(255)Display name of the affected entity
ip_addressvarchar(45)Source IP address
user_agentvarchar(500)Client user agent
detailsjsonbEvent-specific payload (changed fields, error reasons, etc.)
created_attimestamptzEvent timestamp (immutable)

Indexed on (org_id, created_at DESC) for efficient filtering and time-range queries, plus indexes on event_type and actor_id.

authorization_codes

Short-lived authorization codes issued during the OAuth 2.0 authorization code flow. Codes are single-use and expire within minutes.

ColumnTypeNotes
iduuidPrimary key
code_hashbyteaSHA-256 hash of the authorization code (unique)
client_idvarchar(128)FK to oauth_clients.id
user_iduuidFK to users.id
org_iduuidFK to organizations.id
redirect_uritextThe redirect URI used in the authorization request
code_challengevarchar(128)PKCE code challenge
scopevarchar(512)Granted scopes (default: openid)
usedbooleanSet to true on exchange; prevents replay
expires_attimestamptzTypically 10 minutes from issuance
created_attimestamptzIssuance time

JSONB Usage

The audit_events.details column uses jsonb for extensibility without schema migrations. Event-specific payloads vary by event type, and JSONB keeps the audit table schema stable while supporting rich event data.

Querying JSONB

-- Find audit events with a specific detail
SELECT * FROM audit_events
WHERE org_id = $1
AND details->>'reason' = 'brute_force';

Indexing Strategy

Key indexes beyond primary keys:

TableIndexPurpose
users(email, org_id) UNIQUEEmail uniqueness per org
users(username, org_id) UNIQUEUsername uniqueness per org
users(org_id)List users by organization
user_roles(user_id, role_id) PKComposite primary key prevents duplicates
user_roles(role_id)Reverse lookup: users with a given role
roles(name, org_id) UNIQUERole name uniqueness per org
oauth_clients(org_id)List clients by organization
oauth_clients(org_id, enabled)Filter active clients per org
sessions(user_id)List user sessions
sessions(expires_at)Expired session cleanup
audit_events(org_id, created_at DESC)Filtered audit queries
audit_events(event_type)Filter by event type
audit_events(actor_id)Per-user audit history
authorization_codes(code_hash) UNIQUECode exchange lookup
authorization_codes(expires_at)Expired code cleanup

Data Lifecycle

Data TypeRetentionCleanup
Auth codes10 minutesBackground job purges expired codes
SessionsConfigurable (default 24h)Expired sessions cleaned by background job
Audit eventsConfigurable (default 90 days)Archival or deletion based on retention policy
User dataUntil deletionSoft-delete via enabled flag