Memories
Client Android/iOS pour Memories. L'interface Flutter appelle le service Rust partagé via crates/ffi/memories.h ; elle n'implémente aucun protocole distinct de stockage ou de cryptographie.
Choisir un mode
Au premier lancement, y compris après une mise à niveau depuis une version sans préférence de mode, choisissez où vous souhaitez travailler :
| Mode | Emplacement des données | Ce qu'il vous faut |
|---|---|---|
| Standalone | Bibliothèque chiffrée sur ce téléphone ; fonctionne hors ligne | Créer une bibliothèque locale ou déverrouiller celle qui existe |
| Center | Stockage hébergé de votre serveur auto-hébergé | Origine du serveur telle que https://center.example.com et jeton de membre |
| SaaS | Stockage hébergé à https://memories.ivjn.us |
Jeton de membre pour ce service ; l'URL est fixe |
Le mode Center de l'application désigne un serveur fonctionnant avec le stockage hébergé activé (memories-center --data DIR). Il diffère d'un Center d'autorisation limité aux tickets, qui distribue des tickets signés pour les appareils locaux. Center et SaaS utilisent le même client hébergé ; ils diffèrent par la sélection du point d'accès et le profil de connexion enregistré. Aucun des deux n'exige de créer ou déverrouiller d'abord une bibliothèque locale.
Première connexion
- Sélectionnez un mode. L'application mémorise ce choix pour le prochain lancement.
- Pour Standalone, créez ou déverrouillez votre bibliothèque. Pour Center, saisissez l'origine HTTPS du serveur sans chemin d'API ; pour SaaS, conservez l'adresse fixe du service.
- Dans l'un ou l'autre mode hébergé, saisissez le jeton de membre fourni par l'opérateur de votre service et choisissez Se connecter. Le rôle et le périmètre du membre déterminent ses actions possibles ; utilisez un jeton de membre pour travailler sur le contenu, pas un jeton d'administration d'opérateur.
- Après la connexion, ouvrez Connaissances hébergées pour parcourir, rechercher, consulter, modifier ou supprimer des connaissances. Vous pouvez aussi examiner les demandes ou ouvrir Appareils appairés. L'adresse du serveur, le coffre actuel et le périmètre accordé restent visibles.
Le contenu hébergé est chiffré au repos avec un mot de passe détenu par l'opérateur, mais celui-ci peut le lire. L'application affiche Le serveur peut lire. Les secrets scellés entre appareils ont une limite de protection de bout en bout distincte.
Changer de mode, se déconnecter et se reconnecter
Utilisez Changer de mode dans la barre d'outils principale avant ou après la connexion. La boîte de dialogue indique le mode actuel et explique l'effet ; sélectionnez un autre mode pour changer, ou Annuler pour conserver la session. Terminez ou quittez d'abord un éditeur ouvert ; quitter un brouillon non enregistré demande confirmation.
| Action | Résultat |
|---|---|
| Changer de mode | Verrouille la bibliothèque locale, arrête le partage, ferme la connexion hébergée active et efface le contenu visible ; enregistre le nouveau mode |
| Revenir à Standalone | Rouvre l'écran de bibliothèque locale verrouillé ; les données existantes restent présentes |
| Revenir à Center ou SaaS | Restaure la connexion enregistrée de ce mode si elle est disponible |
| Se déconnecter | Retire le jeton enregistré du profil actuel et ferme sa session ; l'URL et le profil de l'autre mode restent présents |
| Passer l'application en arrière-plan | Verrouille l'accès local, efface les sessions hébergées et ferme les pages sensibles et les brouillons |
| Revenir de l'arrière-plan | Utiliser Reconnecter la session enregistrée sur la page hébergée, ou saisir un jeton et se reconnecter |
Le changement de mode n'envoie, ne copie, ne fusionne et ne supprime aucune donnée de bibliothèque. La déconnexion ne révoque pas le jeton sur le serveur. Center et SaaS conservent leurs paires URL/jeton dans des entrées distinctes du stockage sécurisé de la plateforme. Lors d'une mise à niveau, l'ancienne connexion hébergée est importée uniquement dans le mode correspondant ; voir configuration et migration.
Fonctionnalités disponibles
| Fonctionnement | Standalone | Center / SaaS |
|---|---|---|
| Création, sauvegarde et restauration de bibliothèque locale | Disponible | Utiliser le service hébergé ; aucune configuration locale nécessaire |
| Parcourir, rechercher, consulter, modifier et supprimer des connaissances | Disponible après déverrouillage | Connaissances hébergées, avec actions limitées par le périmètre du jeton |
| Créer des cartes de connaissance | Éditeur local pleine page | Éditeur hébergé pleine page avec les champs acceptés par l'API hébergée |
| Examiner les demandes d'autorisation hébergées | Passer à Center ou SaaS | Onglets en attente/historique, confirmation d'approbation/rejet et actualisation |
| Appairage d'appareils | Procédure locale d'approbation d'appareils/membres | Appairage hébergé, secrets scellés et contrôles d'appareil, selon les permissions |
| Partage par relais et connexions cloud | Disponible via les contrôles de bibliothèque locale | Non exposé sur la page hébergée |
La création hébergée omet les étiquettes, la source et les relations ; la modification d'une carte ordinaire existante prend en charge ces champs et préserve les autres métadonnées. Les mémoires ordinaires utilisent l'éditeur de texte. Les profils structurés, les cartes protégées, les modèles de types et les cartes E2E sont uniquement consultables dans cet éditeur général. Les deux éditeurs conservent un brouillon si l'enregistrement échoue ; les brouillons ne persistent pas après le verrouillage de l'application ou son passage en arrière-plan. Voir modification du contenu et autorisation.
Résolution des problèmes de connexion
L'application valide les jetons via /v1/session avant d'enregistrer une connexion, même pour les comptes en écriture seule. Mettez à niveau les serveurs auto-hébergés en même temps que l'application. Les comptes en lecture seule peuvent parcourir sans contrôles de mutation ; ceux en écriture seule peuvent créer sans charger la bibliothèque ou la liste des types. Le serveur autorise à nouveau chaque opération. La recherche exclut les contenus privés, les identifiants et les fichiers ; effacez la requête pour revenir à la liste des connaissances. Les sessions expirées/révoquées, les permissions insuffisantes et les serveurs indisponibles produisent des messages d'erreur localisés. Un 401 exige une déconnexion puis une connexion avec un jeton valide.
- Connexion impossible : vérifiez l'origine HTTPS complète, le réseau, le certificat et le jeton. Un Center auto-hébergé doit activer le stockage hébergé ; un service limité aux tickets ne peut pas fournir cet écran.
localhostsur un téléphone désigne le téléphone lui-même. - Permission refusée : vérifiez le coffre, le rôle, le périmètre et l'expiration du jeton avec l'opérateur du service. Sélectionner un autre mode n'accorde aucun accès.
- Aucune demande en attente : actualisez et vérifiez l'acheminement demandeur/coffre/approbateur. L'onglet historique n'est pas un journal d'audit complet du serveur.
- Session fermée après passage en arrière-plan : reconnectez-vous explicitement. Les brouillons non enregistrés ont été fermés dans le cadre du verrouillage et ne peuvent pas être restaurés.
- Le service natif n'a pas pu démarrer : utilisez une version contenant la bibliothèque Rust correspondante. Les modes hébergés utilisent aussi la FFI et nécessitent la bibliothèque native.
Développement
Construisez d'abord les bibliothèques natives avec scripts/build-android.ps1 ou scripts/build-ios.sh depuis la racine du dépôt. Voir le README racine pour les outils requis et les instructions de signature.
Utilisez Flutter 3.44.2, comme la CI. Depuis ce répertoire :
flutter pub get
flutter gen-l10n
dart format --output=none --set-exit-if-changed lib test integration_test
flutter analyze
flutter test
flutter test integration_test/native_smoke_test.dart -d DEVICE_ID
flutter run -d DEVICE_ID
Les tests de widgets utilisent un backend factice pour exercer le cycle de vie de l'écran de verrouillage, la sélection/le changement de mode, l'isolation des sessions hébergées, les cartes, la localisation et les parcours de références secrètes. Les tests de presse-papiers simulent le canal de plateforme Flutter ; ils ne vérifient pas la livraison au presse-papiers du système d'exploitation. Le test d'intégration charge la véritable bibliothèque native et crée une base jetable sur l'appareil. Les exécutions normales utilisent le répertoire de support applicatif de la plateforme et un facteur local protégé par Keychain/Keystore. Utilisez des données de test pendant le développement ; voir SECURITY.md pour les limites de confiance actuelles.
Secrets des documents et presse-papiers
En mode Standalone, ouvrez le menu d'une mémoire/carte et choisissez Protéger un secret dans le document pour remplacer une valeur exacte de son corps actuel par une référence nommée. Confirmez l'avertissement concernant l'historique, saisissez le nom de référence et le secret masqué, puis autorisez lorsque cela est demandé. Le service Rust stocke l'identifiant et remplace le corps dans une seule transaction.
Choisissez Copier le secret sur un document, une carte protégée ou un identifiant pour copier sa valeur actuelle après confirmation et autorisation. Les documents affichent d'abord un sélecteur de référence. Les cartes OTP copient la graine ; leur action OTP distincte génère un code à usage unique. Les valeurs ne sont pas affichées, et les réponses tardives après passage en arrière-plan ou verrouillage sont écartées. Une copie terminée dans le presse-papiers peut persister après le verrouillage.
Les modifications ordinaires préservent les associations de références. Les anciennes versions des documents et les sauvegardes existantes conservent leur contenu antérieur. Voir le guide des secrets du wiki pour les règles de référence, les mises à jour, la compatibilité et les limites du presse-papiers. Reconstruisez la bibliothèque native avec l'application Flutter lors de l'ajout de ces opérations.
Localisation
Utilisez l'icône de langue dans la barre de l'application pour sélectionner l'anglais, le chinois simplifié, le japonais, l'allemand, le français ou la langue par défaut du système. La préférence est stockée séparément de la bibliothèque chiffrée et reste disponible lorsqu'elle est verrouillée. Le changement de langue préserve la session actuelle de bibliothèque.
Les traductions résident dans lib/l10n/app_*.arb ; flutter gen-l10n produit le répertoire ignoré lib/l10n/generated/. Les contrôles Material et Cupertino utilisent les délégués de localisation Flutter. Suivez le guide de localisation lors de l'ajout de messages ou de langues.
Partager une session (hôte RPC par relais)
En mode Standalone, lorsque la bibliothèque est déverrouillée et au premier plan, le menu supplémentaire propose Partager une session.
Il rejoint wss://…/v1/rpc/{room}/a avec un jeton à forte entropie afin qu'une CLI de bureau puisse se connecter à l'emplacement b (MEMORIES_DAEMON_RELAY / MEMORIES_DAEMON_ROOM / MEMORIES_DAEMON_TOKEN). Le passage en arrière-plan ou le verrouillage arrête la session. Le MVP ne propose aucune écoute HTTP/WS sur mobile ; la liste d'interdiction de la FFI native reste applicable. Voir démon distant.
Déverrouillage biométrique et actions sensibles
Dans la bibliothèque Standalone déverrouillée, utilisez le menu supplémentaire → Déverrouiller avec la biométrie / le verrouillage de l'appareil (désactivé par défaut ; l'activation exige un contrôle système). lib/biometric.dart encapsule local_auth (BiometricPrompt sur Android avec repli sur PIN/schéma/mot de passe de l'appareil ; Face ID / Touch ID sur iOS, NSFaceIDUsageDescription dans Info.plist). MainActivity étend FlutterFragmentActivity comme l'exige local_auth.
- Le mot de passe de bibliothèque destiné au déverrouillage rapide est stocké avec
AndroidOptions.biometric(resetOnError: false): une clé AES Android Keystore avecsetUserAuthenticationRequired(true)etsetInvalidatedByBiometricEnrollment(true). Sur iOS, l'élément Keychain utiliseKeychainAccessibility.passcode. - Actions soumises au contrôle : déverrouiller la bibliothèque, copier/révéler les identifiants en clair, utiliser les cartes protégées / OTP, approuver l'appairage et les demandes Hosted Center, sceller et resceller.
- Lorsque l'inscription biométrique change (
KeyPermanentlyInvalidatedException), l'application informe l'utilisateur, revérifie avec l'identifiant de l'appareil, supprime uniquement la copie obsolète de déverrouillage rapide et recrée la clé après un déverrouillage par mot de passe. La bibliothèque n'est jamais effacée. - Sans biométrie/identifiant d'appareil, ou lorsque l'option est désactivée : le parcours existant par mot de passe.
- Les tests simulent
DeviceAuth/UnlockSecretStore(test/biometric_test.dart).
Éditeur de contenu
lib/content_editor.dart est partagé par les nouvelles mémoires, les identifiants et les entrées Hosted Center : import de fichiers (titre issu du nom de fichier), mode à chasse fixe pour les contenus de type .env (détecté automatiquement ou activé manuellement), et nom privilégiant le contenu, déduit du nom de fichier ou de la première ligne non vide, demandé uniquement à l'enregistrement s'il est encore vide. L'écriture manuscrite (stylo/gomme/annuler/effacer, ML Kit Digital Ink) et l'OCR (caméra/photos/fichier, reconnaissance de texte ML Kit) fonctionnent sur l'appareil ; voir écriture manuscrite et OCR. Les tests de widgets injectent des substituts ScanServices.
Les mémoires et cartes de connaissance restent ouvertes jusqu'à la réussite de l'enregistrement. Une écriture échouée conserve le brouillon pour une nouvelle tentative ; quitter avec des changements demande confirmation. Les brouillons sont conservés uniquement en mémoire et restent supprimés lors du passage en arrière-plan ou du verrouillage pour inactivité. L'import de fichier demande confirmation avant de remplacer le texte existant. Les mémoires exposent leur réglage privé/ordinaire avant l'enregistrement, et les nouvelles tentatives de pièces jointes ignorent les fichiers déjà enregistrés pendant la modification actuelle.
Les cartes de connaissance utilisent un éditeur pleine page avec une action d'enregistrement persistante, des champs personnalisés, des étiquettes, des sources et des relations. Modifier le contenu ouvre l'éditeur de texte partagé pour l'import de fichiers, l'écriture manuscrite et l'OCR. La création hébergée expose uniquement les métadonnées acceptées par son API mobile existante.
Autorisation sur mobile
Les pages Center et SaaS séparent les demandes en attente de l'historique. Examinez le demandeur, le coffre, le périmètre d'accès, la durée et le motif complet avant de confirmer une approbation ou un rejet. L'approbation utilise toujours le contrôle existant d'authentification de l'appareil, et le demandeur doit réclamer l'autorisation séparément. Les erreurs préservent la demande pour une nouvelle tentative ; tirez pour actualiser ou utilisez le bouton d'actualisation.
L'API de demandes du Center ne renvoie pas un historique complet des décisions. L'onglet historique combine les décisions prises pendant la connexion actuelle avec les demandes approuvées renvoyées par le Center. Ces enregistrements locaux sont effacés en quittant la page. Les nouvelles connexions échouées sont fermées sans remplacer le profil enregistré.
La gestion locale des membres affiche uniquement les actions permises par les rôles affichés. Les confirmations identifient le membre et l'ancien/le nouvel accès. Le service Rust revérifie la politique actuelle ; le filtrage de l'interface n'accorde aucune autorité. L'appartenance Owner ne peut pas être modifiée par les actions ordinaires de rôle, et seuls les Owners voient le transfert de propriété.
Identité visuelle
Source du logo : assets/brand/logo.svg (+ logo-mono.svg, adaptatif logo-foreground.svg). Les icônes et images de démarrage ont été rendues avec rsvg-convert ; régénérez les mêmes tailles lorsque le logo change.