Skip to content

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:

  1. First launch: Auth0 OIDC redirect (universal login)
  2. On success: refresh token stored in iOS Keychain / Android Keystore (hardware-backed where available)
  3. Subsequent launches: biometric prompt (Face ID / Touch ID / Android equivalent) unlocks refresh token
  4. 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_version for 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:

  1. 100+ internal operators in TestFlight beta for 4 weeks
  2. <1% crash rate in Sentry (Crash-Free Sessions ≥ 99%)
  3. Auth0 mobile tenant hardened (rate limits, anomaly detection)
  4. Backend /api/mobile/v1/* (ADR 0066) stable + load-tested
  5. App Store + Play Store review approvals received
  6. 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