The mobile app (apps/mobile) is now a live remote for a box running the Ateam engine — it opens a WebSocket over Tailscale and drives the same engine the desktop does (commit 5811459). That makes it a remote-code-execution tool in your pocket: a stolen unlocked phone = a shell on your box.
The desktop doesn't have this threat (it lives behind your Mac's login); mobile does. So gate the app behind device biometrics.
What to build
- Add
expo-local-authentication and require a successful FaceID / Touch ID / passcode check before the app can connect or show a connected board.
- Re-prompt on foreground after the app has been backgrounded (configurable timeout), not just on cold start.
- Graceful fallback to device passcode when biometrics aren't enrolled/available.
Notes / constraints
- Expo SDK 52 (pinned for Xcode 16.2 per the mobile scaffold notes) — use the matching
expo-local-authentication version; it's a config-plugin native module, so it needs a prebuild (not Expo Go).
- Keep it a thin gate at the app root; don't entangle it with the connection layer (
src/connection.ts).
Related mobile hardening (out of scope here, tracking together)
Context: the WS listener is opt-in and tailnet-only (ATEAM_WS_ADDR, refuses wildcard binds); FaceID is the client-side half of the same threat model.
The mobile app (
apps/mobile) is now a live remote for a box running the Ateam engine — it opens a WebSocket over Tailscale and drives the same engine the desktop does (commit 5811459). That makes it a remote-code-execution tool in your pocket: a stolen unlocked phone = a shell on your box.The desktop doesn't have this threat (it lives behind your Mac's login); mobile does. So gate the app behind device biometrics.
What to build
expo-local-authenticationand require a successful FaceID / Touch ID / passcode check before the app can connect or show a connected board.Notes / constraints
expo-local-authenticationversion; it's a config-plugin native module, so it needs a prebuild (not Expo Go).src/connection.ts).Related mobile hardening (out of scope here, tracking together)
wss://with a MagicDNS Let's Encrypt cert (defense-in-depth on top of WireGuard).Context: the WS listener is opt-in and tailnet-only (
ATEAM_WS_ADDR, refuses wildcard binds); FaceID is the client-side half of the same threat model.