Database Schema

Supabase PostgreSQL — 10 tables, RLS-enforced

HostelHack uses Supabase (managed Postgres) with Row-Level Security on every table. The schema supports the full lifecycle: user profiles, email connectivity, hostel discovery cache, proposal management, and AI enrichments.

Entity Relationship Overview
┌─────────────┐       ┌─────────────────────────┐
│   users     │       │ connected_mail_accounts  │
│─────────────│  1:N  │─────────────────────────│
│ id (PK)     │◄──────│ user_id (FK)            │
│ clerk_id    │       │ provider                │
│ email       │       │ access_token            │
│ role        │       │ sync_cursor             │
└──────┬──────┘       └───────────┬─────────────┘
       │                          │ 1:N
       │ 1:N              ┌──────┴──────┐
       │                  │ mail_threads │
┌──────┴──────┐           │─────────────│
│  proposals  │           │ subject     │
│─────────────│           │ snippet     │
│ user_id     │           │ is_unread   │
│ hostel_name │           └──────┬──────┘
│ status      │                  │ 1:N
│ terms (JSON)│           ┌──────┴───────┐
└──────┬──────┘           │ mail_messages│
       │                  └──────┬───────┘
       │                         │ 1:N
  ┌────┴──────────┐    ┌────────┴─────────┐
  │mail_thread    │    │ mail_attachments  │
  │  _links       │    └──────────────────┘
  └───────────────┘
           ┌─────────────────┐  ┌──────────────┐
           │mail_ai_enrichm. │  │ search_cache  │
           └─────────────────┘  └──────────────┘

Table Schemas

Click any table to expand its complete column definition.

usersCore user profiles linked to Clerk auth14 cols
ColumnTypeNotes
iduuidPrimary key, default gen_random_uuid()
clerk_idtextUnique, Clerk external ID
emailtextUnique
full_nametext
display_nametext
headlinetextProfessional tagline
biotext
skillstext[]Array of skill names
avatar_urltext
roletextnomad | hostel | admin
locationtext
is_onboardedbooleandefault false
created_attimestamptz
updated_attimestamptz
connected_mail_accountsOAuth-linked email accounts for unified inbox14 cols
ColumnTypeNotes
iduuidPrimary key
user_iduuidFK → users.id
providertextgoogle | microsoft
email_addresstextAccount email
access_tokentextEncrypted OAuth token
refresh_tokentextEncrypted refresh token
token_expires_attimestamptz
sync_cursortextpageToken / deltaLink
history_idtextGmail history ID
subscription_idtextWebhook subscription ID
subscription_expires_attimestamptz
is_defaultbooleanDefault send account
last_synced_attimestamptz
created_attimestamptz
mail_threadsEmail conversation threads aggregated from messages14 cols
ColumnTypeNotes
iduuidPrimary key
account_iduuidFK → connected_mail_accounts.id
provider_thread_idtextGmail/Outlook thread ID
subjecttext
snippettextPreview text
from_addresstext
to_addressestext[]
is_unreadboolean
is_starredboolean
is_archivedboolean
label_idstext[]Gmail labels / Outlook categories
message_countinteger
last_message_attimestamptz
created_attimestamptz
mail_messagesIndividual email messages within threads13 cols
ColumnTypeNotes
iduuidPrimary key
thread_iduuidFK → mail_threads.id
account_iduuidFK → connected_mail_accounts.id
provider_message_idtext
from_addresstext
to_addressestext[]
cc_addressestext[]
subjecttext
body_texttext
body_htmltext
sent_attimestamptz
is_outboundboolean
created_attimestamptz
mail_attachmentsFile attachments linked to email messages8 cols
ColumnTypeNotes
iduuidPrimary key
message_iduuidFK → mail_messages.id
filenametext
mime_typetext
size_bytesinteger
provider_attachment_idtext
storage_pathtext
created_attimestamptz
mail_thread_linksLinks threads to proposals for deal tracking5 cols
ColumnTypeNotes
iduuidPrimary key
thread_iduuidFK → mail_threads.id
proposal_iduuidFK → proposals.id
link_typetextoutbound | inbound | auto
created_attimestamptz
mail_ai_enrichmentsAI-generated metadata for intelligent inbox features8 cols
ColumnTypeNotes
iduuidPrimary key
thread_iduuidFK → mail_threads.id
sentimenttextpositive | neutral | negative
intenttextinquiry | acceptance | rejection | negotiation
summarytextAI-generated thread summary
suggested_replytext
priority_scoreinteger0-100
created_attimestamptz
mail_sync_eventsAudit log for sync operations8 cols
ColumnTypeNotes
iduuidPrimary key
account_iduuidFK → connected_mail_accounts.id
event_typetextfull | delta | webhook
statustextstarted | completed | failed
messages_syncedinteger
error_messagetext
started_attimestamptz
completed_attimestamptz
proposalsSkill-exchange proposals between nomads and hostels13 cols
ColumnTypeNotes
iduuidPrimary key
user_idtextClerk user ID
hostel_nametext
hostel_idtext
statustextdraft | pending | accepted | rejected | negotiating
termsjsonbDuration, deliverables, accommodation
initial_messagetext
recipient_emailtext
email_sentboolean
email_sent_attimestamptz
email_message_idtextFor reply tracking
created_attimestamptz
updated_attimestamptz
search_cacheCached AI search results to reduce API calls7 cols
ColumnTypeNotes
iduuidPrimary key
query_hashtextSHA-256 of search params
locationtext
skillstext[]
resultsjsonbCached hostel results
expires_attimestamptzTTL for cache invalidation
created_attimestamptz

Row-Level Security

RLS is enabled on every table. Policies ensure users can only access their own data.

TablePolicyRule
usersUsers can read/update own rowauth.uid() = clerk_id
connected_mail_accountsUsers can CRUD own accountsuser_id = auth.uid()
mail_threadsUsers can read own threads via accountaccount_id IN (SELECT id FROM connected_mail_accounts WHERE user_id = auth.uid())
mail_messagesUsers can read own messagesthread_id IN (user's thread IDs)
proposalsUsers can CRUD own proposalsuser_id = auth.uid()
search_cacheRead-only for authenticated usersauth.role() = 'authenticated'

Migrations

11 migrations applied — all versioned in supabase/migrations/

20260325173000
20260325173000_unified_inbox.sql
Core inbox schema — accounts, threads, messages, attachments, thread links, AI enrichments, sync events
20260325180000
20260325180000_profile_photos.sql
Add avatar storage bucket + policies
20260326120000
20260326120000_profile_updates.sql
Additional profile fields — headline, bio, skills
20260326130000
20260326130000_storage_buckets.sql
Supabase storage bucket configuration
20260326140000
20260326140000_users_rls_fix.sql
Fix RLS policies for user table
20260326150000
20260326150000_users_missing_columns.sql
Add missing user columns (display_name, location, etc.)
20260326160000
20260326160000_oauth_tokens.sql
OAuth token storage columns for mail accounts
20260326170000
20260326170000_search_cache.sql
Search cache table with TTL
20260326180001
20260326180001_proposals.sql
Proposals table with JSONB terms
20260326190000
20260326190000_proposals_email_tracking.sql
Add email tracking columns to proposals
20260326200000
20260326200000_proposals_drop_fk.sql
Drop strict FK constraint for flexibility

Realtime Subscriptions

Inbox Threads

Subscribe to mail_threads INSERT/UPDATE events. Powers real-time unread counts and new thread notifications.

supabase
  .channel('inbox')
  .on('postgres_changes', {
    event: '*',
    schema: 'public',
    table: 'mail_threads',
    filter: `account_id=eq.${accountId}`
  }, handleChange)
  .subscribe()
Proposal Updates

Subscribe to proposals status changes. Instant notification when a hostel accepts/rejects.

supabase
  .channel('proposals')
  .on('postgres_changes', {
    event: 'UPDATE',
    schema: 'public',
    table: 'proposals',
    filter: `user_id=eq.${userId}`
  }, handleUpdate)
  .subscribe()
HostelHack Documentation · March 2026
Built with Next.js 15 · Gemini 2.0 · Supabase