Tier 4 Pre-Migration Audit — Onset (T-0 / SPRINT-6)¶
Status: T-0 complete. T-1 complete — STOP for Ryan T-2 authorization.
Date: 2026-08-28
Scope: Planning only (T-0). No provisioning, dump, deploy, DNS, or teardown executed.
Product: Onset — patient iOS app + B2B clinic portal + marketing web
Muster item: migrate-onset-clinic-supabase-to-r940
Parent sequencing: TIER_RESEQUENCING_2026-08-28.md · SPRINT-3_CLOSEOUT.md
Pattern reference: RealOutcomes Tier 3 / SPRINT-1–5 (TIER_3_REALOUTCOMES_MIGRATION_AUDIT.md)
Executive summary¶
Tier 4 is Onset full-stack sovereignty: move Railway clinic compute, hosted Supabase (emxrmjysvxeboavhatry prod + lzpyfohgfdohrtlamzek staging), 24 Edge Functions, pg_cron, Storage, and dependent Cloudflare Pages / iOS client config off vendor hosting onto R940.
Critical difference from RealOutcomes: Onset carries clinical / PHI-adjacent data (protocols, labs, practitioner–patient links, encrypted PII fields) and iOS + LastVet integration complexity, but user volume is pre-user (see §4.1 correction). Cutover still requires gate discipline, low-usage window scheduling, and methodical auth staging — not because of a large active user base, but because of PHI and multi-surface risk.
Scale (prod Supabase, 2026-08-28 MCP):
| Metric | Value |
|---|---|
| Database size | ~20 MB |
auth.users |
8 |
public tables |
55 |
Formal FKs → auth.users |
32 |
| Edge Functions (deployed) | 24 |
| pg_cron jobs | 3 (+ vault-driven HTTP to clinic portal) |
Practitioners (public.practitioners) |
2 |
Recommended calendar: 2–3 weeks wall-clock with explicit Ryan gates (vs RealOutcomes compressed sprint). Operator verification + any confirmed external accounts before production DNS cutover or hosted teardown.
Top pre-flight risks:
- Auth migration — product-local GoTrue today; steady state is
auth.last1.id+@last1id/sdk. 32 FKs toauth.users.idmust survive UUID remap (id_token.sublesson from RealOutcomes Gate D/F). - Multi-surface cutover — iOS App Store build,
onset-webCF Pages env,clinic.theonset.appRailway, pg_cron vault URLs, Stripe/Fullscript/RevenueCat webhooks must move in lockstep. - Pre-user but PHI-bearing — 5 non-demo
auth.usersrows (mostly dormant / operator); encrypted patient PII in DB; sessions invalidated on auth change unless carefully staged. - Edge Function replatform — 24 functions including Anthropic-backed scan/lab paths; secrets rotation in BWS at cutover.
- Staging project — separate hosted Supabase (
Onset-staging); decide prod-first vs dual migration in T-1.
1. Repo location and tech stack¶
Onset repos are not checked out under /home/ryan/code/last1/ today. Audit used hosted Supabase MCP, production HTTP probes, and mirrored snapshots in last1-internal-docs/.tmp-sync/.
| Repo (GitHub) | Role | Stack | Railway? |
|---|---|---|---|
| Last-1-Enterprises/onset-ios | Patient iOS app (App Store id6761314183) |
SwiftUI, SwiftData, HealthKit, RevenueCat | No |
| Last-1-Enterprises/onset-web | Marketing + Pages Functions | Cloudflare Pages, wrangler | No (stays on CF) |
| Last-1-Enterprises/clinic.theonset.app | B2B practitioner portal | Next.js 14, Docker, Stripe, Fullscript | Yes |
Internal docs mirror: last1-internal-docs/content/entities/onset/
Bundle ID: com.last1enterprises.Onset
1.1 Production domains (verified 2026-08-28)¶
| Domain | Purpose | Hosting | Notes |
|---|---|---|---|
https://clinic.theonset.app |
Clinic portal | Railway (x-railway-edge: lax1) |
Tier 4 cutover target for compute |
https://theonset.app |
Marketing, protocol pages, webhooks | Cloudflare Pages | Stays on CF; Supabase URL env must update at cutover |
https://develop.onset-web.pages.dev |
Staging marketing | Cloudflare Pages | Branch develop |
| Staging clinic | clinic-theonset-app-staging-staging.up.railway.app |
Railway staging | Branch develop |
Not in Tier 4 DNS scope: admin.last1.enterprises (Tier 6, paused).
2. Current Railway deployment¶
| Item | Finding |
|---|---|
| Project | disciplined-beauty (same org as legacy RealOutcomes/n8n) |
| Prod service | clinic-theonset-app (branch main) |
| Staging service | clinic-theonset-app-staging (branch develop) |
| Build | Dockerfile · railway.toml · node server.js (Next standalone) |
| Verified prod | HTTP 200 · x-railway-edge: lax1 · Next.js |
T-1 action: Railway GraphQL env audit (service IDs, env key inventory) — same pattern as RealOutcomes S3-8 archive. Do not delete Railway until T-5 after real-user E2E on R940.
onset-web: no Railway service. Marketing cron uses CF Pages Functions / worker (onset-marketing-dispatch-cron).
3. Hosted Supabase¶
3.1 Production — Onset (emxrmjysvxeboavhatry)¶
| Field | Value |
|---|---|
| Region | us-east-1 |
| Postgres | 17.6.1.084 |
| Status | ACTIVE_HEALTHY |
| Database size | ~20 MB |
| Public tables | 55 |
| Migrations (repo mirror) | 60+ files under onset-ios/supabase/migrations/ (canonical) |
Largest public tables (live row estimates):
| Table | ~Rows | Sensitivity |
|---|---|---|
product_events |
244 | Analytics |
provider_access_events |
216 | Audit / PHI-adjacent |
data_share_events |
25 | Consent / sharing |
shared_checkins |
23 | Patient data |
practitioner_consent_events |
14 | Consent |
onset_protocol_*, shared_lab_results, onset_lab_results |
5–10 each | Clinical |
3.2 Staging — Onset-staging (lzpyfohgfdohrtlamzek)¶
| Field | Value |
|---|---|
| Region | us-west-2 |
| Postgres | 17.6.1.127 |
auth.users |
5 |
| Role | Clinic staging + iOS staging xcconfig |
T-1 decision: Migrate prod stack first (RealOutcomes pattern); staging hosted project teardown can trail prod or run in parallel on separate R940 VMs.
3.3 Edge Functions (prod, MCP list_edge_functions)¶
24 active functions (combined iOS + clinic deploy surface):
| Function | verify_jwt |
Category |
|---|---|---|
daily-insight |
false | AI / cron |
admin-dashboard |
false | Ops |
generate-protocol |
true | AI |
client-event |
false | Telemetry |
scan-supplement-label, scan-supplement, search-supplement |
false | AI / Anthropic |
resolve-branded-product, popular-branded-products |
false | Catalog |
simulate-protocol-lab, scan-label, lab-intelligence, parse-lab |
true/false | Lab / PHI-adjacent |
apple-link, google-link, delete-account |
false | Auth helpers |
last1-link, last1-unlink, last1-connect-assertion |
false | Last1 ID bridge |
practitioner-notifications |
true | Clinic |
auth-send-email |
false | GoTrue hook |
protocol-import, protocol-extract, fitness-extract |
false | Clinic |
T-2 gate: Prove self-hosted Edge Runtime replatform (RealOutcomes T3-2 pattern) before Gate B dump restore sign-off.
3.4 pg_cron (prod)¶
| jobid | Schedule (UTC) | Command |
|---|---|---|
| 2 | Mon 13:00 | trigger_clinic_weekly_summary() → hardcoded https://clinic.theonset.app/api/cron/weekly-summary |
| 3 | hourly :00 |
trigger_clinic_protocol_deliveries() → vault clinic_portal_base_url + /api/cron/protocol-deliveries |
| 4 | hourly :15 |
trigger_clinic_fullscript_sync() → vault + /api/cron/fullscript-sync |
Vault secrets: clinic_cron_secret, clinic_portal_base_url — must update at clinic cutover.
4. Real user inventory¶
4.1 auth.users (production)¶
| Count | Category |
|---|---|
| 8 | Total |
| 3 | Demo seed (@demo.theonset.app, never signed in) |
| 5 | Non-demo (auth.users; not @demo.theonset.app) |
Auth providers (auth.identities): email 4 · apple 3 · google 1
Activity (production, 2026-08-28):
| Segment | Count | Notes |
|---|---|---|
| Operator / test (Ryan) | 2 | curryrg@gmail.com (practitioner), hello@unicornryda.com (test workflow) |
| Promo expired / never engaged | 3 | Null Apple, jrdelacruz.a@gmail.com, Apple relay |
| Demo accounts | 3 | Safe to exclude from cutover comms |
Practitioners (public.practitioners) |
2 | Clinic portal operators |
User reality (final, 2026-08-28): Pre-user. Promo codes expired (ASC). No cutover comms. See T-0 sub-audits and T-1 report.
4.2 Last1 ID linkage¶
Table public.patient_last1_identities: columns user_id, last1_identity_id, linked_at. Optional bridge for LastVet / Last1 ID — not primary IdP today.
OAuth client app_id: onset already registered on auth.last1.id (migration in last1.id repo). SPRINT-6 auth phase should extend RealOutcomes pattern: id_token.sub → auth.users.id for FK stability.
4.3 Real-user care flags (carry through all phases)¶
| Phase | User impact | Ryan gate |
|---|---|---|
| T-2 parallel R940 stack | None (hosted prod unchanged) | Gate B restore rehearsal |
| T-3 auth unification | Session invalidation if cutover mishandled | Per-gate browser + iOS test (operator + any confirmed external accounts) |
| T-4 DNS cutover | Production downtime if rollback fails | Sun 03:00–07:00 PDT (Ryan locked); notify any confirmed external users |
| T-5 teardown | Irreversible | E2E PASS + archive + confirmed user inventory |
5. FK dependency mapping (auth.users.id)¶
32 formal PostgreSQL FK constraints reference auth.users(id) (prod query 2026-08-28).
5.1 Auth schema (Supabase internal)¶
auth.identities, auth.sessions, auth.mfa_factors, auth.one_time_tokens, auth.oauth_*, auth.webauthn_* — all ON DELETE CASCADE where applicable.
5.2 Public schema — patient app data¶
| Table | Column | ON DELETE |
|---|---|---|
ai_insights |
user_id |
CASCADE |
apple_identities |
user_id |
CASCADE |
google_identities |
user_id |
CASCADE |
connection_code_attempts |
user_id |
CASCADE |
onset_protocol_items |
user_id |
CASCADE |
onset_protocol_completions |
user_id |
CASCADE |
onset_lab_results |
user_id |
CASCADE |
onset_user_preferences |
user_id |
CASCADE |
onset_performance_windows |
user_id |
CASCADE |
weekly_adherence_preferences |
user_id |
CASCADE |
protocol_generations |
user_id |
CASCADE |
product_events |
user_id |
CASCADE |
scan_logs |
user_id |
(default) |
security_events |
user_id |
SET NULL |
public_protocols |
creator_user_id |
SET NULL |
public_fitness_protocols |
creator_user_id |
SET NULL |
patient_last1_identities |
user_id |
CASCADE |
patient_shares |
patient_user_id |
(FK) |
5.3 Public schema — clinic / practitioner / shared care¶
| Table | Column(s) | Notes |
|---|---|---|
practitioners |
user_id |
Clinic staff login |
clinics |
portal_agreement_accepted_by, deleted_by |
Admin actions |
clinic_support_tickets |
submitted_by_user_id |
CASCADE |
shared_checkins |
patient_app_user_id |
CASCADE |
patient_aggregator_connections |
patient_app_user_id |
CASCADE |
data_share_events |
patient_app_user_id |
CASCADE |
Additional UUID columns (practitioner_id, patient_id, patient_app_user_id) link care graphs; many reference practitioners / patient records without direct FK to auth.users — remap plan must cover both direct FKs and RPC assumptions in T-1.
Migration lesson (RealOutcomes Gate D/F): After OIDC cutover, ensure id_token.sub equals auth.users.id (or run controlled FK remap SQL). Wrong UUID mapping breaks practitioner–patient links silently.
6. Auth architecture today¶
| Surface | Primary auth | Notes |
|---|---|---|
| iOS app | Sign in with Apple → Supabase GoTrue | Optional Last1 ID link (Last1IDLinkService); RevenueCat entitlements |
| Clinic portal | Email/password + Google → Supabase GoTrue | Owner MFA (TOTP); invite-only |
| onset-web | Public + webhook endpoints | Uses service role for admin/marketing APIs |
| auth.last1.id | OAuth client onset exists |
Not primary IdP today |
Target steady state (R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md): auth.last1.id + @last1id/sdk; retire product-local GoTrue on Supabase VM; Kong/PostgREST/Storage/Edge Runtime remain.
Clinic portal caveat: Google OAuth + MFA flows must be revalidated on sovereign stack (RealOutcomes retired GoTrue on VM 117; Onset clinic may still need email provider via auth-send-email edge function until fully on Last1 ID).
7. External SaaS dependencies¶
| Service | Used by | Cutover notes |
|---|---|---|
| Stripe | Clinic portal (Solo/Practice pricing, trials) | Webhook URL + live keys in BWS; rotate at T-2/T-5 |
| SendGrid | Clinic notifications, onset-web marketing | Domain theonset.app; RealSend batch track separate |
| Fullscript | Clinic dispensary integration | OAuth + webhooks + hourly pg_cron sync |
| Anthropic | Edge Functions (scan, lab, protocol AI) | ANTHROPIC_API_KEY in Supabase secrets |
| RevenueCat | iOS subscriptions + onset-web webhook | App Store billing; webhook on theonset.app |
| Apple Sign-In | iOS + clinic | Redirect URIs / Services ID updates at auth migration |
| Last1 ID | Optional patient link | OAuth client exists; expand to primary in T-3 |
| LastVet bridge | last1-connect-assertion |
BWS LAST1_PLATFORM_SHARED_ONSET_IDENTITY_ASSERTION_SECRET |
| Cloudflare | theonset.app, clinic DNS | onset-edge tunnel (new) for clinic cutover |
Not found in clinic .env.example: Twilio (no Onset-specific GG Twilio incidents in D-refinement).
Patient PII encryption: PATIENT_PII_ENCRYPTION_KEY or split PATIENT_PRIVACY_SERVICE_* — must migrate unchanged or re-encrypt under Ryan-approved plan (high risk).
8. GitGuardian D-live / Onset incidents¶
From GITGUARDIAN_CATEGORY_D_REFINEMENT_2026-08-27.md and generated audit:
| ID | Detector | Repo | Validity | Rec. action |
|---|---|---|---|---|
| 29340466 | Supabase JWT | onset-ios (Production.xcconfig) |
no_checker | B-defer-T6 → rotate at R940 cutover |
| 34103515 | Supabase JWT | onset-ios + last1-security | no_checker | Staging xcconfig — rotate at cutover |
| 34103516 | Postgres | onset-ios docs | invalid | Close after teardown / doc scrub |
| 34103517 | Postgres | onset-ios docs | invalid | Close after teardown / doc scrub |
T-1 action: Expand audit to Railway env and Supabase dashboard secrets (Stripe, SendGrid, Fullscript, Anthropic) — not fully enumerated in GG export. Plan D-live rotations into ONSET_APP_PROD_* BWS prefix at T-2.
No clinic-theonset.app or onset-web GG incidents in current generated audit (runtime secrets may still exist in Railway BWS).
9. Downstream integrations (unchanged until Tier 6)¶
| Consumer | Integration | Tier 4 impact |
|---|---|---|
| last1-admin | /api/admin/onset/* direct Supabase BFF |
Stays on Railway + hosted keys until Tier 6 |
| last1-admin | ONSET_SUPABASE_* in BWS |
Update deferred to Tier 6 reconnect |
| onset-web | Supabase anon + service role | Must update CF env at Supabase cutover |
| iOS app | Production.xcconfig Supabase URL/anon |
App Store release or remote config — Ryan decision in T-1 |
Constraint honored: Do not touch admin (paused), lastvet-edge, last1id-edge, or realoutcomes-edge.
10. R940 footprint (T-1 approved)¶
Following RealOutcomes VM 116/117 pattern (R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md):
| VM | VMID | Hostname | LAN IP | Role |
|---|---|---|---|---|
| App | 118 | onset-app |
192.168.1.101 | Next.js clinic Docker :3000, onset-edge cloudflared |
| Supabase | 119 | onset-db |
192.168.1.102 | Self-hosted stack, pg_cron, 24 Edge Functions, LAN Kong :8000 |
Detail: TIER_4_ONSET_T-1_REPORT.md §2.
11. SPRINT-6 phase map (Ryan-approved sequence)¶
| Sprint phase | RealOutcomes analog | Onset scope | STOP |
|---|---|---|---|
| T-0 | T3-0 audit | This document + sub-audits | Ryan review |
| T-1 | T3-1 planning | T-1 report + migration plan | Ryan T-2 go |
| T-2 | T3-2 + infra | VMs, Supabase deploy, Gate B dump/restore, BWS + D-live rotation | Per gate |
| T-3 | SPRINT-2 auth | @last1id/sdk, OIDC client, FK remap, GoTrue retirement |
Per gate + real users |
| T-4 | SPRINT-3 edge | onset-edge, DNS, browser E2E + iOS + clinic |
Ryan DNS + user window |
| T-5 | S3-8 teardown | Railway + hosted Supabase delete, GG closure, archive | After E2E PASS |
Pacing: Methodical gates (PHI + iOS + multi-surface). Pre-user operational volume, but same gate discipline as a live-user migration.
12. Ryan decisions and remaining inputs¶
Approved at T-0 (2026-08-28)¶
| # | Topic | Decision |
|---|---|---|
| Q3 | iOS delivery | TestFlight first; operator verify, then App Store |
| Q4 | Auth strategy | Clinic-first, then iOS; local GoTrue until App Store ships |
| Q5 | VMIDs | 118 onset-app, 119 onset-db |
Sub-audit + T-1 decisions (2026-08-28)¶
| # | Topic | Decision |
|---|---|---|
| Q1 | User inventory | Closed — pre-user; promo expired; no comms |
| Q2 | Cutover window | LOCKED: Sun 03:00–07:00 PDT |
| Q6 | PHI encryption | LOCKED: Migrate-as-is + Gate B decrypt smoke |
T-1 complete¶
Deliverables: TIER_4_ONSET_T-1_REPORT.md, ONSET_MIGRATION_PLAN.md
Staging: Prod-first; hosted Onset-staging teardown at T-5 with prod.
Awaiting T-2¶
- Confirm
onsetBWS project UUID at T-2 kickoff - Ryan authorize T-2 (VM 118/119 + Gate B + PHI smoke)
Explicit gates: T-2 → T-3 clinic OIDC → T-4 DNS (Sun window) → TestFlight → App Store → T-5 teardown.
13. Verdict (T-0 + T-1)¶
| Check | Result |
|---|---|
| Repo + stack documented | PASS |
| Railway + Supabase inventory | PASS |
| Auth.users inventory | PASS — pre-user final |
FK map to auth.users |
PASS (32 constraints) |
| Sub-audits Q1/Q2/Q6 | PASS |
| T-1 planning | PASS |
| Execution blocked | Yes — awaiting Ryan T-2 authorization |
STOP. Do not provision VMs or run pg_dump until Ryan authorizes T-2.
Related documents¶
- ONSET_MIGRATION_PLAN.md
- TIER_4_ONSET_T-1_REPORT.md
- TIER_4_ONSET_T-0_SUBAUDITS.md
- TIER_3_REALOUTCOMES_MIGRATION_AUDIT.md
- SPRINT-2_REALOUTCOMES_AUTH_EXECUTION_PLAN.md
- SPRINT-3_REALOUTCOMES_EDGE_EXECUTION_PLAN.md
- R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md
- GITGUARDIAN_CATEGORY_D_REFINEMENT_2026-08-27.md
- Clinic env matrix:
last1-internal-docs/content/entities/onset/clinic.theonset.app/docs/ENVIRONMENTS.md