Die API für Anwendungen
Latet schützt, was ein Agent an ein Modell sendet. Programme senden vertraulichen Text aber auch an andere Stellen: ihre Logs an Datadog, Splunk oder Elastic, ihre Dokumente an den Index einer RAG-Anwendung. Mit der API für Anwendungen bittet ein Programm einen Latet-Server, mit dem Token eines API-Schlüssels, einen Text:
- zu analysieren (
scan): was darin vertraulich ist, und wo; - zu maskieren (
mask): derselbe Text mit realistischen Ersatzwerten anstelle seiner Werte; - wiederherzustellen (
unmask): die echten Werte wieder anstelle der Ersatzwerte; - Werte zu vergessen (
forget): sie aus dem Tresor des Schlüssels zu entfernen, wenn eine Person verlangt, dass ihre Daten verschwinden (Werte vergessen).
Sie ist eine Funktion des Latet-Servers: API-Schlüssel und ihre Token gibt es nur dort, und ein
Rechner ohne Server hat nichts davon. Um sie auf Ihrem eigenen Rechner auszuprobieren, starten Sie
dort einen Community-Server mit dem Serverprofil von compose.yaml
(Serverbetrieb).
Was sie mehr bietet als eine Schwärzungsregel:
- Jedes Mal derselbe Ersatzwert. Ein Wert erhält in jedem Aufruf, auf jeder Instanz des Servers denselben Ersatzwert, solange der Tresor des Schlüssels ihn behält. In einem Log-Dienst funktionieren Diagramme und Suchen weiter, und ein Kunde lässt sich über Tage von Logs verfolgen, ohne dass jemand sieht, wer er ist. In einem RAG-Index bleibt eine Person über alle Dokumente dieselbe Person.
- Ein Tresor für den ganzen Ablauf. Dokumente und Fragen, die mit demselben Schlüssel maskiert
werden, teilen ihre Ersatzwerte. So arbeitet ein Modell mit stimmigen Ersatzwerten, und
unmasksetzt die echten Werte in seine Antwort wieder ein. - Umkehrbar, unter Kontrolle. Wiederherstellen ist eine eigene Berechtigung, erreicht nur die Ersatzwerte des Schlüssels, und jede Wiederherstellung über die API für Anwendungen wird im Administrationsprotokoll des Servers festgehalten.
Zwei Seiten führen durch die beiden Einsätze, für die sie gebaut wurde: Logs und
RAG. Die HTTP-Referenz beschreibt den Vertrag für Programme in jeder
Sprache, und mit der OpenAPI-Beschreibung der API lässt sich ein Client
dafür erzeugen. Python-Programme können das Paket latet-client
verwenden, das mit LangChain zusammenarbeitet, und n8n-Workflows die Latet-Nodes für n8n.
Ein erster Aufruf
Abschnitt betitelt „Ein erster Aufruf“Ein Programm sendet eine Liste von Texten und erhält eine Liste zurück, in derselben Reihenfolge:
printf 'Authorization: Bearer %s' "$(cat shop.key)" | curl -sS -H @- \ -H "Content-Type: application/json" \ -d '{"texts": ["Bitte erstatten Sie die Bestellung von mario.rossi@acme.it auf IT60X0542811101000000123456."]}' \ https://latet.acme.lan/_latet/apps/v1/mask{"results": [{"text": "Bitte erstatten Sie die Bestellung von tommaso.gallo@margie-6e5a.example auf IT17F3945471087320356678487.", "replacements": [{"type": "EMAIL", "class": "pii", "start": 39, "end": 72}, {"type": "IBAN", "class": "pii", "start": 77, "end": 104}], "counts": {"EMAIL": 1, "IBAN": 1}}]}shop.key enthält den ersten Token des Schlüssels, den keys add schreibt
(weiter unten).
printf, das die Shell selbst ausführt, übergibt den Header über die Standardeingabe an curl
(-H @-). So steht der Token nicht in der Befehlszeile von curl, wo andere Benutzer des Rechners ihn
lesen könnten. Die Ersatzwerte sind die des Schlüssels: Ein anderer Schlüssel oder ein anderer Server
liefert andere. Mit einem Token, der wiederherstellen darf, kommt derselbe Text, an
/_latet/apps/v1/unmask gesendet, so zurück, wie er war, mit "restored": 2.
API-Schlüssel und Token
Abschnitt betitelt „API-Schlüssel und Token“Ein API-Schlüssel ist ein Bereich auf dem Server: seine Regeln, sein Tresor mit Ersatzwerten, sein Verlauf. Er kann mehrere Token haben, die Geheimnisse, die in diesem Bereich handeln, und jeder Token hat seine eigenen Berechtigungen:
| Berechtigung | Was der Token darf |
|---|---|
scan |
Sagen, was ein Text enthält und wo; er ändert nichts. |
mask |
Die Werte eines Textes durch die Ersatzwerte des Schlüssels ersetzen. Ein Token, der maskieren darf, darf auch analysieren. |
unmask |
Die echten Werte wieder anstelle der Ersatzwerte des Schlüssels einsetzen. |
forget |
Werte aus dem Tresor des Schlüssels entfernen (Werte vergessen). Keine andere Berechtigung schließt sie ein. |
Geben Sie jedem Programm nur, was es braucht. Eine Anwendung, die ihre Logs versendet, braucht
mask; das Support-Werkzeug, das eine aus dem Log-Dienst kopierte Zeile liest, braucht unmask; eine
RAG-Anwendung, die ihre Dokumente maskiert und die Antwort des Modells wiederherstellt, braucht beides.
latet server --server https://latet.acme.lan keys add "Shop" --out shop.keylatet server --server https://latet.acme.lan keys add-token "Shop" shipper --mask --out shipper.tokenlatet server --server https://latet.acme.lan keys add-token "Shop" support --unmask --out support.tokenlatet server --server https://latet.acme.lan keys tokens "Shop"Der erste Token eines Schlüssels, den keys add erzeugt, hat scan, mask und unmask, ebenso der
Token jedes Schlüssels, der angelegt wurde, bevor Token Berechtigungen hatten: Diese Schlüssel
funktionieren wie bisher. Der erste Token des Schlüssels hat außerdem eine Adresse für Modelle, das
Gateway, und bedient darüber die
Agenten des Schlüssels: keys tokens kennzeichnet ihn mit serves agents (has an address), damit er
nicht anstelle eines Tokens widerrufen wird, der mit denselben Berechtigungen hinzugefügt wurde. Ein
Token, der mit keys add-token erstellt wurde, hat keine Adresse, welche
Berechtigungen er auch hat, --mask --unmask eingeschlossen: Das Gateway und die Bridge des Plugins
weisen ihn ab (403), und er ruft nur die API für Anwendungen auf.
Jeder Token lässt sich einzeln stoppen: keys tokens "Shop" --revoke t_4kxq2m7zap widerruft einen
anhand der ID, die keys tokens auflistet, innerhalb von Sekunden auf jeder Instanz. keys rotate "Shop" --token t_4kxq2m7zap --out shipper.token ersetzt diesen Token durch einen neuen, mit neuem
Geheimnis und neuer ID, demselben Namen und denselben Berechtigungen; ein Schlüssel mit nur einem
Token braucht kein --token. Jede Änderung steht im Administrationsprotokoll (token.added,
token.revoked, key.rotated), mit den IDs der Token, nie mit einem Geheimnis.
Die API für Anwendungen nimmt nur Token von API-Schlüsseln an: Der Rechner-Token einer Person wird abgewiesen (403), ebenso eine Anfrage, die eine Webseite stellt. Personen verwenden ihr Dashboard.
Prosa und Daten
Abschnitt betitelt „Prosa und Daten“scan und mask nehmen eine Art (kind) für die Texte eines Aufrufs:
prose(die Voreinstellung): Text, den Menschen geschrieben haben, etwa Nachrichten, Tickets und Dokumente. Er wird maskiert, wie das Gateway Ihre eigenen Nachrichten maskiert.data: Logs, Datensätze, die Ausgabe eines Programms. Sie werden maskiert, wie das Gateway die Ausgabe eines Werkzeugs maskiert.
Der Unterschied liegt in dem, was wahrscheinlich ist: Im Profil balanced werden Namen nur in Prosa
gesucht (mit dem auf dem Server installierten
lokalen Namensmodell), und eine
Ziffernfolge zählt nur in Prosa als Telefonnummer, während sie in einem Log weit öfter eine ID ist. Mit
phone_regions: [IT] in den Regeln des Schlüssels erhält die Nummer in
Rufen Sie mich morgen unter 3471234567 an. als Prosa einen Ersatzwert und bleibt als Daten, wie sie
ist. Ein JSON-Dokument, das als Text gesendet wird, wird als Text maskiert und behält sein Layout,
sodass die Positionen in der Antwort Positionen darin sind.
Was eine Maskierung liest
Abschnitt betitelt „Was eine Maskierung liest“scan und mask verwenden die Einstellungen des Schlüssels, wie sie beim Eintreffen des Aufrufs
sind: die eigenen Regeln des Schlüssels über den gemeinsamen Einstellungen des Servers, wie es das
Gateway für die Anfragen des Schlüssels tut. Es gibt keine Regeln nur für die API. Sie werden mit
latet server config --key und latet server rules --key gesetzt
(gemeinsame Einstellungen und Regeln von API-Schlüsseln).
- Der lokale Prüfer liest, wenn die Regeln des Schlüssels einen festlegen, jeden Text eines
mask-Aufrufs vor der Antwort, was auch immerllm_review.inlinefür das Gateway sagt: Eine Anwendung hat keinen späteren Zeitpunkt. Er erhältllm_review.budget_ms, wenn gesetzt, sonst 30 Sekunden. Ein Text, dessen Prüfung scheitert, folgtllm_review.on_failure: Mitstop(der Voreinstellung) lautet die Antwortblocked, mitsendkommt der Text so, wie ihn die Regeln maskiert haben. Bei einem ausgefallenen Prüfer wird also jeder Text blockiert, bis er wieder antwortet. scanführt den lokalen Prüfer nicht aus. Der Prüfer bringt dem Tresor bei, was er findet, und eine Analyse mitscanändert nichts. Bei einem Schlüssel mit Prüfer kannscanweniger melden, alsmaskersetzt.- Der Live-Prüfer hält den Text einer Anwendung nie an. Eine Anwendung kann nicht auf einen Menschen warten: Ein Text wird allein von den Regeln und dem lokalen Prüfer maskiert, auch wenn die Regeln des Schlüssels den Live-Prüfer einschalten.
- Der Dump-Schutz gilt für
data-Texte wie für die Ausgabe eines Werkzeugs: Einer, der mehr personenbezogene Werte enthält, alsdump_guarderlaubt, erhältblocked.
Ein blockierter Text kommt als {"blocked": "<Grund>"} ohne Text zurück, und der Grund nennt keinen
Wert. Wenn im Aufruf etwas eingeschränkt lief (ein Detektor, der einen Text nicht lesen konnte, eine
Prüfung, die nicht fertig wurde), sagt die Antwort das in "degraded", mit den Namen dessen, was es
betraf, etwa ner oder llm-review.
Zweimal maskiert, zweimal wiederhergestellt
Abschnitt betitelt „Zweimal maskiert, zweimal wiederhergestellt“mask hält jeden Wert, den es findet, für echt, auch die eigenen Ersatzwerte des Schlüssels: Niemand
kann einer Zeichenkette ansehen, ob sie ein Ersatzwert ist oder ein echter Wert, der wie einer aussieht.
Ein zweimal maskierter Text enthält Ersatzwerte von Ersatzwerten, und eine Wiederherstellung führt ihn
um einen Schritt zurück:
| Schritt | Text |
|---|---|
| Original | Schreiben Sie an mario.rossi@acme.it |
| Maskiert | Schreiben Sie an tommaso.gallo@margie-6e5a.example |
| Erneut maskiert | Schreiben Sie an simone.desantis@humongous-0e54.example |
| Einmal wiederhergestellt | Schreiben Sie an tommaso.gallo@margie-6e5a.example |
Maskieren Sie einen Text einmal, ausgehend vom Original. Das Gateway folgt derselben Regel: Ein Text, der über die API maskiert und dann über das Gateway von Latet an ein Modell gesendet wird, wird dort erneut maskiert, und die Antwort des Modells kommt mit den Ersatzwerten der API zurück. Eine RAG-Anwendung sendet den maskierten Text deshalb direkt an das Modell und stellt die Antwort über die API wieder her (RAG).
Wer was erfahren kann
Abschnitt betitelt „Wer was erfahren kann“Wiederherstellen liefert die echten Werte der Ersatzwerte des Schlüssels, gleich welcher Aufruf oder
welche Unterhaltung des Schlüssels sie erzeugt hat, auch die vom letzten Jahr. Es erreicht keine
Ersatzwerte eines anderen Schlüssels und keine einer Person: Text, der kein Ersatzwert des Schlüssels
ist, bleibt, wie er ist. Der Name einer Person kommt nur als Ganzes zurück: Ein Teil davon allein (ein
Vorname) kann ein gewöhnliches Wort oder der Name einer anderen Person sein und bleibt, wie er ist. Jede
Wiederherstellung wird im Administrationsprotokoll festgehalten, bevor ihre Antwort gesendet wird
(apps.unmasked, mit dem Token, wie viele Texte und wie viele Werte; nie ein Text oder ein Wert), und
ein Protokoll, das sie nicht aufnehmen kann, weist den Aufruf ab (503). Der erste Token des Schlüssels,
der analysieren, maskieren und wiederherstellen darf, bedient auch Agenten über das Gateway: Ihre
Antworten werden wie die jedes Agenten wiederhergestellt, ohne einen Eintrag im Administrationsprotokoll
für jede Wiederherstellung. Ein Token, der mit keys add-token erstellt wurde, tut das nie, daher ist
jede seiner Wiederherstellungen ein Eintrag apps.unmasked. Geben Sie unmask den Werkzeugen der
Personen, die die echten Daten sehen
dürfen, und lesen Sie das Protokoll mit latet server log --action apps.unmasked.
Auch Maskieren verrät etwas. Mit einem Token, der maskieren darf, und Zugriff auf die maskierten
Logs kann jemand eine Vermutung bestätigen: mario.rossi@acme.it maskieren und in den Logs nach dem
Ersatzwert suchen. Ist er da, ist Mario Rossi ein Kunde. Geben Sie mask deshalb nur den Programmen,
die es brauchen, bewahren Sie den Token dort auf, wo sie ihre Geheimnisse aufbewahren, und behandeln
Sie die maskierten Logs als pseudonyme Daten, nicht als anonyme.
Auch Analysieren verrät etwas. Es erzeugt keinen Ersatzwert und ändert nichts, meldet aber die
Werte, die der Tresor des Schlüssels schon kennt, wo immer sie stehen. Ein Token, der nur analysieren
darf, kann also bestätigen, dass ein Wert, den kein Detektor von sich aus finden würde (ein Name in
einem data-Text, ein Wert, den eine Regel des Schlüssels fand, bevor sie entfernt wurde), einer des
Schlüssels ist. Vergeben Sie scan mit derselben Sorgfalt wie mask.
Auch Vergessen verrät etwas. Seine Antwort sagt, wie viele der Werte der Tresor des Schlüssels
enthielt; ein Token, der vergessen darf, kann also bestätigen, dass ein Wert einer des Schlüssels ist,
wie einer, der analysieren darf. Geben Sie forget nur dem Vorgang, der Löschanfragen bearbeitet;
jedes Vergessen steht im Administrationsprotokoll (apps.forgotten, mit dem Token und den Zahlen, nie
mit einem Wert). Keiner der Token, die keys add erzeugt, darf vergessen: forget erhält ein eigener
Token (keys add-token "Shop" erasure --forget).
Der Tresor eines Schlüssels
Abschnitt betitelt „Der Tresor eines Schlüssels“Der Tresor eines Schlüssels behält jeden Wert, den er maskiert hat, mit seinem Ersatzwert, solange
der Schlüssel besteht: Eine Wiederherstellung funktioniert mit einer Logzeile vom letzten Jahr, und
ein Kunde behält jahrelang denselben Ersatzwert. Werte laufen nicht ab: Ein Wert verlässt den Tresor,
wenn er vergessen wird (Werte vergessen), und alle Werte gehen mit dem Schlüssel, denn
latet server keys remove löscht seinen Bereich und damit seinen Tresor. Ein einmal
gefundener Wert wird von da an überall maskiert, wo er auftaucht, auch dort, wo kein Detektor ihn
wieder finden würde: Die Adresse eines Kunden, die eine inzwischen entfernte Regel des Schlüssels
einmal gefunden hat, bleibt maskiert.
Ein Log-Bereich kann Millionen von Werten enthalten, deshalb hält eine Instanz den Tresor eines
Schlüssels nicht im Speicher. Sie behält die Werte, die der Schlüssel zuletzt verwendet hat
(key_working_values, standardmäßig 5.000, etwa 10 MiB pro Schlüssel), und die Fingerabdrücke des
ganzen Tresors: einen 64-Bit-Fingerabdruck jedes Wortes seiner Werte und Ersatzwerte, mit einem
geheimen Schlüssel gebildet, etwa 15 MB für eine Million Werte. Die Wörter eines Aufrufs werden
zuerst unter den Fingerabdrücken gesucht: Ein Wort ohne Fingerabdruck gehört zu keinem Wert und
keinem Ersatzwert, und PostgreSQL wird nicht danach gefragt. Die Werte und Ersatzwerte, deren Wörter
einen haben, werden aus PostgreSQL gelesen, alle des Aufrufs auf einmal, und jeder wird entschlüsselt
und mit dem Text verglichen, bevor er etwas maskiert oder wiederherstellt, denn zwei Wörter können
denselben Fingerabdruck haben. Weder die Fingerabdrücke noch der Index der Wörter in PostgreSQL
enthalten ein Wort im Klartext. Vor jedem Aufruf liest die Instanz in PostgreSQL den Änderungszähler
des Schlüssels und übernimmt, was die anderen Instanzen seither gespeichert oder vergessen haben;
ein Wert, der auf einer Instanz gespeichert wurde, bevor ein Aufruf beginnt, wird also von diesem
Aufruf auf jeder anderen gefunden, auch wenn die Nachricht darüber verloren ging. Ein Schlüssel mit
wenigen Werten arbeitet genauso: Nur der Tresor einer Person mit 35.000 Werten oder weniger wird ganz
im Speicher gehalten (Serverbetrieb).
Geheimnisse, an die andere Buchstaben grenzen. In PostgreSQL wird ein Geheimnis, das als eine einzige Folge
von Buchstaben und Ziffern ohne Leerzeichen oder Satzzeichen geschrieben ist, über sein Wort gefunden, und über
seine ersten und letzten sechs Zeichen, wenn andere Buchstaben oder Ziffern an es grenzen: auf einer Seite am
Rand der Folge des Textes, die es enthält, und auf beiden Seiten (xhunter2y) in dieser Folge, die ein Aufruf
sechs Zeichen auf einmal liest. Ein Aufruf liest höchstens eine Million solcher Positionen: darüber hinaus
werden seine späteren Folgen nur über ihre Wörter und Ränder gelesen. Ein Geheimnis von sechs Zeichen oder
weniger, oder eines, dessen Wort an einem angrenzenden Rand sechs Zeichen oder weniger hat, findet kein Wort des
Textes, wenn etwas an es grenzt. Darum hält eine Instanz die kurzen Geheimnisse eines Schlüssels dauerhaft im
Speicher, die aus einer einzigen Folge von 16 Zeichen oder weniger oder mit einem so kurzen Wort am Rand: die
neuesten key_held_secrets davon (1.000, sofern die Einstellungen des Servers nichts anderes sagen), gefunden,
wie auch immer etwas an sie grenzt, auch jenseits der Million Positionen eines Aufrufs. Ein älteres oder ein
längeres wird gefunden, wo nichts an es grenzt, mit etwas auf einer Seite, wenn das Wort an diesem Rand sieben
Zeichen oder mehr hat, und mit etwas auf beiden Seiten, wenn es eine einzige Folge von sieben Zeichen oder mehr
ist, innerhalb der Million Positionen des Aufrufs. Erhöhen Sie key_held_secrets für einen Schlüssel, der viele
kurze Geheimnisse sieht, etwa ein Sitzungstoken in jeder Logzeile: Jedes gehaltene belegt etwa 23 KB auf jeder
Instanz, die den Schlüssel bedient, das meiste davon im Detektor, der es findet, und jeder Text, den der
Schlüssel maskiert oder wiederherstellt, wird danach durchsucht, was seine Aufrufe etwas verlangsamt. Gemessen
mit Latet 0.10.0 auf dem Rechner aus Wie schnell es ist, mit einem eigenen Sitzungstoken
in jeder Zeile (ein Wert mehr, der in jeder aufzubewahren ist), maskierte ein neuer Schlüssel mit 1.000
gehaltenen solchen Geheimnissen 73 Zeilen pro Sekunde und stellte 1.935 wieder her, gegenüber 75 und 2.103 ohne
gehaltene. Ein Ersatzwert wird gefunden, wo etwas an ihn grenzt, wie sein Geheimnis: Er behält dessen
Sonderzeichen an ihrer Stelle, sodass ein Ersatzwert, an den so etwas grenzt, dass keines seiner Wörter für sich
steht, der eines im Speicher gehaltenen Geheimnisses ist, und gefunden wird, solange das Geheimnis gehalten
wird. Wie überall in Latet wird ein Wert ab vier Zeichen wiedererkannt, der Name einer Person ab drei (bekannte
Werte).
Kurze Geheimnisse. Ein Geheimnis mit weniger als zwölf Zeichen (password=1234, ein kurzes
Token) erhält einen längeren Ersatzwert derselben Arten, seine Sonderzeichen an ihrer Stelle:
fünfzehn Ziffern für eine Zahl (mit ihrem Vorzeichen und ihrem Dezimalpunkt, damit eine JSON-Zahl
eine Zahl bleibt), zwölf Hexadezimalziffern für einen hexadezimalen Wert, sonst zwölf Buchstaben und
Ziffern. Ein Ersatzwert, so kurz wie sein Wert, käme zufällig in den IDs und Zahlen anderer
Logzeilen vor, und eine Wiederherstellung würde Zeilen verändern, die das Geheimnis nie enthielten.
Ein kurzer Ersatzwert, den der Tresor eines Schlüssels vor Latet 0.10.2 vergeben hat (acht Zeichen
oder weniger), bleibt, wie jeder Ersatzwert. unmask, das jeden Ersatzwert des Tresors des
Schlüssels liest, setzt nur dort den Wert für ihn ein, wo er als ganzes Wort steht, ohne Buchstabe,
Ziffer oder Unterstrich daneben. Der Preis: Wo der Wert eines solchen Geheimnisses in einem längeren
Wort oder einer längeren Zahl maskiert wurde (postgres in postgresql://, 1234 in
order_id=9912343), lässt unmask dort seinen Ersatzwert stehen. Eine Unterhaltung des ersten
Tokens des Schlüssels, über das Gateway oder das Plugin, stellt ihn wie bisher wieder her, auch
innerhalb eines Wortes, da sie nur die Ersatzwerte wiederherstellt, die ihr gezeigt wurden. Solange
eine Instanz des Servers noch 0.10.0 ausführt, gibt sie neuen kurzen Geheimnissen kurze Ersatzwerte,
und auch diese bleiben: Aktualisieren Sie jede Instanz.
Was ein gescheiterter Aufruf behält. Ein mask-Aufruf, der beim Maskieren scheitert (ein 507, ein
Commit, der PostgreSQL nicht erreicht), behält keinen seiner neuen Werte, wenn er dafür eine der
Transaktionen der Instanz hielt, von denen es mit dem standardmäßigen database_pool drei
gleichzeitig gibt: Der Bereich des Schlüssels vergisst sie. Waren diese belegt, hat der Aufruf jeden
neuen Wert sofort gespeichert, und diese bleiben im Tresor des Schlüssels. Ein Fehler nach dem Maskieren, in der lokalen Prüfung oder beim
Erstellen der Antwort, kommt nach dem Commit; auch dann bleiben die neuen Werte des Aufrufs. Die Texte
erneut zu maskieren ist in jedem Fall sicher: Ein Wert, den der Tresor behalten hat, hat seinen
Ersatzwert, und einer, den er vergessen hat, erhält wieder einen. Ein Agent desselben Schlüssels, der
einen vergessenen Wert inzwischen über das Gateway verwendet hat, hat dessen Ersatzwert bereits
gesendet, den keine spätere Wiederherstellung zurückverwandelt.
Grenzen
Abschnitt betitelt „Grenzen“- Der Tresor. Der Tresor eines Schlüssels fasst die
vault_limitWerte des Servers (200.000, wenn die Einstellungen des Servers nichts anderes sagen), oder eine eigene Grenze:latet server keys limit "Shop" 5000000(von 1.000 bis 100.000.000;defaultgibt ihm wieder die des Servers). Ein voller Tresor weist denmask-Aufruf, der einen Wert hinzufügen würde, mit 507 ab: Vom Aufruf kommt nichts zurück, und nichts verlässt den Server im Klartext. Der Status des Servers listet die Identitäten bei 90 % ihrer Grenze auf, und die Metriklatet_vault_near_limitzählt sie; heben Sie eine Grenze also an, bevor sie erreicht ist. Jede Instanz zählt die neuen Werte eines Aufrufs, bevor sie sie bestätigt: Zwei Instanzen, oder ein Aufruf und ein Agent desselben Schlüssels, können die Grenze um die neuen Werte je eines Aufrufs überschreiten, bevor der nächste Wert abgewiesen wird. Ein Index der Fingerabdrücke eines Tresors wird bis zu 16.777.216 Fingerabdrücken aufgebaut, etwa acht Millionen Werten der Art, die Wie schnell es ist misst: darüber wird kein Index aufgebaut, solange der Bereich des Schlüssels besteht, und seine Aufrufe suchen ihre Wörter allein in PostgreSQL, langsamer. - Ein Aufruf nimmt höchstens 1.000 Texte und das
apps_request_mbdes Servers (8 MiB, wenn seine Einstellungen nichts anderes sagen, nie mehr als seinmax_request_mb): Ein Aufruf wartet im Speicher auf den Thread des Schlüssels, undlatet-clientteilt eine längere Liste in Aufrufe von höchstens 8 MiB auf. Texte, deren Wörter mehr als 100.000 Werte oder Ersatzwerte des Tresors nennen, werden ganz mit 422 abgewiesen, in festen Worten, die keinen Wert nennen: Vom Aufruf wird nichts maskiert oder wiederhergestellt, senden Sie also weniger Texte pro Aufruf. Ein 503 ist etwas anderes: Ein Speicher des Servers (PostgreSQL, Valkey) hat nicht geantwortet, und derselbe Aufruf kann später erneut gesendet werden (HTTP-Referenz). - Gleichzeitige Aufrufe. Ein Schlüssel hat höchstens
requests_in_flightAnwendungsanfragen auf einer Instanz (16, wenn die Einstellungen nichts anderes sagen); darüber hinaus 429 mitRetry-After.
Wie schnell es ist
Abschnitt betitelt „Wie schnell es ist“Die Aufrufe eines Schlüssels laufen auf einer Instanz nacheinander, im Thread seines Bereichs; Aufrufe verschiedener Schlüssel und Aufrufe, die verschiedene Instanzen erreichen, laufen nebeneinander. Um mit einem Schlüssel schneller zu werden, betreiben Sie mehr Instanzen hinter dem Lastverteiler, oder mehrere Worker auf einer Linux-Maschine (mehrere Instanzen auf einer Maschine).
Gemessen mit Latet 0.11 auf einer Instanz (Intel i9-10850K, 64 GB, Windows 10, PostgreSQL 18 in
Docker Desktop auf demselben Rechner, die Standardeinstellungen des Servers): 40.000 Logzeilen von
etwa 200 Zeichen mit je einer E-Mail-Adresse und einer öffentlichen IPv4-Adresse, von denen die
Hälfte einen bereits gesehenen Kunden nennt, in Aufrufen zu 500 als data; danach 50 Dokumente von
etwa 20 KB Prosa mit je zehn Werten, eines pro Aufruf. Zeilen pro Sekunde sind die Zeilen geteilt
durch die Zeit, die ihre Aufrufe brauchten; die Zeit eines Dokuments ist der Median seiner 50
Aufrufe:
| Der Tresor des Schlüssels | Maskieren, Zeilen pro Sekunde | Wiederherstellen, Zeilen pro Sekunde | Ein Dokument, maskieren / wiederherstellen |
|---|---|---|---|
| Neu, lernt seine Werte nach und nach | 191 | 2.894 | 126 / 31 ms |
| 200.000 Werte | 871 | 2.960 | 117 / 32 ms |
| 1.000.000 Werte | 773 | 2.967 | 124 / 32 ms |
Jedes Dokument bringt Werte mit, die für den Schlüssel neu sind und die er beim Maskieren in PostgreSQL schreibt: Dieselben 50 Dokumente, erneut maskiert, mit bekannten Werten, brauchten 16,6 ms bei 200.000 Werten und 16,4 ms bei 1.000.000 (Wiederherstellen jeweils 23 ms). Auch ein neuer Schlüssel schreibt die meisten Werte seiner Zeilen, darum maskiert er weniger.
Latet 0.10.0, auf dieselbe Weise auf demselben Rechner gemessen, maskierte 350 und 304 Zeilen pro Sekunde bei 200.000 und 1.000.000 Werten und 153 für einen neuen Schlüssel, stellte 2.114 und 2.014 wieder her und maskierte ein Dokument in 163 bis 182 ms. Latet 0.11 hatte sich vorgenommen, auf einer Instanz mindestens 1.000 solcher Zeilen pro Sekunde bei 200.000 und bei 1.000.000 Werten zu maskieren, und ein Dokument in 30 ms oder weniger: Es maskiert 871 und 773, und ein Dokument in 30 ms nur dann, wenn der Schlüssel seine Werte schon kennt. Der größte Teil der Zeit eines Maskierungsaufrufs geht an die Detektoren, ein Teil an das Warten auf PostgreSQL.
Eine Instanz findet die Werte eines Schlüssels über die Fingerabdrücke der Wörter seines Tresors (der Tresor eines Schlüssels), die etwa 15 MB für eine Million Werte belegen (14,77 MB bei 1.000.000) und im Hintergrund aufgebaut werden, während sich der Bereich des Schlüssels öffnet: in 1,5 Sekunden bei 1.000.000 Werten (der Median von drei Öffnungen, 1,42 bis 1,62 s). Bis sie bereit sind, suchen die Aufrufe des Schlüssels ihre Wörter allein in PostgreSQL: In einer früheren Messung mit Latet 0.11, mit abgeschalteten Fingerabdrücken, maskierte der Schlüssel mit 200.000 Werten 494 Zeilen pro Sekunde und stellte 1.382 wieder her. Jeder Aufruf liest zuerst den Änderungszähler des Schlüssels, 0,51 ms im Median, und übernimmt, was sich seither geändert hat: 1,87 ms für einen neuen Wert und 2,11 ms für 100, ebenfalls im Median.
In einem Aufruf bereits maskierter Texte werden nur die neuen erneut gelesen, aber jeder der anderen wird trotzdem nachgeschlagen und mit dem verglichen, was der Schlüssel maskiert hat: 500 bereits maskierte Texte und ein neuer brauchten 168 ms, der neue allein 6,1 ms (Mediane von drei Läufen).
Auf einer Linux-Maschine addieren sich die Worker. Gemessen in Docker auf demselben Rechner (eine
virtuelle Linux-Maschine mit 20 Prozessoren und 16 GB, PostgreSQL 18.6 und Valkey 9.1.2 in Containern
daneben), mit latet server run --workers und einem API-Schlüssel mit 200.000 Werten: 200.000
verschiedene Zeilen der oben beschriebenen Art in Aufrufen zu 62, vier Verbindungen pro Worker, jede
Anzahl 20 Sekunden lang, der Median von drei Runden; der Speicher ist der residente Speicher jedes
Workers nach den Runden:
| Worker | Maskieren, Zeilen pro Sekunde | Wiederherstellen, Zeilen pro Sekunde | Speicher pro Worker |
|---|---|---|---|
| 1 | 1.190 | 1.947 | 227 MB |
| 2 | 2.260 | 3.711 | 227 MB |
| 4 | 4.253 | 6.594 | 216 MB |
| 8 | 7.477 | 10.147 | 185 MB |
Acht Worker maskierten das 6,28-Fache dessen, was einer maskierte, und stellten das 5,21-Fache wieder her, wo sich Latet 0.11 mindestens das 5-Fache für beides vorgenommen hatte; keine Runde mit acht Workern stellte weniger als das 5,07-Fache wieder her. Der Kernel teilt die Verbindungen den Workern nach einem Hash zu, nicht der Reihe nach, sodass manche Worker mehr davon bekommen als andere: 1 bis 10 der 32 in einer Runde mit acht Workern. Der Prozess, der die Worker startet, belegte daneben 47 bis 48 MB. Diese Aufrufe kommen von mehreren Verbindungen zugleich; vergleichen Sie die Zeilen dieser Tabelle also miteinander, nicht mit der ersten Tabelle.
Neue Werte zu schreiben braucht Verbindungen zu PostgreSQL: Eine Instanz, und jeder Worker, hält
höchstens database_pool davon (10, sofern die Einstellungen des Servers nichts anderes sagen) für
die Arbeit aller Identitäten und lässt drei Zehntel davon (drei von zehn) von Aufrufen halten, die
ihre neuen Werte in je einer Transaktion speichern; die übrigen speichern jeden Wert einzeln, was
langsamer ist. Erhöhen Sie database_pool für eine Instanz, die für viele neue Schlüssel
gleichzeitig maskiert, und mit ihm max_connections von PostgreSQL
(Serverbetrieb).
scripts/bench_apps.py im Repository misst einen Server von Ihnen so, wie die erste Tabelle gemessen
wurde.
Einen Server aktualisieren
Abschnitt betitelt „Einen Server aktualisieren“Latet 0.11 ändert die Tabellen. Stoppen Sie jede Instanz und jeden Worker auf jeder Maschine, führen
Sie latet server migrate einmal mit 0.11 aus und starten Sie dann alle damit
(Aktualisieren). Eine Instanz
startet nicht auf Tabellen einer anderen Version, daher wird zwischen dem Stopp und dem Start kein
Aufruf beantwortet, und latet server migrate ändert nichts, solange eine Instanz läuft, eine von
0.10 noch 30 Sekunden nach ihrem Stopp. Die Migration gibt jeder Wortzeile jedes Tresors einen leeren
Platz für ihren Fingerabdruck und für die Zählung der Änderung, die sie zuletzt geschrieben hat, mit
ihren Indizes: Auf einer Datenbank mit großen Tresoren dauert sie länger als frühere Migrationen, und
die Fingerabdrücke selbst werden später gebildet, im Hintergrund.
Ein Tresor, den Latet 0.10 gespeichert hat, hat noch keine Fingerabdrücke. Nach dem Neustart beantwortet die erste Instanz, die den Bereich eines Schlüssels aufbaut, dessen Aufrufe sofort, indem sie ihre Wörter in PostgreSQL sucht, wie 0.10 es tat, und bildet die Fingerabdrücke des Tresors im Hintergrund, einmal, eine Instanz nach der anderen; jede Instanz baut dann ihren eigenen Index daraus auf und nutzt ihn. Bis dahin maskieren die Aufrufe des Schlüssels dieselben Werte, langsamer (Wie schnell es ist: 494 Zeilen pro Sekunde bei 200.000 Werten mit abgeschalteten Fingerabdrücken, in einer früheren Messung), und keiner wird deswegen abgewiesen. Während eine Instanz die Fingerabdrücke eines Tresors bildet, können auch die anderen Schlüssel und Personen, deren Bereiche sie inzwischen aufbaut, über die Suche in PostgreSQL bedient werden, bis ihr Index aufgebaut ist. Wenn die Fingerabdrücke noch nicht alle gebildet sind oder ein Index nicht aufgebaut werden kann (PostgreSQL oder Valkey antworten nicht, ein fehlgeschlagenes Nachführen des Index), bleiben die Aufrufe bei der Suche in PostgreSQL, bis der Index erneut aufgebaut ist: sofort, wenn eine andere Instanz die Fingerabdrücke gebildet hat, sonst 10, 20 und 30 Sekunden nach jedem Versuch und dann einmal pro Minute. Das Log sagt einmal, wenn ein Index nicht mehr genutzt wird, und einmal, wenn er wieder genutzt wird. Ein Tresor über der Größe, für die ein Index aufgebaut wird (Grenzen), bleibt bei der Suche in PostgreSQL, solange sein Bereich besteht. Die Wörter selbst bleiben, wie 0.10 sie gebildet hat: 0.11 bildet sie nicht neu.
Eine Version, die ändert, wie die Wörter eines Tresors gebildet werden (das Changelog sagt es), bildet die Wörter eines Schlüssels in der ersten Instanz neu, die seinen Bereich aufbaut, bevor dieser Bereich den ersten Aufruf des Schlüssels beantwortet: Latet 0.10.0 brauchte dafür etwa 40 Sekunden für 200.000 Werte und viereinhalb Minuten für 1.000.000 auf dem oben genannten Rechner. Ein Aufruf, der in der Zwischenzeit eintrifft, wartet, und eine Instanz baut vier Bereiche gleichzeitig auf; einige große Schlüssel verzögern also die ersten Aufrufe der anderen Schlüssel auf ihr: Senden Sie nach einer solchen Version jedem großen Schlüssel einen Aufruf mit ausreichend langem Timeout, bevor seine Programme wieder starten.
Solange Instanzen zweier Versionen nebeneinander laufen, was eine Version erlaubt, die die Tabellen gleich lässt, wird ein Wert, den eine ältere Instanz schreibt, von den neueren gefunden, solange sie ihn im Speicher halten, aber nicht in PostgreSQL, bis der Bereich des Schlüssels neu aufgebaut wird. Wenn die API für Anwendungen genutzt wird, stoppen Sie jede ältere Instanz, bevor die neuen starten, auch wenn die Tabellen gleich bleiben.
© 2026 Emanuele Casabona · USt-IdNr. IT02293040990 · Datenschutzerklärung · Impressum · latet.ai