···2121 id("dev.flutter.flutter-plugin-loader") version "1.0.0"
2222 id("com.android.application") version "8.11.1" apply false
2323 id("org.jetbrains.kotlin.android") version "2.2.20" apply false
2424+ id("com.google.gms.google-services") version "4.4.3" apply false
2425}
25262627include(":app")
+255-72
docs/release.md
···11---
22-title: Release Audit - Apple App Store + Google Play
33-updated: 2026-04-15
44-scope: Repository audit for likely store submission blockers or high-risk policy issues.
22+title: Release and Distribution Guide
33+updated: 2026-05-02
54---
6577-## Sources
66+## Shared Release Baseline
77+88+1. Pin and verify toolchain.
99+ - `flutter --version`
1010+ - `dart --version`
1111+2. Set version in `pubspec.yaml` and/or pass build flags.
1212+ - `--build-name` => human version (for example `1.4.0`)
1313+ - `--build-number` => monotonically increasing integer
1414+3. Run release gates.
1515+ - `flutter pub get`
1616+ - `flutter analyze`
1717+ - `gtimeout 1200s flutter test --reporter=failures-only`
1818+4. Tag immutable source.
1919+ - `git tag vX.Y.Z`
2020+5. Build store artifacts from that exact tag/commit.
2121+2222+## Environment Variables
2323+2424+Use the root `.env.example` as the canonical variable list. Keep real values in untracked secrets (`.env.local`, CI secrets manager, etc.).
2525+2626+## Google Play (Android)
2727+2828+### Build
2929+3030+1. Configure real release signing (upload key), not debug signing.
3131+2. Build AAB (preferred by Google Play):
3232+3333+ ```bash
3434+ flutter build appbundle --release \
3535+ --build-name "$FLUTTER_BUILD_NAME" \
3636+ --build-number "$FLUTTER_BUILD_NUMBER"
3737+ ```
3838+3939+3. Artifact: `build/app/outputs/bundle/release/app-release.aab`
4040+4141+### Deploy
4242+4343+1. Enroll in Play App Signing.
4444+2. Upload `app-release.aab` to Internal testing first.
4545+3. Promote to closed/open/production after validation.
4646+4747+### Notes
4848+4949+- If you need the same signing key across multiple stores, provide your own app signing key when configuring Play App Signing.
5050+- For non-Play Android channels, ship signed APKs (Play consumes AAB; side channels consume APK).
5151+5252+## Apple App Store (iOS)
5353+5454+### Build
5555+5656+1. Use an explicit App ID + matching bundle ID.
5757+2. Build signed IPA:
5858+5959+ ```bash
6060+ flutter build ipa --release \
6161+ --build-name "$FLUTTER_BUILD_NAME" \
6262+ --build-number "$FLUTTER_BUILD_NUMBER"
6363+ ```
6464+6565+3. Artifact: `build/ios/ipa/*.ipa`
6666+6767+### Deploy
6868+6969+1. Upload using Xcode or Transporter to App Store Connect.
7070+2. Wait for processing.
7171+3. Ship through TestFlight (internal/external) first.
7272+4. Submit selected build for App Review.
7373+7474+### Notes
7575+7676+- App Store Connect associates build using bundle ID + version + build string.
7777+- As of 2026, Apple requires Xcode 14+ for uploads.
7878+7979+## AltStore.io
8080+8181+AltStore distribution has two distinct paths.
8282+8383+### AltStore PAL (EU marketplace path)
8484+8585+#### Build/Package
8686+8787+1. Build iOS release (`flutter build ipa --release`).
8888+2. Submit via App Store Connect with Notarization (or App Store approval, which also results in notarization).
8989+3. Download the Alternative Distribution Package (ADP).
9090+4. Host ADP exactly as-delivered; preserve directory hierarchy and do not modify `manifest.json`.
9191+9292+#### Deploy
9393+9494+1. Accept Apple Alternative EU Terms Addendum.
9595+2. Register Developer ID with AltStore PAL API.
9696+3. Add returned marketplace token in App Store Connect Integrations.
9797+4. Publish a Source JSON with required app/version metadata.
9898+9999+### AltStore Classic (sideloaded IPA path)
100100+101101+#### Build
102102+103103+1. Build/sign IPA (`flutter build ipa --release`).
104104+2. Host IPA at stable HTTPS URL.
105105+106106+#### Deploy
107107+108108+1. Publish/update Source JSON.
109109+2. Keep newest entry first in `versions` array.
110110+3. Ensure each release updates `version` (`CFBundleShortVersionString`) and/or `buildVersion` (`CFBundleVersion`).
111111+4. Include accurate `downloadURL`, `size`, and optional `minOSVersion` / `maxOSVersion`.
112112+113113+### Notes
114114+115115+- AltStore determines latest release by `versions` ordering, not dates.
116116+- AltStore checks declared app permissions/entitlements against downloaded app package.
117117+118118+## F-Droid
119119+120120+### Build Strategy
121121+122122+1. Decide target:
123123+ - Main F-Droid repo, or
124124+ - Your own F-Droid-compatible repo.
125125+2. For main F-Droid repo, create an `fdroid` flavor without proprietary SDKs (Firebase/Crashlytics/Play Services tracking dependencies are not allowed in main repo).
126126+3. Build signed deterministic APK for that flavor.
127127+128128+Recommended flavor command pattern:
129129+130130+```bash
131131+flutter build apk --release \
132132+ --flavor fdroid \
133133+ --target lib/main_fdroid.dart \
134134+ --build-name "$FLUTTER_BUILD_NAME" \
135135+ --build-number "$FLUTTER_BUILD_NUMBER"
136136+```
137137+138138+### Deploy
139139+140140+1. Prepare fdroiddata metadata and submit inclusion proposal (prefer metadata merge request path).
141141+2. Keep upstream releases tagged.
142142+3. Aim for reproducible builds from day one (strongly recommended by F-Droid, harder to retrofit later).
143143+144144+### Notes
145145+146146+- If Firebase push is required in Play/App Store builds, maintain a clean non-proprietary F-Droid flavor where push is disabled or replaced.
147147+148148+## Obtainium (Android direct update channel)
149149+150150+### Build
151151+152152+1. Produce signed APK artifacts for direct install.
153153+2. Prefer a stable, machine-discoverable release URL source (typically GitHub Releases).
154154+155155+### Deploy
156156+157157+1. Publish release where source exposes:
158158+ - version identifier
159159+ - at least one APK download URL
160160+2. If multiple APK variants exist, keep filenames explicit (`arm64-v8a`, `universal`, etc.) so users can filter reliably.
161161+162162+### Notes
163163+164164+- Obtainium supports GitHub, GitLab, F-Droid repos, direct APK links, and HTML fallback.
165165+- Cross-store signing matters: updating from F-Droid-signed to non-F-Droid-signed builds may fail due to signature mismatch.
166166+167167+## GitHub Releases
168168+169169+### Build
170170+171171+1. Build release artifacts from tagged commit (`vX.Y.Z`).
172172+2. Generate checksums for all distributables.
817399-- Apple App Review Guidelines: <https://developer.apple.com/app-store/review/guidelines/>
1010- - 1.2 User-Generated Content requirements
1111- - 5.1.1 Data Collection and Storage (Privacy Policies)
1212- - 4.8 Login Services (third-party login exception for service clients)
1313-- Google Play User Data policy: <https://support.google.com/googleplay/android-developer/answer/10144311?hl=en>
1414- - Privacy Policy requirement
1515- - Account deletion requirement (if account creation is supported)
1616-- Google Play Developer Programme Policy (UGC section): <https://support.google.com/googleplay/android-developer/answer/16070163>
1717- - UGC terms acceptance + moderation expectations
174174+Example:
181751919-## Findings
176176+```bash
177177+shasum -a 256 build/app/outputs/flutter-apk/*.apk build/ios/ipa/*.ipa > checksums.txt
178178+```
201792121-### No in-app Privacy Policy link/text
180180+### Deploy
221812323-- Policy mapping:
2424- - Apple 5.1.1(i): privacy policy must be linked in App Store Connect and in-app in an easily accessible manner.
2525- - Google Play User Data: privacy policy link/text must exist in Play Console and in-app.
2626-- Evidence in app:
2727- - Login has no privacy/terms surface: `lib/features/auth/presentation/login_screen.dart` (see UI around lines 54-205).
2828- - Settings has no legal/privacy entry: `lib/features/settings/presentation/settings_screen.dart` (lines 69-151).
2929- - About page has external links and email only, no privacy policy link: `lib/features/settings/presentation/about_screen.dart` (lines 8-97).
3030- - Existing backlog confirms missing policy: `docs/TODO.md` (lines 62-65).
3131-- Impact: High probability of rejection by both stores until fixed.
182182+1. Create release from tag.
183183+2. Attach binaries (`.aab`, `.apk`, `.ipa`, `checksums.txt`, optional symbols/maps).
184184+3. Use generated release notes, then curate manually.
321853333-### High Risk (Google Play UGC): No explicit Terms/User Policy acceptance before posting UGC
186186+CLI example:
341873535-- Policy mapping:
3636- - Google Play UGC policy requires robust moderation, including requiring acceptance of Terms of Use and/or user policy before users create/upload UGC.
3737-- Evidence in app:
3838- - Compose allows direct posting with no terms acceptance gate: `lib/features/compose/presentation/compose_screen.dart` (lines 567-588, especially Post action at 584-587).
3939- - No in-app terms/user policy screen found in `lib/`.
4040-- Impact: Elevated Play policy risk for social/UGC apps.
188188+```bash
189189+gh release create "v${FLUTTER_BUILD_NAME}" \
190190+ --generate-notes \
191191+ build/app/outputs/bundle/release/app-release.aab \
192192+ build/app/outputs/flutter-apk/*.apk \
193193+ build/ios/ipa/*.ipa \
194194+ checksums.txt
195195+```
196196+197197+### Hardening (Recommended)
198198+199199+- Add artifact attestations in GitHub Actions for build provenance.
200200+- Keep each release asset < 2 GiB.
201201+202202+## Firebase Push Notifications (iOS + Android)
203203+204204+### 1) Firebase Project and App Registration
205205+206206+1. Install CLI tooling.
207207+ - `firebase login`
208208+ - `dart pub global activate flutterfire_cli`
209209+2. Run:
210210+211211+ ```bash
212212+ flutterfire configure
213213+ ```
214214+215215+3. Commit generated `lib/firebase_options.dart`.
216216+217217+### 2) Platform Config Files
218218+219219+1. Android: place `google-services.json` at `android/app/google-services.json`.
220220+2. iOS: place `GoogleService-Info.plist` at `ios/Runner/GoogleService-Info.plist` and include it in Runner target.
221221+222222+### 3) Android Gradle Wiring
223223+224224+1. Add Google services Gradle plugin in project/plugin management.
225225+2. Apply `com.google.gms.google-services` in app module.
226226+227227+### 4) Apple Push Prerequisites
228228+229229+1. In Xcode, enable `Push Notifications` capability.
230230+2. In Xcode Background Modes, enable:
231231+ - `Background fetch`
232232+ - `Remote notifications`
233233+3. Upload APNs auth key (`.p8`, Key ID, Team ID) in Firebase Console > Project Settings > Cloud Messaging.
412344242-### Moderate Risk (Apple UGC 1.2): Posting-side objectionable-content controls are not explicit
235235+### 5) App Initialization and Runtime
432364444-- Policy mapping:
4545- - Apple 1.2 says UGC/social apps should include a method for filtering objectionable material from being posted.
4646-- Evidence:
4747- - Reporting/blocking exists (good):
4848- - `lib/features/profile/presentation/widgets/profile_action_buttons.dart` (Report/Block UI around lines 85-140).
4949- - `lib/features/profile/presentation/widgets/report_dialog.dart` (report flow lines 10-220).
5050- - Moderation controls exist for viewed content (good): `lib/features/settings/presentation/settings_screen.dart` (Moderation section lines 75-77).
5151- - No explicit compose-time objectionable-content filter is visible in compose flow.
5252-- Impact: Could pass if platform-side moderation is accepted by review, but still a non-trivial risk without clear reviewer notes.
237237+1. Initialize with generated options:
532385454-### Reviewer-access risk for App Store
239239+ ```dart
240240+ await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
241241+ ```
552425656-- Apple "Before You Submit" requires full reviewer access (demo account or demo mode for account-based features).
5757-- App is account-based and login-gated; no repo evidence of dedicated reviewer/demo path.
5858-- Impact: Common review delay/rejection if review notes do not include working credentials.
5959- - Create a real Bluesky account for reviewers, populate it with sample content, and
6060- provide the credentials in App Store Connect / Play Console.
243243+2. Request user notification permission (iOS) before expecting token delivery.
244244+3. Register and sync FCM token with backend; rotate on refresh.
612456262-## OK
246246+### 6) Channel-Specific Caveat
632476464-- In-app report mechanism exists for posts/accounts:
6565- - `lib/features/profile/presentation/widgets/report_dialog.dart`.
6666-- Block/mute actions exist:
6767- - `lib/features/profile/presentation/widgets/profile_action_buttons.dart`.
6868-- User-reachable contact info exists:
6969- - Email link in About: `lib/features/settings/presentation/about_screen.dart` line 11 + UI lines 90-93.
7070-- Sign in with Apple requirement appears likely exempt:
7171- - App behaves as a client for a specific third-party service (Bluesky), matching Apple 4.8 exception language.
248248+- F-Droid mainline build should not include proprietary Firebase dependencies.
249249+Keep push-enabled binaries for Play/App Store/AltStore/Obtainium, and maintain a non-Firebase F-Droid flavor.
722507373-## Fixes (In Priority Order)
251251+## Primary References
742527575-- [x] Add a dedicated Legal screen and surface:
7676- - [x] Privacy Policy (in-app link + readable text summary)
7777- - [x] Terms of Use / User Policy
7878- - [x] Reachable from login and settings/about
7979-- [ ] Add UGC policy acceptance flow before first create/upload action (compose, media upload, messages if applicable).
8080-- [ ] Document moderation operations in policy/reviewer notes:
8181- - [ ] How reports are handled and SLA
8282- - [ ] What objectionable content rules apply
8383-- [ ] Replace placeholder identifiers and release signing setup:
8484- - [ ] Android `applicationId`
8585- - [ ] Android release signing config (non-debug)
8686- - [ ] iOS bundle IDs + distribution signing
8787-- [ ] Prepare App Store review notes with working reviewer credentials/demo path.
8888-- [ ] Verify account deletion obligations:
8989- - [ ] If any account creation is enabled in-app, add in-app deletion entry point per Apple/Google rules.
253253+- Flutter Android release: <https://docs.flutter.dev/deployment/android>
254254+- Flutter iOS release: <https://docs.flutter.dev/deployment/ios>
255255+- Android signing + Play App Signing: <https://developer.android.com/studio/publish/app-signing>
256256+- Play App Signing help: <https://support.google.com/googleplay/android-developer/answer/9842756>
257257+- App Store Connect uploads: <https://developer.apple.com/help/app-store-connect/manage-builds/upload-builds/>
258258+- Apple explicit App ID / bundle ID requirements: <https://developer.apple.com/help/glossary/app-id/> and <https://developer.apple.com/documentation/bundleresources/information-property-list/cfbundleidentifier>
259259+- AltStore PAL distribution: <https://faq.altstore.io/developers/distribute-with-altstore-pal>
260260+- AltStore source format: <https://faq.altstore.io/developers/make-a-source>
261261+- AltStore updates/version ordering: <https://faq.altstore.io/developers/updating-apps>
262262+- F-Droid inclusion policy: <https://f-droid.org/en/docs/Inclusion_Policy/>
263263+- F-Droid inclusion workflow: <https://f-droid.org/en/docs/Inclusion_How-To/>
264264+- F-Droid reproducible builds: <https://f-droid.org/docs/Reproducible_Builds/>
265265+- Obtainium tracking/source behavior: <https://wiki.obtainium.imranr.dev/app_tracking/> and <https://wiki.obtainium.imranr.dev/sources/>
266266+- GitHub releases: <https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository>
267267+- GitHub release notes automation: <https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes>
268268+- GitHub build provenance (artifact attestations): <https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations>
269269+- GitHub release asset limits: <https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases>
270270+- Firebase Flutter setup: <https://firebase.google.com/docs/flutter/setup>
271271+- Firebase FCM Flutter setup: <https://firebase.google.com/docs/cloud-messaging/flutter/get-started>
272272+- Firebase Android config (`google-services.json` + plugin): <https://firebase.google.com/docs/android/setup>