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

# 0482 — Mobile onboarding is a Runway step, and EAS owns signing

> Proposed · 2026-10-30

> [!NOTE]
>
> Proposed (STA-1623); owner answers of 2026-10-30. Amends 0480 decision 8. Builds on
> [0439](https://docs.stackra.figentra.com/adr/0439-mobile-apps-from-one-os-template-with-capability-flags.md) decision 6 (ids), 0480
> (store APIs, root kit, dry run by default) and
> [0062](https://docs.stackra.figentra.com/adr/0062-mobile-release-push-and-crash-reporting.md) (Expo push).

## Context

Runway builds, updates and submits a product's mobile app from its template line, and ADR 0480 drives the App Store
Connect and Play APIs for listings and review. Nothing takes a product from no app to its first store build: the
bundle id, the EAS project and profiles, signing, the Android push key and the Play app are each done by hand, and
only the template has an EAS project.

## Decision

1. **One step list.** `stackra mobile onboard --app <key>` runs Runway's `onboardMobile`: identity, EAS project, EAS
   profiles, Apple bundle id and capabilities, App Store Connect app record, Google project, Firebase, Analytics link,
   Sentry, signing, push, store keys, Play app. Each step is idempotent and reports done, planned, manual or blocked;
   every step runs, and the exit code is 3 while one is manual or blocked.
2. **Ids derive from the product key** when the runway does not set them: `com.figentra.<product>.<app>`, without
   `<app>` when it repeats `<product>`, `_` for `-` on Android. A set id is never changed (0439 decision 6).
3. **EAS owns signing.** Distribution certificates, provisioning profiles, the APNs key and the Android upload keystore
   are created and held by EAS. Runway never generates, stores, prints or moves one; it only asks EAS whether they
   exist.
4. **Store calls behind ports.** `AppleDeveloper`, `PlayConsole`, `ExpoProjects`, `GoogleCloud`, `Firebase` and
   `SentryProjects` are ports with a real driver and a memory driver. ADR 0480's API clients move into Runway as the
   Apple and Play real drivers, and `pnpm store` imports them.
5. **One Google project per product, named, with Firebase.** Runway creates the Google Cloud project with the id the
   runway names (default the product key, never a generated id) under Figentra's organisation folder, adds Firebase,
   registers the iOS and Android apps, fetches their config files into `assets/mobile/<app>/`, and links Figentra's
   Google Analytics account. Runway signs in to Google with GitHub OIDC through workload identity federation; there is
   no Google key. The app carries no Firebase Analytics SDK: Android push is Expo push over FCM v1 (0062), and product
   analytics leave the app only through our analytics service (its own ADR, STA-1624).
6. **Sentry per app.** Runway creates the Sentry project `<product>-<app>` and writes its public DSN to the runway (0062
   decision 3).
7. **Generated keys are written blind.** A third-party key Runway must create (the FCM v1 service-account key, later
   the Wallet pass certificate) is created inside the CI job, written straight into Infisical and handed to EAS; it is
   never returned, printed or written to disk. This amends 0480
   decision 8, which allowed only machine secrets (owner decision 2026-10-30).
8. **Manual where no API exists, detected when done.** The App Store Connect app record and the Play app with its first
   upload are owner steps with exact values from the runway and `assets/store`; Runway reads the store APIs to mark
   them done.
9. **ADR 0480's limits hold:** dry run by default, live only in CI, portal-creating steps only in the product's
   `production` environment, nobody reads a value.

## Consequences

* A new product's app is: the owner creates the App Store Connect app record and the Play app with its first upload,
  and once for all products sets up the Google workload identity pool and its Analytics grant; everything else is the
  command.
* Agents still never read a value; a generated key exists only inside the CI job that made it.
* A store-side change in Apple, Google or Expo shows as a failing onboarding step, caught by the mock-driver contract
  tests only when a recorded fixture is refreshed.

## Enforcement

* `packages/developer/runway` tests: every step's states with mock drivers, idempotency, dry run sends nothing, no
  secret in output.
* planned — STA-1623: the `eas.json` writer refuses a major-only node version.

## Supersedes and sources

`Supersedes: none`. Sources: STA-1623; owner brief 2026-10-30.


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