Skip to content

SPRINT-7 T5-D Step 1 — RadioCheck TestFlight pointer proof

Date: 2026-08-30
Status: POINTER PROOF PASS. Mac archive + TestFlight upload pending. STOP for Ryan journey.
Prerequisite: T5-C PASS (Option B confirmed), api.last1.app live.


Executive summary

Workstream Result
Env injection mechanism ios/.xcode.env.local (not EAS — bare React Native)
Sovereign pair source BWS LAST1APP_DB_PROD_SUPABASE_ANON_KEY + https://api.last1.app
Auth pointer trap fix lib/supabase.ts — direct process.env.* + auth reads env (was hardcoded hosted)
T5-B iOS RPC patches Present in audit clone (joinUnitByInvite, useUnit, auth)
Production bundle verify PASS — api.last1.app ×2, hosted ref 0, RPC strings present
Server preflight PASS — health 200, Apple/Google enabled on sovereign GoTrue
TestFlight IPA upload Pending Ryan Mac (agent-01 / Xcode)

1. How env is injected (NOT eas.json)

RadioCheck is bare React Native (react-native run-ios, Xcode archive). There is no eas.json in the repo.

Layer Mechanism
Build-time env file ios/.xcode.env.local (gitignored) — generated by script
Xcode bundle phase Sources .xcode.env + .xcode.env.local before Metro
Babel babel-plugin-transform-inline-environment-variables inlines listed EXPO_PUBLIC_* keys at bundle time
BWS LAST1APP_DB_PROD_SUPABASE_ANON_KEY from project last1-radiocheck

Script: scripts/last1app/t5-d-ios-render-xcode-env.sh /path/to/RadioCheck

Writes (all four required for auth + data):

export EXPO_PUBLIC_SUPABASE_URL=https://api.last1.app
export EXPO_PUBLIC_SUPABASE_ANON_KEY=<BWS sovereign anon>
export EXPO_PUBLIC_LAST1_SUPABASE_URL=https://api.last1.app
export EXPO_PUBLIC_LAST1_SUPABASE_ANON_KEY=<same sovereign anon>
export NODE_ENV=production

There is no separate “staging scheme” (unlike Onset). Step 1 uses Release archive with sovereign env in .xcode.env.local only. App Store users still on hosted until Step 2 production ship.


2. Hosted-fallback trap (found and fixed)

Trap A — data client only via env

EXPO_PUBLIC_SUPABASE_URL overrides data client, but without injection defaults to tcthzakppndkykatoefx.

Trap B — auth hardcoded (critical)

Before fix, supabaseLast1 (phone OTP / Apple SIWA) used DEFAULT_SUPABASE_URL only, ignoring env. A build with only EXPO_PUBLIC_SUPABASE_* set would still authenticate against hosted.

Trap C — babel did not inline via ENV alias

Code used const ENV = process.env then ENV.EXPO_PUBLIC_*. Babel only inlines direct process.env.EXPO_PUBLIC_* references. Runtime fell back to hosted defaults in the shipped bundle.

Fix (audit clone /tmp/sprint7-audit/RadioCheck)

  • lib/supabase.ts: auth uses process.env.EXPO_PUBLIC_LAST1_* || EXPO_PUBLIC_SUPABASE_* || defaults
  • All env reads use process.env directly for babel inline

Must land in RadioCheck repo before Mac archive.


3. T5-B coupled iOS patches (in build)

File Change
lib/joinUnitByInvite.ts join_unit_by_invite_code RPC
hooks/useUnit.ts join_unit_by_invite_code RPC
lib/auth.ts rc_user_exists_by_email, rc_call_sign_taken RPCs

Bundle verify: join_unit_by_invite_code ×2, rc_user_exists_by_email ×1.


4. Bundle pointer verification (Onset xcconfig equivalent)

Script: scripts/last1app/t5-d-ios-verify-bundle.sh

Log: /tmp/last1app-gate-b/t5d-bundle-verify-20260830T233332Z.log
Artifact: /tmp/radiocheck-t5d-main.jsbundle

Check Result
api.last1.app occurrences 2 (auth + data createClient)
tcthzakppndkykatoefx occurrences 0
Sovereign anon JWT in bundle iss=supabase (not hosted ref)
RPC patches Present

Extract from minified supabase.ts module:

u="https://api.last1.app".trim()
...
I="https://api.last1.app".trim()
createClient(u,o,...)  // supabaseLast1
createClient(I,f,...)  // supabaseRadioCheck

Verdict: PASS — pointer took; do not assume, proved in bundle.


5. Server preflight

Script: scripts/last1app/t5-d-ios-step1-preflight.sh
Log: /tmp/last1app-gate-b/t5d-ios-preflight-20260830T233332Z.log

Check Result
GET /auth/v1/health via CF 200 + cf-ray
GoTrue Apple enabled
GoTrue Google enabled
Realtime websocket probe 400 (expected without WS upgrade)

6. Mac: archive + TestFlight (Ryan)

Agent cannot run Xcode on Linux. On Ryan Mac with RadioCheck repo (include Step 1 commits):

cd /path/to/RadioCheck
bash /path/to/lastvet-gates/scripts/last1app/t5-d-ios-render-xcode-env.sh "$(pwd)"
bash /path/to/lastvet-gates/scripts/last1app/t5-d-ios-verify-bundle.sh "$(pwd)"
# Must print PASS before archive

cd ios && pod install && cd ..
open ios/RadioCheck.xcworkspace
# Scheme: RadioCheck | Configuration: Release
# Product → Archive → Distribute → TestFlight

Or: bash scripts/last1app/t5-d-step1-archive-upload.sh /path/to/RadioCheck (requires export options plist / Apple credentials on Mac).

Version today: 1.1.14 (build 6 in repo). Bump build number if App Store Connect rejects duplicate upload.


7. Ryan journey test (after TestFlight install)

Network: cellular or Wi‑Fi, Tailscale OFF.

# Step Pass criteria
1 Phone OTP login OTP via sovereign Twilio/GoTrue; session established
2 Own check-ins load User sees their rows (RLS), not empty/error
3 Apple SIWA Return URL hits sovereign callback (Ryan added Return URL)
4 Realtime Unit thread / check-in updates live without refresh
5 Sign out Clean logout; re-login works

Failure signals: requests to *.supabase.co, auth loop, empty data with valid user, SIWA 400 on wrong Return URL.

Report template:

T5-D Step 1 journey: PASS | FAIL
Device:
Network: cellular | wifi
Phone OTP: ok | fail
Apple SIWA: ok | fail
Check-ins load: ok | fail
Realtime: ok | fail
Sign out: ok | fail
Notes:

8. Constraints held

Constraint Status
Hosted Supabase rollback net Untouched
Sovereign SMS crons OFF (Option B until T-6)
Both Apple Return URLs Hosted + sovereign (until T-6)
Step 2 production archive Blocked until journey PASS

STOP

Pointer proof PASS. Run Mac archive → TestFlight → Ryan journey. Reply T5-D Step 1 journey PASS before Step 2 production archive.

Repo action: Commit/push audit-clone changes (lib/supabase.ts + RPC patches) to Last-1-Enterprises/RadioCheck before archiving.