SPRINT-7 T2-1 Report — Edge Functions Deploy + Auth Model¶
Date: 2026-08-30
Status: T2-1 PASS. STOP — await Ryan review before T2-2 (last1.app monolith).
VM: 120 last1app-db @ 192.168.1.103
Executive summary¶
| Workstream | Result |
|---|---|
| Edge Functions deployed | 22/22 on R940 Edge Runtime (container recreated) |
| Secret injection | 25 keys via Onset-style .env + docker-compose.yml patch (BWS + Railway + vault + last1.id DB) |
LAST1_ID_API_BASE |
https://auth.last1.id (sovereign; no nztgswcpnwggymncrnyx in edge env) |
| Internal HTTP smoke | 23/23 PASS (22 functions + cron-auth path) |
| pg_cron activation | 3/3 active → internal Kong 192.168.1.103:8000/functions/v1/* |
| Realtime | healthy (unchanged) |
| Hosted | untouched |
Scripts: t2-1-build-edge-secrets.py, t2-1-deploy-edge.sh, t2-1-smoke-edge.sh, t2-1-activate-crons.sh
Flag T2-3: Twilio (GG 29160801) + Stripe (GG 29615733) deployed with current Railway values — rotate at T2-3.
Flag smoke: sms-daily-check anon probe returned messages_sent: 2 (live Twilio credentials). Review whether 2 SMS left the account during smoke.
1. Deploy¶
Functions (22)¶
All RadioCheck supabase/functions/* synced from agent clone → /tmp/last1app-functions → volumes/functions/.
hello staging artifact removed. Count verified: 22.
Secret injection (Onset pattern)¶
t2-1-build-edge-secrets.py(agent or VM + BWS token) writes/tmp/last1app-ef-secrets.envpatch_env→/opt/supabase/docker/.env- Python patch →
docker-compose.ymlunderVERIFY_JWTanchor (25 env keys) docker compose up -d --force-recreate functions
Sovereign last1.id bridge verification¶
| Function | Target | R940 value | nztgswcp? |
|---|---|---|---|
| check-in-proof-events (outbound trust POST) | LAST1_ID_API_BASE |
https://auth.last1.id |
No |
| sync-last1 (outbound bundle/org webhooks) | LAST1_WEBHOOK_URL / LAST1_ORG_ACCESS_WEBHOOK_URL |
https://last1.app/api/webhooks/... (Railway monolith until T2-2) |
No |
| sync-user-from-last1 (inbound from last1.app) | HMAC LAST1_WEBHOOK_SECRET |
Railway shared secret | No |
| revoke-from-last1 (inbound from last1.id) | LAST1_INBOUND_WEBHOOK_SECRET |
From last1.id DB app_clients.webhook_secret_b64 (radiocheck) |
No |
Note: last1.id app_clients.webhook_url for radiocheck still points at hosted https://tcthzakppndkykatoefx.supabase.co/functions/v1/revoke-from-last1. Revoke deliveries remain on hosted until T-5 URL update on last1.id (same class as Twilio/Stripe dashboard cutover).
Note: BWS EXPO_PUBLIC_LAST1_ID_SUPABASE_URL in last1-radiocheck project still references deleted hosted ref — iOS/build T-5, not edge runtime.
Injected secrets (categories)¶
| Source | Keys |
|---|---|
Railway last1.app |
Twilio, Stripe, SendGrid, LAST1_WEBHOOK_SECRET |
| BWS Last1.id | RADIOCHECK_APP_SECRET, sovereign LAST1_ID_API_BASE |
| R940 vault | RADIOCHECK_CRON_SECRET |
| last1.id DB | LAST1_INBOUND_WEBHOOK_SECRET |
| Twilio API | TWILIO_MESSAGING_SERVICE_SID |
| Stripe API | STRIPE_PRICE_* (6 RadioCheck/VSO monthly prices) |
| Derived | Internal webhook URLs on 192.168.1.103:8000, last1.app webhook URLs |
Not injected (hosted-only today): APNs (APNS_*), FCM (FCM_*). silence-engine / trigger-check-ins return 500 Missing APNs credentials until those are added (push path).
2. Per-function auth model (T-5 public exposure prep)¶
Global gateway: FUNCTIONS_VERIFY_JWT=false (all functions). T-5 should add per-function gateway JWT (Onset T5-1 pattern) for user-facing endpoints before api.last1.app goes public.
| Function | Auth model | T-5 gateway recommendation |
|---|---|---|
| sms-webhook | Twilio X-Twilio-Signature |
Public, no JWT — signature only |
| stripe-webhook | Stripe stripe-signature |
Public, no JWT — signature only |
| sync-user-from-last1 | HMAC x-last1-signature + timestamp |
Public, no JWT — HMAC only |
| revoke-from-last1 | HMAC X-Last1-Signature (@last1id/sdk) |
Public, no JWT — HMAC only |
| check-in-proof-events | Bearer RADIOCHECK_CRON_SECRET (preferred) |
Internal/cron only at T-5; block public anon |
| sms-cron | pg_cron → no JWT | Internal (cron + LAN) |
| org-sms-cron | pg_cron → no JWT | Internal |
| sms-checkin-cron | pg_cron → sms-cron |
Internal |
| delete-account | Supabase user JWT in-function | Enable gateway JWT |
| quick-check-in | Supabase user JWT in-function | Enable gateway JWT |
| stripe-checkout | Supabase user JWT in-function | Enable gateway JWT |
| stripe-portal | Supabase user JWT in-function | Enable gateway JWT |
| unit-request-actions | HMAC signed email links + JWT for user routes | Mixed — keep webhook actions on HMAC; JWT for authenticated routes |
| last1-id-callback | Public GET (OAuth redirect HTML) | Public, no JWT |
| sync-last1 | Internal caller / service (outbound to last1.app) | Restrict at gateway if exposed |
| org-sms, sms-send, sms-checkin, sms-daily-check | Service-role / Twilio pipeline | Internal or signed; review before public |
| send-push | Service role today (no in-function JWT) | Enable gateway JWT if client-callable |
| trigger-check-ins, silence-engine | Internal scheduler/service | Keep internal |
| verify-org-email | Internal/admin trigger | Restrict |
3. Internal HTTP smoke (192.168.1.103:8000)¶
Log: /var/lib/supabase-data/gate-b/t2-1-edge-smoke.log
| Check | HTTP | Notes |
|---|---|---|
| sms-webhook (anon) | 403 | Missing Twilio signature ✓ |
| stripe-webhook (anon) | 400 | Missing stripe-signature ✓ |
| sync-user-from-last1 (anon) | 401 | Missing HMAC ✓ |
| revoke-from-last1 (anon) | 401 | invalid_signature ✓ |
| check-in-proof-events (anon) | 401 | Unauthorized ✓ |
| check-in-proof-events (cron bearer, dry_run) | 200 | Sovereign proof path ✓ |
| delete-account (anon) | 401 | ✓ |
| quick-check-in (anon) | 401 | ✓ |
| stripe-checkout / stripe-portal (anon) | 400 | Reachable; body validation before auth |
| All other functions POST (anon) | 200/400/500 | Reachable (not connection refused) |
| last1-id-callback GET | 200 | Public HTML ✓ |
Summary: pass=23, fail=0
4. pg_cron activation¶
After smoke PASS, jobs activated as supabase_admin (postgres lacks cron.job write):
| jobid | jobname | active | Target |
|---|---|---|---|
| 1 | sms-checkin-cron |
true | http://192.168.1.103:8000/functions/v1/sms-cron |
| 2 | org-sms-cron |
true | http://192.168.1.103:8000/functions/v1/org-sms-cron |
| 3 | check-in-proof-events-cron |
true | http://192.168.1.103:8000/functions/v1/check-in-proof-events (+ vault Bearer) |
Manual cron-auth fire: HTTP 200 dry_run on sovereign Edge.
T-5 reminder: sms-checkin-cron + org-sms-cron at 14:00 UTC = first live outbound SMS on sovereign infra — conscious activation at DNS cutover, not auto-flip. Until T-5, crons hit internal Kong only (no public api.last1.app).
Hosted pg_cron on tcthzakppndkykatoefx unchanged (rollback net).
5. Ryan review checklist (before T2-2)¶
- Accept 22-function deploy + 25 secret keys (Twilio/Stripe rotation deferred T2-3)
- Accept
LAST1_ID_API_BASE=https://auth.last1.idon Edge - Accept cron active on internal URLs (note daily SMS crons now scheduled on R940)
- Review
sms-daily-checksmoke sent 2 messages — unintended live SMS? - Plan APNs/FCM secret import before push-heavy paths matter
- Plan T-5 updates: last1.id
webhook_url→api.last1.app, gateway JWT hardening
Next: T2-2 last1.app monolith on VM 121 + cloudflared prep (no DNS).