L'onboarding Tor a été refait de zéro : détection live d'Orbot toutes les 3 secondes, 4 états visuels distincts, lancement direct d'Orbot via intent Android, et un indicateur 🧅 visible dans la nav quand le mode est on. Trois panneaux pédagogiques expliquent ce que « activer Tor » et « ne pas activer » veulent vraiment dire.
Le problème
Avant la v1.6.2, l'app supportait déjà Tor techniquement : PinnedHttpOverrides route le HttpClient global vers 127.0.0.1:9050 quand le toggle est on, et AppConfig.baseUrl bascule automatiquement sur l'adresse .onion. Mais l'option était planquée dans Réglages → Sécurité, accessible une fois l'inscription terminée, sans contexte sur ce que ça apportait.
Résultat : 0 utilisateur sur ~1500 avait le mode Tor activé. C'est symétrique du problème Signal des stickers chiffrés — tant que l'option n'est pas présentée clairement au bon moment, personne ne la trouve.
4 états visuels
La nouvelle page d'onboarding a un status card qui change en temps réel. Un Timer.periodic(3s) ping 127.0.0.1:9050, et l'UI rerend l'état :
Orbot absent
Bouton « Installer Orbot » (F-Droid)
Orbot prêt
Bouton « Activer Tor maintenant »
Tor actif
Trafic anonymisé, indicateur 🧅 visible
Lien rompu
Tor on mais Orbot stoppé — bouton de relance
Le bouton primaire change avec l'état. Pas trois boutons cachés qu'il faut deviner : un seul bouton, qui dit ce qu'il va faire en fonction de où tu en es. C'est l'invariant principal.
Lancer Orbot sans quitter l'app
Quand Orbot est installé mais éteint, il faut le démarrer. Plutôt qu'imposer à l'utilisateur de quitter Nøbody, ouvrir le tiroir d'apps, etc., on lance Orbot via un intent Android avec url_launcher :
// lib/services/tor_service.dart
Future<bool> openOrbot() async {
if (!Platform.isAndroid) return false;
final intent = Uri.parse(
'intent:#Intent;action=android.intent.action.MAIN;'
'category=android.intent.category.LAUNCHER;'
'package=org.torproject.android;end',
);
if (await canLaunchUrl(intent)) {
return await launchUrl(intent, mode: LaunchMode.externalApplication);
}
return false;
}
Sur Android 11+ canLaunchUrl exige que le package cible soit listé dans <queries> du manifeste. On a ajouté :
<!-- AndroidManifest.xml -->
<queries>
<package android:name="org.torproject.android" />
</queries>
Pédagogie : 3 panneaux honnêtes
On ne peut pas demander à un utilisateur d'activer Tor sans lui dire ce que ça fait, ce que ça coûte, et ce qui se passe s'il ne le fait pas. La page contient donc 3 panneaux colorés sous le bouton primaire :
- Avec Tor activé — le serveur ne voit jamais ton IP, routage automatique vers les .onion, couche d'anonymat supplémentaire en plus du chiffrement E2E.
- Si tu n'actives pas — le serveur voit ton IP au moment de la connexion, mais nginx la jette aussitôt (
error_log /dev/null,X-Forwarded-Forvidé). Le contenu reste chiffré E2E. Tor ajoute juste une protection contre l'analyse de trafic et un adversaire réseau (FAI, État, Wi-Fi public). - Caveat UnifiedPush — ton distributeur (ntfy, Gotify) gère les notifs hors-Tor. Si tu utilises Tor sérieusement, choisis un distributeur compatible Tor ou désactive les notifs pour ne rien laisser fuiter.
Honnêteté rédactionnelle. Beaucoup d'apps « privacy » sur-vendent leur mode Tor en prétendant qu'il fait disparaître tout risque. Faux. Tor protège contre l'observateur réseau, pas contre un compromis de l'application elle-même, ni contre des bugs côté serveur. La page le dit : « Tor ajoute une protection contre l'analyse de trafic », pas « Tor te rend invisible ».
L'indicateur 🧅 dans la nav
Une fois Tor activé, on veut que l'utilisateur sache à tout moment qu'il est dans ce mode. Un nouveau widget TorIndicator est droppé dans la AppBar du Feed :
- Petit pill 🧅 Tor — vert si Orbot répond, ambre si Orbot a stoppé.
- Tap → ouvre Réglages → Sécurité pour désactiver / fix d'un coup.
- Render
SizedBox.shrink()quand Tor est off — le widget est sûr à drop dans n'importe quelleAppBarsans vérification préalable.
Localisation : 25 clés × 10 langues
25 nouvelles clés ARB ont été ajoutées dans les 10 langues supportées : FR, EN, DE, ES, IT, PT, TR, AR, RU, UK. Pas de fallback — chaque langue a son texte propre, y compris l'arabe RTL et les cyrilliques russe/ukrainien. Le script check-l10n-sync.py confirme : 798 clés × 10 locales, 0 missing / 0 extras.
Et après ?
L'objectif moyen-terme est d'embarquer un binaire tor directement dans l'app (FOSS, distribué sous licence BSD), pour que les utilisateurs n'aient même plus à installer Orbot. Ce sera la prochaine grosse itération sur le mode anonyme — « Tor par défaut, sans configuration » au lieu de « Tor opt-in après un install séparé ».
Code source de toute la feature : onboarding.dart, tor_service.dart, tor_indicator.dart.