La gouvernance, intégrée au cœur
Dans MindooDB, la gouvernance est une propriété de la couche de données elle-même - pas un ajout greffé sur un serveur de confiance ou sur une couche applicative. La politique d'accès, l'identité et une piste d'audit cryptographiquement prouvable et reproductible à tout instant sont stockées et appliquées dans le cœur de la base. Le gain : vous pouvez prouver non seulement ce qui a changé, mais aussi qui avait le droit de le changer - au moment où la modification est réellement entrée dans le tenant.
Elle comporte deux versants : un versant écriture, qui détermine qui peut créer, modifier ou supprimer des documents - appliqué même lorsque les utilisateurs sont hors ligne - et un versant lecture, qui détermine qui peut voir les données, en combinant un verrou d'accès au niveau de la synchronisation et une distribution de clés à l'aveugle pour l'admin. Les deux reposent sur le chiffrement de bout en bout de MindooDB.
La gouvernance est une propriété de la couche de données
La plupart des architectures remontent la gouvernance vers le serveur ou l'application : la base stocke des lignes, et quelque chose d'autre décide qui peut y toucher et tient le journal. MindooDB inverse cela. Politique, identité, historique complet et piste d'audit vivent dans les mêmes stores append-only chiffrés de bout en bout - les garanties voyagent donc avec les données à travers les serveurs, les pairs et les appareils hors ligne, et survivent à un serveur entièrement compromis.
Les politiques et les règles sont des documents signés par l'admin dans la base d'annuaire, eux-mêmes append-only. Le moment exact où la gouvernance a été activée - ou désactivée temporairement - fait partie du registre permanent ; ce n'est pas un changement de configuration sans trace.
Chaque entrée porte une heure de confiance, et l'annuaire conserve une chaîne de voyage dans le temps de sa propre histoire. wasAllowedAt(op, user, dbid, at) peut donc rejouer quelles règles s'appliquaient et qui était autorisé à n'importe quel moment passé - un verdict déterministe sur lequel s'accorde chaque réplique honnête.
Chaque modification est signée en Ed25519, ce qui la rend non répudiable, et attestée contre une horloge de confiance. Les infractions ne disparaissent pas en silence : elles sont consignées dans un journal de quarantaine et d'audit propre à chaque tenant, que Haven affiche - même une tentative refusée reste imputable.
Les attributions contiennent des tableaux de clés par appareil ; révoquer revient simplement à retirer des clés, et un effacement à distance explicite retire tout le tenant d'un appareil volé ou d'un utilisateur parti dès sa prochaine connexion - le cycle de vie des identités est gouverné dans le même annuaire signé.
Le modèle à deux niveaux
Chaque règle relève de l'un des deux niveaux, selon ce que le serveur de synchronisation peut voir. Le serveur ne manipule que du texte chiffré et un petit ensemble de métadonnées en clair - il ne peut jamais lire le corps d'un document. Cette seule distinction constitue toute l'architecture : elle permet à MindooDB de promettre honnêtement ce qui est garanti cryptographiquement, par opposition à ce qui est appliqué par politique entre clients coopératifs.
| Niveau | Ce qu'il vérifie | Appliqué par | Force |
|---|---|---|---|
| Niveau 1 - Identité | Identité de l'auteur, base de données visée, type d'opération | Serveur et clients | Cryptographique - le serveur refuse d'attester une entrée en infraction, elle ne peut donc pas se propager |
| Niveau 2 - Contenu | Le contenu réel du document (withfields) | Clients uniquement | Politique - encadre les clients honnêtes et façonne l'expérience. Un client altéré ne peut la contourner que localement ; chaque réplique honnête revérifie withfields à la réception et met en quarantaine une modification en infraction, qui ne devient donc jamais visible pour quiconque |
Les reçus de témoin : une horloge de confiance sans avoir à faire confiance au client
La question la plus difficile dans un système local-first est : « à quelle horloge se fier ? ». Un utilisateur qui travaille hors ligne pourrait antidater ses propres entrées pour faire passer une modification malgré une politique. MindooDB y répond par le reçu de témoin : une attestation, signée par un témoin de confiance (votre serveur de synchronisation), qu'une entrée a été acceptée à un moment précis. Chaque point de contrôle s'appuie alors sur exactement une horloge bien définie.
Le SDK évalue les niveaux 1 et 2 contre l'état local de l'annuaire de l'utilisateur, à l'heure locale du moment. Si l'opération est autorisée, l'entrée est stockée en local sans champ de témoin et devient immédiatement visible sur cet appareil. C'est l'horloge de l'utilisateur qui régit sa vue purement locale - sans conséquence, puisque la modification n'est pas encore entrée dans le tenant partagé.
Le serveur évalue le niveau 1 contre son propre état, à l'heure du serveur. Si l'opération est autorisée, il horodate receivedAt, note la clé du témoin, signe le reçu et renvoie les champs de témoin pour qu'ils repartent vers l'émetteur, puis plus loin. En cas de refus, il renvoie un AccessDenied structuré et l'entrée reste locale - elle ne peut pas se propager.
Le destinataire vérifie la signature du reçu contre sa liste de témoins de confiance. Une signature valide signifie que le niveau 1 était satisfait à receivedAt ; par défaut, il n'est donc pas réévalué. Le destinataire vérifie ensuite le niveau 2 en local, puis matérialise la modification ou la dirige vers un journal local de quarantaine et d'audit.
Comment les décisions se configurent
Tout l'état du contrôle d'accès se trouve dans la base directory, réservée aux admins, et se synchronise vers chaque participant. Tout ce dont le serveur a besoin pour le niveau 1 est chiffré avec la clé $publicinfos, donc lisible sans détenir la clé de tenant par défaut ; le contenu withfields, lui, n'est jamais lisible par le serveur. Chaque appel qui modifie l'état est signé par l'admin.
Une politique de tenant par défaut et, en option, des politiques par base de données définissent quelles opérations sont refusées tant qu'aucune règle allow ne correspond :
- Créer, modifier, supprimer & restaurer - autorisés par défaut, jusqu'à ce que vous les refusiez
- Snapshot & purge - réservés aux admins par défaut
- Lecture - la référence du verrou de lecture/synchronisation au niveau de la base, affinée par les règles de lecture
- Identifiants de bases autorisés - la liste blanche des bases de données admises dans le tenant ; le serveur refuse d'en créer ou d'en synchroniser une qui n'y figure pas
- Clé de chiffrement par défaut - la clé qu'utilise un nouveau document quand aucune n'est indiquée ; une commodité, pas une mesure de sécurité
- Interrupteur général - un indicateur qui désactive d'un coup toutes les vérifications d'accès
Un tenant tout neuf n'a aucun document de politique : tout est donc autorisé jusqu'à ce qu'un admin en écrive un. Chaque révision est ajoutée à l'historique, si bien que la fenêtre exacte d'activation et de désactivation reste auditable. Point essentiel : chaque modification est jugée contre les politiques actives à sa propre heure de confiance (receivedAt). Les modifications entrées dans le tenant après l'activation en relèvent ; les plus anciennes se résolvent contre la valeur implicite « tout autorisé » - les données déjà en place bénéficient donc automatiquement d'une clause d'antériorité, sans étape de migration.
Chaque règle vise un type d'opération et une base de données ("*" = toutes), et énumère les utilisateurs ou groupes concernés. L'évaluation est ensembliste et indépendante de l'ordre :
- une règle deny qui correspond → refusé
- sinon une règle allow qui correspond → autorisé
- sinon la politique de référence décide
L'indépendance à l'ordre compte, parce que les documents de règles fusionnent entre répliques via des CRDT - il n'y a aucun ordre de règles sur lequel se contredire.
Un serveur ne peut rendre qu'un verdict de niveau 1. Si seule une règle de contenu de niveau 2 sépare l'autorisation du refus, il traite l'entrée comme autorisée au niveau 1 et laisse la vérification withfields aux clients. Chaque décision renvoie un résultat structuré - allowed, une reason lisible, le matchedRuleId et le tier - dont le SDK se sert pour griser les actions qu'un utilisateur ne peut pas effectuer.
Une clause withfields vérifie un chemin en notation pointée à l'intérieur du document, avec un jeu fermé d'opérateurs (equals, contains, gt, …) et des espaces réservés comme ${user.usernames}. Chaque clause est évaluée contre un état de document choisi :
- before - le document existant (valeur par défaut pour modifier/supprimer) : « vous devez déjà être éditeur ». Une évaluation sur after permettrait à quelqu'un de s'ajouter lui-même et d'autoriser sa propre modification.
- after - le document une fois la modification appliquée (valeur par défaut pour créer) : « celui qui crée doit s'ajouter à
myeditors».
Les règles s'apparient au nom d'utilisateur haché, aux hachages de ses groupes (y compris imbriqués) et à des pseudo-jetons réservés :
$everyone- tous les utilisateurs enregistrés$admin- les admins uniquement$author- le créateur d'origine du document (niveau 1, modèle de propriété)
La révocation consiste à retirer des clés du document d'attribution signé par l'admin - il n'y a pas de document de révocation séparé. Un admin peut aussi déclencher un effacement à distance explicite, à activer expressément, en le signant : le tenant est retiré d'un appareil volé ou d'un utilisateur parti dès sa prochaine connexion.
Un CRM avec des éditeurs par enregistrement
Une politique réaliste est ici déroulée de bout en bout, pour que les pièces s'emboîtent. Le tenant possède une base crm, et nous voulons quatre règles : tout le monde peut créer des contacts mais doit s'inscrire dans myeditors ; seul un éditeur déjà inscrit peut modifier un contact ; seul le créateur d'origine peut le supprimer ; et le groupe HR peut tout modifier, comme échappatoire hiérarchique.
La référence est deny. La règle 1 correspond via $everyone, et son withfields passe parce que le myeditors de l'état after contient Alice → autorisé. Le serveur ne confirme que le niveau 1, puis atteste l'entrée.
La règle 2 correspond via $everyone, mais son withfields échoue : le myeditors de l'état before ne contient pas Bob → refusé en local, même si sa modification cherche à l'y ajouter. Un client altéré pourrait la pousser, mais chaque destinataire honnête la met en quarantaine à la matérialisation.
La règle 3 correspond via $author - la clé du créateur et le signataire de la suppression désignent le même utilisateur → autorisé et attesté (niveau 1, résiste à un client malveillant). La règle 4 permet à tout membre du groupe HR de modifier n'importe quel contact, indépendamment de myeditors.
On voit ainsi la répartition des rôles : les règles de niveau 1 ($author, groupe hr) sont appliquées au serveur et résistent à un client malveillant ; les règles de niveau 2 (les vérifications de contenu sur myeditors) sont appliquées par chaque client honnête et mises en quarantaine à la réception en cas d'infraction.
Contrôle de l'accès en lecture
Les règles d'écriture décident qui peut modifier les données ; l'accès en lecture décide qui peut les voir - et MindooDB l'applique sur deux plans, tous deux signés par l'admin et chiffrés avec $publicinfos, pour que le serveur zero-trust agisse sur les métadonnées dont il a besoin sans jamais détenir la clé du tenant.
1. Un verrou de lecture/synchronisation au niveau de la base de données. Une référence denyDocRead et des règles doc_read (la même évaluation deny-overrides-allow, visant des utilisateurs, des groupes, $everyone ou $admin) décident qui peut tout simplement ouvrir et synchroniser une base. C'est tissé directement dans le système de synchronisation : un utilisateur qui perd l'accès en lecture cesse de recevoir des mises à jour et ne peut même plus ouvrir la copie locale déjà synchronisée. Parce que le verrou se place devant chaque opération de synchronisation, la lecture est le verrou principal par base - elle conditionne aussi l'écriture : qui ne peut pas lire une base ne peut pas non plus y créer de données (il rédigerait sinon des documents qu'il ne verrait jamais).
2. La confidentialité des documents reste cryptographique. Un document ne peut être lu que par quelqu'un dont le KeyBag (son magasin de clés personnel, protégé par mot de passe) contient une clé qui le déchiffre - le serveur ne prend ici aucune décision de lecture. La distribution de clés est le moyen d'acheminer ces clés aux bonnes personnes en toute sécurité : une politique signée par l'admin indique quels utilisateurs et quels groupes doivent détenir chaque clé, et chaque client met son KeyBag en accord avec elle automatiquement. C'est opt-in - avec la clé partagée par défaut et sans verrou de lecture, tout le monde peut tout lire, exactement comme avant.
Au point de passage obligé du serveur, le même hook qui évalue la liste blanche des identifiants de bases évalue aussi doc_read pour le principal authentifié, contre l'heure de confiance du serveur, et refuse de servir une base interdite comme d'en accepter les envois - les mises à jour non autorisées ne sont tout simplement jamais livrées. En parallèle, le chemin d'ouverture côté client refuse d'ouvrir une base à laquelle l'utilisateur a perdu l'accès : un utilisateur révoqué ne peut même pas lire sa copie synchronisée en local. La base directory n'est jamais soumise au verrou (elle porte précisément les politiques dont le verrou dépend) ; l'admin en est exempté.
Quand une clé est poussée vers un utilisateur, elle est chiffrée avec la clé publique (RSA) personnelle de cet utilisateur, si bien que lui seul peut la déballer - aucun autre utilisateur, et pas même l'admin, ne peut la lire. C'est pour cela que la distribution est à l'aveugle pour l'admin : quelqu'un qui détient déjà la clé l'encapsule pour les destinataires, et l'admin ne fait que signer et publier le résultat. Un utilisateur ordinaire peut préparer une distribution et la remettre à un admin pour signature, même à un admin qui ne détient pas la clé.
Chaque client met son KeyBag en accord avec la politique au démarrage et après chaque synchronisation. Poussez une clé vers un utilisateur et sa synchronisation suivante ramène les documents que cette clé déverrouille - ils apparaissent simplement dans la base, désormais lisibles. Retirez la clé et ces documents disparaissent : les copies locales qui ne peuvent plus être déchiffrées sont supprimées de la base, des caches et des vues. Promotions, rotations et changements de service passent tous par là automatiquement.
Par souci de bande passante, le serveur cesse aussi de livrer les données chiffrées avec une clé qu'un utilisateur a perdue. Mais un utilisateur écarté qui a gardé une ancienne copie peut encore lire ce qu'il avait déjà - la vraie coupure est donc la rotation : émettre une nouvelle version de la clé pour les seuls destinataires restants, de sorte que chaque modification future utilise une version que l'utilisateur écarté n'a jamais reçue.
Distribution d'apps par politique
La même mécanique signée par l'admin qui distribue les clés distribue aussi les applications Haven. Un admin publie une politique qui nomme les apps proposées par un tenant et leurs destinataires, et chaque client Haven met sa liste locale d'applications en accord avec cette politique après la synchronisation de l'annuaire - intégrer un utilisateur à un tenant installe donc automatiquement les apps que ce tenant publie, et lui retirer l'accès les supprime.
Contrairement à une clé, une app ne porte aucun secret : la distribution se résume à qui reçoit quoi. La définition de l'app et ses métadonnées sont chiffrées avec la clé du tenant, si bien que le serveur zero-trust ne voit jamais que du texte chiffré tout en acheminant la politique aux bons destinataires.
Une politique vise aussi bien des utilisateurs individuels que des groupes entiers. Une liste push indique qui doit recevoir une app ; une liste pull la reprend. L'appartenance aux groupes est résolue au moment de la distribution : ajouter ou retirer quelqu'un d'un groupe change les destinataires de l'app sans toucher à la politique, et en cas de recouvrement, un pull l'emporte toujours sur un push.
Un utilisateur ordinaire peut préparer une nouvelle distribution - ou la modification d'une distribution existante - et la remettre à un admin sous forme de demande prête à signer. L'admin l'examine et signe le document de politique ; seule cette signature d'admin la rend valable. Le fonctionnement reproduit exactement celui de la distribution de clés : les personnes qui gèrent déjà les clés gèrent aussi les apps.
Après chaque synchronisation de l'annuaire, chaque client compare la politique à ce qu'il possède en local : il installe les apps qu'il devrait désormais avoir, en met à jour une quand la version publiée ou ses détails changent, et retire toute app à laquelle il n'a plus droit - sans que l'utilisateur ait à lever le petit doigt.
Une app distribuée porte la mention gérée par le tenant et indique de quel tenant elle provient. L'utilisateur ne peut ni modifier ni supprimer une app gérée - seulement la dupliquer en copie privée pour construire et tester en local - de sorte que la version publiée par le tenant reste la source de vérité.
Auditable par conception, honnête sur ses limites
Parce que chaque entrée porte une heure de confiance (receivedAt, avec repli sur createdAt pour les entrées purement locales) et que l'annuaire conserve une chaîne de voyage dans le temps de sa propre histoire, vous pouvez répondre à la question « l'utilisateur X avait-il le droit de modifier ce document au moment où la modification est entrée dans le tenant ? » - et reconstituer exactement l'évolution de ses accès dans le temps. La requête wasAllowedAt(op, user, dbid, at) en fait une API à part entière. Les infractions de niveau 2 ne disparaissent pas en silence ; elles sont consignées dans un journal de quarantaine propre à chaque tenant, que Haven affiche dans sa vue d'audit.
Cette couche gouverne à la fois les écritures et les lectures (au niveau des documents et des clés). Elle ne peut pas empêcher un client altéré de rédiger une entrée en local, mais elle empêche cette entrée d'être acceptée dans le tenant, et le verrou de lecture du serveur empêche des données non autorisées d'atteindre un client. La v1 s'appuie sur la synchronisation médiée par un serveur comme témoin. La synchronisation d'appareil à appareil via Iroh existe désormais, mais un pair n'émet pas de reçu de témoin : l'appareil qui reçoit évalue lui-même les règles d'identité, et le serveur juge à nouveau l'entrée quand elle arrive. Les reçus de témoin émis par un pair et un vrai contrôle de lecture au niveau du champ (clés par champ) restent prévus pour plus tard. L'historique n'est jamais rechiffré sur place, puisque les signatures des auteurs portent sur le texte chiffré.
MindooDB Haven réunit le même chiffrement de bout en bout, le même historique signé et la même gouvernance intégrée dans un espace de travail sobre et multiplateforme - y compris la vue d'audit qui fait apparaître les modifications en quarantaine. Essayez la bêta gratuite ou explorez les analyses détaillées.