WebView Capacitor autour de l'app rendue
L'app est un PWA Next.js rendu côté serveur (server components, server actions, route de streaming du copilote, middleware d'auth). Elle ne peut pas être exportée en bundle statique embarqué : la coquille native iOS charge donc l'app en cours d'exécution sur HTTP. En dev, localhost:4003 ou l'IP LAN de ton Mac ; en prod, le domaine HTTPS m.freelance-os.fr. Le bundle iOS est fr.freelanceos.app, la coquille laisse le contenu web gérer les env(safe-area-inset).
Entièrement sur l'API publique
Chaque écran lit et écrit via @fos/api-client. withApiAuth prend la session Supabase courante, en extrait le JWT et le passe à l'API sur chaque appel ; le workspace part en paramètre de chemin {workspaceId}. Les tâches, contacts, deals, interactions, réponses inbox, agenda, résumé analytics, revenu, funnel et sauvegarde des tours du copilote transitent tous par cette surface. Rien ne court-circuite l'API pour parler à la base en direct côté données.
- Tâches : créer, modifier, changer de statut, supprimer
- CRM : contacts, deals, journal d'interactions, marquer un rendez-vous honoré ou non
- Inbox : répondre sur un fil LinkedIn (envoi via Unipile quand connecté, sinon écrit en local)
- Copilote : réponse en streaming plus sauvegarde du tour dans le stock partagé du Kernel
- Stats : résumé, revenu et funnel sur une période choisie dans l'URL
Sept surfaces, une barre d'onglets
Aujourd'hui et Copilote sont toujours là : tout membre a droit à un accueil et à un assistant. Les onglets Tâches, Agenda et CRM apparaissent selon la permission qui les débloque (workbench.read, booking.read, contacts.read). Inbox et Stats vivent dans l'écran Plus et dans le tiroir, gardés par leurs propres droits. L'écran Aujourd'hui fait un seul passage serveur : agenda du jour, tâches en focus, snapshot analytics 7 jours.
Gating par rôle, argent redacté
Les clés de permission viennent de @fos/domains-identity/permissions, le registre canonique que la base persiste et que le Kernel applique aussi. Les onglets qu'un rôle ne peut pas ouvrir sont masqués, pas juste désactivés, pour qu'un setter ne tape jamais dans une surface qui le renverrait vers Aujourd'hui. Les chiffres monétaires (revenu encaissé, MRR) ne sortent que pour les rôles finance ; un setter avec funnel.read voit le funnel mais jamais le revenu.
Auth locale et bascule de workspace
Pas de résolution host vers workspace comme dans l'app membre : le propriétaire choisit son workspace actif dans l'app, épinglé par cookie. Le changement de workspace revalide l'appartenance côté serveur, donc un id forgé ne peut jamais faire entrer l'utilisateur dans un workspace qui n'est pas le sien. L'édition du nom d'affichage est scopée sur l'id d'auth de l'appelant, jamais sur un id fourni par le client.
Hors ligne et packaging iOS
Un service worker sert une page hors ligne quand une navigation échoue sans réseau. La coquille iOS est générée par la CLI Capacitor et signée via une clé API App Store Connect. Un compte démo (appreview@freelance-os.fr) sur un workspace de démonstration sert la revue Apple. Cette version est soumise à la revue App Store : le PWA tourne en prod, la coquille native attend la validation d'Apple.