Database Schema

AllyProof uses Supabase (PostgreSQL) with Row Level Security (RLS) for multi-tenant data isolation. This page documents the core tables, their columns, and relationships.

Entity Relationship Overview

organizations
  ├── org_members (users ↔ organizations, many-to-many)
  ├── sites
  │     ├── scans
  │     │     ├── scan_pages
  │     │     └── violations
  │     │           └── ai_fix_suggestions
  │     ├── accessibility_statements
  │     └── vpat_reports
  ├── api_keys
  └── subscriptions

Core Tables

organizations

Top-level tenant. All data is scoped to an organization.

ColumnTypeDescription
iduuid (PK)Primary key
nametextOrganization display name
slugtext (unique)URL-safe identifier
scan_frequencytextdaily, weekly, or manual
plantextstarter, agency, or enterprise
contact_emailtextAccessibility contact email (used in statements)
created_attimestamptzCreation timestamp
updated_attimestamptzLast update timestamp

org_members

Join table linking users to organizations with roles.

ColumnTypeDescription
iduuid (PK)Primary key
org_iduuid (FK)References organizations.id
user_iduuid (FK)References auth.users.id
roletextowner, admin, or member
notify_scan_completebooleanEmail on scan completion
notify_criticalbooleanEmail on critical violations
created_attimestamptzJoin date

sites

Websites registered for scanning.

ColumnTypeDescription
iduuid (PK)Primary key
org_iduuid (FK)References organizations.id
nametextDisplay name
urltextBase URL
verification_methodtextdns or meta
verification_tokentextToken to verify ownership
verified_attimestamptzWhen ownership was verified (null if unverified)
is_pausedbooleanWhether scheduled scans are paused
latest_scoreintegerCached score from most recent scan
created_attimestamptzCreation timestamp

scans

Individual scan runs for a site.

ColumnTypeDescription
iduuid (PK)Primary key
site_iduuid (FK)References sites.id
statustextqueued, running, completed, failed
trigger_sourcetextmanual, scheduled, or api
scoreintegerCalculated accessibility score (0-100)
pages_scannedintegerNumber of pages scanned
violations_countintegerTotal violation count
duration_msintegerScan duration in milliseconds
started_attimestamptzWhen scanning began
completed_attimestamptzWhen scanning finished
created_attimestamptzWhen the scan was queued

scan_pages

Individual pages within a scan.

ColumnTypeDescription
iduuid (PK)Primary key
scan_iduuid (FK)References scans.id
urltextFull page URL
status_codeintegerHTTP response status
violations_countintegerViolations found on this page
scanned_attimestamptzWhen this page was scanned

violations

Individual accessibility violations found during scans.

ColumnTypeDescription
iduuid (PK)Primary key
scan_iduuid (FK)References scans.id
scan_page_iduuid (FK)References scan_pages.id
rule_idtextaxe-core or HTMLCS rule identifier
enginetextaxe-core, htmlcs, or apca
severitytextcritical, serious, moderate, minor
wcag_criteriatext[]Mapped WCAG success criteria (e.g. {1.1.1, 4.1.2})
html_snippettextAffected HTML element
selectortextCSS selector for the element
messagetextHuman-readable violation description
statustextopen, in_progress, resolved, false_positive
created_attimestamptzWhen the violation was found

ai_fix_suggestions

AI-generated fix suggestions for violations (via Claude Haiku).

ColumnTypeDescription
iduuid (PK)Primary key
violation_iduuid (FK)References violations.id
suggestiontextAI-generated fix suggestion (markdown)
fixed_htmltextSuggested corrected HTML snippet
confidencerealAI confidence score (0-1)
modeltextLLM model used (e.g. claude-haiku-4.5)
created_attimestamptzWhen the suggestion was generated

api_keys

API keys for CI/CD and programmatic access.

ColumnTypeDescription
iduuid (PK)Primary key
org_iduuid (FK)References organizations.id
nametextDescriptive key name
key_hashtextSHA-256 hash of the API key (key itself is not stored)
key_prefixtextFirst 8 characters for identification
last_used_attimestamptzLast API call timestamp
created_attimestamptzCreation timestamp

Row Level Security

Every table has RLS enabled. Policies enforce that users can only access data belonging to organizations they are a member of. The general pattern:

-- Example RLS policy on sites table
CREATE POLICY "Users can view sites in their org"
  ON sites FOR SELECT
  USING (
    org_id IN (
      SELECT org_id FROM org_members
      WHERE user_id = auth.uid()
    )
  );

Write policies additionally check the user's role — only owner and admin roles can create, update, or delete records. The member role has read-only access.

Migrations

Schema migrations are managed via Supabase CLI. Migration files are in supabase/migrations/ and are applied with:

npx supabase db push     # Apply pending migrations to remote
npx supabase db reset    # Reset local database and reapply all migrations