Mobile App — EAS Build & Deployment
The SAPOT mobile app (mobile-app/sapot-mobile-app/) is an Expo managed-workflow React Native app built with EAS Build (Android only).
Build profiles (eas.json)
| Profile | Distribution | Build type | APP_VARIANT | OTA channel |
|---|---|---|---|---|
development | internal | APK + dev client | development | development |
preview | internal | APK | preview | preview |
production | internal | APK (autoIncrement) | (unset) | production |
All profiles produce APK files distributed internally (sideloaded). There is no Play Store submission.
App variants
app.config.ts reads APP_VARIANT at build time:
| Variant | App name | Package ID |
|---|---|---|
development | SAPOT (Dev) | com.devamt.sapotmobileapp.dev |
preview | SAPOT (Preview) | com.devamt.sapotmobileapp.preview |
| (unset) | SAPOT: LAN Messenger | com.devamt.sapotmobileapp |
TLS CA pinning
The app pins a private CA (not the server's leaf certificate) at build time:
- Place the CA's PEM at
mobile-app/sapot-mobile-app/server_ca.pem. - The
withServerCaconfig plugin copies it intoandroid/app/src/main/res/raw/server_ca.pem. - The
withNetworkSecurityConfigplugin writesnetwork_security_config.xml:- Dev builds — cleartext permitted;
system+user+ bundled@raw/server_catrusted (so Metro and a locally-trusted dev cert both work). - Preview/Production builds —
cleartextTrafficPermitted="false", scoped to the domainserver.sapot.lan(includeSubdomains="false"), trusting only@raw/server_ca.
- Dev builds — cleartext permitted;
Because the pin is on the CA, the server can rotate its leaf certificate with no mobile rebuild;
only a CA rotation requires one. There is no hardcoded IP in the network-security config — the
pin is scoped to the hostname server.sapot.lan, so the server's LAN IP can change freely as long
as that name still resolves to it.
For EAS cloud builds, set the SERVER_CA environment variable (EAS secret) to the base64-encoded
CA PEM. app.config.ts decodes it to server_ca.pem at prebuild time, and the IS_REAL_EAS_BUILD
guard refuses a non-dev build if server_ca.pem is still the committed placeholder
(subject matching /placeholder/i) or has expired.
Android signing
The prebuild hook (hooks.prebuild) runs:
node ./scripts/setup-android-signing.js
This script configures android/app/build.gradle with signing credentials. For EAS builds, set signing credentials via EAS Secrets (not in source).
EAS Expo Updates (OTA)
The app uses Expo Updates for over-the-air JS bundle delivery:
- Expo project ID:
ee940ed5-5653-43cb-8938-d5f54a830c59 - Runtime version:
preview(all channels share the same native binary) - Update URL:
https://u.expo.dev/ee940ed5-5653-43cb-8938-d5f54a830c59
Channel routing:
| EAS build profile | OTA channel |
|---|---|
development | development |
preview | preview |
production | production |
OTA updates apply only to JS bundles. Any change that modifies native code (new native module, permission, config plugin) requires a full EAS build.
Required Android permissions
Declared in app.config.ts android.permissions:
ACCESS_NETWORK_STATE,ACCESS_WIFI_STATE,CHANGE_WIFI_MULTICAST_STATE— LAN peer discoveryFOREGROUND_SERVICE,FOREGROUND_SERVICE_DATA_SYNC,RECEIVE_BOOT_COMPLETED— background connectivityBLUETOOTH,BLUETOOTH_CONNECT,BLUETOOTH_ADMIN— audio routingWAKE_LOCK— keep connection aliveINTERNET— LAN sockets (despite "Internet" label, used for LAN TCP/WebSocket)CAMERA,RECORD_AUDIO— video/voice callsACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION— GPS featureVIBRATE,SYSTEM_ALERT_WINDOW— notifications and incoming calls
Local development build
cd mobile-app/sapot-mobile-app/
pnpm install
npx expo run:android
For EAS local build:
eas build --platform android --profile development --local
Build commands
# Preview APK (internal distribution)
eas build --platform android --profile preview
# Production APK
eas build --platform android --profile production
# Push an OTA update to a channel
eas update --channel preview --message "fix: crash on startup"
TODO (human input required): Document Sentry release tracking setup, signing keystore rotation procedure, and the exact EAS Secrets keys required for CI builds.