> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cadmus-cad.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Store listings, Firebase, and secrets

> What to fill in after Play Console and App Store Connect approve the MDT app, and which placeholders and environment variables are still empty.

This page is for Cadmus operators finishing **Android and iPhone** production. Field officers do not need it. Use [Notification health](/mobile/notification-health) on the phone to see whether a build already has push config.

Nothing below belongs in git: keystores, Apple certificates, Firebase Admin JSON, `google-services.json`, or `GoogleService-Info.plist`.

The installed app ID is already **`app.cadmus.mdt`** on Android and iPhone. Older `com.example.*` installs are a different app. Officers must install the new ID.

## What you can wait on

Play and App Store **approval** is not required to keep coding. The app already uses placeholders for store listing numbers. Fill those numbers when the consoles assign them.

You also do **not** need Clerk webhook endpoints on dev, staging, or production. Cadmus logout and Cadmus admin session revoke still work. A Clerk-Dashboard-only kill waits until the token expires or the next exchange.

## After Google Play approves the listing

1. Open **Play Console** → the Cadmus MDT app.
2. Confirm the package name is `app.cadmus.mdt`. If Play forced a different package, tell engineering before any store upload. The installed ID and the Play package must match.
3. Copy the numeric **App ID** (the number in the Play Console URL / app settings).
4. Replace `PLACEHOLDER_PLAY_CONSOLE_APP_ID` in `native/identity.properties` with that number. Leave `PLAY_CONSOLE_PACKAGE_NAME=app.cadmus.mdt` unless the package changed.
5. Create the Firebase **Android** app with that same package name.
6. Download `google-services.json` onto the **build machine only**. Put it at `android/app/google-services.json`. Do not commit it.
7. Create a real Play **upload keystore** if you do not have one. Do not use the Android debug keystore. Signed release jobs fail on purpose until the GitHub keystore secrets exist.

## After App Store Connect approves the listing

1. Open **App Store Connect** → the Cadmus MDT app.
2. Confirm the bundle ID is `app.cadmus.mdt`.
3. Copy:
   * Apple **Team ID** (10 characters, Developer membership)
   * App Store Connect **team / issuer** id
   * Numeric **Apple ID** for the app listing
4. Replace these placeholders in `native/identity.properties`:
   * `PLACEHOLDER_APPLE_TEAM_ID`
   * `PLACEHOLDER_APPLE_ITC_TEAM_ID`
   * `PLACEHOLDER_APPLE_APP_STORE_ID`
5. Turn on **Push Notifications** for that App ID.
6. Create provisioning profiles (development for staging, distribution for TestFlight / App Store).
7. Create an APNs key. Production APNs is required before you treat iPhone background push as done.
8. Create the Firebase **iOS** app with bundle `app.cadmus.mdt`.
9. Download `GoogleService-Info.plist` onto the **Mac build machine only**. Put it at `ios/Runner/GoogleService-Info.plist`. Do not commit it.
10. Put the Apple team ID and signing files in GitHub secrets (table below). The signed iOS job will not run until they exist.

<Warning>
  The committed iPhone push entitlement is **development**. A store or TestFlight build must use a profile with **production** APNs. Android and iPhone Firebase apps should be separate per environment (dev, staging, production).
</Warning>

## Placeholders still in the mobile repo

These live in `native/identity.properties`.

| Name | Today | Replace when |
| - | - | - |
| `ANDROID_APPLICATION_ID` | `app.cadmus.mdt` | Play rejects that package (rare) |
| `IOS_BUNDLE_ID` | `app.cadmus.mdt` | Apple rejects that bundle (rare) |
| `IOS_BUNDLE_ID_TESTS` | `app.cadmus.mdt.RunnerTests` | Bundle ID changes |
| `PLAY_CONSOLE_PACKAGE_NAME` | `app.cadmus.mdt` | Must stay equal to the Android application ID |
| `PLAY_CONSOLE_APP_ID` | `PLACEHOLDER_PLAY_CONSOLE_APP_ID` | Play listing exists |
| `APPLE_TEAM_ID` | `PLACEHOLDER_APPLE_TEAM_ID` | Apple team is known |
| `APPLE_ITC_TEAM_ID` | `PLACEHOLDER_APPLE_ITC_TEAM_ID` | App Store Connect team is known |
| `APPLE_APP_STORE_ID` | `PLACEHOLDER_APPLE_APP_STORE_ID` | App Store listing exists |

The Android Kotlin namespace stays `com.example.cadmus_mdt_frontend`. That is the code package, not the Play package. Do not rename it for store approval.

## GitHub secrets to set on `cad-mobile`

Repo: **AutonomyToday/cad-mobile** → Settings → Secrets.

### Android signing (empty today — jobs fail closed)

| Secret | What to paste |
| - | - |
| `ANDROID_KEYSTORE_BASE64` | Upload `.jks` / `.keystore` encoded as base64 |
| `ANDROID_KEYSTORE_PASSWORD` | Keystore password |
| `ANDROID_KEY_ALIAS` | Key alias |
| `ANDROID_KEY_PASSWORD` | Key password |

On a local machine you can use `android/key.properties` instead. Same values, still not in git.

### Apple signing (empty today — iOS signed job will not start)

| Secret | What to paste |
| - | - |
| `APPLE_TEAM_ID` | Same 10-character team you put in `identity.properties` |
| `IOS_SIGNING_CERTIFICATE_P12_BASE64` | Signing `.p12` encoded as base64 |
| `IOS_SIGNING_CERTIFICATE_PASSWORD` | Password for that `.p12` |
| `IOS_PROVISIONING_PROFILE_BASE64` | `.mobileprovision` encoded as base64 |

### Already in use (do not recreate)

| Secret | Used for |
| - | - |
| `CLERK_PUBLISHABLE_KEY_PROD` | Signed app login |
| Cloudflare R2 + update signing keys | In-app updates |
| SSL.com eSigner secrets | **Windows** signing only, not Android/iPhone |

## Azure / API environment variables

The phone registers with Cadmus over `PUT /push-devices`. The **server** sends FCM later. When you turn send on, set these on Azure **dev**, **staging**, and **production** separately:

| Variable | Where | What to put |
| - | - | - |
| `PUSH_NOTIFICATIONS_ENABLED` | Container App setting | Leave `false` until send is approved, then `true` |
| `FIREBASE_SERVICE_ACCOUNT_JSON` | Azure secret named `firebase-sa`, referenced as `secretref:firebase-sa` | Firebase **Admin** service account JSON (server key). Not the phone config file |

That Admin JSON is the Firebase credential shared earlier. Keep it on the API only. If it was pasted into chat, rotate it in Google Cloud and update the Azure secret.

### Already required (not store-listing work)

| Variable | Why it matters |
| - | - |
| `APP_TOKEN_SECRET` | Signs Cadmus app tokens |
| Redis (`REDIS_ENABLED` and the Redis host/password/TLS settings) | Session revoke fails closed without Redis on staging/prod |
| `CLERK_SECRET_KEY` / `CLERK_PUBLISHABLE_KEY` | Login exchange |
| `CLERK_WEBHOOK_SIGNING_SECRET` | Only if you later add Clerk webhooks. **Skip for now** |

Leave the operational outbox worker off.

## Build flags (not Azure env)

Signed Android/iOS jobs pass Flutter dart-defines:

| Flag | Typical value | Meaning |
| - | - | - |
| `BUILD_MODE` | `production` | Production client behavior |
| `UPDATE_CHANNEL` | `prod` | Update feed |
| `RELEASE_STAGE` | `staging` or `beta` | Which ring the build is |
| `CLERK_PUBLISHABLE_KEY` | From GitHub `CLERK_PUBLISHABLE_KEY_PROD` | Clerk on the signed binary |
| `CADMUS_FIREBASE_CONFIG_PRESENT` | Set `true` only when that build actually includes the Firebase files | Notification health shortcut. Leave unset until the files are on the builder |

## Suggested order

<Steps>
  <Step title="Keep the installed ID">
    Ship `app.cadmus.mdt` unless a store console rejects it.
  </Step>

  <Step title="Record store numbers">
    After each console creates the listing, replace the `PLACEHOLDER_*` IDs.
  </Step>

  <Step title="Create Firebase apps">
    One Android and one iOS app per environment. Download config files to the build machine only.
  </Step>

  <Step title="Set GitHub signing secrets">
    Android keystore first, then Apple cert + profile. Run **Native signed staging** and install on a real phone.
  </Step>

  <Step title="Add the Azure Admin secret">
    Store `firebase-sa`. Keep `PUSH_NOTIFICATIONS_ENABLED=false` until Cadmus is ready to send.
  </Step>

  <Step title="Prove it on hardware">
    Use [Notification health](/mobile/notification-health). Then test background and locked-phone alerts. That evidence still closes the native issues.
  </Step>
</Steps>

## Related guides

* [Notification health](/mobile/notification-health)
* [Voice alerts](/mobile/voice-alerts)
* [Settings and display](/mobile/settings-and-display)
* [User notification preferences](/admin/user-notification-preferences)

<Warning>
  Do not paste keystores, Apple certificates, or the Firebase Admin JSON into chat, git, or a phone build. Rotate anything that has already been pasted.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.