Kwilo Learn app reference¶
Operational reference for the consumer Android app. For what the app does, see Kwilo Learn mobile app. The institutional app, Kwilo Campus, is apps/mobile-b2b (ai.kwilo.campus) in the webapp repo.
No secrets on this page. Token values live in the team secret manager, GitHub environments and EAS secrets.
Identifiers¶
| Item | Value |
|---|---|
| Source | MySetu-AI/kwiloai-b2c, apps/mobile |
| Display name | Kwilo Learn |
| Android package / iOS bundle id | ai.kwilo.learn (iOS declared, nothing ships) |
| EAS project | kwiloai/kwilo-b2c |
| Signing | EAS-managed Android key |
| Runtime version policy | fingerprint |
| Stack | React Native + Expo (new architecture), expo-router, NativeWind, @kwilo/ui-native, Zustand, TanStack Query |
| Staging API | The B2C staging Container App origin, called directly |
| Production API | https://b2c-api.kwilo.ai/api/v1, In build |
The app talks only to the consumer API. It never calls api.kwilo.ai or api-staging.kwilo.ai.
Environment variables¶
Expo inlines every EXPO_PUBLIC_* value into the JS bundle at build time. Defaults sit in apps/mobile/eas.json per profile; override locally in .env.local.
| Var | Purpose |
|---|---|
EXPO_PUBLIC_API_URL |
Consumer backend base URL |
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID |
Google OAuth Web client id, the only one the native SDK reads |
EXPO_PUBLIC_POSTHOG_KEY |
PostHog token. Production profile only; blank disables analytics |
EXPO_PUBLIC_POSTHOG_HOST |
https://t.kwilo.ai |
EXPO_PUBLIC_ENVIRONMENT |
The analytics environment super property |
These values ship in the binary
Anything EXPO_PUBLIC_* is extractable from a distributed APK or AAB. Never put a credential there. The staging build workflow fails if an Access token ends up in the bundle.
Google Sign-In¶
The native SDK authenticates against the Web OAuth client, which the backend trusts. An Android OAuth client (package + signing SHA-1) must still exist in vidyanet-staging so Google recognises the app.
Every keystore has its own SHA-1: the EAS upload key, Play App Signing, and each developer's debug keystore. A build whose SHA-1 isn't registered fails with DEVELOPER_ERROR.
eas credentials -p android # EAS keystore SHA-1
keytool -keystore ~/.android/debug.keystore -list -v \
-alias androiddebugkey -storepass android
Builds and releases¶
| Workflow | Does |
|---|---|
deploy-mobile-staging.yml |
Manual, from staging. Local EAS build of the preview APK against staging, checked for the right API origin and no embedded credentials, uploaded as a run artifact. |
mobile-release-label.yml |
Every PR touching mobile needs exactly one label: major, minor, patch or skip-release. |
mobile-play.yml |
On merge to main: computes the version from labels, tags learn-vX.Y.Z, builds an AAB and submits to Play internal testing. Gated on B2C_PLAY_RELEASE_ENABLED. |
Version code is major * 10000 + minor * 100 + patch. Promotion past internal testing is a manual step in Play Console.
Native builds run locally on the runner (eas build --local), so they don't spend the Expo cloud build quota and still use the EAS-managed keystore. Generating a fresh keystore instead breaks Google Sign-In.
Fingerprint rule¶
runtimeVersion.policy: "fingerprint". A JS-only change keeps the fingerprint, so an EAS Update reaches installed builds. A native change (new module, SDK bump, config plugin) changes it; an OTA would be invisible, so a new build is required.
Notifications¶
Local reminders and streak nudges need no credentials. Remote push needs FCM, which Expo's relay wants as a service-account key, and the org blocks key creation (constraints/iam.disableServiceAccountKeyCreation). Until that's solved, devices register a push token and nothing is delivered.
Troubleshooting¶
| Symptom | Cause / fix |
|---|---|
DEVELOPER_ERROR on Google Sign-In |
Signing SHA-1 not registered on the Android OAuth client |
| Merged JS change never arrives | Fingerprint changed; cut a native build |
Cold launch lands on /login instead of /welcome |
The 401 interceptor must reject the boot /auth/me probe, not redirect |
| Gesture handlers never fire | react-native-gesture-handler doesn't receive touches under the new architecture here. Use PanResponder + Animated |
| Animation at the router root doesn't run | Reanimated doesn't run at the expo-router root. Use core Animated |
CI build dies with kill ESRCH at expo doctor |
Race in the local-build plugin. Re-run; the staging workflow skips the doctor step |
Open risk: keystore in failed-build logs¶
When eas build --local fails, the plugin prints its job payload, which contains the signing keystore and its passwords in base64. Anyone who can read the Actions logs can sign an APK as Kwilo. Mitigations, cheapest first: filter the payload out of the log, restrict log access, rotate the keystore (breaks Sign-In until the new SHA-1 is registered and forces reinstalls).