pediatric-ai-scribe-v3/docs/mobile-build.md
Daniel cf0af53dd4 Security hardening: low-risk easy wins
- JWT_SECRET fails fast at startup in production
- CORS fails closed if APP_URL + CORS_ORIGINS are both missing
- Explicit HSTS (1y, includeSubDomains, preload)
- Rate limit sensitive auth endpoints (change-password, 2FA)
- /api/health now returns {ok:true}; details gated behind admin auth
- Login enumeration removed — generic 401 + dummy bcrypt on miss
- ReDoS guard: 20KB input cap on /suggest-codes
- showToast uses textContent, no innerHTML
- clearSession() clears service worker caches on logout
- OIDC state is now HMAC-signed and stateless (survives restart)
- SSRF guard on admin-set OIDC issuer (blocks private IPs, requires HTTPS)

Adds docs/mobile-build.md covering APK build, release, git push,
keystore, and troubleshooting for both PedScribe and PedsHub apps.
2026-04-14 02:42:32 +02:00

3.6 KiB

Mobile App Build & Release

Capacitor wrappers for PedScribe (this repo) and PedsHub Quiz (/home/danvics/docker/quiz). Both ship as Android APKs and iOS builds.

One-time setup

  • Keystore (reused for both apps):

    keytool -genkeypair -v -keystore ~/pedscribe-release.jks \
      -keyalg RSA -keysize 2048 -validity 10000 -alias pedscribe
    

    Store the password somewhere safe — losing it means rotating signing keys.

  • Android Studio path (required when you want to open the IDE):

    export CAPACITOR_ANDROID_STUDIO_PATH="/snap/android-studio/209/bin/studio.sh"
    

    Put it in your ~/.bashrc if you want it permanent.

Release build — PedScribe

cd /home/danvics/docker/ped-ai/mobile
npm install                                # picks up any new plugins
npx cap sync android                       # copies web assets + plugin glue
cd android
./gradlew assembleRelease \
  -Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
  -Pandroid.injected.signing.store.password=YOUR_KEYSTORE_PASSWORD \
  -Pandroid.injected.signing.key.alias=pedscribe \
  -Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
# APK lands at: android/app/build/outputs/apk/release/app-release.apk

Release build — PedsHub Quiz

cd /home/danvics/docker/quiz/mobile
npm install
npx cap sync android
cd android
./gradlew assembleRelease \
  -Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
  -Pandroid.injected.signing.store.password=YOUR_KEYSTORE_PASSWORD \
  -Pandroid.injected.signing.key.alias=pedscribe \
  -Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD

Publish APK on GitHub Releases

The login page links to github.com/<owner>/<repo>/releases/latest (see public/index.html around line 114). Publishing a tagged release updates the download link automatically — no site redeploy needed.

cd /home/danvics/docker/ped-ai
gh release create v6.0.1 \
  mobile/android/app/build/outputs/apk/release/app-release.apk \
  --title "PedScribe 6.0.1" \
  --notes "Hardware-backed secure storage for auth token on mobile."

For the quiz app:

cd /home/danvics/docker/quiz
gh release create v1.0.0 \
  mobile/android/app/build/outputs/apk/release/app-release.apk \
  --title "PedsHub 1.0.0" \
  --notes "Initial Android release."

Push source changes to git

Standard flow — the mobile project lives alongside the web app:

cd /home/danvics/docker/ped-ai
git add mobile/ public/ src/
git commit -m "Your message"
git push

Same for quiz at /home/danvics/docker/quiz.

iOS

iOS requires macOS + Xcode. On Linux the sync still runs but you cannot build the .ipa:

cd /home/danvics/docker/ped-ai/mobile
npx cap sync ios
# Then on a Mac: open ios/App/App.xcworkspace and Archive.

Reinstall on device after rebuild

adb install -r android/app/build/outputs/apk/release/app-release.apk

-r preserves app data (saved server URL, cached sessions).

Troubleshooting

  • npx cap can't find the project — you must cd mobile/ first, not run from the repo root.
  • Keystore was tampered with — wrong password. Do not generate a new keystore unless you are ready to rotate the signing identity on Play Store.
  • Microphone "denied" in the app — open system settings, long-press the app icon → App info → Permissions → Microphone → Allow. Web-side prompt does not always surface because the native layer intercepts it.
  • Foreground recording stops on newer Android — the service must declare foregroundServiceType="microphone" in AndroidManifest.xml.