ADR 0057 — Mobile Companion App (Wave-8)¶
| Status | Date | Author | Supersedes | Superseded by |
|---|---|---|---|---|
| Accepted | 2026-05-16 | André Luiz Gallon | — | — |
Context¶
TLSStress.Art Wave-1..7 ships operator workflows via the Next.js admin
console (admin.tlsstress.art). Field operators visiting customer DCs,
oncall engineers stepping away from desks, and customer-success ops
need a first-class mobile experience for the most-frequent
workflows.
This ADR formalizes Wave-8 Mobile Companion App. Trigger gate: post-beta launch w/ 100+ operadores in production rotation. Below the gate, web admin console remains the only operator interface (Dashboard-only operator access invariant relaxed for Wave-8 specifically).
Architectural decision¶
10 LOCKED decisions.
D1: Framework = Expo + React Native 0.76¶
Expo Managed Workflow with React Native 0.76 (New Architecture default). Reasons:
- Single codebase iOS + Android (vs SwiftUI + Compose split)
- OTA updates via EAS Update (bypass App Store review for JS-only bugfixes — RFC 9773 cert renewal urgency analog)
- Expo SDK depth: SecureStore (Keychain/Keystore), Notifications (APNs+FCM), LocalAuthentication (biometric), Updates (EAS), Build (CI/CD)
- TypeScript strict as project default (matches admin-console)
- Mature ecosystem: 1M+ apps in production; Microsoft, Discord, Shopify use it
D2: UI library = Tamagui¶
Universal component library with:
- Compile-time atomic CSS (smaller bundle, faster render)
- Native + web parity (same components for mobile + admin-console React if shared)
- A11y first-class (screen reader, dynamic type, contrast)
- Theming engine matches TLSStress.Art brand tokens
Alternatives rejected: Native Base (slower), React Native Paper (Material Design lock-in), Restyle (manual styling burden).
D3: Auth = Auth0 + biometric + Hardware Security¶
Auth flow:
- First launch: Auth0 OIDC redirect (universal login)
- On success: refresh token stored in iOS Keychain / Android Keystore (hardware-backed where available)
- Subsequent launches: biometric prompt (Face ID / Touch ID / Android equivalent) unlocks refresh token
- No password storage in app
Same Auth0 tenant as admin-console. Mobile app gets dedicated client
config with mobile_app audience claim.
D4: Backend = NEW /api/mobile/v1/* namespace¶
Mobile app does NOT consume admin-console internal APIs directly. Reasons:
- Versioning isolation: mobile UX iterates faster than admin UI
- Mobile-specific payloads: smaller, denormalized, image thumbnailed
- Rate limits per device: prevents abuse from compromised devices
- Audit chain separation: mobile actions carry
device_fingerprint app_versionfor forensics
API contract formalized in ADR 0066.
D5: Push notifications = APNs + FCM via Expo Push¶
Expo Push Notifications service abstracts APNs (iOS) + FCM (Android):
- Single Push Token per device
- Server-side fan-out via Expo's push proxy OR direct APNs/FCM (operator choice)
- Token rotation on user logout
- Critical alerts use iOS Critical Notifications entitlement (bypass Do Not Disturb for security pages)
Categories:
- incident.page — high-severity alerts (Sigstore break-glass, region
outage, etc.)
- test.completion — test job finished
- approval.required — operator approval needed for cell migration etc.
- audit.notification — important audit chain event
D6: Offline mode = SQLite + sync engine¶
App functional WITHOUT network for last-known state:
- WatermelonDB (lightweight SQLite-backed) for reactive local store
- Background sync on reconnect (Last-Write-Wins for now; reconcile via server-canonical version on conflict)
- 24h offline window guaranteed; beyond → require sync before destructive actions
Inspired by pkg/octopus/crdt/ Wave-4 CRDT work — future PR-W8-12.1 may adopt CRDT semantics for offline-first sync.
D7: Operator workflows by phase¶
P1 (6-8 weeks, ship to internal beta first): - Login + biometric - Dashboard summary (overall fleet health, alerts) - Test management (list, detail, start/stop) - Push notifications (incident.page + test.completion) - Audit log viewer
P2 (3-4 weeks): - Operator approvals flow (cell migration, break-glass) - Fleet view (geographic map of cells) - In-app WhatsApp deep-link for customer-success - Offline mode + sync
P3 (4-6 weeks): - Apple Watch + Wear OS companion apps - iOS Live Activities (long-running test progress) - AR overlay for DC physical inventory (ARKit / ARCore) - iOS + Android Home Screen widgets
Total ~13-18 weeks. ~9k LoC mobile + ~3k LoC backend.
D8: Patent claim mapping (Family F)¶
Wave-8 establishes Patent Family F (Mobile Multi-Channel):
- Claim 24: Operator multi-factor authentication combining OIDC refresh token + biometric + hardware-attested keystore for security workflows
- Claim 25: Critical-incident push notification bypassing OS Do Not Disturb based on audit chain severity classification
- Claim 26: AR-overlay physical inventory verification with audit chain attestation
- Claim 27: Watch-companion break-glass approval flow with 4-eyes-by-default
Provisional filing target Q1 2027 (post-P3 + 6 months production data).
D9: Distribution¶
| Tier | Mechanism |
|---|---|
| Internal beta (50 ops) | TestFlight (iOS) + Internal App Sharing (Android) |
| Customer ops (post-beta) | Public App Store + Play Store |
| Defense/Air-gapped customers | Direct .ipa/.apk distribution via Cisco Crosswork (Wave-5 D10) |
Sigstore-equivalent signing on Expo build artifacts. EAS Update
channel production requires manual approval; beta ships
automatically on main merge.
D10: Cost + ops¶
Launch cost: - Apple Developer Program: $99/yr - Google Play Developer: $25 one-time - Auth0 Mobile add-on: ~$300/mo - Expo EAS: ~$200/mo (Production tier) - Sentry Mobile: ~$200/mo - Launch total: ~$700/mo + ~$5k one-time setup
Per-customer ops cost: ~$0 (push fanout amortized; no per-seat infra cost vs admin-console which scales linearly with operator count).
Codebase layout¶
apps/mobile/ # Expo + RN 0.76 + TypeScript strict
├── app.json # Expo config
├── eas.json # EAS Build profiles
├── package.json
├── tsconfig.json
├── src/
│ ├── App.tsx # root navigator
│ ├── api/ # /api/mobile/v1/* client
│ ├── auth/ # Auth0 + biometric flow
│ ├── screens/
│ │ ├── Login.tsx
│ │ ├── DashboardSummary.tsx
│ │ ├── TestList.tsx
│ │ ├── TestDetail.tsx
│ │ ├── AuditLog.tsx
│ │ ├── ApprovalsInbox.tsx
│ │ ├── FleetMap.tsx
│ │ └── Settings.tsx
│ ├── components/ # Tamagui-based reusables
│ ├── store/ # WatermelonDB models
│ ├── push/ # APNs/FCM handlers
│ ├── theme/ # brand tokens
│ └── utils/
└── e2e/ # Detox E2E tests
Trigger gates (BLOCKING)¶
Do NOT ship publicly until ALL met:
- 100+ internal operators in TestFlight beta for 4 weeks
- <1% crash rate in Sentry (Crash-Free Sessions ≥ 99%)
- Auth0 mobile tenant hardened (rate limits, anomaly detection)
- Backend
/api/mobile/v1/*(ADR 0066) stable + load-tested - App Store + Play Store review approvals received
- Defense/Air-gapped distribution validated via Cisco channel (if Wave-5 customers in pipeline)
Below the gates, mobile app is internal-only (TestFlight) and operators continue using admin-console as primary.
Wave-8 PR roadmap¶
| PR | Scope | Estimated LoC | Phase |
|---|---|---|---|
| PR-W8-1 | ADR 0057 mobile app umbrella (this) | docs | — |
| PR-W8-2 | ADR 0066 /api/mobile/v1/* contract | docs | — |
| PR-W8-3 | apps/mobile/ scaffold (Expo + RN 0.76 + Tamagui) | ~600 | P1 |
| PR-W8-4 | Reviewer checklist + ops planning doc | docs | — |
| PR-W8-5 | Auth flow (Auth0 + biometric + SecureStore) | ~800 | P1 |
| PR-W8-6 | Backend /api/mobile/v1/auth + session refresh | ~500 | P1 |
| PR-W8-7 | Dashboard summary screen + API client | ~700 | P1 |
| PR-W8-8 | Test management screens (list + detail) | ~900 | P1 |
| PR-W8-9 | APNs/FCM push registration + handlers | ~600 | P1 |
| PR-W8-10 | Audit log viewer | ~400 | P1 |
| PR-W8-11 | Operator approvals flow | ~700 | P2 |
| PR-W8-12 | Fleet view (geographic map) | ~600 | P2 |
| PR-W8-13 | Offline mode + WatermelonDB sync | ~1000 | P2 |
| PR-W8-14 | Watch companion (Apple Watch + Wear OS) | ~1200 | P3 |
| PR-W8-15 | AR overlay + Home Screen widgets | ~800 | P3 |
| PR-W8-16 | E2E Detox + observability + runbook | ~800 | — |
| Total | ~9k LoC + ~3k backend |
Cross-references¶
- ADR 0055 — admin console (web sibling)
- ADR 0054 — PQC mandate (preserved in mobile TLS pin)
- ADR 0058 — dual-stack (mobile uses Happy Eyeballs v2)
- ADR 0066 — mobile API contract (sibling PR-W8-2)
- Memo: Wave-8 planning
- Expo docs: https://docs.expo.dev/
- Tamagui docs: https://tamagui.dev/
- Auth0 React Native quickstart
- WatermelonDB docs