Skip to content

Tier 4 T-1 Report — Onset Planning + BWS + VMIDs

Date: 2026-08-28
Status: T-1 COMPLETE. STOP here. Await Ryan authorization for T-2 (VM provision + Gate B + PHI smoke).
Parent audit: TIER_4_ONSET_MIGRATION_AUDIT.md
Execution plan: ONSET_MIGRATION_PLAN.md


Executive summary

T-1 confirms Option A (self-hosted Supabase full stack) on R940 is feasible for Onset, following the RealOutcomes VM 116/117 pattern at smaller scale (~20 MB DB, 24 Edge Functions, 3 pg_cron jobs).

User reality (final): Pre-user. Promo codes expired; no active external users. Only operator workflow (hello@unicornryda.com test). No cutover comms.

Auth (Ryan Q4): Clinic-first OIDC to auth.last1.id; GoTrue on VM 119 stays alive until iOS App Store ships. Expect a cross-stack window: clinic on R940, iOS App Store on hosted Supabase until TestFlight/App Store gates.

PHI (Ryan Q6): Migrate-as-is PATIENT_PII_ENCRYPTION_KEY with mandatory Gate B decrypt smoke test. No privacy-service validation during migration.

BWS: Use existing onset Bitwarden project (Ryan confirm UUID at T-2 kickoff) with new ONSET_APP_PROD_* keys; cross-project secrets on Last1.id + Last1.platform.

VMIDs (Ryan Q5): 118 onset-app @ 192.168.1.101, 119 onset-db @ 192.168.1.100.

Cutover window (Ryan Q2): Sun 03:00–07:00 PDT for clinic DNS + vault cron updates.

Pacing: Compressed soak (pre-user); gate discipline retained for PHI smoke, TestFlight, App Store, cross-stack management.


1. Final user inventory (Q1 closed)

Email / ID Classification Cutover comms
curryrg@gmail.com PRACTITIONER (Ryan) None
hello@unicornryda.com TEST (Ryan) — unicorn PC workflow None (operator)
Null Apple 474ef7c8-… PROMO EXPIRED — never engaged None
jrdelacruz.a@gmail.com PROMO EXPIRED — dormant one-time trial None
Apple relay d97ede01-… PROMO EXPIRED — never engaged None

ASC / RevenueCat: Promo codes expired; no active redemptions. Confirms pre-user posture.

TestFlight cohort: Operator only (Ryan) until external users exist post-launch.


2. VM planning (118 / 119)

Following RealOutcomes split: memory-heavy Supabase on 119, Next.js clinic + tunnel on 118.

VM VMID Hostname LAN IP Spec Role
Supabase 119 onset-db 192.168.1.100/24 4 vCPU · 16 GB RAM · 50 GB boot + 100 GB ZFS (tank-fast/onset-db) Supabase Docker Compose, Postgres 17, Kong :8000, pg_cron, pg_net, 24 Edge Functions, GoTrue (until App Store), Storage
App 118 onset-app 192.168.1.101/24 2 vCPU · 4 GB RAM · 50 GB boot Next.js clinic Docker :3000, onset-edge cloudflared, node_exporter

IP rationale: Next sequential after RealOutcomes .98/.99 and admin .97. Ryan confirm no collision on pve-r940 before T-2 provision.

Tailscale: Join both VMs; BWS DATABASE_URL uses LAN IP (portfolio convention).

Sizing notes:

Factor RealOutcomes 117 Onset 119
DB size ~1.3 GB / 2M IRS rows ~20 MB
Edge Functions 8 24 (3× count; Anthropic-heavy)
ZFS data 150 GB 100 GB (sufficient)
RAM 16 GB 16 GB (keep; EF runtime headroom)

Ingress: Per R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md — dedicated onset-edge on VM 118. Do not add Onset routes to lastvet-edge.

Optional public Supabase hostname: Required for TestFlight iOS hitting R940 before clinic cutover if not using LAN-only builds. Recommend onset-db.last1.enterprises CNAME via onset-edge → Kong :8000 (T-4b decision). Clinic server uses LAN http://192.168.1.100:8000.


3. Self-hosted Supabase feasibility (Option A)

Verdict: GO, with T-2 proof tasks

Capability Feasibility Notes
Edge Runtime (24 functions) Yes 20 from onset-ios + 4 from clinic.theonset.app; sync to volumes/functions/
Storage Yes Audit small object count; local filesystem backend
GoTrue Yes (phased retirement) Lift auth.users (8 rows) in Gate B; retain through App Store for iOS/TestFlight
pg_cron (3 jobs) Yes Restored from dump; vault HTTP to clinic portal
Postgres 17 Yes Hosted 17.6.1; canonical migrations in onset-ios/supabase/migrations/
Realtime N/A Disable in compose
RLS Medium review Mixed patient + clinic policies; clinic uses service role heavily; T-2 verify critical paths

T-2 must prove (gaps)

Gap Mitigation
24 EF vs 8 (RealOutcomes) Batch smoke script; prioritize Anthropic + practitioner-notifications + Last1 bridge functions
auth-send-email Hosted-only today; export hook config or redeploy from Supabase dashboard snapshot
Apple / Google EF secrets Export from hosted Supabase secrets vault at T-2
First Onset Docker ops Reuse RealOutcomes scripts as template; pin .supabase-version
PHI decrypt Mandatory smoke script (§7)
Option B fallback Port critical EFs to Next.js routes only if Deno deps fail (+5–8 days); do not trigger unless proof fails

4. Auth unification plan (clinic-first)

4.1 Steady-state target

Surface IdP When
Clinic portal https://auth.last1.id + @last1id/sdk T-3 (before clinic DNS cutover)
iOS App Store prod Product GoTrue (hosted → R940) T-5 (App Store release gate)
TestFlight GoTrue on R940 VM 119 T-4b

4.2 OIDC client (onset)

Live state: ONSET_APP_SECRET already on last1id-app (per RealOutcomes SPRINT-2 audit). OAuth client app_id: onset registered on auth.last1.id.

T-3 S2-A tasks (mirror RealOutcomes):

Item Target
client_id onset
redirect_uris https://clinic.theonset.app/auth/callback, LAN dev http://192.168.1.101:3000/auth/callback
scopes openid, email, profile, offline_access, identity:read, app_links:read, trust_events:write
BWS (Last1.id) LAST1_ID_R940_PROD_ONSET_APP_SECRET
BWS (onset) ONSET_APP_PROD_LAST1_OAUTH_*, ONSET_APP_PROD_LAST1_ID_ISSUER

4.3 Clinic code changes (T-3)

Repo Files / work
clinic.theonset.app Add @last1id/sdk, last1Auth.ts, OAuth callback route, replace Supabase password/Google login for practitioner flows
Server JWKS validation (jose) replacing supabase.auth.getUser() on protected routes
Google OAuth Moves to auth.last1.id (not product GoTrue)
MFA (TOTP) Revalidate owner MFA via Last1 ID credentials or defer forced MFA to post-migration

4.4 FK remap (Gate D)

32 FKs → auth.users.id. RealOutcomes lesson: id_token.sub must equal auth.users.id after remap.

T-3 remap scope (pre-user):

User Action
curryrg@gmail.com (practitioner) Map to Last1 identity UUID
hello@unicornryda.com (test) Map or recreate test practitioner row
Demo + promo expired Low risk; preserve or orphan

Practitioner–patient graph: Verify practitioners.user_id, shared_checkins.patient_app_user_id, encrypted practitioner_patients after remap.

4.5 Phased auth timeline

T-2: Gate B restores auth.users + GoTrue on VM 119
T-3: Clinic portal → auth.last1.id (GoTrue still serves iOS/TestFlight EF JWT paths)
T-4: Clinic DNS to R940 (clinic uses OIDC; iOS App Store still hosted Supabase)
T-4b: TestFlight → R940 Supabase + GoTrue
T-5: App Store → R940; retire GoTrue; optional full portfolio SSO for iOS via future sprint

4.6 Cross-stack window (critical)

After T-4, clinic and iOS App Store use different Postgres until App Store ships:

Writer Database Risk
Clinic (R940) VM 119 Active
iOS App Store (hosted) emxrmjys… Stale fork

Mitigation (accepted): No external users; operator uses TestFlight against R940 after T-4b; dormant promo accounts unaffected.


5. Edge Function replatform strategy (24 vs RealOutcomes 8)

5.1 Function inventory

From onset-ios (20 deployed names):

Function JWT Category T-2 smoke priority
daily-insight false AI/cron P2
admin-dashboard false Ops P3
generate-protocol true AI P1
client-event false Telemetry P2
scan-supplement-label, scan-supplement, search-supplement false AI/Anthropic P1
resolve-branded-product, popular-branded-products false Catalog P2
simulate-protocol-lab, scan-label, lab-intelligence, parse-lab mixed Lab/PHI-adjacent P1
apple-link, google-link, delete-account false Auth helpers P1
last1-link, last1-unlink, last1-connect-assertion false Last1/LastVet bridge P0
auth-send-email false GoTrue hook P1 (export)

From clinic.theonset.app (4):

Function JWT T-2 smoke
practitioner-notifications true P0 (PHI decrypt + SendGrid)
protocol-import, protocol-extract, fitness-extract false P1

5.2 Deployment approach

  1. Single compose on VM 119 — all 24 functions in volumes/functions/<name>/.
  2. _shared/ copied into each function dir or mounted per Supabase self-host pattern (match RealOutcomes T3-2).
  3. Secrets: unified .env.functions on VM 119 + BWS sync.
  4. Smoke tiers: P0 must PASS for Gate B sign-off; P1 before T-4; P2/P3 before T-5.
  5. No systemd timers for Onset EFs (unlike RealOutcomes) — patient AI functions are on-demand; clinic crons use pg_cron → clinic HTTP, not EF cron.

5.3 Anthropic / AI path

Functions share _shared/anthropic_models.ts, ai_sanitize.ts. T-2 verify ANTHROPIC_API_KEY on stack; run one label scan + one lab parse smoke with test payload (no prod PHI in logs).


6. pg_cron migration plan

6.1 Architecture

Onset uses pg_cron + pg_net + vault to POST to clinic portal routes (not Edge Functions):

pg_cron → trigger_clinic_*() → trigger_clinic_cron_route(path)
         → vault: clinic_portal_base_url + clinic_cron_secret
         → net.http_post → https://clinic…/api/cron/…

Migration source: onset-ios/supabase/migrations/20260624121000_fullscript_hourly_cron.sql

6.2 Jobs (prod, restored via Gate B)

jobname Schedule (UTC) Pacific Clinic route
clinic_protocol_deliveries 0 * * * * top of hour /api/cron/protocol-deliveries
clinic_fullscript_sync 15 * * * * :15 /api/cron/fullscript-sync
clinic_weekly_summary 0 13 * * 1 Mon 06:00 PDT /api/cron/weekly-summary

6.3 T-2 / T-4 checklist

Step When
Restore jobs via Gate B dump T-2
Verify vault.secrets present (clinic_cron_secret, clinic_portal_base_url) T-2
Point clinic_portal_base_url to R940 staging hostname; fire manual trigger; confirm clinic 200 T-2
Pause or accept misfire during T-3 clinic auth work if staging URL unstable T-3
Update vault to https://clinic.theonset.app + R940 CRON_SECRET T-4 (Sun window)
Confirm Monday weekly summary after first post-cutover week Ryan spot-check

T-4 alignment with cutover window: Complete vault update before Mon 06:00 PDT if cutover runs Sun 03:00–07:00 PDT.


7. Gate B rehearsal + PHI decrypt smoke test

7.1 Gate B (standard)

Step Acceptance
pg_dump hosted emxrmjysvxeboavhatry (PG 17 client) Archive to backups/onset-gate-b-YYYY-MM-DD/
pg_restore to VM 119 Non-fatal auth/storage errors documented (RealOutcomes pattern)
Row parity auth.users=8, practitioner_patients=5, product_events≈244, cron.job=3
Extensions pg_cron, pg_net, vault
Stack health All compose services healthy

7.2 PHI decrypt smoke (mandatory — Ryan Q6)

Step Acceptance
Copy exact PATIENT_PII_ENCRYPTION_KEY from Railway prod to BWS + R940 clinic env + EF secret Byte-identical to hosted
Run decrypt on all 5 practitioner_patients rows Plaintext email/name match expected operator/demo values
Verify patient_email_hash unchanged SHA-256 stable
Clinic API smoke on R940 (staging hostname) Practitioner patient list renders decrypted names
practitioner-notifications EF Decrypt path returns email for test patient row

Fail criteria: Any decrypt returns empty string → STOP T-2; do not proceed to T-3.

Script: scripts/onset/gate-b-phi-decrypt-smoke.sh (T-2 deliverable).


8. BWS project recommendation

Option Verdict
New Bitwarden project Not needed if onset project exists
Existing onset project Recommended — mirror RealOutcomes pattern
Store clinic keys in realoutcomes Reject — wrong product boundary
Store clinic keys in Last1.id Reject — only OIDC app secret cross-ref

Ryan T-2 kickoff action: Confirm onset BWS project UUID (admin references ONSET_SUPABASE_* keys today — likely same project or HQ; consolidate under onset + ONSET_APP_PROD_*).

Add alongside legacy keys (delete at T-5): full inventory in ONSET_MIGRATION_PLAN.md § BWS.

LastVet identity assertion coordination

Consumer Secret env BWS key
Onset EF last1-connect-assertion ONSET_IDENTITY_ASSERTION_SECRET ONSET_APP_PROD_ONSET_IDENTITY_ASSERTION_SECRET (= platform shared)
LastVet API verifier (existing) LAST1_PLATFORM_SHARED_ONSET_IDENTITY_ASSERTION_SECRET

T-3 gate: Rotate only if LastVet + Onset updated atomically; otherwise migrate unchanged like PHI key.


9. GitGuardian D-live map (onset-ios)

ID Detector Action at T-2/T-5
29340466 Supabase JWT prod xcconfig New R940 anon key in TestFlight/App Store; RESOLVE at T-5
34103515 Supabase JWT staging RESOLVE when staging hosted deleted T-5
34103516 Postgres docs Close INVALID after doc scrub
34103517 Postgres docs Close INVALID after doc scrub

Expand at T-2: Railway GraphQL env audit for clinic-theonset-app (prod + staging services); Supabase dashboard secrets export (Anthropic, Apple, Google, SendGrid EF).


10. Staging decision

Option Verdict
Migrate hosted staging to separate R940 VMs Defer — pre-user; not worth 2× infra
Keep hosted Onset-staging until T-5 Accept — iOS staging xcconfig + clinic staging Railway
Tear down staging with prod at T-5 Recommended

11. Compressed sprint gates (Ryan pacing)

Gate Required Soak compressed?
Gate B + PHI decrypt smoke Yes N/A
Clinic OIDC (T-3) Yes No multi-day soak; same-day Ryan login
Clinic DNS (T-4 Sun window) Yes No 48h soak
TestFlight (T-4b) Yes Operator same-day
App Store (T-5) Yes Wait for Apple review (external)
Hosted teardown (T-5) Yes Archive first

12. Ryan decisions captured (T-1)

# Decision Disposition
Q1 Pre-user inventory final Recorded §1
Q2 Sun 03:00–07:00 PDT cutover Locked
Q3 TestFlight → App Store In plan T-4b/T-5
Q4 Clinic-first auth; GoTrue until App Store §4 phased plan
Q5 VMIDs 118/119 §2
Q6 Migrate-as-is + Gate B PHI smoke §7
T-1 go Authorized 2026-08-28 This report

13. STOP — Next gate

Phase Status Ryan action
T-0 + sub-audits Done Approved
T-1 Planning Done Approve T-2 kickoff
T-2 VMs + Gate B + PHI smoke Blocked Authorize provision 118/119
T-3 Clinic OIDC + app deploy Blocked —
T-4 onset-edge + DNS Blocked Sun window
T-4b TestFlight Blocked —
T-5 App Store + teardown Blocked —

T-2 will: provision VMs 118/119, deploy Supabase Docker, Gate B dump/restore, PHI decrypt smoke PASS, populate BWS ONSET_APP_PROD_*, Edge Function P0 smoke — without clinic DNS cutover or hosted teardown.