# PAILot App Store Readiness Checklist Date: 2026-03-25 Source: Security & Code Quality Review ## CRITICAL (Must fix before submission) - [x] **C1: Remove NSAllowsArbitraryLoads** — ATS bypass, Apple will reject. Use NSAllowsLocalNetworking only *(fixed 2026-03-25)* - [x] **C2: Add TLS to MQTT** — All conversations and auth token travel in plaintext. Set `client.secure = true`, configure TLS on AIBroker broker *(fixed 2026-03-25 — self-signed cert auto-generated at ~/.aibroker/tls/, onBadCertificate accepts it; TODO: pin cert fingerprint)* - [x] **C3: Remove debug log files in production** — `mqtt_debug.log` and `_chatLog` write truncated message content to Documents. Wrap in `kDebugMode` or remove entirely *(fixed 2026-03-25)* ## HIGH (Should fix before submission) - [x] **H1: Unbounded image cache** — `_imageCache` in message_bubble.dart grows without limit. Add LRU eviction (cap at 50) *(fixed 2026-03-25)* - [x] **H2: Audio temp files never cleaned** — `_base64ToFile` creates .m4a files never deleted. Clean up after playback completes *(fixed 2026-03-25)* - [x] **H3: TextEditingController leak** — Rename dialog in session_drawer.dart creates controller but never disposes it *(fixed 2026-03-25)* - [x] **H4: Input validation on settings** — No validation on host IPs, port range, MAC format. Add regex validators *(fixed 2026-03-25)* - [x] **H5: LifecycleObserver never removed** — AudioService.init() adds observer but dispose() doesn't remove it *(fixed 2026-03-25)* - [ ] **H6: MQTT token in memory** — Acceptable for personal use, document as known limitation ## MEDIUM (Improve before submission) - [x] **M1: Subnet scan hammers 254 hosts** — Batched in groups of 20 with early exit *(fixed 2026-03-25)* - [x] **M2: No loadMore debounce** — Added isLoadingMore guard *(fixed 2026-03-25)* - [x] **M3: NavigateNotifier global singleton** — Moved to `navigateNotifierProvider` (StateProvider) in providers.dart; NavigateNotifier class extracted to `lib/services/navigate_notifier.dart` *(fixed 2026-04-01)* - [x] **M4: Unbounded _seenSeqs set** — Replaced O(n log n) sort-based eviction with FIFO List+Set pair; O(1) eviction by removing oldest entry from list front *(fixed 2026-04-01)* - [x] **M5: Screenshots bloat iCloud backup** — Messages directory excluded from iCloud/iTunes backup via NSURLIsExcludedFromBackupKey (MethodChannel com.mnsoft.pailot/backup registered in SceneDelegate); screenshots are in-memory only (never persisted) *(fixed 2026-04-01)* - [x] **M6: Unused import** — Already removed *(fixed 2026-03-25)* - [x] **M7: Silent error swallowing** — Added debugPrint on config load failure *(fixed 2026-03-25)* ## LOW (Nice-to-haves) - [x] **L1: PrivacyInfo.xcprivacy** — Created `ios/Runner/PrivacyInfo.xcprivacy` declaring UserDefaults (CA92.1), FileTimestamp (C617.1), DiskSpace (E174.1); added to Xcode project Copy Bundle Resources phase *(fixed 2026-04-01)* - [x] **L2: Privacy policy** — Created `PRIVACY.md` at repo root; describes self-hosted server model, no third-party data collection *(fixed 2026-04-01)* - [x] **L3: Unused dependencies** — Removed web_socket_channel and wakelock_plus *(fixed 2026-03-25)* - [x] **L4: Unnecessary _http._tcp** — Removed from NSBonjourServices *(fixed 2026-03-25)* - [x] **L5: Typing indicator timeout** — Auto-clear after 10s *(fixed 2026-03-25)* - [x] **L6: Version number** — Already `1.0.0+1` in pubspec.yaml *(confirmed 2026-04-01)* - [x] **L7: App icon** — All required sizes present (20x20 through 1024x1024, iPhone + iPad); Flutter-generated icons have no alpha channel *(confirmed 2026-04-01)* ## APNs Push Notifications (implemented 2026-04-01) - [x] **APNs server side** — `src/apns/client.ts` in AIBroker: ApnsClient initialised from APNS_KEY_PATH/APNS_KEY_ID/APNS_TEAM_ID env vars, sandbox by default. Token storage in `~/.aibroker/apns-tokens.json`. `sendPush()` fires when `getMqttClientCount() == 0`. - [x] **APNs Flutter side** — `lib/services/push_service.dart`: `push` package (3.3.3, no Firebase). Requests permission on app start, publishes token to `pailot/device/token` MQTT topic. Re-registers on reconnect. - [x] **Entitlements** — `ios/Runner/Runner.entitlements` with `aps-environment=development`. All three build configs have `CODE_SIGN_ENTITLEMENTS` set. - [x] **UIBackgroundModes** — `remote-notification` added to Info.plist. - [x] **AppDelegate** — forwards UNUserNotificationCenter delegate methods to push plugin. - [ ] **Provisioning profile** — APNs requires an explicit provisioning profile from Apple Developer portal that includes Push Notifications capability. Must update in Xcode before deployment. Switch `aps-environment` to `production` in entitlements for App Store builds. ## App Store Requirements | Requirement | Status | Action | |------------|--------|--------| | NSMicrophoneUsageDescription | PASS | - | | NSCameraUsageDescription | PASS | - | | NSPhotoLibraryUsageDescription | PASS | - | | NSLocalNetworkUsageDescription | PASS | - | | NSBonjourServices | PASS | Fixed - removed _http._tcp | | NSAppTransportSecurity | PASS | Fixed - removed NSAllowsArbitraryLoads | | UIBackgroundModes: audio | PASS | - | | UIBackgroundModes: remote-notification | PASS | Added 2026-04-01 | | Push Notifications entitlement | PASS | Added Runner.entitlements 2026-04-01 | | APNs provisioning profile | FAIL | Must update in Xcode / Developer Portal | | Privacy Policy | PASS | Fixed L2 - PRIVACY.md created | | PrivacyInfo.xcprivacy | PASS | Fixed L1 - declared UserDefaults/FileTimestamp/DiskSpace | | TLS for network | PASS | Fixed C2 - self-signed cert, onBadCertificate=true |