> ⛔ **NE PAS COMMITER CE FICHIER TANT QUE LA VERSION iOS N'EST PAS EN PLACE ET VALIDÉE.**
> C'est un plan de travail / aide-mémoire (décisions, étapes, éléments d'infra). On le supprime ou on le déplace hors du dépôt une fois la bascule faite.

# Plan de mise en place de la version iOS — PhotoSync

> État actuel : app Expo SDK 54 / RN 0.81 / React 19, **buildée et distribuée pour Android uniquement** (APK via EAS, servi depuis le NAS). Le backend est l'agent Node sur le Pi, accédé en **HTTP clair** sur le LAN (+ WireGuard). La sauvegarde instantanée app fermée repose sur un **module natif Kotlin** (`modules/photosync-bg`, `platforms: ["android"]`).
>
> Objectif : produire une version iOS installable (TestFlight d'abord, App Store ensuite) avec parité fonctionnelle maximale, en acceptant que le modèle « background app fermée » diffère structurellement d'Android.

---

## ✅ Avancement (branche `chantier-ios-webdav`)

**Déjà fait en code (profite à iOS car partagé) :**
- ✅ Toute la logique est **cross-platform** (abstraction backend, modes Agent/WebDAV, onboarding, scan QR) — marchera sur iOS sans réécriture.
- ✅ **Bloc `ios` d'`app.json`** : `NSAppTransportSecurity.NSAllowsLocalNetworking` (autorise le HTTP vers le NAS/agent sur le LAN), `NSLocalNetworkUsageDescription`, `buildNumber`. Permissions photo/caméra via les plugins.
- ✅ **Chemins Android-only neutralisés** : `checkAppUpdate` renvoie `available:false` sur iOS (l'auto-update APK ne s'affiche jamais ; MAJ iOS = App Store). `downloadAndInstall` lève déjà sur non-Android.

**Reste à faire (nécessite un Mac + compte Apple Developer) :**
- ⏳ **Module natif background en Swift** (`modules/photosync-bg` est Kotlin only) — cf. §3/§4. Sans lui : pas de sauvegarde instantanée app fermée sur iOS (best-effort `BGTask`/`URLSession` à coder).
- ⏳ **Build iOS** (EAS cloud ou Xcode local) + signature (compte Apple 99 $/an).
- ⏳ **HTTPS pour l'accès distant** : l'agent en **Tailscale HTTP** (`100.64.0.0/10`) **n'est pas** couvert par `NSAllowsLocalNetworking` → iOS le bloquera. Solution propre = agent en HTTPS (cf. [HTTPS-MIGRATION.md](HTTPS-MIGRATION.md)). À la maison (IP LAN privée) ça passe.

---

## 0. TL;DR — ce qui est facile, dur, et impossible tel quel

| Sujet | Difficulté iOS | Verdict |
|---|---|---|
| UI React Native, galerie NAS, navigation, trash, log | 🟢 Facile | Marche quasi sans changement (RN cross-platform) |
| HTTP clair vers le Pi (LAN + WireGuard) | 🟠 Moyen | Bloqué par **ATS** → exceptions Info.plist + permission **Réseau local** |
| Picker médias, permissions photos | 🟢 Facile | `expo-media-library` / `expo-image-picker` sont cross-platform |
| Self-update (install APK via IntentLauncher) | 🔴 N/A | À **supprimer** côté iOS → remplacé par TestFlight / App Store |
| Module natif background (`photosync-bg`) | 🔴 Dur | À **réécrire en Swift** ; le modèle Android (foreground service permanent) **n'existe pas** sur iOS |
| « Sauvegarde instantanée app fermée » | 🔴 Dur | iOS ne le permet pas à l'identique → best-effort via `BGProcessingTask` + `URLSession` background |
| Distribution sans store | 🟠 Moyen | Pas d'« APK iOS » : tout périme hors App Store → **App Store visé** (cf. §8) |
| Build sur machine | 🟢 Résolu | **Mac + iPhone dispo** → build/debug Xcode local possible (EAS cloud optionnel) |

**Décisions actées** (cf. §10) :
- ✅ **Compte Apple Developer payant** (99 $/an) — pris, débloque signature + tout canal.
- ✅ **Mac + iPhone de test disponibles** — build natif local et débogage Swift confortables.
- ✅ **Cible = App Store public** — seul canal **zéro-maintenance** (ne périme jamais), au prix d'une review à préparer (cf. §8). Ad-hoc/TestFlight = replis qui périment (1 an / 90 j).

---

## 1. Pré-requis & comptes (à régler avant tout code)

- [ ] **Compte Apple Developer Program** (99 $/an). S'inscrire sur developer.apple.com avec l'Apple ID dédié au projet (utiliser `contact@taaazzz-prog.fr`, pas le gmail).
- [ ] **App Store Connect** : créer l'app `PhotoSync`, bundle `com.taaazzz.photosync` (déjà réservé dans `app.json` → cohérent).
- [ ] **EAS** : le projet est déjà lié (`projectId` dans `app.json`). Vérifier que le compte EAS a les **identifiants Apple** configurés (`eas credentials`).
- [ ] **Device de test** : un iPhone réel (le background, le réseau local et l'upload ne sont pas testables fidèlement sur simulateur — pas d'accès photothèque réelle ni de vraies tâches background).
- [ ] **Décision distribution** :
  - **TestFlight** (recommandé pour usage perso/famille) : jusqu'à 100 testeurs internes + 10 000 externes, build valable 90 jours, review légère pour l'externe.
  - **App Store public** : review Apple complète (cf. §8, risques réels avec le HTTP clair / réseau local).
  - **Ad-hoc / Enterprise** : ad-hoc = max 100 UDID enregistrés, lourd ; enterprise = hors sujet (299 $/an, usage interne entreprise).

> ✅ **Mac disponible** : le build natif peut se faire localement (`expo prebuild` + Xcode) **et** le débogage Swift (Xcode, `os_log`, profiler) est confortable. EAS cloud reste utilisable en parallèle (comme pour Android) mais n'est plus obligatoire.

### 1.1 Durée de vie selon le canal (pourquoi App Store)

| Canal | Périme après | Ré-intervention |
|---|---|---|
| **App Store public** | **jamais** | aucune (Apple signe) |
| Ad-hoc (IPA signé) | ~1 an | rebuild + réinstaller chaque appareil (manuel) |
| TestFlight | 90 j/build | pousser un nouveau build (testeurs MAJ en 1 tap) |
| Compte gratuit | 7 jours | recompiler/réinstaller chaque semaine |

> Hors App Store, **le profil de provisioning embarqué a une date de péremption** : passée cette date, iOS refuse de lancer l'app jusqu'à réinstallation d'un build re-signé (pas de perte de données, mais redéploiement). Comme l'objectif est « faite une fois, peu de MAJ », **seul l'App Store évite toute ré-intervention** → c'est la cible. Ad-hoc en repli si la review devient un blocage.

---

## 2. Phase 1 — Faire tourner l'app iOS « nue » (sans background natif)

Objectif : un build iOS qui démarre, se connecte au Pi, affiche la galerie et fait un upload manuel. **Sans** le module natif background (on retombe sur « option A » = écoute au premier plan, qui est cross-platform JS).

### 2.1 Config Expo (`mobile/app.json`)
- [ ] Compléter le bloc `ios` :
  ```json
  "ios": {
    "supportsTablet": true,
    "bundleIdentifier": "com.taaazzz.photosync",
    "buildNumber": "1",
    "infoPlist": {
      "NSPhotoLibraryUsageDescription": "PhotoSync accède à vos photos pour les sauvegarder sur votre NAS.",
      "NSPhotoLibraryAddUsageDescription": "PhotoSync enregistre des médias sur votre appareil.",
      "NSLocalNetworkUsageDescription": "PhotoSync se connecte à votre NAS sur le réseau local.",
      "NSAppTransportSecurity": {
        "NSAllowsLocalNetworking": true,
        "NSExceptionDomains": {
          "192.168.1.136": { "NSExceptionAllowsInsecureHTTPLoads": true },
          "192.168.1.124": { "NSExceptionAllowsInsecureHTTPLoads": true }
        }
      }
    }
  }
  ```
  > Les permissions `NSPhotoLibrary*` sont aussi posées par les plugins `expo-media-library` / `expo-image-picker` (déjà présents) — vérifier qu'il n'y a pas de doublon contradictoire ; la valeur du plugin gagne.
- [ ] **WireGuard / IP variables** : les exceptions ATS par domaine sont statiques. Comme les bases sont multiples et changeantes (LAN, WireGuard `10.x`, etc.), préférer `NSAllowsLocalNetworking: true` (couvre `*.local`, `*.localhost` et les plages RFC1918 non routables) **et/ou** en dernier recours `NSAllowsArbitraryLoads: true` (⚠️ justification obligatoire à la review App Store — voir §8). À trancher en §1 selon le canal de distribution.
- [ ] `usesCleartextTraffic` (bloc `expo-build-properties.android`) n'a **aucun effet iOS** — l'équivalent est ATS ci-dessus.

### 2.2 Code JS — neutraliser les chemins Android-only
- [ ] [lib/appupdate.js](mobile/lib/appupdate.js) : utilise `expo-intent-launcher` + install d'APK (`content://`, `REQUEST_INSTALL_PACKAGES`). **Tout est Android.** Garder derrière `Platform.OS === 'android'`, et côté iOS soit masquer la fonction « mettre à jour », soit la rediriger vers TestFlight/App Store. `expo-intent-launcher` reste dans le bundle mais n'est jamais appelé sur iOS.
- [ ] [lib/notify.js](mobile/lib/notify.js) : déjà branché sur `Platform.OS` → vérifier que la branche iOS demande bien l'autorisation notifications (`expo-notifications` : sur iOS le prompt est explicite et obligatoire avant tout envoi).
- [ ] Vérifier tous les `content://` ([lib/sync.js](mobile/lib/sync.js)) : sur iOS `expo-media-library` renvoie des URIs `ph://` / `assets-library` — confirmer que l'upload (lecture du fichier via `expo-file-system`) gère les deux. **Point de test prioritaire.**
- [ ] Auto-détection des bases ([App.js](mobile/App.js#L39-L40), URLs Pi en dur) : inchangé, mais la **première connexion** déclenchera le prompt « Réseau local » iOS — gérer le cas où l'utilisateur refuse (toutes les bases LAN deviennent injoignables, seul WireGuard route).

### 2.3 Build & smoke test
- [ ] `eas build --platform ios --profile development` (client de dev, `developmentClient: true` déjà dans `eas.json`). EAS gère la création des certificats/provisioning.
- [ ] Installer sur iPhone réel via le QR EAS (TestFlight pas encore requis pour le profil `development` interne).
- [ ] **Checklist smoke** : app démarre → prompt photos OK → prompt réseau local OK → galerie NAS s'affiche → upload manuel d'1 photo → l'upload arrive sur le NAS → trash/log/folder-picker fonctionnent.

> 🎯 Fin de Phase 1 = parité **premier plan** avec Android. La sauvegarde instantanée app-fermée n'existe pas encore.

---

## 3. Phase 2 — Background sur iOS : ce qui est réellement possible

> ⚠️ **Recadrage des attentes.** Sur Android vous avez un *Foreground Service* qui tourne en permanence et un *ContentObserver* qui réagit à la milliseconde (cf. [PhotoSyncObserverService.kt](mobile/modules/photosync-bg/android/src/main/java/com/taaazzz/photosync/bg/PhotoSyncObserverService.kt)). **iOS interdit ce modèle.** Une app fermée/suspendue n'exécute pas de code arbitraire. Il n'y a pas d'équivalent au foreground service permanent.

Ce qu'iOS offre à la place (best-effort, planifié par le système) :

1. **`BGProcessingTaskRequest`** (BackgroundTasks framework) — tâche longue planifiée par iOS quand il « le décide » (typiquement la nuit, en charge, sur Wi-Fi). **Pas de garantie de fréquence ni de délai.** C'est le plus proche de la sync « 15 min » mais sans périodicité garantie.
2. **`URLSession` background configuration** — uploads confiés au démon système `nsurlsessiond` : ils **continuent même app tuée/redémarrée**, l'app est réveillée à la complétion. C'est LE mécanisme fiable pour finir un transfert lancé au premier plan.
3. **`PHPhotoLibraryChangeObserver`** — ne marche que pendant que l'app est **active** (premier plan). Pas de réveil sur nouvelle photo en arrière-plan.
4. **`UIBackgroundTaskIdentifier`** — prolonge ~30 s l'exécution quand l'app passe en arrière-plan (finir un envoi en cours), pas plus.
5. **Notifications push silencieuses** (`content-available`) — réveil possible, mais throttlé par iOS et nécessiterait un **serveur push (APNs)** → le Pi devrait envoyer des push : surcomplexe, hors scope initial.

### Stratégie iOS retenue (réaliste)
- **Premier plan** : `PHPhotoLibraryChangeObserver` → détecte les nouvelles photos → lance l'upload immédiatement (équivalent « option A », déjà en JS, à doubler ou pas en natif).
- **Sortie d'app** : `URLSession` background → les transferts en cours **se terminent** même app fermée.
- **Rattrapage** : `BGProcessingTask` planifiée → au prochain réveil système, scanne la photothèque depuis la dernière baseline et uploade le delta (même logique que [Uploader.kt](mobile/modules/photosync-bg/android/src/main/java/com/taaazzz/photosync/bg/Uploader.kt) `queryNew` + file de retry).
- **Filet de sécurité** : la sync au premier plan / périodique JS reste la **vérité NAS** (comme aujourd'hui sur Android). Ce qui n'est pas parti en background partira à la prochaine ouverture.

> 👉 Message à assumer auprès de l'utilisateur : **« sur iPhone, une photo prise app fermée ne part pas dans la seconde ; elle part à la prochaine fenêtre background décidée par iOS, ou à l'ouverture de l'app. »** C'est une limite de la plateforme, pas un bug.

---

## 4. Phase 3 — Réécriture du module natif `photosync-bg` en Swift

Le module Expo `photosync-bg` est `platforms: ["android"]` only. Il faut ajouter le versant Apple.

### 4.1 Structure
- [ ] `modules/photosync-bg/expo-module.config.json` → ajouter `"apple"` :
  ```json
  {
    "platforms": ["android", "apple"],
    "android": { "modules": ["com.taaazzz.photosync.bg.PhotoSyncBgModule"] },
    "apple":   { "modules": ["PhotoSyncBgModule"] }
  }
  ```
- [ ] Créer `modules/photosync-bg/ios/` :
  - `PhotoSyncBgModule.swift` — expose `start()`, `stop()`, `setConfig(json)` (mêmes signatures que [index.ts](mobile/modules/photosync-bg/index.ts) ; `requireOptionalNativeModule` renverra le module sur iOS aussi).
  - `Uploader.swift` — portage de la logique [Uploader.kt](mobile/modules/photosync-bg/android/src/main/java/com/taaazzz/photosync/bg/Uploader.kt) : lecture config (UserDefaults au lieu de SharedPreferences), `pickBase` (failover multi-bases), calcul du dossier NAS (racine + Année/Mois/Jour, même tableau de mois FR), POST par fichier, **upload résumable par chunks** (≥24 Mo → tranches 6 Mo), **file de retry persistée** (bornée 500).
  - `Diag.swift` — équivalent de [Diag.kt](mobile/modules/photosync-bg/android/src/main/java/com/taaazzz/photosync/bg/Diag.kt) (ping diagnostic).
- [ ] **Parité du contrat config** : le JSON poussé par `setConfig` (`token`, `bases[]`, `rules[]`, `wifiOnly`) doit être lu **à l'identique** côté Swift → factoriser le format pour ne pas diverger d'Android.

### 4.2 Sémantique adaptée iOS (ne PAS porter le foreground service)
- `start()` côté iOS ≠ démarrer un service permanent. À la place :
  - enregistrer le `BGTaskScheduler` (identifiant ex. `com.taaazzz.photosync.sync`) — déclaré dans `Info.plist > BGTaskSchedulerPermittedIdentifiers` + `UIBackgroundModes: ["fetch", "processing"]`.
  - poser la baseline (`lastAdded`) comme le fait `initBaseline`.
  - configurer une `URLSession` background partagée (identifiant fixe, `isDiscretionary`, `sessionSendsLaunchEvents`).
- `stop()` : annuler la BGTask planifiée + invalider la session.
- L'upload doit se faire via la **`URLSession` background** (pas `URLSession.shared`) pour survivre à la fermeture. ⚠️ Conséquence : le chunking résumable « maison » d'Android (`HttpURLConnection` synchrone) doit être **repensé** — `URLSession` background n'aime pas les requêtes très longues ni le streaming custom ; privilégier des `uploadTask(with:fromFile:)` par chunk, état persistant entre réveils.

### 4.3 Plugin de config (pour injecter Info.plist sans `expo prebuild` manuel)
- [ ] Ajouter un **config plugin** au module (`app.plugin.js`) qui injecte `UIBackgroundModes`, `BGTaskSchedulerPermittedIdentifiers` et les clés ATS — pour que `eas build` (managed workflow) produise le bon `Info.plist` sans avoir à éjecter.

---

## 5. Dépendances Expo — audit iOS

| Package (présent) | iOS ? | Note |
|---|---|---|
| `expo-media-library` | ✅ | URIs `ph://` — tester la lecture fichier |
| `expo-image-picker` | ✅ | OK |
| `expo-file-system` | ✅ | OK |
| `expo-notifications` | ✅ | Prompt explicite obligatoire ; pas de push serveur prévu |
| `expo-background-task` | ✅ | Wrappe `BGTaskScheduler` — **réutiliser ça** plutôt que tout coder en Swift si la granularité suffit |
| `expo-task-manager` | ✅ | OK (support iOS background fetch) |
| `expo-network` | ✅ | Détection Wi-Fi pour `wifiOnly` |
| `expo-video` | ✅ | Lecture vidéo OK |
| `expo-updates` | ✅ | OTA marche sur iOS (runtimeVersion fingerprint déjà configuré) |
| `expo-intent-launcher` | ❌ | **Android only** — garder derrière `Platform.OS` |
| `expo-application` | ✅ | OK |

> 💡 **Piste à évaluer avant d'écrire du Swift** : `expo-background-task` + `expo-task-manager` couvrent peut-être 80 % du besoin (BGProcessingTask en JS). Le natif Swift n'est indispensable que si l'upload doit survivre à la **fermeture totale** via `URLSession` background — ce que le JS ne sait pas faire (même limite qu'Android, cf. [option-b-background-native]). **Faire un POC `expo-background-task` sur iOS d'abord** : s'il suffit, on évite la réécriture Swift complète.

---

## 6. Backend / agent Pi — impacts

- L'agent Node n'a **rien de spécifique Android** : les endpoints HTTP (upload, dedup nom+contenu, galerie, trash) marchent tels quels pour iOS.
- [ ] **CORS / User-Agent** : vérifier que l'agent n'a aucun filtrage par UA Android.
- [ ] **Réseau local iOS** : confirmer que les bases LAN (`192.168.1.136`, `.124`) + WireGuard sont joignables une fois la permission « Réseau local » accordée. Tester aussi le cas refus (fallback WireGuard).
- [ ] **HTTPS (option propre)** : la vraie solution pérenne au casse-tête ATS serait de mettre l'agent derrière **TLS** (certificat — Let's Encrypt via le domaine, ou CA interne). Ça supprimerait le besoin d'exceptions ATS et faciliterait la review App Store. À considérer si passage public store (lié à WIREGUARD-MIGRATION).

---

## 7. CI / Build / Distribution

- [ ] `eas.json` : les profils existants n'ont pas de bloc `ios` explicite — le défaut EAS gère le simulateur/device. Ajouter au besoin `"ios": { "simulator": false }` pour les builds device, et un `"buildConfiguration": "Release"` en production.
- [ ] **Distribution dev** : `eas build -p ios --profile development` → install via QR (device enregistré dans le provisioning ad-hoc, EAS gère).
- [ ] **Distribution famille** : `eas build -p ios --profile preview` puis `eas submit -p ios` → **TestFlight**. Pas d'« APK servi depuis le NAS » possible sur iOS ([apk-delivery-preference] ne s'applique pas) — le canal est TestFlight, point.
- [ ] **OTA** : `expo-updates` channel `preview`/`production` fonctionne iOS — mêmes mises à jour JS sans repasser par TestFlight (tant que le natif ne change pas ; sinon nouveau build).
- [ ] **Versioning** : ajouter `ios.buildNumber` et l'auto-increment EAS (`autoIncrement` est déjà à `true` en production).

---

## 8. Risques review App Store (à anticiper)

1. **HTTP clair / ATS arbitraire** : Apple demande une justification pour `NSAllowsArbitraryLoads`. « App communique avec un appareil local de l'utilisateur (NAS) » est une justification recevable, mais `NSAllowsLocalNetworking` (sans arbitrary loads) passe beaucoup mieux. → Privilégier `NSAllowsLocalNetworking`, garder arbitrary en dernier recours documenté.
2. **Permission Réseau local** : la `NSLocalNetworkUsageDescription` doit être claire ; rejets fréquents si floue.
3. **Background modes** : déclarer `processing`/`fetch` sans usage réel = rejet. Notre usage (sync NAS) est légitime, le décrire dans les notes de review.
4. **Fonctionnalité « cachée » / app dépendante d'un serveur** : l'app exige un NAS+agent pour faire quoi que ce soit → Apple doit pouvoir l'évaluer (cf. §8 bis).
5. **Self-update** : aucune app ne doit s'auto-mettre à jour hors App Store sur iOS → la branche `appupdate.js` doit être **inerte** sur iOS (sinon rejet immédiat).

> Décision : **viser l'App Store** (zéro-maintenance). Les risques ci-dessus sont gérables ; le seul vrai travail spécifique est de **rendre l'app évaluable** (ci-dessous).

## 8 bis. Rendre l'app évaluable par Apple (condition de l'App Store)

La review = scan automatique **+** un humain qui installe l'app et vérifie qu'elle fonctionne vraiment. Comme PhotoSync ne sert à rien sans un agent NAS joignable, il faut préparer ça :

- [ ] **Onboarding propre** : sans serveur configuré, l'app ouvre sur un **écran de configuration** clair (jamais un crash ni un écran blanc) — sinon le reviewer est bloqué → rejet.
- [ ] **Cible de démo joignable depuis l'internet public** : le reviewer est sur le réseau d'Apple, **pas** sur ton LAN ni ton VPN. Deux options :
  - **(a) Agent de démo exposé publiquement** le temps de la review (un Pi/NAS de test, ou l'agent en VPS) avec un **compte test** limité à **un dossier + quelques sous-dossiers** (exactement ce qu'attend Apple).
  - **(b) Mode démo intégré** (données factices, sans serveur) — plus de code, mais zéro infra à exposer.
- [ ] **Notes de review** (App Store Connect) : fournir l'URL de démo + token de test + un mot expliquant « l'app relie le téléphone au NAS **de l'utilisateur** ; le HTTP est local à son réseau ; aucune donnée ne transite par le développeur ».
- [ ] **Atout privacy** : « aucune donnée ne transite par nos serveurs » = privacy label quasi vide, argument fort pour l'approbation. **Ne pas** ajouter de logs/kill-switch vers un serveur du développeur : ça casse cet argument **et** ajoute des obligations de déclaration (cf. §8 ter).

## 8 ter. Ce qu'il NE faut PAS faire (sous peine de rejet)

- ❌ **Kill-switch / « détruire l'app à distance »** : techniquement impossible sur une app installée, et drapeau rouge pour Apple. À abandonner.
- ❌ **Logs d'accès envoyés vers le serveur du développeur** : te transforme en collecteur de données (déclaration privacy obligatoire) et casse le modèle « rien ne transite par chez moi ».
- ✅ **À la place** : la sécurité vit **côté agent de chaque utilisateur** (son Pi/NAS) — l'agent a déjà l'auth fail-closed par token + comparaison à temps constant ([pi-agent/src/server.js](pi-agent/src/server.js#L197-L213)) ; logs d'accès et révocation/rotation de token se font **là**, chez l'utilisateur. Voir [CONFIGURATION.md](CONFIGURATION.md).

---

## 9. Plan d'exécution séquencé (jalons)

1. **J1 — Comptes** : Apple Developer + App Store Connect + `eas credentials` iOS. *(bloquant, ~1 jour de validation Apple)*
2. **J2 — Build nu** : Phase 1 (§2). Build `development`, smoke test premier plan sur iPhone réel. *Critère de succès : upload manuel arrive sur le NAS.*
3. **J3 — POC background JS** : tester `expo-background-task` seul sur iOS (§5). Décider Swift ou pas. *Critère : une photo prise hors-app remonte au prochain réveil background.*
4. **J4 — Natif Swift (si nécessaire)** : Phase 3 (§4), `URLSession` background pour survie app-fermée. *Critère : upload lancé puis app tuée → l'upload se termine.*
5. **J5 — TestFlight** : `eas submit`, inviter testeurs (famille). Itérer OTA via `expo-updates`.
6. **J6 — Durcissement** : décision HTTPS sur l'agent (§6), nettoyage ATS, doc utilisateur sur les limites background.

---

## 10. Décisions

- [x] **Compte Apple Developer** : ✅ payant 99 $/an (fiabilité + débloque tout canal).
- [x] **Canal de distribution** : ✅ **App Store public** (zéro-maintenance) ; ad-hoc en repli.
- [x] **Mac de build/debug** : ✅ Mac + iPhone dispo → build natif et debug Swift locaux.
- [ ] **Niveau de background visé** : best-effort (`expo-background-task` pur JS, simple) vs natif Swift `URLSession` (survie app-fermée, lourd). → **À décider après le POC J3** (§5/§9).
- [ ] **HTTPS sur l'agent** maintenant (propre, supprime les exceptions ATS, facilite la review) ou exceptions ATS (rapide, suffisant en LAN). → Penche **HTTPS** vu la cible App Store ; à confirmer.

---

*Voir aussi : [WIREGUARD-MIGRATION.md](WIREGUARD-MIGRATION.md) (accès distant / TLS), [README.md](README.md) (architecture agent Pi ↔ app), et le module Android de référence [modules/photosync-bg/](mobile/modules/photosync-bg/).*
