Nadzór wbudowany w rdzeń
W MindooDB nadzór jest cechą samej warstwy danych - nie dodatkiem przykręconym do zaufanego serwera albo warstwy aplikacji. Polityka dostępu, tożsamość i kryptograficznie dowodliwa, odtwarzalna dla dowolnego momentu ścieżka audytu są przechowywane i wymuszane w rdzeniu bazy danych. Zysk: udowodnisz nie tylko, co się zmieniło, ale też kto miał prawo to zmienić - w chwili, gdy zmiana faktycznie trafiła do tenanta.
Nadzór ma dwie połowy: stronę zapisu, która decyduje, kto może tworzyć, zmieniać i usuwać dokumenty - wymuszaną nawet wtedy, gdy użytkownicy są offline - oraz stronę odczytu, która decyduje, kto może dane zobaczyć, łącząc bramę dostępu na poziomie synchronizacji z dystrybucją kluczy bez wglądu administratora. Obie opierają się na szyfrowaniu end-to-end w MindooDB.
Nadzór jest cechą warstwy danych
Większość stosów wypycha nadzór w górę - na serwer albo do aplikacji: baza danych przechowuje wiersze, a coś innego decyduje, kto może ich dotknąć, i prowadzi dziennik. MindooDB odwraca ten układ. Polityka, tożsamość, pełna historia i ścieżka audytu leżą w tych samych magazynach szyfrowanych end-to-end i prowadzonych w trybie append-only - gwarancje wędrują więc razem z danymi przez serwery, peery i urządzenia offline, i przetrwają nawet całkowicie przejęty serwer.
Polityki i reguły to podpisane przez administratora dokumenty w bazie danych katalogu, same również append-only. Dokładny moment włączenia nadzoru - albo jego chwilowego wyłączenia - jest częścią trwałego zapisu, a nie zmianą konfiguracji, która nie zostawia śladu.
Każdy wpis nosi zaufany czas, a katalog prowadzi łańcuch podróży w czasie po własnej historii. Dzięki temu wasAllowedAt(op, user, dbid, at) odtworzy, jakie reguły obowiązywały i kto był uprawniony w dowolnej przeszłej chwili - z deterministycznym wyrokiem, na który zgodzi się każda uczciwa replika.
Każda zmiana jest podpisana kluczem Ed25519, co czyni ją niezaprzeczalną, i poświadczona wobec zaufanego zegara. Naruszenia nie znikają po cichu - trafiają do dziennika kwarantanny i audytu prowadzonego dla każdego tenanta, który pokazuje Haven, więc rozliczalna jest nawet odrzucona próba.
Dokumenty nadania zawierają tablice kluczy dla każdego urządzenia; odwołanie polega po prostu na usunięciu kluczy, a wyraźne zdalne czyszczenie urządzenia usuwa całego tenanta ze skradzionego lub porzuconego urządzenia przy następnym połączeniu - cały cykl życia tożsamości jest zarządzany w tym samym podpisanym katalogu.
Model dwóch poziomów
Każda reguła należy do jednego z dwóch poziomów, w zależności od tego, co widzi serwer synchronizacji. Serwer zawsze dostaje tylko szyfrogram plus niewielki zestaw metadanych w postaci jawnej - treści dokumentów nie przeczyta nigdy. To jedno rozróżnienie jest całą architekturą: pozwala MindooDB uczciwie powiedzieć, co dokładnie jest zagwarantowane kryptograficznie, a co wymuszają polityką współpracujące klienty.
| Tier | Co sprawdza | Kto wymusza | Siła |
|---|---|---|---|
| Tier 1 - tożsamość | Tożsamość autora, docelowa baza danych, typ operacji | Serwer i klienty | Kryptograficzna - serwer odmawia poświadczenia wpisu łamiącego regułę, więc wpis nie może się rozejść |
| Tier 2 - treść | Rzeczywista treść dokumentu (withfields) | Tylko klienty | Polityka - ogranicza uczciwe klienty i kształtuje UX. Zmanipulowany klient obejdzie ją tylko lokalnie; każda uczciwa replika sprawdza withfields ponownie przy odbiorze i poddaje kwarantannie zmianę łamiącą regułę, więc nigdy nie stanie się ona widoczna dla nikogo innego |
Poświadczenia świadka: zaufany zegar, do którego nie musisz ufać klientowi
Najtrudniejsze pytanie w systemie local-first brzmi: "któremu zegarowi wierzymy?". Kto pracuje offline, mógłby antydatować własne wpisy, żeby przemknąć zmianę obok polityki. MindooDB odpowiada na to poświadczeniem świadka: potwierdzeniem podpisanym przez zaufanego świadka (Twój serwer synchronizacji), że wpis został przyjęty w określonym czasie. Każdy punkt wymuszania korzysta wtedy z dokładnie jednego, jasno określonego zegara.
SDK ocenia Tier 1 + Tier 2 wobec lokalnego stanu katalogu u użytkownika, na bieżący czas lokalny. Jeśli operacja jest dozwolona, wpis zapisuje się lokalnie bez pól świadka i od razu jest widoczny na tym urządzeniu. O czysto lokalnym widoku decyduje własny zegar użytkownika - i to nie szkodzi, bo zmiana jeszcze nie weszła do wspólnego tenanta.
Serwer ocenia Tier 1 wobec własnego stanu, na czas serwera. Jeśli operacja jest dozwolona, stempluje receivedAt, zapisuje klucz świadka, podpisuje poświadczenie i zwraca pola świadka, żeby wróciły do nadawcy i poszły dalej. Jeśli odmawia, zwraca strukturalne AccessDenied, a wpis zostaje lokalnie - nie może się rozejść.
Odbiorca sprawdza podpis poświadczenia wobec swojej listy zaufanych świadków. Poprawny podpis oznacza, że Tier 1 był spełniony w chwili receivedAt, więc domyślnie nie jest oceniany ponownie. Następnie odbiorca sprawdza lokalnie Tier 2 i albo materializuje zmianę, albo kieruje ją do lokalnego dziennika kwarantanny i audytu.
Jak konfiguruje się decyzje
Cały stan kontroli dostępu leży w dostępnej tylko administratorowi bazie danych directory i synchronizuje się do wszystkich uczestników. Wszystko, czego serwer potrzebuje do Tier 1, jest zaszyfrowane kluczem $publicinfos, więc da się to odczytać bez domyślnego klucza tenanta; treści withfields serwer nie przeczyta nigdy. Każde wywołanie zmieniające stan jest podpisane przez administratora.
Domyślna polityka tenanta i opcjonalne polityki dla poszczególnych baz danych określają, które operacje są zabronione, dopóki nie pasuje żadna reguła zezwalająca:
- Create, change, delete & undelete - dozwolone domyślnie, dopóki ich nie zabronisz
- Snapshot & purge - domyślnie tylko dla administratora
- Read - poziom bazowy dla bramy odczytu i synchronizacji na poziomie bazy danych, doprecyzowany regułami odczytu
- Dozwolone identyfikatory baz danych - lista tenanta określająca, jakie bazy danych mogą istnieć; serwer odmawia utworzenia i synchronizacji każdej bazy poza nią
- Domyślny klucz szyfrujący - którego klucza używa nowy dokument, gdy żaden nie jest podany; to wygoda, a nie mechanizm bezpieczeństwa
- Globalny wyłącznik - jedna flaga, która od razu wyłącza wszystkie kontrole dostępu
Zupełnie nowy tenant nie ma jeszcze żadnego dokumentu polityki, więc wszystko jest dozwolone, dopóki administrator go nie napisze. Każda wersja dopisuje się do historii, więc dokładne okno włączenia i wyłączenia zostaje sprawdzalne. Rzecz kluczowa: każda zmiana jest oceniana wobec polityk aktywnych w jej własnym zaufanym czasie (receivedAt). Zmiany, które weszły do tenanta po włączeniu, są nią objęte, a wcześniejsze rozstrzygają się wobec domyślnego, milczącego przyzwolenia - dane istniejące wcześniej zachowują więc swoje prawa automatycznie, bez żadnego kroku migracji.
Każda reguła celuje w typ operacji i bazę danych ("*" = wszystkie) i wymienia użytkowników lub grupy, których dotyczy. Ocena opiera się na zbiorach i nie zależy od kolejności:
- pasuje jakakolwiek reguła deny → zabronione
- w przeciwnym razie pasuje reguła allow → dozwolone
- a jeśli nie, decyduje polityka bazowa
Niezależność od kolejności jest ważna, bo dokumenty reguł scalają się między replikami przez CRDT - nie ma kolejności reguł, o którą można się nie zgodzić.
Serwer może dojść tylko do wyroku na poziomie Tier 1. Jeśli jedyną rzeczą stojącą między zezwoleniem a odmową jest treściowa reguła Tier 2, traktuje wpis jako dozwolony na Tier 1 i zostawia sprawdzenie withfields klientom. Każda decyzja zwraca strukturalny wynik - allowed, czytelny reason, matchedRuleId oraz tier - z którego SDK korzysta, żeby wyszarzyć akcje niedostępne dla użytkownika.
Klauzula withfields sprawdza ścieżkę kropkową wewnątrz dokumentu, zamkniętym zestawem operatorów (equals, contains, gt, …) i podstawieniami w rodzaju ${user.usernames}. Każda klauzula jest oceniana wobec wybranego stanu dokumentu:
- before - dokument w obecnej postaci (domyślne dla zmiany i usunięcia): "musisz już być edytorem". Ocena na stanie after pozwoliłaby dopisać siebie i samemu autoryzować własną edycję.
- after - dokument po zastosowaniu zmiany (domyślne dla tworzenia): "kto tworzy, musi dopisać siebie do
myeditors".
Reguły dopasowuje się do zahaszowanej nazwy użytkownika, hashy jego grup (także zagnieżdżonych) oraz zarezerwowanych pseudotokenów:
$everyone- wszyscy zarejestrowani użytkownicy$admin- tylko administrator$author- pierwotny twórca dokumentu (Tier 1, model własności)
Odwołanie polega na usunięciu kluczy z podpisanego przez administratora dokumentu nadania - nie ma osobnych dokumentów odwołania. Administrator może też podpisać wyraźne, włączane osobno zdalne czyszczenie urządzenia, które usuwa tenanta ze skradzionego lub porzuconego urządzenia przy jego następnym połączeniu.
CRM z edytorami dla każdego rekordu
Tu jedna realistyczna polityka przechodzi całą drogę, żeby wszystkie elementy się zgrały. Tenant ma bazę danych crm i chcemy czterech reguł: wszyscy mogą tworzyć kontakty, ale muszą wpisać siebie do myeditors; zmienić kontakt może tylko już wpisany edytor; usunąć go może tylko pierwotny twórca; a grupa HR może zmieniać wszystko jako furtka dla przełożonych.
Poziom bazowy to deny. Reguła 1 pasuje przez $everyone, a jej withfields przechodzi, bo myeditors w stanie after zawiera Alice → dozwolone. Serwer potwierdza tylko Tier 1, a potem poświadcza wpis.
Reguła 2 pasuje przez $everyone, ale jej withfields nie przechodzi: myeditors w stanie before nie zawiera Boba → lokalnie zabronione, nawet jeśli jego zmiana próbuje go dopisać. Zmanipulowany klient mógłby ją wypchnąć, ale każdy uczciwy odbiorca poddaje ją kwarantannie przy materializacji.
Reguła 3 pasuje przez $author - klucz twórcy i podpis usunięcia rozwiązują się do tego samego użytkownika → dozwolone i poświadczone (Tier 1, przetrwa złośliwego klienta). Reguła 4 pozwala każdemu członkowi grupy HR zmienić dowolny kontakt, niezależnie od myeditors.
Widać tu podział pracy: reguły Tier 1 ($author, grupa hr) są wymuszane na serwerze i przetrwają złośliwego klienta; reguły Tier 2 (sprawdzenia treści myeditors) wymusza każdy uczciwy klient i poddaje je kwarantannie przy odbiorze, jeśli zostały złamane.
Kontrola dostępu do odczytu
Reguły zapisu decydują, kto może dane zmieniać; dostęp do odczytu decyduje, kto może je widzieć - a MindooDB wymusza to na dwóch poziomach, obu podpisanych przez administratora i zaszyfrowanych kluczem $publicinfos, żeby serwer zero-trust mógł działać na potrzebnych mu metadanych, nigdy nie trzymając klucza tenanta.
1. Brama odczytu i synchronizacji na poziomie bazy danych. Poziom bazowy denyDocRead plus reguły doc_read (ta sama ocena deny-overrides-allow, celująca w użytkowników, grupy, $everyone albo $admin) decydują, kto w ogóle może otworzyć i synchronizować bazę danych. Jest to wplecione wprost w system synchronizacji: kto traci dostęp do odczytu, przestaje dostawać aktualizacje i nie otworzy już nawet swojej lokalnej, wcześniej zsynchronizowanej kopii. Ponieważ brama stoi przed każdą operacją synchronizacji, odczyt jest nadrzędną bramą dla każdej bazy danych - steruje też zapisem: kto nie może czytać bazy danych, nie utworzy w niej też danych (bo tworzyłby dokumenty, których nigdy by nie zobaczył).
2. Poufność dokumentów zostaje kryptograficzna. Dokument przeczyta tylko ten, kto w swoim KeyBagu (osobistym magazynie kluczy chronionym hasłem) ma klucz, który go odszyfruje - serwer nie podejmuje tu żadnej decyzji o odczycie. Dystrybucja kluczy odpowiada za to, by te klucze dotarły bezpiecznie do właściwych osób: podpisana przez administratora polityka mówi, którzy użytkownicy i które grupy mają trzymać dany klucz, a każdy klient automatycznie uzgadnia z nią swój KeyBag. Działa to na zasadzie opt-in - przy wspólnym kluczu domyślnym i bez bramy odczytu każdy może czytać wszystko, dokładnie jak wcześniej.
W wąskim gardle na serwerze ten sam hook, który sprawdza listę dozwolonych identyfikatorów baz danych, ocenia też doc_read dla uwierzytelnionego podmiotu wobec zaufanego czasu serwera i odmawia zarówno wydania zabronionej bazy danych, jak i przyjęcia wypchnięć do niej - niedozwolone aktualizacje po prostu nigdy nie zostają dostarczone. Równolegle ścieżka otwierania na kliencie odmawia otwarcia bazy danych, do której użytkownik stracił dostęp, więc użytkownik z odwołanym dostępem nie przeczyta nawet swojej lokalnie zsynchronizowanej kopii. Baza danych directory nigdy nie podlega bramie (nosi bowiem właśnie te polityki, od których brama zależy); administrator jest wyłączony spod niej.
Gdy klucz jest wysyłany do użytkownika, szyfruje się go jego własnym osobistym kluczem publicznym (RSA), więc rozpakuje go tylko on - żaden inny użytkownik ani nawet administrator nie przeczyta tego klucza. Dlatego dystrybucja działa bez wglądu administratora: ten, kto klucz już ma, pakuje go dla odbiorców, a administrator tylko podpisuje i publikuje wynik. Zwykły użytkownik może przygotować dystrybucję i przekazać ją administratorowi do podpisu - także administratorowi, który sam tego klucza nie ma.
Każdy klient uzgadnia swój KeyBag z polityką przy starcie i po każdej synchronizacji. Wyślij komuś klucz, a jego następna synchronizacja pobierze dokumenty, które ten klucz otwiera - po prostu pojawią się w bazie danych, już czytelne. Odbierz klucz, a te dokumenty znikną: lokalne kopie, których nie da się już odszyfrować, usuwane są z bazy danych, pamięci podręcznych i widoków. Awanse, rotacje i przejścia między działami przechodzą przez to automatycznie.
Dla oszczędności pasma serwer przestaje też wydawać dane zaszyfrowane kluczem, który użytkownik stracił. Ale odsunięty użytkownik, który zachował starą kopię, wciąż przeczyta to, co już miał - prawdziwym odcięciem jest więc rotacja: wydaj świeżą wersję klucza tylko pozostałym odbiorcom, a każda przyszła zmiana będzie używać wersji, której odsunięty użytkownik nigdy nie dostał.
Dystrybucja aplikacji oparta na politykach
Ta sama podpisywana przez administratora mechanika, która rozsyła klucze, rozsyła też aplikacje Haven. Administrator publikuje politykę, która nazywa aplikacje oferowane przez tenanta i wskazuje, kto ma je dostać, a każdy klient Haven po synchronizacji katalogu uzgadnia z tą polityką swoją lokalną listę aplikacji - wdrożenie użytkownika do tenanta samo instaluje więc aplikacje publikowane przez tego tenanta, a odebranie dostępu je usuwa.
W przeciwieństwie do klucza aplikacja nie nosi żadnego sekretu, więc dystrybucja to po prostu kwestia tego, kto co dostaje. Definicja aplikacji i jej metadane są zaszyfrowane kluczem tenanta, więc serwer zero-trust widzi wyłącznie szyfrogram, a mimo to kieruje politykę do właściwych odbiorców.
Polityka celuje zarówno w pojedynczych użytkowników, jak i w całe grupy. Lista push mówi, kto ma dostać aplikację; lista pull ją odbiera. Członkostwo w grupach rozwiązuje się w czasie dystrybucji, więc dodanie albo usunięcie kogoś z grupy zmienia, kto dostaje aplikację, bez ruszania polityki, a przy nakładaniu się list pull zawsze wygrywa z push.
Zwykły użytkownik może przygotować nową dystrybucję - albo zmianę w istniejącej - i przekazać ją administratorowi jako gotowe żądanie do podpisu. Administrator przegląda je i podpisuje dokument polityki; tylko ten podpis nadaje jej moc. Odpowiada to dokładnie dystrybucji kluczy, więc aplikacjami zarządzają ci sami ludzie, którzy zarządzają już kluczami.
Po każdej synchronizacji katalogu klient porównuje politykę z tym, co ma lokalnie: instaluje aplikacje, które powinien już mieć, aktualizuje aplikację, gdy zmieni się opublikowana wersja albo szczegóły, i usuwa każdą, do której nie ma już uprawnień - wszystko bez kiwnięcia palcem przez użytkownika.
Rozesłana aplikacja jest oznaczona jako zarządzana przez tenanta i pokazuje, z którego tenanta pochodzi. Aplikacji zarządzanych użytkownik nie może edytować ani usunąć - tylko zduplikować do prywatnej kopii na potrzeby lokalnego budowania i testów - żeby opublikowana wersja tenanta pozostała źródłem prawdy.
Sprawdzalne z założenia, uczciwe wobec swoich granic
Ponieważ każdy wpis nosi zaufany czas (receivedAt, a dla wpisów wyłącznie lokalnych zapasowo createdAt), a katalog prowadzi łańcuch podróży w czasie po własnej historii, odpowiesz na pytanie "czy użytkownik X miał prawo zmienić ten dokument w chwili, gdy zmiana faktycznie weszła do tenanta?" - i odtworzysz dokładnie, jak jego dostęp zmieniał się z czasem. Zapytanie wasAllowedAt(op, user, dbid, at) czyni z tego pełnoprawne API. Naruszenia Tier 2 nie znikają po cichu; trafiają do dziennika kwarantanny prowadzonego dla każdego tenanta, który Haven pokazuje w widoku audytu.
Warstwa ta reguluje zarówno zapisy, jak i odczyty (na poziomie dokumentów i kluczy). Nie powstrzyma zmanipulowanego klienta od utworzenia wpisu lokalnie, ale nie dopuści, by ten wpis został przyjęty do tenanta, a brama odczytu na serwerze nie pozwoli nieuprawnionym danym w ogóle dotrzeć do klienta. W v1 świadkiem jest synchronizacja przez serwer. Synchronizacja z urządzenia na urządzenie przez Iroh już istnieje, ale peer nie wystawia pokwitowania świadka: urządzenie odbierające samo ocenia reguły tożsamości, a serwer ocenia wpis ponownie, gdy ten dotrze. Pokwitowania świadka wystawiane przez peery i prawdziwa kontrola odczytu na poziomie pól (klucze dla każdego pola) pozostają na później. Historia nie jest nigdy szyfrowana ponownie w miejscu, bo podpisy autorów obejmują szyfrogram.
MindooDB Haven wkłada to samo szyfrowanie end-to-end, podpisaną historię i wbudowany nadzór w spokojny, wieloplatformowy obszar roboczy - razem z widokiem audytu, który pokazuje zmiany w kwarantannie. Wypróbuj darmową betę albo poczytaj szczegóły.