Vos notes restent dans votre poche. L'IA aussi.
v1.0.4 — Mai 2026 · Politique de confidentialité · CGU · Sécurité
Application Android Flutter de prise de notes Markdown chiffrées,
100 % locale, zéro permission Internet. Interface bilingue FR / EN.
Coffres par dossier (passphrase Argon2id ou PIN Keystore-bound), recherche
sémantique on-device, Q&A Gemma 3 1B, dictée Whisper, backlinks [[note]],
mode panique multi-step.
- i18n FR / EN complète : 9 écrans + 14 widgets, sélecteur Langue dans Réglages, locale système suivie par défaut.
- Sélecteur de thème : Clair / Sombre / Système, persisté.
- Splits ABI : APK livré ~1/3 plus léger par device (arm64-v8a, armeabi-v7a, x86_64).
- Mode panique whitelist :
db_encrypted_v1etsecure_window_enabledpréservés pour cohérence du redémarrage (conformément à PRIVACY.md). - A11y P0 : streaming TalkBack + Semantics complets sur les bulles Q&A, le PIN pad, la sauvegarde.
- Helpers UI extraits :
PassphraseTextField,VaultWarningBanner,BlockingProgressDialog(factorisation cross-écrans). - Mentions légales : page rendue depuis
assets/legal/PRIVACY.{fr,en}.mdetTERMS.{fr,en}.mdviaflutter_markdown. - Performance :
Selectorciblé surthemeMode + locale,MediaQuery.sizeOfdans les bulles Q&A, scroll Gemma throttlé 80 ms.
Pour penseurs, thérapeutes, étudiants, chercheurs, écrivains et journalistes qui veulent prendre des notes sensibles ou denses sans qu'elles ne quittent jamais leur téléphone.
Différenciateur unique vs Notesnook / Obsidian / Bear / Logseq : l'IA tourne dans le téléphone, pas sur un serveur.
- Aucune permission
INTERNETdans le manifeste — vérifiable à l'œil nu (AndroidManifest.xml). Les 7 permissions transitives (INTERNET, ACCESS_NETWORK_STATE, WAKE_LOCK, RECEIVE_BOOT_COMPLETED, FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC, POST_NOTIFICATIONS) sont neutralisées viatools:node="remove". - Seule permission runtime :
RECORD_AUDIOsi vous activez la dictée. - Aucun compte, aucune inscription, aucun tracker, aucune publicité, aucune télémétrie.
- Open source Apache 2.0, code intégral vérifiable.
allowBackup=false+dataExtractionRulescomplet (pas d'exfiltration via Smart Switch ou Android Backup).- Modèles ML (Gemma, Whisper) importés via SAF — jamais bundlés, jamais téléchargés en réseau par l'app.
- Création / édition / auto-save debounced
- Épingler / favoris / archives / corbeille (rétention 30 j)
- Mode clair / sombre / système (palette GitHub)
- Tri configurable (modifié, créé, titre)
- Mode passphrase — Argon2id (m=64 Mo, t=3) + AES-256-GCM, KEK enveloppée par la clé maître Keystore (hardware-backed).
- Mode PIN — 4 à 6 chiffres, dérivation Argon2id allégée (m=32 Mo, t=2) + clé Keystore-bound dédiée par coffre, auto-wipe à 5 tentatives échouées (atomique, repris au boot si crash en cours).
- AAD partout :
folder_idlié au wrap KEK,note_idlié au contenu chiffré — anti rejeu / anti substitution. - HMAC verifier en temps constant pour détecter passphrase incorrecte sans déchiffrer toutes les notes.
- Auto-lock configurable (15 min par défaut, ou au pause).
- FTS5 instantané (tokenizer
unicode61, diacritiques normalisés). - Recherche sémantique on-device (opt-in) via
all-MiniLM-L6-v2-quant.onnx(~22 Mo, bundlé) — trouve des notes proches par le sens, cross-langue FR/EN. - Repli automatique sur encodeur n-grammes local si MiniLM est désactivé.
- Gemma 3 1B int4 (~530 Mo, importé via SAF), inférence MediaPipe.
- RAG : top-K sémantique injecté dans un prompt durci (délimiteurs
<note>+ sanitisation anti-injection). - Streaming token-par-token, conversation effaçable, aucun envoi réseau.
- Vérification SHA-256 obligatoire à l'import (rejet d'un
.tasknon vérifié, override explicite réservé aux utilisateurs avertis).
- Whisper on-device via le module sibling
files_tech_voice. - Modèles Whisper Base q5_1 (57 Mo) ou Tiny q5_1 (32 Mo), importés via SAF (téléchargement par le navigateur système, pas par l'app).
- Vérification SHA-256 systématique avant chargement, cache TTL 30 j.
- Audio jamais persisté (tmp + delete dans tous les chemins).
- MlMemoryGuard : mutex sériel Gemma ↔ Whisper anti-OOM sur 4 Go RAM.
- Liens
[[Titre]], auto-complétion, panneau Mentions / liens sortants. - Réindexation différée 2 s : seule la note modifiée est retraitée (O(1) par save, batching transparent).
- Liens fantômes auto-résolus à la création / au renommage de la cible.
- Export d'une note :
.mdavec frontmatter YAML compatible Obsidian, Logseq, Bear, Foam, Dendron. - Export ZIP global : arborescence par dossier + README d'export.
- Encodage en isolate (
compute()), nom de fichier durci anti-path- traversal et anti-Unicode-bidi.
- Réglages → Mode panique → Tout effacer maintenant.
- Confirmation par mot tapé (
EFFACER). - Séquence ordonnée et best-effort (un step qui throw n'interrompt
pas les suivants) :
FLAG_SECUREforcé ON- Capture micro coupée
foldersLockAll— verrouille tous les coffres ouvertspinKeysWipe— supprime toutes les clés Keystore PIN (deleteKeysWithPrefixcôté Kotlin)kekDestroy— détruit la clé maître Keystore (DB instantanément illisible)- Pause des background workers
dbWipe— écrase header SQLCipher 16 Mo + delete + sidecars- Effacement Whisper, Gemma, préférences, tmp
- Activé par défaut : pas de capture d'écran ni d'aperçu dans les apps récentes.
- DB SQLCipher chiffrée AES-256-GCM, clé maître scellée par AndroidKeystore (hardware-backed sur S24).
- KEK Keystore-bound CSPRNG 32 octets, dérivation Argon2id côté passphrase, scellage Keystore direct côté PIN.
- AAD partout :
folder_idpour le wrap KEK,note_idpour le contenu — empêche la réutilisation d'un blob chiffré dans un autre contexte. - HMAC verifier en temps constant pour détecter une mauvaise passphrase / un mauvais PIN sans test exhaustif des notes.
- Mode PIN avec auto-wipe : 5 tentatives, flag prefs atomique, reprise au boot si interruption.
- Mode panique ordonné :
foldersLockAll → pinKeysWipe → kekDestroy → dbWipe, garantit que la KEK disparaît avant la base. - Wipe DB header 16 Mo (la KEK destroy précédente garantit déjà le
secret ; l'écrasement complet n'apporte rien sur eMMC moderne avec
wear-leveling — décision de design, voir
SECURITY.md). setUserAuthenticationRequired(false)sur la clé Keystore PIN : le PIN applicatif est l'unique facteur, le doubler avec biométrie l'exposerait à la contrainte (clé biométrique survit au reboot).- FLAG_SECURE par défaut.
allowBackup=false,dataExtractionRulesdurci.
Voir SECURITY.md pour le modèle de menace complet et
la procédure de signalement de faille.
| Permission | État | Usage |
|---|---|---|
INTERNET |
REMOVED (tools:node="remove") |
aucun |
ACCESS_NETWORK_STATE |
REMOVED | aucun |
WAKE_LOCK |
REMOVED | aucun |
RECEIVE_BOOT_COMPLETED |
REMOVED | aucun |
FOREGROUND_SERVICE |
REMOVED | aucun |
FOREGROUND_SERVICE_DATA_SYNC |
REMOVED | aucun |
POST_NOTIFICATIONS |
REMOVED | aucun |
RECORD_AUDIO |
runtime, opt-in | uniquement si dictée Whisper activée |
À auditer sur chaque release via aapt dump permissions.
Ce dépôt n'a pas de pipeline de release CI automatique. Deux options :
- Build local (recommandé pour audit) — voir section suivante.
- APK release manuelle : récupérer le dernier
.apkpublié sur GitHub Releases (v1.0.0), vérifier la signature, side-loader.
Pas de Play Store : distribution side-load uniquement (cohérent avec la promesse de confidentialité — aucun compte requis pour installer).
flutter pub get
flutter analyze
flutter test
flutter build apk --release --split-per-abi --obfuscate \
--split-debug-info=build/symbolsPour une release strictement signée (pas de fallback debug), créer
android/key.properties :
storeFile=/chemin/absolu/vers/votre.jks
storePassword=...
keyAlias=...
keyPassword=...
APK release arm64 : ~327 Mo (MiniLM ONNX bundlé, runtimes ML, SQLCipher — Gemma et Whisper téléchargés séparément, non bundlés).
Pré-requis :
- Flutter 3.x (Dart
^3.11.5) - Android SDK + NDK installés via Android Studio
- Module sibling
files_tech_voiceà../files_tech_voice(clone le repo à côté denotes_tech/)
lib/
├── main.dart # bootstrap parallèle + DI Provider
├── app.dart # MaterialApp
├── core/ # constants, exceptions, theme
├── data/
│ ├── models/ # Note, Folder, NoteEmbedding,
│ │ NoteLink, NoteChangeEvent
│ ├── db/ # SQLite (FTS5 + sqlcipher), DAOs
│ └── repositories/ # façades + streams typés
├── services/
│ ├── embedding/ # EmbeddingProvider, LocalEmbedder,
│ │ MiniLmEmbedder, BertTokenizer
│ ├── ai/ # GemmaService (SHA-256), RagService
│ ├── security/ # VaultService (KEK Keystore +
│ │ passphrase/PIN), PanicService
│ ├── secure_window_service.dart # FLAG_SECURE via MethodChannel
│ ├── indexing_service.dart # worker idempotent (hash diff)
│ ├── embedder_coordinator.dart # swap Local ↔ MiniLM à chaud
│ ├── semantic_search_service.dart # top-K cosine, cache invalidé
│ ├── backlinks_service.dart # parsing [[]], reindex différé 2s
│ ├── note_actions.dart # actions UI réutilisables
│ └── settings_service.dart
├── ui/
│ ├── screens/ # home, editor, search, ai_chat,
│ │ settings, about, vault_unlock
│ └── widgets/ # NoteCard, BacklinksPanel, ...
└── utils/ # debouncer, hash_utils, vector_math
- Flutter 3.x / Dart
^3.11.5 sqflite_sqlcipher(SQLite chiffré AES-256 + FTS5)flutter_secure_storage(KEK scellée AndroidKeystore)cryptography(Argon2id RFC 9106 + AES-GCM, Dart pur)crypto(SHA-256 streaming pour vérification modèles)onnxruntime(MiniLM L6 v2 quantifié)flutter_gemma(Gemma 3 1B int4)files_tech_voice(sibling, Whisper STT)provider,shared_preferences,archive,share_plus,url_launcher- Aucune dépendance réseau
- Samsung Galaxy S24 / S24 FE (validés)
- Samsung S9, POCO C75 (validés en mode dégradé)
- minSdk 23 (Android 6+)
Apache License 2.0 — voir aussi NOTICE et
THIRD_PARTY_NOTICES.md.
Notes Tech fait partie de la suite Files Tech (toutes 100 % locales) :