Governance, vast in de kern
In MindooDB is governance een eigenschap van de datalaag zelf - geen opzetstuk op een vertrouwde server of applicatielaag. Toegangsbeleid, identiteit en een cryptografisch bewijsbaar audittrail dat je op elk moment kunt reproduceren, staan in de databasekern en worden daar afgedwongen. De winst: je kunt niet alleen bewijzen wat er is gewijzigd, maar ook wie het mocht wijzigen - op het moment dat de wijziging echt in de tenant belandde.
Governance heeft twee helften: een schrijfkant die bepaalt wie documenten mag aanmaken, wijzigen of verwijderen - ook afgedwongen als gebruikers offline zijn - en een leeskant die bepaalt wie gegevens mag zien, met een toegangspoort op sync-niveau plus sleuteldistributie zonder admin-inzage. Beide bouwen op de end-to-end-versleuteling van MindooDB.
Governance is een eigenschap van de datalaag
De meeste stacks schuiven governance omhoog naar de server of de applicatie: de database bewaart rijen en iets anders beslist wie eraan mag komen en houdt het logboek bij. MindooDB draait dat om. Beleid, identiteit, volledige geschiedenis en het audittrail liggen in dezelfde end-to-end versleutelde, append-only stores - zo reizen de garanties mee met de gegevens over servers, peers en offline apparaten heen, en overleven ze ook een volledig gekraakte server.
Beleid en regels zijn admin-ondertekende documenten in de directory-database en zelf ook append-only. Het exacte moment waarop governance werd aangezet - of tijdelijk uitgezet - hoort bij de blijvende vastlegging en is geen configuratiewijziging die geen spoor achterlaat.
Elke vermelding draagt een vertrouwde tijd, en de directory houdt een tijdreisketen van haar eigen geschiedenis bij. Daarmee kan wasAllowedAt(op, user, dbid, at) opnieuw afspelen welke regels golden en wie bevoegd was op elk moment in het verleden - een deterministisch oordeel waar elke eerlijke replica het over eens is.
Elke wijziging is Ed25519-ondertekend en dus onweerlegbaar, en wordt bezegeld tegen een vertrouwde klok. Overtredingen verdwijnen niet stil: ze komen in een quarantaine- en auditlogboek per tenant dat Haven laat zien, dus zelfs een afgewezen poging blijft na te trekken.
Toekenningen bevatten sleutelreeksen per apparaat; intrekken is simpelweg sleutels verwijderen, en een expliciet wissen op afstand haalt de hele tenant van een gestolen of afgedankt apparaat zodra dat weer verbinding maakt - de levensloop van een identiteit wordt in dezelfde ondertekende directory beheerd.
Het model met twee tiers
Elke regel valt in een van twee tiers, afhankelijk van wat de sync-server kan zien. De server verwerkt uitsluitend versleutelde gegevens plus een handvol leesbare metadata - de inhoud van documenten kan hij nooit lezen. Dat ene onderscheid is de hele architectuur: het laat MindooDB eerlijk zeggen wat precies cryptografisch is gegarandeerd en wat via beleid wordt afgedwongen tussen clients die meewerken.
| Tier | Controleert | Afgedwongen door | Kracht |
|---|---|---|---|
| Tier 1 - identiteit | Identiteit van de auteur, doeldatabase, type bewerking | Server en clients | Cryptografisch - de server weigert een overtredende vermelding als getuige te bevestigen, dus die kan zich niet verspreiden |
| Tier 2 - inhoud | De werkelijke documentinhoud (withfields) | Alleen clients | Beleid - houdt eerlijke clients binnen de lijnen en vormt de interface. Een gemanipuleerde client komt er alleen lokaal langs; elke eerlijke replica controleert withfields bij ontvangst opnieuw en zet een overtredende wijziging in quarantaine, zodat niemand anders die ooit ziet |
Getuigenbewijzen: een vertrouwde klok waarvoor je de client niet hoeft te vertrouwen
De moeilijkste vraag in een local-first systeem is: "welke klok vertrouwen we?" Wie offline werkt, zou eigen vermeldingen kunnen antedateren om een wijziging langs het beleid te smokkelen. MindooDB antwoordt daarop met een getuigenbewijs: een verklaring, ondertekend door een vertrouwde getuige (jouw sync-server), dat een vermelding op een bepaald moment is geaccepteerd. Elk punt waar iets wordt afgedwongen, gebruikt daarmee precies één helder gedefinieerde klok.
De SDK beoordeelt Tier 1 + Tier 2 tegen de lokale directory-stand van de gebruiker op de huidige lokale tijd. Mag het, dan wordt de vermelding lokaal opgeslagen zonder getuigevelden en is ze op dit apparaat meteen zichtbaar. Over de eigen lokale weergave beslist de eigen klok - dat is niet erg, want de wijziging is nog niet in de gedeelde tenant terechtgekomen.
De server beoordeelt Tier 1 tegen zijn eigen stand op servertijd. Mag het, dan stempelt hij receivedAt, legt de getuigesleutel vast, ondertekent het bewijs en geeft de getuigevelden terug, zodat ze naar de afzender en verder stromen. Weigert hij, dan komt er een gestructureerde AccessDenied terug en blijft de vermelding lokaal - ze kan zich niet verspreiden.
De ontvanger controleert de handtekening op het bewijs tegen zijn lijst met vertrouwde getuigen. Een geldige handtekening betekent dat Tier 1 op receivedAt in orde was, dus standaard wordt dat niet opnieuw beoordeeld. Daarna controleert de ontvanger Tier 2 lokaal en materialiseert hij de wijziging, of stuurt hij die naar een lokaal quarantaine- en auditlogboek.
Hoe beslissingen worden ingesteld
Alles wat de toegangscontrole bepaalt, staat in de directory-database die alleen voor beheerders is en synchroniseert naar alle deelnemers. Alles wat de server voor Tier 1 nodig heeft, is versleuteld met de $publicinfos-sleutel en dus leesbaar zonder de standaardsleutel van de tenant; withfields-inhoud kan de server nooit lezen. Elke wijzigende aanroep is admin-ondertekend.
Een standaardbeleid voor de tenant en optioneel beleid per database bepalen welke bewerkingen worden geweigerd zolang geen toestaan-regel past:
- Aanmaken, wijzigen, verwijderen & terugzetten - standaard toegestaan, tot je ze weigert
- Snapshot & wissen - standaard alleen voor beheerders
- Lezen - de basis voor de lees-/sync-poort op databaseniveau, verfijnd met leesregels
- Toegestane database-ID's - de toelatingslijst van de tenant met welke databases mogen bestaan; de server weigert een database daarbuiten aan te maken of te synchroniseren
- Standaard-versleutelingssleutel - welke sleutel een nieuw document gebruikt als er geen is opgegeven; een gemak, geen beveiligingsmaatregel
- Hoofdschakelaar - één vlag die alle toegangscontroles in één keer uitzet
Een kersverse tenant heeft helemaal geen beleidsdocument, dus alles mag tot een beheerder er een schrijft. Elke revisie wordt aan de geschiedenis toegevoegd, zodat het exacte venster van aan- en uitzetten na te trekken blijft. Belangrijk: elke wijziging wordt getoetst aan het beleid dat gold op haar eigen vertrouwde tijd (receivedAt). Wijzigingen die na het aanzetten in de tenant belandden, vallen eronder; eerdere worden afgehandeld volgens de impliciete alles-mag-basis - bestaande gegevens houden dus automatisch hun status, zonder migratiestap.
Elke regel richt zich op een type bewerking en een database ("*" = alle) en noemt de gebruikers of groepen waarvoor ze geldt. De beoordeling werkt op verzamelingen en is onafhankelijk van de volgorde:
- past een weigeren-regel → geweigerd
- anders past een toestaan-regel → toegestaan
- anders beslist het basisbeleid
Onafhankelijk van de volgorde is belangrijk, omdat regeldocumenten via CRDT's tussen replica's worden samengevoegd - er is geen regelvolgorde om oneens over te worden.
Een server kan alleen tot een oordeel op Tier 1 komen. Staat alleen een Tier 2-inhoudsregel tussen toestaan en weigeren, dan behandelt hij de vermelding als toegestaan op Tier 1 en laat hij de withfields-controle aan de clients. Elke beslissing levert een gestructureerd resultaat op - allowed, een leesbare reason, de matchedRuleId en de tier - waarmee de SDK acties uitgrijst die een gebruiker niet mag uitvoeren.
Een withfields-clausule toetst een puntpad in het document met een vaste set operatoren (equals, contains, gt, …) en plaatshouders als ${user.usernames}. Elke clausule wordt beoordeeld tegen een gekozen documentstatus:
- before - het bestaande document (standaard voor wijzigen/verwijderen): "je moet al bewerker zijn". Beoordelen met after zou iemand toestaan zichzelf toe te voegen en zo zijn eigen bewerking goed te keuren.
- after - het document met de wijziging erin (standaard voor aanmaken): "wie aanmaakt, moet zichzelf in
myeditorszetten".
Regels worden getoetst aan de gehashte gebruikersnaam van een gebruiker, de hashes van zijn groepen (ook geneste groepen) en gereserveerde pseudotokens:
$everyone- alle geregistreerde gebruikers$admin- alleen beheerders$author- de oorspronkelijke maker van het document (Tier 1, eigenaarsmodel)
Intrekken gebeurt door sleutels te verwijderen uit het admin-ondertekende toekenningsdocument - aparte intrekkingsdocumenten bestaan niet. Een beheerder kan daarnaast een uitdrukkelijk wissen op afstand ondertekenen dat je zelf aanzet, en dat de tenant bij de volgende verbinding van een gestolen of afgedankt apparaat haalt.
Een CRM met bewerkers per record
Hier loopt één realistisch beleid helemaal door, zodat de onderdelen op hun plek vallen. De tenant heeft een crm-database en we willen vier regels: iedereen mag contacten aanmaken, maar moet zichzelf in myeditors zetten; een contact wijzigen mag alleen wie er al in staat als bewerker; verwijderen mag alleen de oorspronkelijke maker; en de groep HR mag als escalatieweg alles wijzigen.
De basis is weigeren. Regel 1 past via $everyone en haar withfields komt erdoor, omdat myeditors in de after-status Alice bevat → toegestaan. De server controleert alleen Tier 1 en bevestigt de vermelding daarna als getuige.
Regel 2 past via $everyone, maar haar withfields faalt: myeditors in de before-status bevat Bob niet → lokaal geweigerd, ook als zijn wijziging hem er zelf aan wil toevoegen. Een gemanipuleerde client zou hem kunnen pushen, maar elke eerlijke ontvanger zet hem bij het materialiseren in quarantaine.
Regel 3 past via $author - de sleutel van de maker en die van de ondertekenaar van het verwijderen leiden naar dezelfde gebruiker → toegestaan en door de getuige bevestigd (Tier 1, overleeft een kwaadwillende client). Regel 4 laat elk lid van de groep HR elk contact wijzigen, ongeacht myeditors.
Dit toont de werkverdeling: Tier 1-regels ($author, groep hr) worden op de server afgedwongen en overleven een kwaadwillende client; Tier 2-regels (de inhoudscontroles op myeditors) dwingt elke eerlijke client af en zet bij overtreding in quarantaine bij ontvangst.
Toegangscontrole op lezen
Schrijfregels bepalen wie gegevens mag wijzigen; leestoegang bepaalt wie ze mag zien - en MindooDB dwingt dat op twee niveaus af, beide admin-ondertekend en versleuteld met $publicinfos, zodat de zero-trust-server met de metadata kan werken die hij nodig heeft zonder ooit de tenant-sleutel te hebben.
1. Een lees-/sync-poort op databaseniveau. Een denyDocRead-basis plus doc_read-regels (dezelfde beoordeling met weigeren-overstemt-toestaan, gericht op gebruikers, groepen, $everyone of $admin) bepalen wie een database helemaal mag openen en synchroniseren. Het zit rechtstreeks in het sync-systeem: wie leestoegang kwijtraakt, krijgt geen updates meer en kan de al gesynchroniseerde lokale kopie ook niet meer openen. Omdat de poort vóór elke sync-bewerking staat, is lezen de hoofdpoort per database - die stuurt ook het schrijven: wie een database niet kan lezen, kan er ook geen gegevens in aanmaken (en zou anders documenten schrijven die hij nooit te zien krijgt).
2. De vertrouwelijkheid van documenten blijft cryptografisch. Een document is alleen te lezen door wie in zijn KeyBag (de persoonlijke, met een wachtwoord beschermde sleutelopslag) een sleutel heeft die het ontsleutelt - de server neemt hier nooit een leesbeslissing. Sleuteldistributie brengt die sleutels veilig bij de juiste mensen: een admin-ondertekend beleid bepaalt welke gebruikers en groepen elke sleutel horen te hebben, en elke client houdt zijn KeyBag daar automatisch mee in lijn. Het is opt-in - met de gedeelde standaardsleutel en zonder leespoort kan iedereen alles lezen, precies als eerder.
Op het knelpunt in de server beoordeelt dezelfde hook die de toelatingslijst met database-ID's controleert ook doc_read voor de geauthenticeerde principal tegen de vertrouwde servertijd, en weigert hij een geweigerde database uit te leveren of er pushes voor aan te nemen - niet-toegestane updates worden simpelweg nooit bezorgd. In hetzelfde ritme weigert de client een database te openen waarvoor de gebruiker de toegang kwijt is, zodat een ingetrokken gebruiker zelfs zijn lokaal gesynchroniseerde kopie niet kan lezen. De directory-database valt nooit onder de poort (die draagt juist het beleid waarvan de poort afhangt); de beheerder is uitgezonderd.
Wordt een sleutel naar een gebruiker gepusht, dan is hij versleuteld met de persoonlijke publieke (RSA-)sleutel van die gebruiker, zodat alleen hij hem kan uitpakken - geen andere gebruiker en zelfs de beheerder niet kan de sleutel lezen. Daarom ziet de beheerder bij de distributie niets: wie de sleutel al heeft, pakt hem in voor de ontvangers, en de beheerder ondertekent en publiceert alleen het resultaat. Ook een gewone gebruiker kan een distributie voorbereiden en aan een beheerder geven om te ondertekenen, zelfs aan een beheerder die de sleutel niet heeft.
Elke client houdt zijn KeyBag bij het starten en na elke sync in lijn met het beleid. Push je een sleutel naar een gebruiker, dan haalt zijn volgende sync de documenten binnen die die sleutel opent - ze verschijnen gewoon in de database en zijn nu leesbaar. Haal je een sleutel terug, dan verdwijnen die documenten: de lokale kopieën die niet meer te ontsleutelen zijn, gaan uit de database, de caches en de weergaven. Promoties, rotaties en overstappen naar een andere afdeling lopen hier automatisch via.
Om bandbreedte te sparen levert de server ook geen gegevens meer uit die met een sleutel zijn versleuteld die een gebruiker kwijt is. Maar wie is verwijderd en een oude kopie hield, kan nog steeds lezen wat hij al had - de echte grens is dus rotatie: geef een verse versie van de sleutel alleen aan de resterende ontvangers, dan gebruikt elke volgende wijziging een versie die de verwijderde gebruiker nooit kreeg.
App-distributie via beleid
Dezelfde admin-ondertekende machinerie die sleutels verspreidt, verspreidt ook Haven-applicaties. Een beheerder publiceert een beleid dat benoemt welke apps een tenant aanbiedt en wie ze horen te krijgen, en elke Haven-client stemt zijn lokale applicatielijst na elke directory-sync op dat beleid af - zo installeert het toelaten van een gebruiker tot een tenant automatisch de apps die die tenant publiceert, en haalt intrekken ze weer weg.
Anders dan een sleutel draagt een app geen geheim, dus gaat distributie simpelweg over wie wat krijgt. De app-definitie en haar metadata zijn versleuteld met de tenant-sleutel, dus de zero-trust-server ziet alleen versleutelde gegevens en bezorgt het beleid toch bij de juiste ontvangers.
Een beleid richt zich op losse gebruikers én op hele groepen. Een push-lijst zegt wie een app horen te krijgen; een pull-lijst haalt hem terug. Het groepslidmaatschap wordt bij de distributie bepaald, dus iemand aan een groep toevoegen of eruit halen verandert wie de app krijgt zonder het beleid aan te raken, en bij overlap wint een pull altijd van een push.
Een gewone gebruiker kan een nieuwe distributie - of een wijziging aan een bestaande - voorbereiden en die als kant-en-klaar verzoek aan een beheerder geven. De beheerder beoordeelt het en ondertekent het beleidsdocument; pas die admin-handtekening maakt het geldig. Het werkt precies als sleuteldistributie, dus wie al sleutels beheert, beheert ook apps.
Na elke directory-sync vergelijkt elke client het beleid met wat hij lokaal heeft: hij installeert apps die hij nu horen te hebben, werkt een app bij als de gepubliceerde versie of details wijzigen, en verwijdert elke app waar hij geen recht meer op heeft - allemaal zonder dat de gebruiker iets hoeft te doen.
Een verspreide app is gemarkeerd als beheerd door tenant en laat zien uit welke tenant hij komt. Beheerde apps kan de gebruiker niet bewerken of verwijderen - alleen dupliceren naar een eigen kopie om lokaal te bouwen en te testen - zodat de gepubliceerde versie van de tenant de bron van waarheid blijft.
Controleerbaar van opzet, eerlijk over de grenzen
Omdat elke vermelding een vertrouwde tijd draagt (receivedAt, en createdAt als terugval bij vermeldingen die alleen lokaal bestaan) en de directory een tijdreisketen van haar eigen geschiedenis bijhoudt, is de vraag te beantwoorden: "mocht gebruiker X dit document wijzigen op het moment dat de wijziging echt in de tenant belandde?" - en is precies te reconstrueren hoe de toegang van een gebruiker in de loop van de tijd veranderde. De query wasAllowedAt(op, user, dbid, at) maakt daar een volwaardige API van. Overtredingen op Tier 2 verdwijnen niet stil; ze worden vastgelegd in een quarantainelogboek per tenant dat Haven in zijn auditweergave laat zien.
Deze laag regelt zowel schrijven als lezen (op document- en sleutelniveau). Ze kan een gemanipuleerde client niet beletten lokaal een vermelding te schrijven, maar voorkomt wel dat die vermelding in de tenant wordt opgenomen, en de leespoort van de server houdt gegevens waarop geen recht bestaat tegen voordat ze ooit bij een client aankomen. v1 gebruikt sync via een server als getuige. Synchronisatie van apparaat naar apparaat over Iroh bestaat inmiddels, maar een peer geeft geen getuigenisbewijs af: het ontvangende apparaat beoordeelt de identiteitsregels zelf, en de server beoordeelt de vermelding opnieuw als die aankomt. Door peers afgegeven getuigenisbewijzen en echte leescontrole op veldniveau (sleutels per veld) blijven toekomstig werk. Geschiedenis wordt nooit achteraf opnieuw versleuteld, omdat de handtekeningen van auteurs over de versleutelde gegevens liggen.
MindooDB Haven brengt dezelfde end-to-end versleuteling, ondertekende geschiedenis en ingebouwde governance in één rustige werkruimte op elk platform - inclusief de auditweergave die wijzigingen in quarantaine laat zien. Probeer de gratis bèta of lees je in de details in.