Overview
Nexus Access Vault uses Supabase as its database backend. This guide covers setting up your database schema, running migrations, and understanding the database structure.Prerequisites
Supabase Project
Active Supabase project with admin access
Supabase CLI
Supabase CLI installed globally
Initial Setup
Link to Your Project
Link your local project to your Supabase project:You’ll be prompted to enter your database password.
Find your project ID in the Supabase dashboard URL:
https://app.supabase.com/project/[PROJECT_ID]Run Migrations
Apply all database migrations to set up the schema:This will apply all migration files from
supabase/migrations/ in order.Database Schema
Nexus Access Vault uses a multi-tenant architecture with row-level security (RLS). Here’s an overview of the main tables:Core Tables
organizations
organizations
Stores organization/tenant information.Columns:
id(UUID, PK): Organization identifiername(TEXT): Organization namelogo_url(TEXT): Organization logo URLcreated_at(TIMESTAMPTZ): Creation timestamp
- Global admins can view all organizations
- Users can view their own organization
- Global admins can create organizations
profiles
profiles
Extends auth.users with organization and role information.Columns:
id(UUID, PK, FK → auth.users): User identifierorganization_id(UUID, FK → organizations): Organization membershipfull_name(TEXT): User’s full namerole(user_role enum): User’s rolecreated_at(TIMESTAMPTZ): Creation timestamp
global_admin: Full system accessorg_admin: Organization administrationsupport: Support role with extended permissionsuser: Standard user access
- Users can view their own profile
- Users can view profiles in their organization
- Org admins can update profiles in their org
resources
resources
Stores remote access resources (VMs, RDP sessions, etc.).Columns:
id(UUID, PK): Resource identifierorganization_id(UUID, FK → organizations): Owner organizationname(TEXT): Resource nameresource_type(resource_type enum): Type of resourceip_address(TEXT): Resource IP addressconnection_method(TEXT): Connection methodmetadata(JSONB): Additional resource metadatacreated_at(TIMESTAMPTZ): Creation timestamp
windows_vm: Windows virtual machinelinux_vm: Linux virtual machinerdp: RDP sessionssh: SSH sessionguacamole_session: Apache Guacamole sessiontsplus_html5: TSplus HTML5 sessiontailscale_node: Tailscale network nodecustom: Custom resource type
- Users can view resources in their organization
- Org admins can manage resources in their org
user_resource_access
user_resource_access
Manages user access permissions to resources.Columns:
id(UUID, PK): Access record identifieruser_id(UUID, FK → profiles): User with accessresource_id(UUID, FK → resources): Resource being accessedstatus(access_status enum): Access statuscreated_at(TIMESTAMPTZ): Request timestampactivated_at(TIMESTAMPTZ): Activation timestamprevoked_at(TIMESTAMPTZ): Revocation timestamp
pending: Access requested but not yet activatedactive: Access is currently activerevoked: Access has been revoked
- UNIQUE(user_id, resource_id): One access record per user-resource pair
- Users can view their own access
- Admins can view all access in their org
- Admins can manage access in their org
audit_logs
audit_logs
Records all important system events for auditing.Columns:
id(UUID, PK): Log entry identifierorganization_id(UUID, FK → organizations): Related organizationuser_id(UUID, FK → profiles): User who triggered the eventevent(TEXT): Event name/typedetails(JSONB): Event detailscreated_at(TIMESTAMPTZ): Event timestamp
- Admins can view audit logs in their org
- System can insert audit logs
Authentication Tables
zitadel_configurations
zitadel_configurations
Stores Zitadel OIDC configuration per organization.Columns:
id(UUID, PK): Configuration identifierorganization_id(UUID, FK → organizations): Owner organizationname(TEXT): Configuration nameissuer_url(TEXT): Zitadel issuer URLclient_id(TEXT): OIDC client IDclient_secret(TEXT): OIDC client secret (encrypted)redirect_uri(TEXT): OAuth redirect URIscopes(TEXT[]): Requested OIDC scopesapi_token(TEXT): Zitadel Management API tokensync_groups(BOOLEAN): Enable automatic group syncis_active(BOOLEAN): Configuration is activecreated_at(TIMESTAMPTZ): Creation timestampupdated_at(TIMESTAMPTZ): Last update timestamp
- UNIQUE(organization_id, name): One config name per organization
- Only org admins can manage Zitadel configs
zitadel_group_mappings
zitadel_group_mappings
Maps Zitadel groups to local groups.Columns:
id(UUID, PK): Mapping identifierzitadel_config_id(UUID, FK → zitadel_configurations): Config referencezitadel_group_id(TEXT): Zitadel group IDzitadel_group_name(TEXT): Zitadel group namelocal_group_id(UUID, FK → groups): Local group referenceauto_sync(BOOLEAN): Enable automatic user assignmentcreated_at(TIMESTAMPTZ): Creation timestamp
- UNIQUE(zitadel_config_id, zitadel_group_id): One mapping per Zitadel group
- Admins can manage group mappings
user_zitadel_identities
user_zitadel_identities
Links users to their Zitadel identities.Columns:
id(UUID, PK): Identity link identifieruser_id(UUID, FK → profiles): Local userzitadel_config_id(UUID, FK → zitadel_configurations): Config usedzitadel_user_id(TEXT): Zitadel user subjectzitadel_groups(TEXT[]): User’s Zitadel groupslast_synced_at(TIMESTAMPTZ): Last sync timestampcreated_at(TIMESTAMPTZ): Creation timestamp
- UNIQUE(user_id, zitadel_config_id): One identity per config per user
- UNIQUE(zitadel_config_id, zitadel_user_id): One local user per Zitadel user
- Users can view their own identity
- Admins can manage identities in their org
Database Functions
handle_new_user()
Automatically creates a profile when a new user signs up. Trigger:on_auth_user_created (AFTER INSERT on auth.users)
Functionality:
- Extracts user metadata from auth.users
- Creates corresponding profile record
- Sets default role to ‘user’
- Preserves full_name from metadata
get_active_sso_configs()
Returns active SSO configurations (safe for public access). Returns:id: Configuration IDname: Configuration nameissuer_url: Zitadel issuer URLorganization_id: Owner organization ID
Migration Files
Migrations are stored insupabase/migrations/ and are applied in chronological order:
Common Operations
Create a New Migration
To create a new migration:supabase/migrations/.
Apply Migrations
Apply all pending migrations:Reset Database
Rollback Migration
To roll back the last migration:Dump Database Schema
Export the current schema:Dump All Data
Export all data:Initial Data Setup
Create Initial Organization
After running migrations, create your first organization:Create Admin User
Create an admin user (after they sign up through the UI):Configure Zitadel OIDC
Insert Zitadel configuration:Row-Level Security (RLS)
All tables use Row-Level Security to ensure data isolation:Multi-Tenant Architecture: Users can only access data within their own organization. Global admins can access all organizations.
Key RLS Patterns
-
Organization-based access:
-
Role-based access:
-
Self-access:
Performance Indexes
The schema includes indexes for optimal query performance:Database Configuration
The Supabase configuration is stored insupabase/config.toml:
Troubleshooting
Migration fails
Migration fails
If a migration fails:
- Check the error message for SQL syntax errors
- Verify all referenced tables exist
- Ensure foreign key constraints are satisfied
- Check that enums are created before use
- Review the migration file for issues
supabase db reset to start fresh.RLS policies blocking access
RLS policies blocking access
If users can’t access data due to RLS:
- Check that user has correct role:
SELECT * FROM profiles WHERE id = auth.uid() - Verify user is assigned to organization
- Review RLS policies on the table
- Check auth.uid() is returning correct value
- Test policy in SQL Editor:
SELECT * FROM table WHERE <policy-condition>
Foreign key violations
Foreign key violations
If you get foreign key errors:
- Ensure referenced records exist before inserting
- Check that UUIDs match exactly
- Verify cascade rules (ON DELETE CASCADE, etc.)
- Use transactions for multi-table operations
- Check that RLS isn’t blocking visibility of referenced rows
Connection issues
Connection issues
If you can’t connect to the database:
- Verify Supabase project is active
- Check database password is correct
- Ensure project ID matches your project
- Test connection:
supabase db ping - Check Supabase service status
Backup and Recovery
Automated Backups
Supabase automatically backs up your database:- Point-in-time recovery (PITR) available on Pro plan
- Daily backups retained for 7 days
- Access backups through Supabase dashboard
Manual Backups
Create manual backups:Restore from Backup
Next Steps
Environment Variables
Configure Supabase connection variables
Zitadel OIDC
Set up authentication with Zitadel
API Reference
Explore the database API
Self-Hosted Deployment
Complete deployment guide