# App Store release runbook

This runbook covers dashboard work that cannot be committed to the repository. Do it in order: later systems depend on identifiers and keys from earlier ones.

## What is already in the repository

- App identity: `Glide`, bundle ID `com.h14h.glide`, version `1.0.0`.
- EAS production profile with remote build numbers and automatic incrementing.
- StoreKit purchase and restore flow through RevenueCat.
- Server-side RevenueCat verification before Convex allows sync.
- Production-oriented Private Sync E2EE implementation, still blocked on independent protocol/crypto review.
- 15-day read-only grace period; expiry pauses access without partially deleting the encrypted vault.
- In-app account deletion and batched Convex data deletion.
- Subscription disclosures and Privacy Policy / Terms links.
- App privacy manifest declarations.
- App Store metadata draft in `store.config.json`.
- Public Privacy Policy: https://glide.expo.app/privacy
- Public support page: https://glide.expo.app/support

A production build intentionally fails if its Clerk, Convex, or RevenueCat client configuration is missing or uses test credentials.

## 1. App Store Connect

1. Open **Agreements, Tax, and Banking**.
2. Accept the Paid Apps Agreement and complete banking and tax forms.
3. In **Certificates, Identifiers & Profiles**, confirm these identifiers/capabilities:
   - `com.h14h.glide`
   - `com.h14h.glide.widgets`
   - App Group `group.com.h14h.glide`
4. In **My Apps**, create Glide if it does not exist:
   - Platform: iOS
   - Name: Glide
   - Primary language: English (U.S.)
   - Bundle ID: `com.h14h.glide`
   - SKU: an internal stable value such as `glide-ios`
5. Record the app's numeric **Apple ID**; this becomes `ascAppId` for EAS submission.

## 2. App Store subscriptions

1. In Glide's App Store Connect record, create one subscription group named **Glide Sync**.
2. Add two auto-renewable subscriptions:
   - `glide_sync_monthly` — one month — target price USD $2
   - `glide_sync_yearly` — one year — target price USD $20
3. Add an English display name and description for each product.
4. Add availability and pricing for intended storefronts.
5. Add the required subscription review screenshot. It should show Glide's plan selection/confirmation UI and the displayed price.
6. Keep both products ready to submit; they are attached to the first app version later.
7. Create/download an **In-App Purchase key** for RevenueCat and record its key ID and issuer ID. The `.p8` file is downloadable only once.

## 3. Production Clerk

1. Create or promote a production Clerk instance.
2. Enable the authentication methods Glide supports (email/password and email verification).
3. Enable first/last-name editing if desired.
4. **Enable users deleting their own accounts.** Glide disables its deletion button when Clerk reports that this is off.
5. Create a JWT template named `convex` with audience `convex`.
6. Record:
   - Production publishable key (`pk_live_…`)
   - JWT issuer domain used by the template
7. Complete any Clerk production-domain/DNS requirements.

## 4. RevenueCat production app

1. In the existing RevenueCat project, add an **App Store** app with bundle ID `com.h14h.glide`.
2. Upload/configure the App Store Connect In-App Purchase key from step 2.
3. Import `glide_sync_monthly` and `glide_sync_yearly`.
4. Confirm the project-level entitlement is exactly `sync`.
5. Attach the products to the current offering:
   - Monthly product → `$rc_monthly`
   - Annual product → `$rc_annual`
6. Make that offering current.
7. Copy the public Apple SDK key (`appl_…`) for EAS.
8. Create/copy a **secret REST API v1 key** for Convex. Never put this key in an `EXPO_PUBLIC_` variable or the app bundle.
9. Configure App Store Server Notifications V2 using the RevenueCat-provided URL for both sandbox and production.
10. Review RevenueCat's restore/transfer behavior. Glide identifies customers by Clerk user ID and expects restoration with the same Glide account.

## 5. Production Convex

1. Open the production Convex deployment's **Settings → Environment Variables**.
2. Add:
   - `CLERK_JWT_ISSUER_DOMAIN` = the production Clerk issuer domain
   - `REVENUECAT_SECRET_API_KEY` = the RevenueCat secret REST API key
3. Deploy the current backend:

   ```bash
   npx convex deploy
   ```

4. Record the production deployment URLs:
   - `https://….convex.cloud`
   - `https://….convex.site`
5. Confirm the deployment contains `account`, `entitlements`, `revenuecat`, `privateSync`, and
   `encryptedSync` functions plus entitlement, vault, device, link-request, encrypted-task, and
   deletion-tombstone tables. Do not deploy the legacy plaintext narrowing step independently of
   the staged cutover in `docs/engineering/private-sync-protocol.md`.

## 6. EAS production environment

In Expo/EAS project **Environment Variables**, create these variables for the **production** environment. They are client-visible by design and can use Plain text visibility:

- `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY` = `pk_live_…`
- `EXPO_PUBLIC_CONVEX_URL` = production `convex.cloud` URL
- `EXPO_PUBLIC_CONVEX_SITE_URL` = production `convex.site` URL
- `EXPO_PUBLIC_REVENUECAT_KEY` = RevenueCat `appl_…` key

RevenueCat Test Store keys are accepted only by native **Debug builds**. Put `EXPO_PUBLIC_REVENUECAT_KEY=test_…` in the EAS **development** environment and use the `preview-test-store` profile. That profile is a standalone internal Debug build: `EX_UPDATES_NATIVE_DEBUG=1` embeds the app bundle through `expo-updates`, while its EAS pre-install hook excludes dev-client/launcher modules so it needs neither Metro nor a developer launcher. Never put a `test_…` key in `preview`, `testflight`, or `production`: RevenueCat intentionally terminates Release/distribution binaries that contain one. Configure the V1 secret key separately on the isolated preview Convex deployment.

Do not set `EXPO_PUBLIC_ENABLE_PURCHASE_STUB` in EAS. The stub is development-only, but leaving it unset avoids confusion.

## 7. Submission credentials

1. In App Store Connect **Users and Access → Integrations**, create an App Store Connect API key with App Manager access for EAS submission.
2. Configure it with `eas credentials -p ios`, or supply it when EAS prompts during the first submission.
3. Optionally add `appleId`, `appleTeamId`, and `ascAppId` under `submit.production.ios` in `eas.json` after confirming their exact values.

Never commit `.p8`, `.mobileprovision`, password, or secret API key files.

## 8. Build and TestFlight

Before paid-production setup is complete, exercise RevenueCat Test Store with the standalone native-Debug preview:

```bash
eas build --platform ios --profile preview-test-store
```

A TestFlight or ad-hoc Release build must instead use an `appl_…` key. Until the real App Store products exist, that Release build can exercise local features, auth, sync, and account deletion, but not Test Store purchases. Test Store does not validate Apple's real product catalog, receipts, or server notifications.

After the live Clerk/Convex/RevenueCat configuration is complete, replace it with the true production build:

```bash
eas build --platform ios --profile production
eas submit --platform ios --profile production --latest
```

Then test on at least one physical iPhone:
   - Fresh install and local-only task use
   - Planning, task completion, streaks, dark mode
   - Widget and quick actions
   - Monthly purchase and annual purchase in sandbox
   - Cancelled purchase
   - Restore after reinstall using the same Glide account
   - Private Sync first-device setup and exact recovery-phrase confirmation
   - Initial ciphertext cutover with more than one push batch and verified plaintext cleanup
   - QR and manual device approval, matching words, denial, and expiry
   - Signed-in encrypted sync across iOS and Android
   - Offline edits and later reconciliation
   - Recovery and interrupted two-phase recovery-phrase replacement
   - Device access revocation, with no claim that a copied key was erased
   - Billing/grace UI with RevenueCat sandbox/Test Store controls
   - Account deletion, including confirmation that cloud rows and Clerk identity disappear while local tasks remain
Finally, put the tested live-production build through external TestFlight beta review if possible.

## 9. Private Sync security and export-compliance gate

Private Sync must remain disabled from production claims/releases until all release gates in
`docs/engineering/private-sync-protocol.md` pass. In particular, obtain an independent review of the
protocol composition and user-facing E2EE claims.

Glide now ships application-layer AES-256-GCM, X25519, HKDF-SHA-256, and BIP39 code. Before upload,
have qualified export-compliance counsel or a specialist confirm whether the app remains exempt,
whether annual U.S. mass-market self-classification/reporting applies, and whether
the conservative current `ITSAppUsesNonExemptEncryption: true` declaration is required or may be
changed to `false` under an exemption. Record the conclusion and any
filing identifiers in the private release record. This runbook is not legal advice.

## 10. App Store listing

1. Review `store.config.json`, especially copyright ownership and marketing copy.
2. After the first binary exists in App Store Connect, push the draft metadata:

   ```bash
   eas metadata:push --profile production
   ```

3. In App Store Connect, supply anything not handled by metadata:
   - iPhone screenshots
   - App icon/build selection
   - Subscription review screenshots
   - Pricing and availability
   - Age rating answers
   - App Review contact information
   - Review notes and a demo Glide account
4. Use these URLs:
   - Privacy Policy: https://glide.expo.app/privacy
   - Support: https://glide.expo.app/support
   - Marketing: https://glide.expo.app
   - Terms: https://www.apple.com/legal/internet-services/itunes/dev/stdeula/
5. Complete App Privacy answers consistently with `app.json` and the policy. Expected categories include name, email address, user ID, device ID, task content, and purchase history, all for app functionality and not tracking. Verify Clerk, Convex, and RevenueCat disclosures before answering.
6. Add review notes explaining:
   - Core tasks work without an account.
   - Glide Sync is the subscription feature.
   - The reviewer reaches it through **You → Enable Sync**.
   - Account deletion is under **You → Account**.
   - Restore is on the Sync plan screen.
7. Attach both subscriptions to version 1.0 and submit the subscriptions with the app's first review.
8. Select manual release for the first version, then submit for review.
