Skip to main content

Overview

DualMind Lab uses PostgreSQL hosted on Supabase. All tables live in the public schema. The backend accesses data via Supabase’s PostgREST API (not an ORM).
9 tables in the public schema. All primary keys are UUID v4. The backend uses the Supabase service role key (bypasses RLS).

Entity relationship diagram

Table definitions

users

Stores all registered users. Synced from Supabase Auth via UserSyncService.EnsureUserExistsAsync().
ColumnTypeNullableDefaultDescription
user_iduuidNoPrimary key. Matches Supabase Auth id
full_nametextYesDisplay name
emailtextYesEmail address
roletextYes'user'Role: user, admin
created_attimestamptzYesnow()Account creation time
last_login_attimestamptzYesLast login timestamp

ai_models

Registry of all AI models available for comparison.
ColumnTypeNullableDefaultDescription
model_iduuidNogen_random_uuid()Primary key
model_nametextNoModel identifier (e.g., llama-3.3-70b-versatile)
provider_nametextYesProvider name (e.g., groq, bytez)
api_urltextYesAPI endpoint URL
descriptiontextYesHuman-readable display name
statustextYes'active'Status: active, inactive, deprecated
created_byuuidYesFK to users.user_id
created_attimestamptzYesnow()Creation time
updated_attimestamptzYesLast update time

threads

Conversation threads owned by users.
ColumnTypeNullableDefaultDescription
thread_iduuidNogen_random_uuid()Primary key
user_iduuidYesFK to users.user_id
titletextYesThread title
visibilitytextYes'private'private, public, unlisted
created_attimestamptzYesnow()Creation time

thread_messages

Individual messages within a thread. Supports both single-model and dual-model responses.
ColumnTypeNullableDefaultDescription
message_iduuidNogen_random_uuid()Primary key
thread_iduuidNoFK to threads.thread_id
prompt_texttextYesUser’s prompt
model1_iduuidYesFK to ai_models.model_id (or model name stored)
model2_iduuidYesFK to ai_models.model_id (null for single chat)
model1_responsetextYesResponse from model 1
model2_responsetextYesResponse from model 2 (null for single chat)
model1_time_msintegerYesResponse time for model 1
model2_time_msintegerYesResponse time for model 2
comparison_iduuidYesFK to comparisons.comparison_id
created_attimestamptzYesnow()Message timestamp

comparisons

Records of dual-chat arena comparisons.
ColumnTypeNullableDefaultDescription
comparison_iduuidNogen_random_uuid()Primary key
user_iduuidYesFK to users.user_id
prompt_texttextYesThe prompt sent to both models
model1_iduuidYesFK to ai_models.model_id
model2_iduuidYesFK to ai_models.model_id
model1_responsetextYesFull response from model 1
model2_responsetextYesFull response from model 2
model1_time_msintegerYesResponse time (ms) for model 1
model2_time_msintegerYesResponse time (ms) for model 2
created_attimestamptzYesnow()Comparison timestamp

model_votes

User votes on comparison outcomes.
ColumnTypeNullableDefaultDescription
vote_iduuidNogen_random_uuid()Primary key
user_iduuidYesFK to users.user_id
comparison_iduuidYesFK to comparisons.comparison_id
winner_model_iduuidYesFK to ai_models.model_id
created_attimestamptzYesnow()Vote timestamp

providers

AI provider registry (Groq, Bytez, etc.).
ColumnTypeNullableDefaultDescription
provider_nametextNoPrimary key (e.g., groq)
display_nametextYesHuman-readable name
is_enabledbooleanNotrueWhether provider is active
priorityintegerNo0Selection priority (higher = preferred)
created_attimestamptzYesnow()Creation time
updated_attimestamptzYesLast update time

provider_api_keys

API keys for each provider with rotation and health tracking.
ColumnTypeNullableDefaultDescription
key_iduuidNogen_random_uuid()Primary key
provider_nametextNoFK to providers.provider_name
api_keytextNoEncrypted API key
display_masktextYesMasked key for display (e.g., sk-...abc)
is_activebooleanNotrueWhether key is active
failure_countintegerNo0Consecutive failure count
total_callsintegerNo0Total API calls made with this key
last_used_attimestamptzYesLast successful use
last_error_typetextYesLast error classification
last_error_categorytextYesError category
cooldown_untiltimestamptzYesKey is in cooldown until this time
created_byuuidYesFK to users.user_id
created_attimestamptzYesnow()Creation time
updated_attimestamptzYesLast update time

system_settings

Key-value store for system configuration and feature flags.
ColumnTypeNullableDefaultDescription
keytextNoPrimary key (e.g., public_sharing)
valuetextYesSetting value (e.g., true)
created_attimestamptzYesnow()Creation time
updated_attimestamptzYesLast update time

Key relationships

  • threads.user_idusers.user_id (thread ownership)
  • thread_messages.thread_idthreads.thread_id (message belongs to thread)
  • comparisons.user_idusers.user_id (who created the comparison)
  • comparisons.model1_id / model2_idai_models.model_id
  • model_votes.comparison_idcomparisons.comparison_id
  • model_votes.winner_model_idai_models.model_id
  • provider_api_keys.provider_nameproviders.provider_name
The UserSyncService.EnsureUserExistsAsync() pattern exists because Supabase Auth creates users in auth.users but not in public.users. The backend must insert the public.users row before any operation that references user_id as a foreign key.