Security

What we store. What we don’t. How it’s protected.

Beacon is built around one rule: no patient-identifying data ever lands in our database. Everything below either enforces that rule or makes the consequences of breaking it impossible.

This page describes the shipping system as of today. It will be updated in lockstep with every security-relevant change.

No PHI ever lands in Beacon's database

Patient-identifying columns are stripped from every report BEFORE parsing. Beacon stores only aggregates — appointment counts, production totals, KPI roll-ups. There is no row in our database that ties a number back to a specific patient.

  • Names, dates of birth, SSNs, chart numbers, contact info, addresses, insurance IDs, guardian/guarantor names — all stripped at the sanitizer layer (packages/adapters/sanitizer.ts).
  • What survives sanitation: aggregated counts, financial totals, KPI values, status codes, ADA procedure codes — none of which identify a patient.
  • The audit pass after every upload verifies no per-patient row was retained. Failure halts the upload.

Tenant isolation enforced at the database

Every table carries a tenant_id and is gated by Row-Level Security. There is no SQL path that returns one practice's data to another's user — even if application code has a bug, the database refuses.

  • RLS policies on metric_values, report_uploads, tasks, goals, integrations, tenants, memberships — every table.
  • Service-role bypass is restricted to a narrow set of server-side ingest paths, audited per write.
  • Cross-tenant tests in CI catch any policy regression before deploy.

Financials are owner-gated

Production-level numbers are visible to the whole team. Profit-and-loss / balance-sheet / AR aging data is gated to owners and any non-owner the owner has explicitly granted financial access to (typically an outside accountant or consultant).

  • The is_financial column on metric_values is enforced by RLS, not the UI.
  • Advisor seats default to read-only without financials.
  • Owners control financial access per-invite via Settings → Team.

Encrypted at rest and in transit

Beacon runs on Supabase + Vercel. Database storage, file storage, and backups are encrypted at rest. All traffic is TLS 1.2+. Integration tokens (QuickBooks, Google) are encrypted application-side before being stored — not just relying on Supabase's encryption at rest.

  • OAuth refresh tokens for QBO + Google encrypted with AES-256-GCM via apps/web/lib/integrations/crypto.ts.
  • Application-side encryption means a database leak (highly unlikely on Supabase, but planning for the worst) doesn't expose tokens.
  • Encryption key lives in env var, separate from the database, rotated on a documented schedule.

AI processing is bounded and bounded data flows through it

Beacon AI (the insight panel, the goal generator) runs on Claude via Anthropic's API. The data sent is the SAME aggregate metrics already in your dashboard — no PHI ever leaves our database, because no PHI is in our database. Anthropic does not train on API requests.

  • Anthropic's API contract: zero data retention beyond the request lifecycle, no training on requests (verified in Anthropic's terms).
  • Beacon AI only sees aggregate metric key/value pairs — same data the dashboard renders.
  • Beacon will sign a BAA with you for Pro-tier accounts, with Anthropic listed as a subprocessor under the same.

Backups and recovery

Supabase Postgres has point-in-time recovery enabled. We can restore the database to any point in the last 7 days. File storage (logos, uploaded CSV originals) has versioning enabled.

Subprocessors

Beacon uses the following subprocessors. A signed BAA with each is required before a practice goes live with patient-adjacent data, and is available on request as part of the security questionnaire.

  • Supabase — database + auth + file storage.
  • Vercel — application hosting.
  • Anthropic — AI processing (Claude).
  • Resend — transactional email (digests, invites).

Questions about a specific compliance requirement?

Send us your security questionnaire or BAA template and we’ll turn it around in 48 hours.