Vos notes restent dans votre poche. L'IA aussi.
Application Android Flutter de prise de notes Markdown chiffrées,
100 % locale, zéro permission Internet. 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.
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 (v0.9.4), 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) :