SPRINT-7 T-0 Audit — last1.app + RadioCheck + Shared Supabase¶
Date: 2026-08-29
Scope: Read-only reconnaissance. No provisioning, no Supabase changes, no teardown.
Author: agent-01 (repo + docs audit; no live Supabase/Railway/BWS secret reads)
Ryan gate: Review this document before any T-1 provisioning.
Executive summary¶
Hosted Supabase project tcthzakppndkykatoefx (“Last 1 App + Radio Check”) is a three-way coupling:
| Consumer | How it uses the project |
|---|---|
| last1.app | Railway Node monolith; GoTrue JWT auth; direct Postgres (DATABASE_URL, session store); reads/writes org/funder tables |
| RadioCheck | iOS app (hardcoded hosted URL); 22 Edge Functions; pg_cron → Edge; Realtime; Storage |
| last1-admin (VM 115, paused) | BFF proxies same project for admin auth + RadioCheck dashboards (LAST1ADMIN_APP_PROD_*) |
No fourth runtime consumer of tcthzakppndkykatoefx was found in workspace grep (docs + muster/tracker.yaml only elsewhere). radiocheck-web uses a separate project (yloemokdrxfjkfwkskim) and is out of this bundle.
Schema is one public namespace, not separate Postgres schemas. auth.users is shared. last1.app and RadioCheck cannot be logically split on R940 without a prior DB split migration. SPRINT-7 must treat this as one cutover with an Onset-style iOS lag (backend/DNS first, App Store build second).
1. Shared-DB coupling map¶
1.1 Confirm: same project, not lookalikes¶
| Evidence | Finding |
|---|---|
| Supabase dashboard name | “Last 1 App + Radio Check” (last1.app/supabase/.temp/linked-project.json) |
| last1.app linked ref | tcthzakppndkykatoefx |
| RadioCheck iOS defaults | https://tcthzakppndkykatoefx.supabase.co (RadioCheck/lib/supabase.ts) |
| Admin BWS (validated 2026-08-29) | LAST1ADMIN_APP_PROD_SUPABASE_URL → same ref (ADMIN_BWS_DB_VALIDATION.md) |
| Separate project? | No for prod mobile/app/admin auth. yloemokdrxfjkfwkskim is radiocheck-web marketing/Stripe only. |
1.2 Three-way coupling (admin)¶
┌─────────────────────────────────────┐
│ Hosted Supabase tcthzakppndkykatoefx │
│ Postgres + GoTrue + Edge + Storage │
└──────────────┬──────────────────────┘
┌───────────────────────┼───────────────────────┐
│ │ │
last1.app (Railway) RadioCheck iOS + Edge last1-admin (VM 115 PAUSED)
Node monolith Functions + pg_cron LAST1ADMIN_APP_PROD_* URLs
SUPABASE_* + DATABASE EXPO_PUBLIC_* defaults service-role proxy reads
Admin does not own a DB VM; it holds service-role keys and HTTP URLs to this project. Even paused, BWS still encodes the hosted URL as intentional until bundle cutover. SPRINT-8 “reconnect” assumes upstream sovereign URLs exist first.
1.3 Other consumers (repo grep)¶
| Location | Reference |
|---|---|
lastvet-gates/docs/architecture/* |
Planning docs |
muster/tracker.yaml |
Sprint tracker |
last1.app, RadioCheck clones |
Runtime/config |
| last.vet-api, angel, onset-ios, etc. | None |
Blast radius: Any migration/teardown of tcthzakppndkykatoefx hits last1.app + RadioCheck + admin auth proxy simultaneously unless admin is repointed to sovereign stack first.
1.4 Schema separation within the shared DB¶
| Question | Answer |
|---|---|
| Separate Postgres schemas per product? | No. All migrations target public. |
| Migration ownership | 88 SQL migrations in RadioCheck/supabase/migrations; 0 checked-in migrations in last1.app/supabase/ (linked project only). Schema history lives in RadioCheck repo. |
| Table namespaces | Prefix convention, not schema isolation: last1_* (platform/org), RadioCheck core (users, units, check_ins, …), shared bridge tables |
last1.app–leaning tables (examples):
organizations,organization_members,user_profiles,beneficiariesirs_exempt_org,irs_status_changes, validation/audit tableslast1_organizations,last1_programs,last1_beneficiaries,last1_check_ins, … (20260129_1326_001_last1_integration.sql)last1_trust_event_outbox,last1_webhook_deliveries- Drizzle-defined tables in
last1.app/shared/schema.ts(orgs, programs, IRS, sessions)
RadioCheck–leaning tables (examples):
public.users(extendsauth.users), units, check-ins, SMS threads, outreach, streaksorganization_settings, org SMS usage, push tokens, widgets
Cross-app coupling points (read/write both sides):
| Coupling | Mechanism |
|---|---|
auth.users |
Single GoTrue store. last1.app web users, RadioCheck phone/email users, org admins share one auth table. |
user_profiles |
last1.app reads is_system_admin, identity fields; RadioCheck writes last1_identity_id, onboarding flags |
organizations |
RadioCheck org/VSO features + last1.app org validation/IRS |
sync-last1 / sync-user-from-last1 Edge Functions |
RadioCheck ↔ last1.app webhook/sync paths |
revoke-from-last1 Edge Function |
last1.id revocation → shared DB |
last1.app storage.ts |
Reads RadioCheck instances by phone (radiocheck_instances) |
| Trust event outbox | last1.app Node drainer → auth.last1.id; rows in shared DB |
Conclusion: Co-located on R940 as one last1app-db stack until an explicit split migration. Not separable at cutover without data model work.
1.5 auth.users: shared or disjoint?¶
SHARED. One GoTrue project. Evidence:
- RadioCheck
public.users.id→auth.users(id)FK - last1.app
customAuth.tsverifies Bearer JWT viasupabaseAdmin.auth.getUser(token)on same project - Apple review seeds query
auth.usersby email (seed_apple_review.sql) - Identity dedupe migrations reconcile
auth.usersacross phone/email (20260529060000_identity_dedupe_and_sync_reconcile.sql)
Populations overlap by design (org staff on last1.app may be veterans on RadioCheck; same UUID when linked).
2. last1.app — infrastructure¶
| Field | Finding |
|---|---|
| Repo | Last-1-Enterprises/last1.app |
| Frontend + backend | Single Railway Node monolith (Vite SPA + Express API). Not CF Pages/Vercel for app tier. |
| Railway | railway.toml: Dockerfile, port 5000, build args VITE_SUPABASE_*, Stripe, optional admin flags |
| Domains (documented Aug 2026) | Apex last1.app / www.last1.app → Railway (k1v3g8rr.up.railway.app per consolidation plan). admin.last1.app = separate Railway build (VITE_ADMIN_ONLY, VITE_API_BASE=https://last1.app) — distinct from admin.last1.enterprises (last1-admin product). |
| Supabase | tcthzakppndkykatoefx via SUPABASE_URL, pooler DATABASE_URL / SUPABASE_DATABASE_URL |
| Edge functions (shared project) | None in last1.app repo. All Edge Functions live in RadioCheck repo. last1.app uses Node routes + Supabase client/service-role. |
| pg_cron (shared project) | None attributable to last1.app in repo. Scheduled work: in-process (e.g. trust event drainer, IRS validation engine) on Railway Node. |
| Storage buckets (shared project) | No last1.app-specific bucket migrations found. App uses Supabase Storage API via service role where needed; RadioCheck owns bucket DDL (profile-photos, unit-chat-images). |
| Auth pattern | Supabase GoTrue (primary): email/password, Google OAuth (configured in Supabase dashboard per .env.example). Optional Last1 ID OAuth (LAST1_OAUTH_ENABLED, /api/auth/last1/*). Express sessions stored in sessions table via SUPABASE_DATABASE_URL. Not on auth.last1.id at steady state today. |
| Integrations | Stripe, SendGrid (noreply@last1.app), Twilio, Anthropic Claude API, last1.id API (https://last1.id / sovereign auth.last1.id for new identity work) |
3. last1.app — user reality & data sensitivity¶
| Topic | Assessment |
|---|---|
| User counts | Unknown without DB query (no Supabase dashboard access in this audit). |
| Real vs test | Platform targets real nonprofits/funders; no formal “pre-user only” gate like Onset clinic. Likely mix of production orgs + internal/test accounts. Not assessed live. |
| PHI? | No clinical PHI (not LastVet/Onset). Yes sensitive civilian PII: org staff emails, beneficiary contact fields (encrypted columns noted in schema comments), IRS/EIN validation data, grant/outcome metadata. |
| Compliance posture | Org verification, 42 CFR not in scope; treat as PII + financial (Stripe) data. |
T-1 requirement: Ryan or agent with read-only SQL should run user/org counts before cutover (mirror Onset T-0 sub-audit).
4. RadioCheck — infrastructure¶
| Field | Finding |
|---|---|
| Mobile repo | Last-1-Enterprises/RadioCheck (React Native / Expo + native iOS) |
| Bundle ID | com.last1.radiocheck (+ widget com.last1.radiocheck.widget) |
| App Store (repo) | Marketing 1.1.14, build 6. App Store URL in .env.example: The Radio Check |
| Backend | Hosted Supabase on tcthzakppndkykatoefx (Auth, PostgREST, Realtime, Edge, Storage). No Railway for mobile backend. |
| Web/marketing | radiocheck-web → separate Supabase yloemokdrxfjkfwkskim (Stripe checkout, form notify). Not in shared-db bundle. |
| Domains | App deep links (radiocheck://); Twilio/webhooks hit *.supabase.co/functions/v1/* |
| iOS backend pointer (production) | Hardcoded in RadioCheck/lib/supabase.ts: https://tcthzakppndkykatoefx.supabase.co + embedded anon JWT default. Override via EXPO_PUBLIC_SUPABASE_* at build time. |
| Auth pattern | Phone OTP on shared GoTrue (supabaseLast1 client, same URL). Also email/password, Google, Apple SIWA on same project. Last1 ID Connect: separate client supabaseLast1Identity — docs still reference deleted hosted nztgswcpnwggymncrnyx; runtime should use auth.last1.id publishable key (SPRINT-1 done). |
| Last1 ID callback Edge | last1-id-callback on shared project |
4.1 Edge Functions (shared project — RadioCheck repo)¶
22 deployed function names (from repo):
check-in-proof-events, delete-account, last1-id-callback, org-sms, org-sms-cron, quick-check-in, revoke-from-last1, send-push, silence-engine, sms-checkin, sms-cron, sms-daily-check, sms-send, sms-webhook, stripe-checkout, stripe-portal, stripe-webhook, sync-last1, sync-user-from-last1, trigger-check-ins, unit-request-actions, verify-org-email
4.2 pg_cron jobs (shared project)¶
| Job name | Target | Notes |
|---|---|---|
check-in-proof-events-cron |
check-in-proof-events |
Every ~5 min; Vault/RADIOCHECK_CRON_SECRET pattern |
sms-checkin-cron |
sms-cron |
Daily SMS check-ins |
org-sms-cron |
org-sms-cron |
Org SMS batch |
~~check-in-proof-outbox~~ |
— | Removed (broken app.settings.* pattern) |
All pg_cron attributable to RadioCheck, not last1.app.
4.3 Storage buckets (shared project)¶
| Bucket | Owner | Purpose |
|---|---|---|
profile-photos |
RadioCheck | Public profile images |
unit-chat-images |
RadioCheck | Unit thread chat media |
5. RadioCheck — user reality¶
| Topic | Assessment |
|---|---|
| User counts | Unknown (no DB access). App Store live ⇒ non-zero production users on 1.1.14 (6) unless pulled from sale. |
| Real vs test | seed_apple_review.sql: review@radiocheck.app, orgadmin@radiocheck.app, demo org siloed via RLS (20260421234500_silo_demo_units_for_apple_review.sql). Production users isolated from Apple demo units by policy, not separate DB. |
| Who’s on live build | All App Store users on current store version point at hosted tcthzakppndkykatoefx until new binary shipped. |
| Data sensitivity | Veteran check-in, unit/SMS content, org admin data. Not clinical EHR/PHI, but high-sensitivity veteran wellbeing data + phone numbers. |
6. GitGuardian / secrets (SPRINT-7 rotation scope)¶
Source: GITGUARDIAN_CATEGORY_D_REFINEMENT_2026-08-27.md
6.1 D-live — rotate during SPRINT-7 (before hosted teardown)¶
| GG ID | Service | Repos | Notes |
|---|---|---|---|
| 29160801 | Twilio (valid) | last1.app, last1.id .env |
Shared Twilio; used by RadioCheck SMS Edge + last1.app |
| 29615732 | Claude API (valid) | last1.app | Anthropic |
| 29615733 | Stripe (valid) | last1.app | Payments |
| 29615730 | Stripe webhook secret | last1.app | Rotate with Stripe endpoint |
| 29615727 | SendGrid | last1.app | GG marked invalid; verify dashboard anyway |
D-live? (identify then rotate): 29615725, 29615731, 29615734 (last1.app .env high-entropy)
6.2 Shared DB connection strings — rotate at sovereign cutover¶
| Class | GG IDs (defer label) | Action in SPRINT-7 |
|---|---|---|
| Supabase JWT (anon/service) | 29160796, 33887929, 33887930, D4 bundle | New keys on last1app-db; update BWS LAST1APP_DB_PROD_*, RADIOCHECK_*, Railway, Edge secrets, iOS build |
| Postgres pooler URLs | 29160797, 29615726, 29615728 | Replace with LAN/last1-api.* URLs in BWS; retire hosted pooler |
6.3 D4 RadioCheck — mostly bundle JWT/Postgres (B-defer-T6 → now SPRINT-7)¶
Five incidents in RadioCheck + shared .env — close after sovereign sign-off, not before rotation.
6.4 BWS keys (three consumers — naming per R940_NAMING_CONVENTION_v1.md)¶
| Consumer | Expected BWS pattern | Shared DB note |
|---|---|---|
| Stack owner | LAST1APP_DB_PROD_* (SUPABASE_URL, JWT_SECRET, DATABASE_URL, anon/service) |
Single source of truth for Postgres |
| last1.app app | LAST1APP_APP_PROD_* (Stripe, SendGrid, Twilio, Claude, session, VITE_*) |
Mirrors DB anon for builds |
| RadioCheck Edge/iOS | RADIOCHECK_APP_PROD_* / function secrets (RADIOCHECK_CRON_SECRET, Twilio, Stripe, SendGrid, APNs/FCM) |
Same DATABASE_URL owner as LAST1APP_DB_PROD_* |
| Admin (paused) | LAST1ADMIN_APP_PROD_SUPABASE_URL, *_RADIOCHECK_SUPABASE_URL |
Repoint to sovereign public URL in SPRINT-7 cutover or explicit decision to leave on hosted rollback until SPRINT-8 |
Additional secret inventory (RadioCheck .env.example, not GG): LAST1_WEBHOOK_SECRET, RADIOCHECK_APP_SECRET, UNIT_REQUEST_ACTION_SECRET, FCM/APNs keys, Stripe price IDs.
7. Proposed R940 VM topology¶
Follows R940_NAMING_CONVENTION_v1.md and R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md. LAN IPs proposed — next free after Onset .101/.102 (R940_LAN_NETWORK_INVENTORY.md).
| VMID (TBD) | Hostname | LAN IP (proposed) | Role |
|---|---|---|---|
| TBD | last1app-db |
192.168.1.103 | Self-hosted Supabase stack: Postgres, Kong :8000, GoTrue (interim), Edge Runtime, Storage (MinIO), pg_cron |
| TBD | last1app-app |
192.168.1.104 | last1.app Node monolith :5000 + last1app-edge cloudflared |
Not a separate radiocheck-app VM: RadioCheck backend is Edge + Auth on last1app-db. iOS is client-only.
Public DNS (proposed — Ryan confirm):
| Hostname | Target | Purpose |
|---|---|---|
last1.app |
last1app-edge → app :5000 |
Web monolith (move off Railway) |
last1-api.last1.app (or similar) |
last1app-edge → Kong on last1app-db |
Supabase API for browsers + RadioCheck iOS + Edge webhooks (Twilio, Stripe) |
Staging (naming convention): last1app-app.last1.enterprises, last1app-db.last1.enterprises
admin-app (VM 115): Stays paused through SPRINT-7 cutover. BWS URLs should still be updated to sovereign when hosted teardown is authorized so SPRINT-8 reconnect is flip-only.
radiocheck-web: Separate sprint/project (yloemokdrxfjkfwkskim); not blocking shared-db cutover.
8. Sequencing recommendation¶
8.1 Single shared-DB cutover (mandatory)¶
last1.app and RadioCheck must migrate in one database cutover (dump/restore or logical replication) because:
- One
auth.users - Cross-table FKs and sync Edge Functions
- Admin proxy expects one Supabase URL
Do not split last1.app DB to R940 while leaving RadioCheck on hosted (or vice versa) without a designed split migration.
8.2 iOS App Store clock (Onset pattern)¶
| Phase | Work | User impact |
|---|---|---|
| T-1 … T-4 | Provision last1app-db + last1app-app, restore DB, deploy Edge, smoke on staging |
None |
| T-5 backend | Public last1-api.* + DNS; last1.app Railway → R940; iOS still on hosted URL via old binary |
last1.app web on sovereign; mobile unchanged |
| T-5-iOS Step 1 | TestFlight build pointing at last1-api.*; Ryan journey |
TestFlight only |
| T-5-iOS Step 2–3 | Production xcconfig + App Store submit | Review clock ~1–7 days |
| T-5-2 | Hosted Supabase rollback net | Keep hosted until App Store live |
| T-6 | Teardown hosted tcthzakppndkykatoefx + Railway last1.app |
After Ryan sign-off |
RadioCheck requires clinic-first-style split: backend/public API first, iOS later. Shipping only a server cutover breaks App Store users until new binary is live.
8.3 Admin dependency — SPRINT-7 vs SPRINT-8¶
| Aspect | SPRINT-7 | SPRINT-8 |
|---|---|---|
| Hosted DB teardown | Blocked until bundle + iOS live | Final admin reconnect |
| Admin BWS URL update | Should happen at sovereign cutover (even if VM paused) | Resume compute, E2E |
| Admin compute move | Out of scope (already on VM 115, paused) | Reconnect + decommission hosted refs |
SPRINT-7 must account for admin in the shared DB URL sense. SPRINT-8 owns resuming admin and full multi-product upstream realignment.
8.4 Suggested gate order (within SPRINT-7)¶
- T-0 — This audit (Ryan sign-off)
- T-1 — Provision VMs, BWS
LAST1APP_DB_PROD_*, restore dump, apply 88 migrations - T-2 — Edge deploy + pg_cron + secret rotation (Twilio/Stripe/SendGrid/Claude)
- T-3 —
last1app-edge+ DNS staging smoke - T-4 — last1.app Railway cutover to R940
- T-5 — Public Supabase API hostname + last1.app production DNS
- T-5-iOS — TestFlight → App Store (Ryan Mac)
- T-6 — Hosted teardown + GG defer close
9. Open questions (Ryan decisions)¶
- Public Supabase hostname:
last1-api.last1.appvsapi.last1.appvs Kong on same apex? (Twilio/Stripe webhooks must be updated.) - GoTrue vs Last1 ID unification: Steady-state per R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md is
auth.last1.idonly. Does SPRINT-7 retire GoTrue on this stack or run interim dual auth through cutover? - RadioCheck
supabaseLast1Identity: Repoint from deletednztgswcpnwggymncrnyxdefaults toauth.last1.idkeys in same iOS release as API flip? - Hardcoded anon key in
RadioCheck/lib/supabase.ts: Remove/redact and rely on BWS-rendered xcconfig before App Store build? - User census: Approve read-only SQL on hosted project for org/user counts before T-1?
- admin BWS at cutover: Update
LAST1ADMIN_APP_PROD_*to sovereign URL while VM stays paused, or keep hosted as admin-only rollback until SPRINT-8? - radiocheck-web (
yloemokdrxfjkfwkskim): Migrate in SPRINT-7 or park? - Marketing split: last1.app monolith → CF Pages deferred, or bundled into SPRINT-7 scope?
- Real user communication: RadioCheck App Store users — in-app force-upgrade vs silent backend compatibility window?
- VMIDs / IPs: Confirm
.103/.104assignment onpve-r940.
10. Evidence gaps (read-only constraints)¶
| Gap | Impact |
|---|---|
| No Supabase MCP / dashboard | No live MAU, disk size, extension list, cron job_run_details |
| No Railway CLI | No live memory/replica/env verification |
| No BWS secret reads | URLs/keys inferred from docs + repo only |
| No production DB query | User counts marked unknown |
T-1 prep should close gaps 1, 3, 4 before restore/cutover.
11. References¶
- LAST1_INFRASTRUCTURE_CONSOLIDATION_PLAN.md
- ADMIN_BWS_DB_VALIDATION.md
- R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md
- R940_NAMING_CONVENTION_v1.md
- TIER_RESEQUENCING_2026-08-28.md
- GITGUARDIAN_CATEGORY_D_REFINEMENT_2026-08-27.md
- Repos (shallow clone 2026-08-29):
last1.app,RadioCheck,radiocheck-webunder/tmp/sprint7-audit/
STOP. No T-1 provisioning until Ryan reviews and resolves §9 open questions.