Skip to main content

Database schema

This is the public contract for the database schema owned by the SDK. Every column listed here is stable from the version it first appears in. Removing or renaming any column requires a major version bump.

The SDK owns 13 tables. They live in a configurable Postgres schema (default: identsphere). Host applications may join their own tables to users.id and organizations.id via foreign keys and may read directly from any table here.

Table index

TablePurpose
organizationsTenant containers
usersIndividual accounts
organization_memberships(user, org) → role
member_invitationsPending org invites
user_sessionsRefresh-token-based sessions
user_passkeysWebAuthn credentials
passkey_challengesIn-flight WebAuthn ceremonies
user_recovery_codesMFA backup codes
email_verification_tokensSignup / email-change verifications
email_otp_challengesOne-time login codes
trusted_browsersRemember-this-device
platform_adminsOptional control-plane admin tier
audit_logsAppend-only authorization audit
api_keysProgrammatic credentials (auth-only fields)

organizations

Top-level tenant container. Every user belongs to exactly one organization (v1 limitation; many-to-many lands in v2).

ColumnTypeNotes
idUUIDPrimary key.
nameTEXTHuman-readable.
slugVARCHAR(100)Globally unique URL-safe identifier.
settingsJSONBFree-form. SDK never reads this.
authz_versionBIGINTBumped on any change that affects authorization caches.
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ
deleted_atTIMESTAMPTZSoft-delete; SDK never hard-deletes.

users

Individual accounts. The primary identity record.

ColumnTypeNotes
idUUIDPrimary key. FK-able from host tables.
emailTEXTGlobally unique; case-insensitive lookups recommended.
password_hashTEXTArgon2id (preferred) or bcrypt (legacy).
organization_idUUIDFK to organizations(id).
roleVARCHAR(20)Default role; authoritative role in organization_memberships.
statusVARCHAR(20)active, invited, disabled, deleted.
display_nameTEXT
job_titleTEXT
phone_numberTEXT
avatar_urlTEXT
bioTEXT
preferencesJSONBFree-form per-user settings.
mfa_enabledBOOLEANTOTP enrollment.
mfa_secretTEXTBase32-encoded TOTP secret.
passkey_enabledBOOLEANAt least one passkey enrolled.
last_password_changeTIMESTAMPTZUsed for forced-rotation policies.
email_verifiedBOOLEAN
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ

organization-memberships

Authoritative (user, organization) role mapping. Read this, not users.role, when checking permissions.

ColumnTypeNotes
idUUID
organization_idUUID
user_idUUID
roleVARCHAR(40)Default: owner, admin, billing, member, viewer.
invited_byUUID | null
joined_atTIMESTAMPTZ
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ

Unique on (organization_id, user_id).

member-invitations

Pending invites. Tokens stored as SHA-256 hashes.

ColumnTypeNotes
idUUID
organization_idUUID
emailVARCHAR(255)Invited address.
roleVARCHAR(40)The role to grant on acceptance.
token_hashVARCHAR(64)SHA-256 hex.
invited_byUUID
statusVARCHAR(20)pending, accepted, revoked, expired.
expires_atTIMESTAMPTZ
accepted_atTIMESTAMPTZ
created_atTIMESTAMPTZ

user-sessions

Refresh-token-rotated sessions.

ColumnTypeNotes
idUUID
user_idUUID
session_tokenTEXTOpaque public identifier.
session_family_idUUIDFamily for theft detection.
refresh_token_hashTEXTSHA-256 of the current refresh token.
csrf_token_hashTEXT
dpop_thumbprintTEXTReserved for future DPoP binding.
revoked / revoked_atBOOLEAN / TIMESTAMPTZ
user_agent / ip_addressTEXTFor session-list display.
expires_at / last_active_at / rotated_at / created_atTIMESTAMPTZ

user-passkeys

WebAuthn credentials.

ColumnTypeNotes
idUUID
user_idUUID
credential_idTEXTBase64url; globally unique.
public_keyTEXTBase64-encoded serialized Passkey struct.
sign_countBIGINTMonotonic; clone-detection signal.
labelVARCHAR(100)User-chosen device name.
aaguidVARCHAR(64)
transportVARCHAR(20)usb, nfc, ble, internal, hybrid.
backup_eligible / backup_stateBOOLEANPer W3C spec.
created_at / last_used_atTIMESTAMPTZ

passkey-challenges

Ephemeral state for in-flight WebAuthn ceremonies. 5-minute TTL. Internal — host apps should not read this directly.

user-recovery-codes

MFA backup codes. Hashes only; raw codes shown once at enrollment.

email-verification-tokens

Tokens issued on signup and email-change flows. SHA-256 hashed.

email-otp-challenges

One-time codes for passwordless login + password reset. purpose column discriminates: login, password_reset.

trusted-browsers

"Remember this device." Skips MFA on a matching fingerprint within TTL.

platform-admins

Optional. Only relevant for hosts operating multi-tenant platforms that need a control-plane admin tier.

ColumnTypeNotes
idUUID
user_idUUID
platform_roleVARCHAR(20)account_owner, admin, viewer.
is_bootstrapBOOLEANTrue for the seed admin.
granted_by / granted_at / revoked_atUUID, TIMESTAMPTZ

A unique partial index enforces "exactly one active account_owner".

audit-logs

Append-only authorization event log. No foreign keys on organization_id or actor_id so records survive deletion of referenced rows (intentional — audit trails must outlive their subjects for compliance reviews).

api-keys

Programmatic credentials. Auth-only. No billing, no quota, no rate limits — those belong in host-owned tables foreign-keyed to api_keys.id.

ColumnTypeNotes
idUUID
key_hashTEXTSHA-256 hex. Lookup column.
key_prefixTEXTFirst 16 chars; safe to log.
user_idUUIDOwner.
organization_id / team_id / project_idUUIDScope.
nameTEXTUser label.
scopesJSONB arrayPermission strings.
allowed_ips / allowed_referrersJSONB arrayNetwork constraints.
environmentVARCHAR(40)Free-form tag.
created_at / expires_at / last_used_at / revoked_atTIMESTAMPTZ
is_activeBOOLEAN

Foreign-key integration from host apps

Any host table can reference SDK tables via standard Postgres FKs:

CREATE TABLE my_app.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
owner_id UUID NOT NULL REFERENCES IdentSphere.users(id),
org_id UUID NOT NULL REFERENCES IdentSphere.organizations(id) ON DELETE CASCADE
);

This is the integration seam. Treat the IdentSphere schema as a stable library, the same way you'd treat the public schema of a Postgres extension.

Stability policy

  • Stable: every column documented above. Removing or renaming a column requires a major version bump.
  • Internal: columns starting with underscores (none currently) and the _identsphere_migrations tracking table.
  • Additive changes are free: new columns appear in minor versions.