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 websocketchannel 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 APNSKEYPATH/APNSKEYID/APNSTEAMID 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 |