Documentation technique de Memories
Ce document décrit le stockage actuellement implémenté, les limites de confiance, les autorisations locales, le Center hébergé, les secrets scellés, les flux de données de recherche, la synchronisation d'objets cloud et les limites de l'état du bureau. Il décrit le même comportement que SECURITY.md, docs/architecture.md, docs/authorization.md et docs/center.md. Le format de stockage est en version 1. Il s'agit d'un logiciel de développement ; la réussite des tests ne vaut pas audit de sécurité indépendant.
Stockage et limites de confiance
Les tables de la base de données locale sont header, state et checkpoints. Hormis les métadonnées du format, les sels et les numéros de ligne, les valeurs sont du texte chiffré avec données associées. Les journaux contiennent également du texte chiffré. Aucun index de texte intégral en clair n'est persisté.
Le champ version de l'en-tête du format doit être 1, sinon l'ouverture échoue. Le mot de passe utilisateur est traité par Argon2id (version 0x13, 64 MiB de mémoire, 3 itérations, parallélisme 1) avec un sel de 16 octets pour dériver une clé de 32 octets. Cette clé enveloppe une clé maîtresse aléatoire avec les données associées memories/master/v1. La clé maîtresse chiffre l'état et les points de contrôle locaux. Le chiffrement symétrique utilise XChaCha20-Poly1305.
Chaque epoch de permissions possède sa propre clé de coffre-fort. Chaque révision possède une clé de données aléatoire enveloppée par la clé de cet epoch. Le contenu sérialisé est découpé en blocs de 256 KiB authentifiés individuellement ; la position des blocs et leur nombre total font partie des données associées. La signature de la révision couvre l'ID de ressource, les versions parentes, la tête de politique, le marqueur de suppression, le texte chiffré et le certificat de l'appareil. L'algorithme de signature est Ed25519.
Les modifications sont d'abord préparées sur un état cloné, puis l'état chiffré et le point de contrôle sont validés dans une même transaction SQLite avec synchronous=FULL. Les intentions de lecture et d'exécution sont validées avant livraison. Une défaillance du disque entraîne donc un refus de livraison ; un plantage après validation de l'intention peut laisser un résultat inconnu. Un verrou exclusif du système d'exploitation limite chaque répertoire de données à un seul auteur coopératif. L'initialisation écrit d'abord entièrement la base dans le même répertoire, la ferme et la synchronise, puis la publie à l'emplacement cible sans écraser une cible existante. Chaque appareil utilise son propre stockage. Faire synchroniser par un client cloud un fichier SQLite en cours d'utilisation n'assure pas la sécurité transactionnelle entre appareils.
La clé Ed25519 de l'utilisateur certifie la clé de signature Ed25519 et la clé d'échange X25519 de l'appareil. Le certificat seul ne suffit pas : chaque politique de coffre-fort doit également autoriser explicitement l'appareil. Les clés des différents appareils d'un même utilisateur sont indépendantes. La capacité initiale de signature de l'utilisateur n'est transmise aux appareils personnels approuvés que dans un paquet d'approbation authentifié et chiffré pour le destinataire. Après révocation d'un appareil, la clé d'identité utilisateur ne peut pas contourner la liste d'appareils autorisés du coffre-fort.
Une enveloppe de transport contient la version du format, la clé de l'appareil émetteur, la clé d'échange du destinataire, une clé publique X25519 éphémère, une charge utile AEAD et une signature Ed25519. La clé de transport est dérivée par HKDF. L'identité du destinataire et la clé éphémère sont authentifiées. L'ajout d'un appareil utilise une demande signée à durée limitée et fixe explicitement la clé publique de l'appareil approbateur. La demande n'est consommée qu'après une validation réussie. L'empreinte utilisée pour l'approbation est le SHA-256 complet. Les valeurs raccourcies affichées dans les listes ne suffisent pas à approuver.
La politique est un historique linéaire signé contenant membres, rôles, listes d'appareils autorisés, ensemble de révocations ne pouvant que croître et epochs. Les rôles sont vérifiés à partir de la politique précédente. Un utilisateur ordinaire peut autoriser un appareil supplémentaire pour sa propre identité sans modifier son appartenance. Seuls Admin ou Owner peuvent inviter des membres, approuver leur arrivée et autoriser leurs appareils. Admin peut modifier les membres Admin, Editor et Reader, mais ne peut transformer personne en Owner, modifier un Owner existant, révoquer des appareils ou transférer la propriété. Owner le peut. Le transfert de propriété conserve exactement un Owner. La validation de politique refuse les modifications d'un membre Owner ou de l'ensemble de révocations par Admin.
L'historique des ressources est un graphe orienté acyclique adressé par contenu. Une ressource est en conflit si elle possède plusieurs révisions non encore consommées. Une révision de résolution doit nommer toutes les versions parentes visibles. Un marqueur de suppression retire la ressource de la vue actuelle sans effacer l'historique.
Les sauvegardes conservent l'état local signé et le matériel de récupération dans une enveloppe Argon2id et AEAD distincte. La restauration génère de nouvelles clés d'appareil et de nouveaux ID de coffres-forts, conserve le contenu actuel et les têtes de conflit, et place l'historique signé original dans une archive chiffrée en lecture seule. Elle ne prétend pas que l'ancienne appartenance partagée reste valide. Les coffres-forts propres à l'appareil, les autorisations cloud et les files d'envoi et de réception cloud sont exclus des sauvegardes.
Les situations suivantes font partie de la protection prévue : lecture ou falsification du contenu des ressources par le relais ; remplacement de l'identité du correspondant après vérification explicite des empreintes ; modification de texte chiffré authentifié ; lecture d'une copie de la base sans mot de passe local ; falsification d'écritures par un lecteur ; changement de propriété par un administrateur ; appareil révoqué utilisant la clé d'identité utilisateur pour s'autoriser un nouvel appareil ; impression accidentelle d'identifiants par le CLI ou le MCP, acceptation de mots de passe sur la ligne de commande ou héritage de l'élévation du terminal par une capacité MCP ; modification non détectée de révisions signées ou de l'audit, lacunes dans les chaînes et troncature des points de contrôle par rapport à l'état restant de la base.
Les situations suivantes sont hors de ces limites, et la documentation ne les présente pas comme résolues :
- Terminal compromis, administrateur du système d'exploitation, programme malveillant autorisé, inspection de la mémoire, enregistrement des frappes et copie délibérée de texte en clair par l'utilisateur. Les tampons de clés appartenant à Rust sont remis à zéro. Aucune garantie d'effacement ne couvre le swap du système, les dumps de plantage, les allocations des dépendances ou les chaînes gérées par le ramasse-miettes Dart. Le bureau ne possède pas de stockage matériel de clés.
- Le relais peut observer les métadonnées de trafic, bloquer les connexions, enregistrer lui-même le texte chiffré, retarder les révocations ou séparer les appareils. Le serveur de relais fourni ne stocke pas les ressources, mais un serveur malveillant ne peut pas être contraint à ne pas enregistrer le trafic. Le relais ne met pas les données en attente pour les appareils hors ligne.
- La révocation hors ligne ne peut pas effacer le texte en clair, les clés ou les sauvegardes passés. Un terminal n'ayant pas encore reçu la révocation peut toujours utiliser ses anciennes copies locales.
- L'audit prouve le contenu signé et sa continuité par rapport à un état connu, pas qu'un appareil compromis a enregistré chaque accès. Si toute la base et les preuves locales sont remplacées par une sauvegarde plus ancienne mais cohérente, cela ne peut pas être détecté sans point de contrôle plus récent d'un autre appareil ou ancrage externe. Aucun service d'horodatage de confiance n'existe.
- Les autres processus du même utilisateur du système sont à l'intérieur de la limite du compte local. L'IPC authentifie l'utilisateur du système. L'élévation du terminal utilise en plus le PID du processus parent. Cela ne constitue ni une isolation contre les processus malveillants du même utilisateur ni une identité de session persistante insensible à la réutilisation des PID.
- L'empreinte de l'exécutable est vérifiée avant son lancement. Il n'existe ni bac à sable du système, ni signature de code obligatoire, ni exécution atomique par handle de fichier, ni confinement des processus descendants.
- Les copies dans le presse-papiers, les fichiers exportés et les identifiants dont l'utilisateur demande explicitement la copie quittent le stockage chiffré selon son action. Après copie, le contenu reste présent même après verrouillage ; l'historique du système ou d'autres applications peuvent le conserver.
IPC local et autorisations
service::Request est l'interface d'opérations typée commune, distinguée par op en snake_case. Les champs inconnus au niveau supérieur sont refusés. L'IPC local utilise des trames JSON préfixées par une longueur 32 bits big-endian et une enveloppe en version 1 ; les écouteurs HTTP/WebSocket facultatifs et les RPC du relais partagent des trames JSON binaires WebSocket (authentification par jeton). Après déverrouillage/élévation, l'accès distant du bureau expose les mêmes opérations que l'IPC local (ws:remote / relay:remote) ; la FFI/Share mobile conserve des listes de refus propres à la plateforme. Voir docs/remote-daemon.md et SECURITY.md. Le centre d'autorisation facultatif ([[centers]]) interdit l'amorçage local. Le mode tickets émet des tickets d'enregistrement, d'autorisation MCP et de déverrouillage ; le mode hébergé place le contenu dans le stockage chiffré du serveur Center. Voir ci-dessous, ainsi que docs/center.md et docs/authorization.md.
Le daemon ne sert que l'utilisateur actuel du système d'exploitation. Windows utilise des canaux nommés ; Linux et macOS des sockets Unix. L'IPC accepte au maximum 16 clients simultanés. La lecture des trames des canaux nommés Windows reste bloquante : des clients du même utilisateur qui stagnent occupent ces places, mais un client stagnant ou un transfert cloud ne bloque pas toutes les autres connexions. Les E/S de trames Unix ont des échéances. Les opérations de service détenant le verrou d'écriture, y compris l'exécution de sous-processus de confiance, restent sérialisées.
Le déverrouillage ordinaire expire après 15 minutes d'inactivité. sudo utilise le même mot de passe local, dure au maximum 15 minutes, expire après 5 minutes d'inactivité et s'applique au processus parent du terminal émettant la commande. Le verrouillage ou le redémarrage efface l'élévation. Les tentatives en état entièrement verrouillé sont limitées en fréquence, mais ne sont pas inscrites dans l'audit signé persistant. Les opérations échouées sont auditées lorsque le stockage est déverrouillé et qu'une identité locale de signature est disponible. L'intention est validée avant livraison ; un plantage soudain laisse une intention validée plutôt qu'un résultat inventé.
L'autorisation MCP est une capacité locale au porteur, écrite dans un fichier désigné par l'appelant et non imprimée dans le terminal. Pour mcp grant, --minutes détermine la durée de validité ; seul --write permet de créer des souvenirs ordinaires. mcp serve --grant-file lit la capacité dans ce fichier et expose sur stdio, via MCP 2024-11-05, les outils memory_search, memory_retrieve, memory_ask, memory_read, memory_read_revision, memory_history, memory_diff, memory_write, memory_delete, card_write, card_graph, card_link. Chaque appel inclut le coffre-fort de l'autorisation et vérifie séparément la capacité avant dispatch.
Le MCP ne peut pas hériter de l'élévation du terminal. Il ne peut lire les souvenirs privés, fichiers ou identifiants, ni exécuter de processus, sessions distantes, proxies de boucle locale ou opérations cryptographiques, ni gérer les membres du coffre-fort, ni demander DesktopStatus. Le verrouillage efface les autorisations en mémoire ; celles non expirées sont restaurées après déverrouillage, et le verrouillage lui-même ne les supprime pas. L'émission, la liste, la révocation, la demande et le retrait du service d'autorisation peuvent être appelés par des outils MCP, toujours avec la capacité MCP elle-même, sans hériter de l'élévation du terminal ni utiliser le bearer administrateur du Center. La lecture ordinaire ne résout pas {{secret:标签}}. card_write ne transporte pas de correspondance de secrets : la mise à jour d'une fiche contenant des références à des secrets échoue donc à la validation. Les ID courts des références raccourcissent uniquement les ID typés d'identifiants ; les marqueurs dans le corps restent inchangés.
otp_code implémente TOTP selon RFC 6238 et HOTP selon RFC 4226. HOTP enregistre le compteur incrémenté dans la même validation que le retour du code. parse_otp_import accepte otpauth:// et otpauth-migration://offline?data=... de Google Authenticator, et transforme les comptes en fiches TOTP ou HOTP. Si le type figure dans l'hôte de l'URI, le paramètre type ou les données de migration, cette valeur l'identifie ; sans type explicite, la présence d'un compteur indique HOTP, sinon TOTP. L'appelant n'a pas à sélectionner le type séparément. Le --image de la ligne de commande décode d'abord ces deux types d'URI depuis une image avec rqrr, puis emprunte le même chemin d'importation ; l'URI et --image ne peuvent être fournis simultanément. Pour plusieurs fiches, --title sert seulement de préfixe de titre et --id ne peut remplacer qu'une fiche. La caméra mobile n'importe pas ces deux types de codes QR OTP. La numérisation pour l'appairage d'appareils hébergés suit un autre chemin, décrit ci-dessous. Les révisions de fiches peuvent aussi inclure alias, notes et catégories facultatifs ; leur omission lors d'une mise à jour conserve les valeurs, une chaîne vide les efface. Les graines sont exclues de la recherche et du MCP. TOTP dépend de l'exactitude de l'horloge locale. Le résultat est un code temporaire autorisé, pas une exportation de graine ; l'audit ne consigne ni code ni graine.
credential_copy constitue une exception explicite à la limitation de sortie des identifiants. Il exige une élévation locale, valide d'abord une intention d'audit, puis remet le texte UTF-8 en clair au client du presse-papiers. Le CLI n'imprime {"copied":true} qu'après une écriture réussie dans le presse-papiers système. Le MCP ne peut pas demander de copie. L'audit consigne credential.copy et l'intention de livraison, pas un reçu de réussite du presse-papiers du système.
L'ABI mobile est déclarée dans crates/ffi/memories.h. Les handles sont des ID numériques enregistrés, pas des pointeurs Rust bruts. Les chaînes renvoyées sont libérées une fois avec memories_free. Un JSON invalide produit une erreur générique sans réafficher l'entrée. memories_lock_all avance la génération globale de verrouillage lors du passage de l'application en arrière-plan, supprime les réponses en vol tardives et abandonne toutes les sessions hébergées. Le chemin mobile ne contient pas l'exécution de processus, la création locale d'autorisations MCP, la gestion locale des autorisations ni les RPC de demandes d'accès. Les appels hosted_* vers le Center hébergé constituent un chemin distinct : le contenu n'expose que les listes, la lecture et l'écriture de kind = credential.
Les opérations cryptographiques vérifient l'empreinte d'approbation avant de résoudre les ID courts, de sorte qu'elle est liée aux références vues par l'appelant. Le service vérifie l'élévation locale et l'accès au coffre-fort, ne résout que les ressources d'identifiants, valide l'intention d'exécution puis appelle l'exécuteur partagé. Celui-ci revérifie les outils, copie et vérifie les entrées, matérialise les identifiants dans un espace de travail accessible au seul propriétaire, exécute les commandes sans shell, masque la sortie des outils et ne publie que les artefacts réussis. Les espaces réservés ne remplacent que des chemins. La liste d'autorisation MCP et la FFI mobile refusent les tâches de processus.
Autorisations, demandes d'accès, hébergement et secrets scellés
Les autorisations locales et le Center hébergé utilisent les mêmes plafonds de rôles. Reader a pour maximum read, Editor read_write, Admin read_write_delete, Owner allow_all. Une portée omise vaut read. Reader et Editor ne peuvent ni émettre ni déléguer. L'émission est limitée à la fois par le plafond du rôle et par la portée de l'autorisation détenue par l'appelant ; elle ne peut couvrir plusieurs coffres-forts ni durer plus longtemps que l'autorisation parente. La durée est de 1–60 minutes. L'émission directe par un membre a une profondeur de 1 ; l'émission via une autorisation ajoute 1 et est refusée au-delà de 2. Les identifiants persistants de membre sur le Center ont une profondeur de 0 et ne comptent pas comme délégation. Le destinataire doit déjà être membre du coffre-fort, son rôle ne peut dépasser celui de l'émetteur et son nom ne peut être le nom réservé center-admin. L'autorisation de contenu ne modifie pas son rôle.
Le maximum d'autorisations actives simultanément est de 10000 (service::MAX_ACTIVE_GRANTS), avec la même limite pour le daemon local et le Center hébergé. Le jeton n'apparaît que dans l'unique réponse d'émission ou de retrait. L'état persistant ne conserve que SHA-256 et les métadonnées ; la comparaison s'effectue en temps constant. Le verrouillage abandonne les clés en mémoire et suspend les autorisations sans les révoquer ; celles non expirées restent valides après déverrouillage ou redémarrage. Les autorisations expirées et leurs descendants sont effacés au chargement et lors des vérifications périodiques. La révocation se propage aux descendants. Si un membre est retiré ou si le rôle de l'émetteur descend sous la portée émise, les autorisations concernées deviennent invalides lors de la validation de politique. Les autorisations sont exclues de la synchronisation des coffres-forts et des sauvegardes de restauration.
Les demandes d'accès sont d'abord approuvées, puis retirées. L'approbation d'une demande de portée n'enregistre que les limites, sans créer de jeton ; le demandeur le retire ensuite une fois avec les identifiants de membre utilisés à la création de la demande. Si cette réponse est perdue, le jeton ne peut plus être récupéré. Les demandes de rôle n'existent que localement ; leur approbation modifie immédiatement la politique, sans retrait. Le rôle Owner passe par un transfert de propriété signé ; une autorisation temporaire ne peut pas approuver un changement permanent de rôle. Le Center hébergé n'accepte que les demandes de portée. Un demandeur ne peut approuver ni refuser sa propre demande. Un appelant utilisant une autorisation dont le sujet diffère de l'émetteur peut soumettre une demande, mais ne peut approuver, refuser, annuler ou retirer. Les demandes en attente sont limitées à 20 par demandeur et par coffre-fort, et à 200 par coffre-fort. Elles expirent après 7 jours. Les demandes décidées, expirées ou retirées sont supprimées 30 jours après leur dernière transition.
L'audit local est une chaîne de hachages signée par appareil. DIR/audit.jsonl du Center est une chaîne sans clé : elle détecte une altération par rapport à une tête connue, mais n'empêche pas l'opérateur de réécrire le journal entier.
Le Center possède deux modes. mode = ticket (par défaut) n'émet que des tickets d'enregistrement, d'autorisation MCP et de déverrouillage ; le contenu reste dans le stockage chiffré propre aux appareils. Un ticket de déverrouillage est réutilisable jusqu'à expiration, n'est lié à aucun appareil particulier et n'autorise que le déverrouillage local avec le mot de passe de l'appareil, sans sudo ni élévation MCP. Avec mode = saas et un Center lancé avec --data, le contenu réside dans le stockage chiffré du serveur. L'opérateur possède le mot de passe et peut lire le texte en clair déverrouillé. C'est du chiffrement côté serveur, pas une limite de confidentialité envers l'opérateur. Lorsque la configuration contient exactement un mode = "saas", le daemon transfère les RPC de contenu au /v1/rpc de ce Center. Les opérations non prises en charge échouent directement, sans repli sur le stockage local. Le bearer administrateur gère le verrouillage, le déverrouillage et les membres ; son utilisation sur les routes de contenu renvoie 401. allow_all ne couvre que le contenu hébergé, sans administration du serveur, gestion des appareils, sudo, enregistrement, restauration, exécution ni opérations cryptographiques.
Les fiches e2e: true d'un stockage hébergé refusent les secrets en clair. Après appairage, les secrets sont placés dans des enveloppes destinées à la clé publique X25519 de l'appareil et signées par une clé Ed25519 connue. Le Center ne conserve que les clés publiques et le texte chiffré. Il voit qui, quelle fiche et quand, et peut retenir ou rejouer d'anciennes enveloppes, mais ne voit pas leur corps. Les autorisations d'appareil ne peuvent ni émettre ni approuver. La révocation supprime les enveloppes de l'appareil sans récupérer le texte en clair déjà déchiffré. Seule une autorisation Owner peut retirer le marqueur e2e, et cette écriture ne peut transporter aucun secret. L'ajout d'un appareil à un coffre-fort local utilise toujours des demandes signées à durée limitée et une vérification d'empreintes complètes, sans caméra.
Recherche et flux de données des modèles
Le cache de recherche conserve les index de deux coffres-forts au maximum. Chaque coffre-fort est limité à 10,000 fragments, 16 MiB de texte source et métadonnées, et 32 MiB de vecteurs. Le contenu privé, les identifiants, les fichiers et les révisions supprimées, en quarantaine ou en conflit sont exclus avant indexation. Le stockage temporaire SQLite est forcé en mémoire. Les caches sont exclus des sauvegardes et des exports cloud. Le verrouillage efface tous les index et vecteurs. Un changement de politique, de révision ou de quarantaine invalide le cache correspondant.
Le corps ordinaire est découpé près de limites respectant Unicode en fragments d'au maximum 768 caractères, généralement avec un chevauchement de 128 caractères. Le chinois et les textes mixtes utilisent la segmentation Jieba. Les poids FTS5 BM25 du titre, du corps et des métadonnées sûres sont 5, 1 et 2. any combine les mots-clés, all exige chaque mot et phrase exige l'expression originale. Ces contraintes filtrent aussi les candidats vectoriels et hybrides. Les requêtes sont échappées et ne sont pas exécutées comme des opérateurs FTS arbitraires. La recherche de base memory search regroupe les fragments par document ; memory_retrieve et rag search renvoient des fragments individuels avec ID de ressource, révision, hachage stable du fragment, décalage UTF-8 original, score et étiquette de citation.
Les vecteurs sont normalisés et mis en cache en mémoire. La recherche vectorielle utilise la similarité cosinus. La recherche hybride utilise la fusion par rangs réciproques, avec k à 60. Le reclassement léger par défaut favorise, dans un ensemble limité de candidats, la couverture de la requête et les correspondances exactes d'expressions. Un point de terminaison de modèle configuré peut effectuer un reclassement appris.
L'embedding par défaut est EmbeddingGemma 300M Q4 dans le processus, avec inférence ONNX locale et vecteurs de dimension 768. La télémétrie ONNX est explicitement désactivée avant la création de la session d'inférence. Le moteur d'inférence est fourni avec l'application ; les fichiers du modèle, d'environ 209 MiB, sont des données publiques téléchargées par rag model-install depuis une révision fixe du dépôt, puis vérifiées fichier par fichier avec SHA-256 avant publication, ou fournis par rag.local_model_dir. Les fichiers vérifiés peuvent être réutilisés hors ligne. Ils relèvent d'une licence Gemma distincte, ne sont pas secrets et ne constituent pas une limite de sécurité auditée indépendamment.
Une fois une adresse d'embedding distante configurée, les opérations RAG autorisées peuvent envoyer tous les fragments de documents admissibles à ce point de terminaison. La génération n'envoie que la question et les fragments sources sélectionnés. Le reclassement appris envoie ses candidats. Les URL distantes doivent être en HTTPS ; HTTP n'est permis qu'en boucle locale. Les redirections des API de modèles sont refusées. Les requêtes de boucle locale ne passent pas par un proxy. Les autres peuvent utiliser des proxies HTTP, HTTPS ou SOCKS et les variables d'environnement de proxy. Les nouvelles tentatives sont limitées et ne couvrent que les erreurs transitoires de connexion ou de lecture, HTTP 429 et certains 5xx. Elles peuvent entraîner une facturation répétée. La vérification TLS n'est pas désactivée. Les clés API ne sont référencées que par le nom d'une variable d'environnement dans la configuration, sans être écrites dans TOML, les URL ou les arguments de commande. Les arguments des outils MCP ne peuvent choisir les URL de fournisseurs, identifiants ou proxies.
Les coffres-forts propres à un appareil refusent, avant inférence, les étapes de modèle destinées à des fournisseurs hors boucle locale. L'inférence locale et les services de boucle locale explicitement configurés restent disponibles.
Retrieve, RagIndex, RagStatus et RagAsk préservent les autorisations du coffre-fort et les portées MCP. L'IPC effectue le travail des modèles hors du mutex du service et revérifie la génération de déverrouillage, les autorisations, les droits d'accès et les marqueurs de révision des sources avant et après le travail externe, ainsi qu'avant livraison. L'intention signée précède l'inférence ou l'exportation. L'audit enregistre la réussite ou l'échec et les ID des sources livrées, pas les prompts ou les clés. La génération traite le texte source comme des données non fiables et ne dispose d'aucun outil applicatif. Le modèle doit renvoyer une réponse structurée et des citations. Les ID de citation inexistants sont refusés. Sans preuve, la génération n'est pas appelée et le résultat est marqué insufficient_evidence. La validation des citations vérifie seulement leur existence ; elle ne prouve pas l'exactitude factuelle de la prose générée et n'élimine pas les injections de prompts dans les sources non fiables.
Le verrouillage, les autorisations expirées ou révoquées et les changements d'état des sources suppriment les résultats tardifs et empêchent les étapes suivantes. Une requête distante ou une inférence déjà commencée peut se terminer ; le texte déjà envoyé à distance ne peut être rappelé.
La FFI mobile bénéficie de la recherche lexicale améliorée et prend en charge BM25 retrieve. La configuration des modèles et l'interface d'inférence n'ont pas été ajoutées à Flutter. Les opérations utilisant des modèles passent par le daemon de bureau configuré.
Synchronisation d'objets cloud
Le CLI de bureau utilise PKCE, un rappel de boucle locale et des points de terminaison fixes de fournisseurs pour l'OAuth dans le navigateur. CloudBegin crée sous sudo un ticket à durée limitée lié à l'appelant, au fournisseur et au client. Les requêtes réseau s'effectuent hors du daemon. CloudComplete vérifie le ticket et l'autorisation actuelle avant validation. Le verrouillage, la déconnexion et le redémarrage invalident les tickets inachevés pour empêcher qu'un résultat tardif rétablisse la connexion.
State.cloud_accounts conserve les jetons localement sous forme chiffrée. L'état ne renvoie que les métadonnées du compte. Les sauvegardes, l'appairage, la synchronisation de coffres-forts et le MCP n'incluent pas les jetons. La connexion n'établit pas l'appartenance à un coffre-fort. Chaque fournisseur connecte un compte par appareil ; une nouvelle connexion ne remplace ce compte qu'après réussite de la nouvelle autorisation.
State.cloud_sync place la file d'envoi persistante et les reçus de réception dans le même état chiffré. CloudSyncEnqueue écrit les octets exacts de l'enveloppe sérialisée et le descripteur signé avant toute E/S de transport. L'ID d'objet est le SHA-256 de ces octets et les nouvelles tentatives le réutilisent. CloudSyncWork renvoie le texte chiffré et les curseurs de liste, sans jetons OAuth. sync::execute effectue des téléversements, téléchargements et listes bornés sans détenir le mutex du service. CloudSyncAck vérifie la génération de session locale au processus, puis valide ensemble les marqueurs de téléversement, les ID découverts, les octets téléchargés et la fusion du coffre-fort. Un descripteur arrivé avant son objet reste en attente. Le verrouillage, le déverrouillage et la déconnexion incrémentent la génération de session pour empêcher les imports à partir de confirmations tardives.
memories cloud sync fait renouveler les jetons par le daemon et téléverse/télécharge via les dossiers d'application OneDrive ou Google Drive. Ce trafic HTTP s'effectue hors du verrou d'écriture. Les jetons ne sont jamais renvoyés via IPC. Les tests utilisent MemoryTransport et des fixtures Graph/Drive de boucle locale. Les charges utiles terminées de la file sont compactées en reçus de hachage et marqueurs de publication par fournisseur et destinataire. Les reçus restent locaux sans supprimer l'historique cloud. Les objets manquants sont réessayés avec un délai exponentiel persistant. La capacité des éléments en attente exerce une contre-pression sur les listes. Les workers des fournisseurs sérialisent le travail par compte et refusent les résultats de renouvellement ou d'importation issus de sessions invalidées.
L'autorisation CLI et cloud sync sont donc implémentés, et les transports des fournisseurs ont été testés avec des fixtures locales. Le consentement, le renouvellement, la convergence entre deux appareils, les nouvelles tentatives hors ligne et la révocation sur de vrais comptes OneDrive ou Google Drive n'ont pas encore été livrés comme comportements validés. Les contrôles de connexion et de synchronisation du mobile utilisent AppAuth et le worker Rust partagé ; le consentement réel et la validation des plateformes restent des conditions de publication. La synchronisation mobile en arrière-plan n'est pas activée.
La révocation abandonne les exports cloud en attente et invalide le travail déjà capturé avant l'opération d'objet suivante. Les requêtes réseau déjà commencées ne peuvent être rappelées.
La synchronisation explicite des appareils suit un autre chemin. sync export et sync import échangent des enveloppes authentifiées complètes. Les commandes en ligne sync send et sync receive remettent les mêmes types de paquets au relais sans état. Un envoi réussi signifie uniquement que le relais a reçu le paquet. Les écritures inconnues d'anciens epochs sont placées en quarantaine et ne peuvent être signées à nouveau qu'avec sync reapply, après examen local. Une bifurcation de permissions exige qu'une personne propriétaire sur les deux branches signe la tête choisie et toute la chaîne de politique abandonnée. Les transferts de propriété incompatibles restent bloquants.
Limites de l'état du bureau
Request::DesktopStatus renvoie les états d'initialisation et de verrouillage, les nombres de coffres-forts et de connexions, les types de fournisseurs configurés et les compteurs de files de synchronisation. Il s'exécute avant le dispatch des opérations déverrouillées, sans cloner ni valider l'état chiffré, sans toucher aux temporisateurs d'inactivité ou d'élévation et sans exposer d'identifiants, résumés de comptes, contenu ou identifiants secrets. La réponse verrouillée ne contient pas ces détails. La liste d'autorisation MCP refuse cette requête.
La bibliothèque CLI héberge le tableau de bord local et les icônes de zone de notification Windows et macOS. memories tray et le programme distinct memories-tray utilisent une boucle d'événements native sur le thread principal. Le service HTTP et les sondages d'état bornés s'exécutent sur des threads distincts, empêchant un daemon occupé de bloquer l'icône ou le navigateur. Les sondages appellent le CLI du même répertoire par l'IPC habituel authentifié par le système, avec échéance et nettoyage des sous-processus. L'échéance de lecture des canaux Windows pour les sondages est de trois secondes. Un échec efface les détails ; le verrouillage les masque au sondage suivant. Les sondages ne renouvellent ni le délai d'inactivité du déverrouillage ni l'élévation.
L'interface HTTP ne sert que des ressources embarquées et une projection fixe de l'état, sans dispatcher de requêtes de service arbitraires. Elle n'écoute que sur 127.0.0.1. Son seul point de terminaison de données est GET /api/status, protégé par un jeton de 256 bits généré aléatoirement pour chaque processus. Le lanceur place ce jeton dans le fragment de l'URL. La page le retire de la barre d'adresse, le conserve uniquement dans le stockage de session de l'onglet et le transmet dans un en-tête Authorization. L'URL complète de lancement est une capacité du tableau de bord et ne doit pas être partagée. Les Host inattendus, Origin étrangers et métadonnées de récupération intersites sont refusés. Il n'existe ni CORS, ni ressources tierces, ni intégration dans des cadres, ni routes de modification. Le navigateur reçoit une projection typée de l'état, pas un pont IPC générique, des identifiants secrets, le corps des souvenirs, des noms de comptes, des chemins ou des jetons d'autorisation.
L'icône peut démarrer le daemon local par une action explicite du menu. Fermer l'icône ou le tableau de bord arrête son écoute HTTP et sa surveillance, sans arrêter ni déverrouiller un daemon indépendant. L'authentification reste effectuée dans le CLI. Un programme malveillant du même compte système reste à l'intérieur de la limite de confiance locale existante.
La compilation Linux inclut memories webui, avec le même tableau de bord de boucle locale et les mêmes limites de jeton. La boucle d'événements native de la zone de notification n'est compilée que sur Windows et macOS. Linux ne dispose pas d'icône native.
Le TOML local ne contient que des paramètres non secrets de stockage, de relais et de bureau. Les fichiers explicitement manquants, clés inconnues et valeurs mal formées sont refusés avant l'ouverture du stockage. Les chemins relatifs de stockage sont résolus par rapport au fichier de configuration. Un changement de chemin sélectionne un autre stockage sans migrer les données. Les URL du relais conservent les restrictions TLS distantes existantes et ne peuvent contenir d'identifiants secrets.