HTTP: Verwaltung (Referenz)
Die Verwaltungsschnittstelle unter /admin/v1/ bietet den Verwaltungsspeicher als OData-Mengen und
Befehle an; die Verwaltungsanwendung für Access und die VBA-Klasse DbGwClient bauen auf ihr auf.
Syntax
Mit einem Verwaltungsspeicher bietet der Dienst dessen Tabellen unter
/admin/v1/ an: als OData-Mengen, die ein Verwaltungsprogramm wie Tabellen verknüpft – Access
über den ODBC-Treiber dieses Dienstes, mit den Formularen, die es für Tabellen ohnehin hat –, und
als Befehle für das, was keine Änderung einer Zeile ist.
| Form | Adresse | Wofür |
|---|---|---|
| OData | GET /admin/v1/odata/, $metadata, {Menge}, {Menge}({Schlüssel}), {Menge}/$count | Lesen mit $select, $filter, $orderby, $top, $skip, $count |
| OData | POST /admin/v1/odata/{Menge}, PATCH und DELETE auf {Menge}({Schlüssel}) | Anlegen (201), Ändern (200, If-Match mit der gelesenen Version), Löschen (204) |
| Befehl | POST /admin/v1/issue-key | Persönlichen Schlüssel ausstellen: einmal im Rumpf, als Einmal-Link oder als Einmal-Link per Mail |
| Befehl | POST /admin/v1/activate-key | Schlüssel auf Abruf für eine Zeit freischalten (id, hours) |
| Befehl | POST /admin/v1/deactivate-key | Freischaltung eines Schlüssels auf Abruf sofort beenden (id) |
| Befehl | POST /admin/v1/save-sign-in-rule | Anmelderegel mit allen Netzen, Personen und Gruppen in einer Änderung anlegen oder als Ganzes ersetzen (id) |
| Befehl | POST /admin/v1/explain-sign-in | Was die Anmelderichtlinie für eine Person sagt, je Anmeldeart und Herkunft, mit den auslösenden Regeln |
| Befehl | POST /admin/v1/sign-in-profile | Was für eine Person bei der Anmeldung üblich ist, über einen gewählten Zeitraum (Anmeldeprofil) |
| Befehl | POST /admin/v1/test-connection | Datenquelle öffnen; Antwort: erreichbar, Dauer, Version, Datenbank, Anmeldung oder die Meldung des Servers mit Fehlernummer und Art des Fehlers (Verbindungstest und Dienstkonto) |
| Befehl | GET /admin/v1/service-account | Das Konto, unter dem der Dienst sich mit Windows-Anmeldung an Datenbanken anmeldet (Verbindungstest und Dienstkonto) |
| Befehl | POST /admin/v1/reload-catalog | Ressourcenkatalog einer (dataSource) oder aller Datenquellen verwerfen; die nächste Anfrage liest ihn neu |
| Befehl | GET /admin/v1/check-store | Speicher prüfen wie beim Neuladen, ohne etwas zu ändern |
| Befehl | POST /admin/v1/backup-store | Je eine geprüfte Kopie des Speichers und seiner Protokolldatei in den Ordner sicherungen neben ihm, Name nach dem Zeitpunkt (gateway-JJJJMMTT-hhmmss.db und gateway-JJJJMMTT-hhmmss-logs.db); Antwort file, schemaVersion, bytes, logFile, logBytes (die beiden letzten leer ohne Protokolldatei). Den Ort wählt der Dienst, eine Anfrage nennt keinen Pfad. Zwei Sicherungen in derselben Sekunde: die zweite 409. Die Kopien enthalten keinen Hauptschlüssel |
| Befehl | POST /admin/v1/reseal-store | Wie der gleichnamige Befehl auf dem Rechner |
| Befehl | POST /admin/v1/enroll-totp | Zweiten Faktor einer Person einrichten (person, delivery: key, link oder mail); beendet ihre Sitzungen, bei mail erst beim Abholen. Die Seite des Einmal-Links zeigt die Adresse für die Authenticator-App als QR-Code (SVG in der Seite, nichts wird nachgeladen) und daneben den Schlüssel für die Eingabe von Hand |
| Befehl | POST /admin/v1/reset-totp | Zweiten Faktor einer Person entfernen und ihre Sitzungen beenden |
| Befehl | POST /admin/v1/set-geoip-source | Quelle der Geodatenbank wählen |
| Befehl | POST /admin/v1/update-geoip | Geodatenbank jetzt von der gewählten Quelle laden (der Server braucht Zugang zum Internet) |
| Befehl | POST /admin/v1/upload-geoip | Eine mitgebrachte Datei der Geodatenbank übernehmen; Rumpf = Datei |
| Befehl | POST /admin/v1/export-load | Das Lastprotokoll als Datei für den Hersteller, ohne Personen, Herkunft und Werte |
| Befehl | GET /admin/v1/update-databases | Datenbanken des Update-Moduls mit Einstellungen, dazu ausgelassene Konfigurationen |
| Befehl | POST /admin/v1/update-status | Stand einer Datenbank: offene Pakete mit Frist, blockierende und unbekannte Sitzungen, Drift (Datenbank-Updates) |
| Befehl | POST /admin/v1/update-history | Abgeschlossene Pakete einer Datenbank mit ihren Schritten, neueste zuerst |
| Befehl | POST /admin/v1/confirm-update, dismiss-update, retry-update | Wartendes Paket bestätigen; gescheitertes oder abgelehntes verwerfen oder erneut einspielen |
| Befehl | POST /admin/v1/move-update-deadline, cancel-update-notice | Frist eines angekündigten Pakets verschieben; Ankündigung abbrechen |
| Befehl | POST /admin/v1/upload-update | Paket in den Eingang einer Datenbank legen und sofort annehmen; Rumpf = Datei |
| Seite | GET/POST /admin/v1/pickup/{Kennung} | Einmal-Link; ohne Anmeldung, die Kennung ist der Nachweis |
Parameter
Warum zwei Formen. Was eine Liste ist, ist eine Menge: Ein Verwaltungsprogramm braucht dafür keinen eigenen Code. Was ein Geheimnis zurückgibt oder über die Zeile hinaus wirkt, ist ein Befehl: Eine Menge, die Access nach dem Anlegen neu liest, müsste den eben ausgestellten Schlüssel entweder wieder herausgeben oder verlieren.
Fertige Clients. Die „WKL Database Gateway Manager und VBA-Klasse – Überblick“ (Dokumentation der Verwaltungsanwendung) bedient alle Mengen und Befehle (Anmeldung mit Schlüssel oder Windows-Konto und zweitem Faktor); die „DbGwClient – VBA-Klasse für den Datendienst“ (Dokumentation der Verwaltungsanwendung) ruft die Befehle aus eigenen VBA-Anwendungen.
Mengen
Mengen. Sie folgen den Tabellen des Speichers eins zu eins; Listen einer Regel oder eines Schlüssels sind Zeilen einer Untermenge mit dem Schlüssel der übergeordneten Zeile.
| Menge | Schlüssel | Anmerkung |
|---|---|---|
DataSources | Name | Name folgt der Namensregel für den Alias. Die Verbindung kommt entweder als Zeichenfolge Connection – nur schreibbar, liest sich immer leer – oder als Verbindungseigenschaften ConnectionProperties, aus denen der Provider die Zeichenfolge baut; beim Anlegen eins von beiden, beim Ändern ersetzt ein Wert die versiegelte Zeichenfolge, leer lässt sie stehen. Provider, ProviderVersion und ProviderOptions (JSON-Objekt aus Texten) wie in der Konfiguration. Schalter AllowPassThrough, AllowCheckedStatements (Vorgabe true), AllowSqlLogins wie in der Konfiguration |
Providers, ProviderProperties | Id, PackageVersion; Provider, PackageVersion, Scope, Name, Language | Nur lesen: die installierten Provider-Pakete und die Eigenschaften, die jeder für seine Verbindung beschreibt, siehe Verbindungseigenschaften eines Providers |
DataSourceTables | DataSource, Setting, Pattern | Setting ist row_stamp oder keyless_write; nur anlegen und löschen |
ImpersonationDenyRules, ImpersonationDenyItems | Id; RuleId, Item, Value | Item: login, role, from, except_from |
ClientRules, ClientRuleItems | Id; RuleId, Item, Value | Item: database, resource, verb, object, except_object (Rechte je Objekt; ein Eintrag in falscher Form lehnt die Prüfung mit 422 ab). Holders zählt, wer die Regel hat (Personen, Windows-Konten und -Gruppen, SQL-Anmeldungen) |
WindowsAccounts, SqlLogins | Account, Client; Login, Client | Zuordnungen zu Regeln |
Persons, PersonRules | Id; PersonId, Client | SqlLogin (eigene SQL-Server-Anmeldung der Person, für Impersonate mit persönlichem Schlüssel), WindowsAccount (Name oder SID, wird beim Schreiben aufgelöst), Email (eine Adresse ohne Anzeigenamen, Vorschlag für eine Einladungsmail; sonst 400), DefaultRole (Vorgabe-Identität), SecondFactor (none, pending, active). Zur Anzeige: Status/StatusKind, SecondFactorText/SecondFactorKind, Zählungen Rules, ValidKeys, OpenSessions, ExpiringKeys (gültige Schlüssel, die in 14 Tagen ablaufen); PersonRules.Person nennt den Namen. DELETE einer Person, die Spuren hinterlassen hat (Schlüssel, Sitzungen, Anmeldungen, Last oder eigene Änderungen), lehnt der Dienst mit 422 ab; sperren Sie sie mit Enabled = false (Aufbewahrung und Löschen) |
PersonalKeys, PersonalKeyItems | Id; KeyId, Item, Value | Ausgestellt nur mit issue-key. Änderbar: Label, ReadOnly, TotpExempt (nur bei nur lesendem Schlüssel), ExpiresAt (nicht über die Höchstlaufzeit ab Ausstellung hinaus), OnDemand und Revoked – eine Sperre lässt sich nicht aufheben. ActiveUntil setzen nur activate-key/deactivate-key. Zur Anzeige: Status („gültig“, „läuft in 5 Tagen ab“, „abgelaufen“, „gesperrt“, „ruht“, „frei bis 15:30 UTC“), StatusKind, ExpiresInDays, CanRevoke, CanActivate |
SignInFileRules | Position | Nur lesen. Anmelderegeln, die noch in der Konfigurationsdatei stehen (Gateway:Security:SignInRules). Mit Verwaltungsspeicher gelten sie nicht: Sie dienten nur als Startwert, als der Speicher angelegt wurde, und der Dienst warnt beim Start vor ihnen. Felder: Position (n wie in file:n), Scope, Method, Effect, Mode, Label, Conditions (belegte Listen als Text, etwa ExceptFrom: 10.0.0.0/8; ExceptHours: Mon-Fri 06:00-20:00), Exceptions (Anzahl), RuleVersion, InForce (mit Speicher immer false). Aufräumen: die Einträge aus der Datei entfernen |
SignInRules, SignInRuleItems | Id; RuleId, Item, Value | Anmelderichtlinie; Item: from, except_from, person, except_person (Wert: Id der Person), windows_group, except_windows_group (Name oder SID), country, except_country (Ländercode), operator, except_operator (Netzbetreiber, AS3320), client, except_client (Programmart), hours, except_hours (Zeitfenster). RuleVersion einer Regel (nur lesbar) ist die Version, die Anmeldeereignisse und explain-sign-in nennen; weicht sie von der Version in einem Ereignis ab, wurde die Regel seitdem geändert. Mode einer Regel: Enforce (Vorgabe) oder Report (Regel erproben). Eine Person, die es nicht gibt, oder ein ungültiger Ländercode, Netzbetreiber oder Zeitfenster lehnt die Prüfung mit 422 ab |
SignInRuleExceptions, SignInRuleExceptionItems | Id; ExceptionId, Item, Value | Befristete Ausnahmen einer Regel: RuleId, ValidUntil (UTC, leer = ohne Ende), Label, LapseLogged (nur lesbar: der Ablauf steht im Änderungsprotokoll); Item: person (Wert: Id der Person), windows_group, country, from, operator, client, hours. Eine Ausnahme ohne Merkmal und ohne Ende lehnt die Prüfung mit 422 ab. Wer ValidUntil verschiebt, setzt LapseLogged zurück |
Settings | Name | totp_session_hours, self_service_key_days (Selbstbedienung), totp_enrol_hours, max_activation_hours (Schlüssel auf Abruf), installation_name (geht Gateway:Store:TotpIssuer vor, siehe Konfiguration), mail_link_hours (Gültigkeit eines Einmal-Links per Mail, 1–720 Stunden); ein anderer Name wird abgelehnt, ein ungültiger Wert mit 422 |
MailTemplates | Id | HTML-Vorlagen für Einladungsmails, für alle Administratoren gleich: Name, Purpose (KeyPickup, TotpEnrollment, Welcome), Subject, Html; Builtin und ChangedAt setzt der Dienst. Platzhalter nur aus {{Person}}, {{Link}}, {{GueltigBis}}, {{Dienst}}, {{Absender}}, {{Organisation}}; KeyPickup und TotpEnrollment brauchen {{Link}}; höchstens 256 KB. Ein Verstoß: 400 mit allen Gründen. Die drei eingebauten Vorlagen (eine je Zweck) lassen sich ersetzen, aber nicht umbenennen (400) oder löschen (422). Das Logo bindet eine Vorlage als src="cid:logo" ein; der Client hängt es mit dieser Kennung an. Der Dienst verschickt keine Mail |
SessionTokens | Id | Sitzungen des zweiten Faktors; nur Revoked (endgültig) und Löschen; Role nennt die gebundene Identität. Zur Anzeige: Status („offen“, „abgelaufen“, „beendet“), StatusKind, CanEnd |
DatabaseIdentities, IdentityAssignments | Id; Id | Datenbank-Identitäten. Kind: Login, Own, Alias; eine Sammelanmeldung prüft der Dienst beim Speichern durch einen Probewechsel. Eine Zuordnung nennt genau eine Quelle: PersonId, WindowsAccount oder KeyId; Source, DataSource und Role ergänzt der Dienst |
DataSourceObjects | DataSource, Schema, Name | Nur lesen. Die Objekte im Katalog jeder eingeschalteten Datenquelle, so geschrieben, wie eine Regel sie nennt: Kind ist die Art unter Resources (tables, views, functions, procedures), Pattern der Eintrag unter Objects (schema.name). Vorschläge für ein Verwaltungsprogramm; eine Datenquelle, deren Katalog gerade nicht lesbar ist, fehlt in der Liste |
UpdateSettings | Database | Betriebseinstellungen der Datenbank-Updates je Datenbank: Expand und Contract (automatic oder confirm), NoticeMinutes (0–10080), SessionWindowMinutes (1–1440), Meldewege NotifyEventLog (Vorgabe an), NotifyFile und NotifyEmail (Vorgabe aus, siehe Meldungen) mit den Empfängern NotifyEmailTo (Mail-Adressen, getrennt durch ;; NotifyEmail ohne Empfänger wird mit 400 abgelehnt), Aufbewahrung der Sicherungen BackupKeepCount (1–1000, Vorgabe 10) und BackupKeepDays (0–3650, Vorgabe 30); ChangedAt setzt der Dienst. Ein ausgelassenes Feld erhält die Vorgabe, ein Wert außerhalb der Bereiche wird mit 422 abgelehnt. Ohne Eintrag und nach dem Löschen gelten die Vorgaben |
SignInHours, SignInOrigins, SignInEvents, SignInFindings, SignInAdvice, GeoIpFiles, GeoIpSource | Nur lesen; siehe Anmeldeprotokoll und Geodatenbank | |
LoadShapes, LoadHours, LoadPersons, SlowRequests, LoadFindings, LoadAdvice | Nur lesen; siehe Lastprotokoll | |
ChangeLog | Id | Nur lesen |
AuditEntries | Sequence | Nur lesen. Der Audit-Strom (`Gateway:AuditLog`): Zugriffe (Kind = Zugriff: Zeit, Aufrufer, Alias, Ressource, Methode, Ergebnis, Status, Korrelation) und eigene Anweisungen (Anweisung, mit Text). Mit Verwaltungsspeicher aus der Protokolldatei, auch nach einem Neustart, für Gateway:AuditLog:Days; Sequence ist dann die Nummer in der Datei. Je Abfrage liefert die Menge höchstens die neuesten 20 000 Einträge, die zum Zeitraum (At) und zur Person (Person eq) des Filters passen. Ohne Speicher die letzten 10 000 Einträge seit dem Start, Anweisungen auf 4 000 Zeichen gekürzt. Person und KeyId nennen Person und persönlichen Schlüssel eines Aufrufs, damit sich danach filtern lässt. SignInRule nennt bei einer Ablehnung durch eine Anmelderegel deren Kennung und Version (2@8da4235bc439aac0, siehe Grundlage der Entscheidung) |
ServiceStatus | Id | Nur lesen, eine Zeile: Last des Dienstes seit dem Start aus dem Arbeitsspeicher – RequestsLastMinute, RequestsLastFiveMinutes, InFlight, MedianMilliseconds, P95Milliseconds, DeniedLastFiveMinutes (401/403/429; ohne die Antwort auf eine Anfrage ohne Anmeldedaten, mit der jede Windows-Anmeldung beginnt), ErrorsLastFiveMinutes (≥ 500), Saturated (mehr als 50 000 Anfragen in fünf Minuten: Zahlen sind eine Untergrenze), die Anfragegrenze (RateLimitPerCaller, RateLimitWindowSeconds, leer ohne Grenze) und aus dem Speicher OpenSessions, ValidKeys, ExpiringKeys (14 Tage). Ohne die Proben unter /health |
Felder zur Anzeige. Status ist ein Text für die Anzeige, StatusKind eine von fünf Arten
(ok, warn, bad, info, neutral), nach der ein Verwaltungsprogramm das Etikett einfärbt –
der Text steht immer dabei, die Farbe nie allein. Can…-Felder sagen, ob ein Zeilenbefehl gerade
möglich ist. Der Dienst berechnet sie beim Lesen nach der Uhr des Speichers; sie sind schreibgeschützt
(Schreiben 400) und gehören nicht zur Zeilenversion – ein Schlüssel ändert sich nicht dadurch, dass
ein Tag vergeht. Geliefert statt in Access berechnet, weil ein Ausdruck wie IIf(…) in einer
Access-Abfrage nicht über den ODBC-Treiber übersetzt wird und ein Datengitter im Server-Modus dann auf
das langsame Lesen über Access zurückfiele.
Jede Menge hat die Eigenschaft Version (acht Bytes einer SHA-256 über die gespeicherten Werte
der Zeile, versiegelte eingeschlossen) als Zeilenversion. Wer eine Zeile ändert, die ein anderer
seit dem Lesen geändert hat, erhält 412 – in Access die gewohnte Meldung des Schreibkonflikts.
Nummern sind int (Edm.Int32), nicht bigint: Access zeigt bigint ohne seine Option für große
Zahlen als Text und lehnt dann eine Bedingung wie Id = 3 ab. Text vergleicht der
Filter ohne Unterschied der Groß- und Kleinschreibung, wie SQL Server und wie der Speicher Namen
vergleicht.
Zählen, Höchstwert und Gruppieren
Die Verwaltung liest Zeilen und zählt sie; sie gruppiert nicht. $apply weist sie mit 400 ab, und
ihr Schemadokument kündigt keine Gruppierung an (Aggregation.ApplySupported fehlt). Der ODBC-Treiber
entscheidet nach dieser Ankündigung, was er an die Verwaltung schickt (Übersetzung im Einzelnen:
„Unterstütztes SQL (Referenz)“ in der Dokumentation des ODBC-Treibers):
| Anweisung, die Access an den Treiber gibt | Anfrage an die Verwaltung | Ergebnis |
|---|---|---|
SELECT COUNT(*) FROM "Menge" – etwa aus DCount("*", "Menge") | Menge/$count | Anzahl |
SELECT COUNT(*) FROM "Menge" WHERE … – etwa aus DCount("*", "Menge", "Id > 4711") | Menge/$count?$filter=… | Anzahl der passenden Zeilen |
SELECT … FROM "Menge" WHERE … ORDER BY … | Menge?$select=…&$filter=…&$orderby=… | Zeilen |
MAX, MIN, SUM, AVG, COUNT(Spalte), GROUP BY, HAVING, DISTINCT – etwa aus DMax, DMin, DSum, DAvg, DCount("Spalte", …) oder einer Summenabfrage | keine | Der Treiber weist die Anweisung mit 42000 ab („Der Dienst kuendigt die Gruppierung ueber $apply … nicht an“); Access meldet „ODBC-Aufruf fehlgeschlagen“ (3146) |
Zählen Sie mit DCount("*", …), nicht mit DCount("Spalte", …): Nur COUNT(*) wird zur
Zählanfrage, mit oder ohne Bedingung.
Den höchsten Wert einer Spalte liefert die Verwaltung als erste Zeile einer absteigend sortierten
Abfrage. Über HTTP, etwa mit dem eigenen HTTP-Zugriff eines Verwaltungsprogramms:
GET /admin/v1/odata/SignInFindings?$select=Id&$orderby=Id desc&$top=1. Bei SignInFindings ist das
selten nötig: Wer nur Neues zählen will, merkt sich die höchste gelesene Id und fragt
SignInFindings/$count?$filter=Id gt 4711 (siehe Befunde der Anmelderegeln).
Prüfung und Änderungsprotokoll
Jede Änderung wird vor dem Übernehmen geprüft. Eine Änderung ist eine Transaktion: Version
vergleichen, schreiben, ins Änderungsprotokoll eintragen und den gesamten Inhalt danach so
prüfen, wie ein Neuladen ihn prüft – einschließlich der Regeln, die eine Person nennt. Besteht er
nicht, wird alles zurückgenommen, und die Antwort 422 nennt jeden Fehler. Ohne diese Prüfung
stünde eine falsch geschriebene Regel im Speicher, jedes Neuladen würde sie ablehnen, und der
Dienst bliebe unbemerkt auf der alten Version, obwohl die Schnittstelle Erfolg gemeldet hätte.
Eine übernommene Änderung gilt sofort (der Dienst lädt neu, ohne auf PollSeconds zu warten).
Eine Einschränkung des Speichers selbst (unbekannter Item, doppelter Schlüssel, Untereintrag
ohne übergeordnete Zeile) beantwortet der Dienst ebenfalls mit 422 und der Meldung von SQLite.
Änderungsprotokoll. In derselben Transaktion wie die Änderung: Zeit, Aufrufer (Person (Kennung), Dienstname, Windows-Konto oder Einrichtung), Vorgang (insert, update, delete,
command), Menge oder Befehl, Schlüssel, Werte vorher und nachher als JSON. Eine
Verbindungszeichenfolge und Verbindungseigenschaften erscheinen nur als „neu gesetzt“, ein Schlüssel nie. Eine zurückgenommene
Änderung hinterlässt keinen Eintrag; eine übernommene hat immer einen. Untereinträge, die mit
ihrer übergeordneten Zeile gelöscht werden, stehen nicht einzeln darin.
Recht und erster Administrator
Recht. Eine Regel muss die Ressource admin wörtlich nennen; * genügt nicht, wie beim
Recht sql. Die Databases der Regel begrenzen die Verwaltung nicht – sie gehört zu keiner
Datenquelle. Eine Regel für die Verwaltung:
{ "Id": "verwaltung", "Resources": [ "admin" ], "Verbs": [ "*" ] }
Mit "ReadOnly": true liest dieselbe Regel die Verwaltung und prüft Verbindungen, ändert aber nichts
(etwa für eine Revision):
{ "Id": "revision", "Resources": [ "admin" ], "Verbs": [ "*" ], "ReadOnly": true }
Anmelden lässt sich an der Verwaltung mit persönlichem Schlüssel und Windows-Konto, mit einem
Dienstschlüssel nur, wenn Gateway:Store:RequireAdminTotp = false ist und keine Anmelderegel für die
Verwaltung einen zweiten Faktor verlangt – ein neuer Speicher enthält eine solche Regel (sonst 403
„… gehoert zu keiner Person“, weil ein Dienstschlüssel keinen zweiten Faktor haben kann), nicht mit einer SQL-Anmeldung: Deren Kennwort prüft
SQL Server an einer Datenquelle, und die Verwaltung gehört zu keiner. Ein persönlicher Schlüssel,
der auf Datenquellen beschränkt ist, erreicht die Verwaltung nie; ein nur lesender Schlüssel
liest sie höchstens. Jede Adresse der Schnittstelle antwortet nur über
eine geschützte Verbindung: HTTPS, ein vertrauter Proxy mit X-Forwarded-Proto: https oder eine
Anfrage vom eigenen Rechner. Sonst 403, auch für den Einmal-Link.
Erster Administrator. Die Verwaltung erreicht nur, wen eine Regel mit admin nennt. Die erste
solche Person legt der Befehl init-admin am Server an, nicht eine Anfrage über das Netz, siehe
Erster Administrator und Rettung.
Einmal-Links
Einmal-Link. issue-key mit "delivery": "link" gibt statt des Schlüssels eine Adresse
zurück, gültig für Gateway:Store:PickupMinutes (Vorgabe 15 Minuten, siehe
Konfiguration). Die Seite dahinter zeigt beim Öffnen nur eine Schaltfläche; erst
deren POST zeigt den Schlüssel, einmal. So holt ein Mailprogramm oder Chat, der Links zur
Vorschau öffnet, den Schlüssel nicht vorher ab. Der wartende Schlüssel liegt nur im Speicher des
Prozesses, nie auf der Platte; ein Neustart macht offene Links ungültig. Die Abholung steht im
Änderungsprotokoll (Einmal-Link, mit Herkunft).
<a id=„einmal-link-per-mail“></a>Einmal-Link per Mail. issue-key und enroll-totp mit "delivery": "mail" geben eine
Adresse für eine Einladungsmail zurück (person, delivery, link, linkExpiresAt), gültig
mail_link_hours bzw. MailLinkHours (Vorgabe 72 Stunden). Anders als ein gewöhnlicher Einmal-Link:
- Kein Geheimnis wartet. Der Speicher hält den Auftrag und die SHA-256-Prüfsumme des Links, keinen Schlüssel. Schlüssel bzw. zweiter Faktor entstehen erst beim Abholen; bis dahin bleibt ein vorhandener Faktor der Person unverändert. Eine Kopie der Speicherdatei öffnet daher nichts.
- Übersteht einen Neustart des Dienstes.
- Genau einmal. Ein zweites Öffnen zeigt „bereits abgeholt am … um …“ (
410) mit der Bitte, das der Verwaltung zu melden, falls der Empfänger es nicht selbst war; ein abgelaufener oder unbekannter Link404. - Antwort ohne Schlüsselkennung: Den Schlüssel gibt es erst nach dem Abholen; er erscheint dann in
PersonalKeys. EinexpiresAtvor dem Ende des Links lehnt der Dienst mit400ab. - Protokoll: Ausstellen als Befehl mit
delivery: mail, Abholen alsissue-keybzw.enroll-totpmit dem AkteurEinmal-Link von <Aussteller>und der Herkunft. - Lässt sich der Auftrag beim Abholen nicht mehr erfüllen (Person entfernt), zeigt die Seite das mit
409; der Link bleibt dann unverbraucht.
{ "person": "Erika Muster", "label": "Excel Bericht", "expiresAt": "2027-03-31T00:00:00Z",
"readOnly": true, "databases": [ "fachdaten" ], "from": [ "10.0.0.0/8" ], "exceptFrom": [],
"delivery": "key" }
Antworten
Für alle Mengen und Befehle der Verwaltung:
| Antwort | Bedeutung |
|---|---|
401 „Es wurde keine gueltige Anmeldung vorgelegt.“ | Kein oder ein unbekannter Schlüssel |
403 „Die Verwaltung ist nur ueber HTTPS oder vom eigenen Rechner erreichbar.“ | Ungeschützte Verbindung |
403 „Die Verwaltung ist fuer diesen Aufrufer nicht freigegeben.“ | Keine Regel mit admin; nur lesende Regel bei einer Änderung; Schlüssel auf Datenquellen beschränkt |
400 | Unbekanntes Feld, fehlendes Pflichtfeld, Menge ohne diesen Vorgang (PersonalKeys anlegen, ChangeLog ändern), Sperre aufheben, Ablauf zu spät |
412 | Die Zeile wurde seit dem Lesen geändert |
422 | Inhalt nach der Änderung ungültig oder Einschränkung des Speichers; die Meldung nennt den Grund |
Verbindungstest und Dienstkonto
test-connection. Rumpf { "dataSource": "fachdaten" }. Die Antwort nennt dataSource,
reachable, elapsedMilliseconds, productVersion, database und login; ist die Datenquelle
nicht erreichbar, dazu message (die Meldung des Servers in seiner Sprache), number und failure.
Der Test öffnet die Verbindung einmal, ohne Verbindungspool, und meldet die Antwort des Servers; eine
Wiederholung des Datenbanktreibers gibt es dabei nicht. Nur auf 233 („kein Prozess am anderen Ende der
Pipe“) öffnet er sofort ein zweites Mal: Ein SQL Server auf demselben Rechner meldet eine abgelehnte
Anmeldung über Shared Memory oder Named Pipes manchmal so statt mit 18456. Antwortet der Server wieder
mit 233, ist er tatsächlich nicht erreichbar:
{ "dataSource": "fachdaten", "reachable": false, "elapsedMilliseconds": 7,
"message": "Login failed for user 'NT SERVICE\\Wkl.Data.Gateway'.", "number": 18456, "failure": "Login" }
number ist die Nummer der Datenbank (bei SQL Server die Fehlernummer); leer, wenn es keine gibt.
failure ordnet den Fehler unabhängig von der Sprache des Servers ein; ein Programm erklärt danach
die Ursache und schlägt die Abhilfe vor:
failure | Bedeutung | SQL Server, Beispiele | Abhilfe |
|---|---|---|---|
Login | Anmeldung abgewiesen: unbekannte Anmeldung, falsches Kennwort, gesperrt, abgelaufen | 18456, 18452, 18470, 18486–18488 | Anmeldung in SQL Server anlegen oder freigeben (Datenbankverwaltung) |
Database | Anmeldung angenommen, Datenbank fehlt oder ist für sie nicht zugänglich | 4060, 4064, 911 | Benutzer in der Datenbank anlegen oder Namen der Datenbank prüfen |
Permission | Verbunden, eine Berechtigung fehlt | 229, 230, 262, 297, 300, 916 | Berechtigung in der Datenbank erteilen |
Network | Server nicht erreicht: Name, Port, Dienst gestoppt, Firewall, Verschlüsselung abgelehnt | 2, 26, 53, 258, 10060, 10061, Schweregrad ab 20 | Name, Port und Firewall prüfen |
Timeout | Keine Antwort in der Zeit | -2 | Last des Servers, Connect Timeout |
Unreadable | Server antwortet, kann die Datenbank aber nicht lesen | 605, 823, 824, 942, 945 | Laufwerk prüfen, Datenbankserver neu starten |
Configuration | Einstellungen unbrauchbar: Zeichenfolge abgelehnt, Provider nicht verwendbar | – | Verbindung bzw. Provider der Datenquelle korrigieren |
Other | Anderer Fehler | – | message und number lesen |
Ein Provider, der für eine ältere Version des Provider-Vertrags gebaut ist, liefert number und
failure nicht; sie bleiben dann leer.
service-account. Ohne Rumpf. Das Konto, unter dem eine Datenquelle mit Windows-Anmeldung
(Integrated Security) sich an der Datenbank anmeldet – der Name, für den eine Datenbankverwaltung
die Anmeldung anlegt:
{ "account": "NT SERVICE\\Wkl.Data.Gateway", "kind": "Virtual",
"networkAccount": "FIRMA\\SRV-DATEN$", "domain": "FIRMA" }
| Feld | Inhalt |
|---|---|
account | Das Konto des Dienstes; so sieht es ein SQL Server auf demselben Rechner |
kind | Virtual (virtuelles Dienstkonto NT SERVICE\…), NetworkService, LocalSystem, LocalService, Managed (gruppenverwaltetes Dienstkonto DOMÄNE\name$) oder User |
networkAccount | So sieht es ein SQL Server auf einem anderen Rechner: bei Virtual, NetworkService und LocalSystem das Computerkonto DOMÄNE\RECHNER$, bei einem Domänenkonto dieses selbst. Leer, wenn kein anderer Rechner das Konto kennt (Rechner ohne Domäne, LocalService, lokales Benutzerkonto) – dann gelingt die Windows-Anmeldung nur an einem SQL Server auf demselben Rechner |
domain | Domäne des Rechners (NetBIOS-Name); leer ohne Domäne |
Was SQL Server für dieses Konto braucht, steht unter Installation – Hinweise.
Schlüssel auf Abruf, Anmelderegeln, Erklärung
Schlüssel auf Abruf. issue-key mit "onDemand": true stellt einen Schlüssel aus, der ruht,
bis die Verwaltung ihn freischaltet. activate-key mit { "id": "k…", "hours": 4 } schaltet ihn
frei; eine neue Freischaltung ersetzt die laufende. Abgelehnt (400, die Meldung nennt den Grund)
wird ein Schlüssel, der nicht auf Abruf gilt, gesperrt oder abgelaufen ist, eine Dauer über
max_activation_hours und eine Freischaltung über das Ablaufdatum des Schlüssels hinaus.
deactivate-key mit { "id": "k…" } beendet sie sofort. Beides steht mit Akteur im
Änderungsprotokoll; die Antwort nennt activeUntil.
Anmelderegel in einem Schritt. Eine Regel gilt, sobald sie gespeichert ist. Über die Mengen
entstehen zuerst die Regel und danach ihre Merkmale – eine Ablehnung gilt dazwischen für alle, auch
für den, der sie speichert. save-sign-in-rule schreibt Regel und Merkmale in einer Änderung:
{ "scope": "All", "method": "Any", "effect": "Deny", "label": "außen nur Frank",
"exceptFrom": [ "10.0.0.0/8" ], "exceptPersons": [ "Frank Mustermann" ] }
Weitere Listen: from, persons, windowsGroups, exceptWindowsGroups, countries,
exceptCountries, operators, exceptOperators, clients, exceptClients, hours,
exceptHours (Bedeutung unter Anmelderichtlinie).
Netzbetreiber speichert der Dienst als AS mit Nummer, Zeitfenster in ausgeschriebener Form
(Mon-Fri wird Mon,Tue,Wed,Thu,Fri 00:00-24:00). mode ist Enforce (Vorgabe) oder Report für
eine Regel in Probe. Befristete Ausnahmen stehen in exceptions, je mit persons, windowsGroups,
countries, from, operators, clients, hours, until (UTC) und label:
{ "scope": "Data", "effect": "Deny", "label": "nur Deutschland",
"exceptCountries": [ "DE" ], "exceptFrom": [ "10.0.0.0/8" ],
"exceptions": [ { "persons": [ "Frank Mustermann" ], "countries": [ "TH" ],
"until": "2026-11-02T00:00:00Z", "label": "Urlaub" } ] }
Mit "id" ersetzt der Befehl die Regel samt allen Merkmalen und Ausnahmen; ohne legt er eine neue an. Antwort { "id" }. Eine
unbekannte Person oder Regel beantwortet der Dienst mit 400, einen danach ungültigen Inhalt mit
422; in beiden Fällen bleibt nichts gespeichert.
Anmeldung erklären. explain-sign-in rechnet mit derselben Richtlinie wie eine echte
Anmeldung, was für eine Person gilt – für jede Anmeldeart der Person (Windows-Konto,
SQL-Anmeldung, jeder persönliche Schlüssel) und jede genannte Herkunft:
{ "person": "Frank Mustermann", "scope": "Data",
"origins": [ { "name": "Firmennetz", "address": "10.1.2.3" },
{ "name": "Außen", "address": "203.0.113.7" },
{ "name": "Thailand", "address": "203.0.113.7", "country": "TH" },
{ "name": "Unbekannt", "address": null } ] }
Eine Herkunft darf ihr Land mit country selbst nennen; so lässt sich „was gilt für Frank in
Thailand?“ fragen, ohne eine Adresse von dort zu kennen. Ohne country liest der Dienst das Land wie
bei einer echten Anmeldung aus der Geodatenbank. Ebenso nimmt eine Herkunft operator (Netzbetreiber,
AS3320; ohne Angabe aus der Geodatenbank), client (Programmart; ohne Angabe unbekannt) und at
(Zeitpunkt in UTC; ohne Angabe jetzt), etwa
{ "name": "Samstag mit Excel", "address": "203.0.113.7", "client": "Power Query", "at": "2026-10-24T10:00:00Z" }.
Die Antwort führt je Anmeldeart (ways) eine Zelle je Herkunft mit result – allowed, code
(zweiter Faktor nötig), denied oder ein Zustand, der vor der Richtlinie greift: revoked,
expired, rests (Schlüssel auf Abruf ruht), personDisabled, outsideKeyNetworks – und den
zutreffenden Regeln (id, version, label, effect, method, scope); floor zeigt die Untergrenze der
Verwaltung, country das Land, nach dem geurteilt wurde (leer, wenn es unbekannt ist oder keine
Regel Länder nennt), operator, client und at die übrigen beurteilten Signale, tried die Regeln in Probe, die zuträfen (wie rules; sie gehen nicht in
result ein), und spared die Regeln, von denen eine Ausnahme befreit (id, version,
rule, effect, exception, until). id und version sind dieselben Werte, die ein
Anmeldeereignis nennt (siehe Grundlage der Entscheidung); so lässt sich
prüfen, ob eine Regel noch so steht wie bei einer früheren Anmeldung. Ein Windows-Konto wird ohne seine Gruppen beurteilt, denn die kennt nur eine echte
Anmeldung; nennt eine Regel Gruppen, steht groupsUnknown dabei. 1 bis 16 Herkünfte je Aufruf.
Anmeldeprotokoll und Geodatenbank
Das Anmeldeprotokoll hält jede Anmeldeentscheidung mit Herkunft in der
Protokolldatei neben dem Speicher fest
(Einstellungen unter `Gateway:SignInLog`, Vorgehen in
So prüfen Sie Anmeldemuster und Herkunft). Der Dienst schreibt
die gesammelten Entscheidungen einmal in der Minute und wendet dabei die Anmelderegeln an; eine
Anmeldung erscheint in den Mengen also bis zu einer Minute später. Alle Mengen sind nur lesbar.
SignInFindings liest gespeicherte Befunde, die übrigen Mengen werden beim Lesen berechnet.
| Menge | Schlüssel | Inhalt |
|---|---|---|
SignInHours | Hour, Method, Outcome, Country, NetClass | Anzahl (Count) je Stunde (UTC, auf die volle Stunde), dazu Weekday (1 = Montag) und HourOfDay nach der Uhr des Servers. Ohne Person und Adresse; bleibt HourDays Tage |
SignInRuleReports | Id | Je Regel in Probe und Version, über die Aufbewahrung der Ereignisse: Rule (Kennung), RuleVersion, Label, Effect, Would (Anmeldungen, die sie getroffen hätte), Spared (Anmeldungen, für die eine Ausnahme sie aufgehoben hätte), Persons und Origins (Anzahl verschiedener), FirstAt, LastAt. Nur Zählungen, ohne Person und Adresse, daher ohne das Recht sign-in-events lesbar. Gezählt werden Anmeldeereignisse, wie das Protokoll sie führt: eine erfolgreiche Anmeldung je Aufrufer, Verfahren und Herkunft je SuccessWindowMinutes |
SignInOrigins | Id | Je eigenem Netz als Ganzes (NetName, Network leer), sonst je Teilnetz (Network, /24 bzw. /48), mit Country, CountryName, City, Latitude, Longitude, Asn, AsnOrganisation, Zählungen Successes, Refusals, Challenges, Persons und FirstAt/LastAt |
SignInEvents | Id | Die einzelnen Ereignisse, je Abruf höchstens 20 000 der passenden, die neuesten zuerst. Bedingungen auf Person, At und Id liest der Dienst schon aus der Protokolldatei, die Grenze gilt also nur für Passendes. Felder: At, Repeats, Method, Scope (data, admin), Alias, Outcome, Reason, Status, Person, KeyId, Presented, Origin, Network, NetClass, NetName, Ort und Netzbetreiber wie oben mit Region, Client, CorrelationId, Rules, Signals (siehe Grundlage der Entscheidung), Weekday, HourOfDay. Nur mit dem Recht sign-in-events, sonst 403 |
SignInFindings | Id | Die gespeicherten Befunde der Anmelderegeln, Id aufsteigend und beständig: Rule, RuleVersion, Level (act, ask), At, Person, KeyId, Observed, Usual, BaselineDays, Text, Advice, EventIds (die Id der zugrunde liegenden Ereignisse, durch Komma getrennt). Person, KeyId, Usual und EventIds nur mit dem Recht sign-in-events, sonst leer. Je Abruf höchstens 20 000 |
SignInAdvice | Id | Auffällige Muster der letzten sieben Tage, beim Lesen berechnet, Id nur innerhalb eines Abrufs: Code, Text, Advice, FromAt, UntilAt, Count, Origin, Country; Person und KeyId nur mit dem Recht sign-in-events. Der Text nennt nie eine Person |
GeoIpFiles | Kind | Je Datei (City, Asn): DatabaseType, BuildDate, AgeDays, Stale (älter als StaleDays), Bytes, InstalledAt, Attribution. Leer ohne Dateien |
GeoIpSource | Id | Eine Zeile: Source, Account, HasLicenseKey, UpdateDays, LastAttemptAt, LastError, Attribution. Der Lizenzschlüssel wird nie ausgegeben |
Werte. Method: apiKey, personalKey, windows, sqlLogin, none. Outcome: success,
refused, challenge (Anfrage ohne Anmeldedaten oder Zwischenschritt einer Windows-Anmeldung; auch
die Aufforderung zum zweiten Faktor), totpSuccess, totpFailure. NetClass: loopback, private
(eigenes Netz, RFC 1918, eindeutig lokale IPv6-Adressen, Link-Local, 100.64.0.0/10), public,
unknown. In SignInHours ist Country leer, wenn kein Land bekannt ist. Reason (bei refused): notSignedIn, unknownKey, revoked, expired, rests,
personDisabled, outsideKeyNetworks, denied, deniedByCountry (jede ablehnende Regel nennt
Länder), noPerson, enrolmentOpen, noFactor,
secondFactorMissing, sessionExpired, limit, unprotected (Anmeldung, die nur über eine
geschützte Verbindung angenommen wird, kam unverschlüsselt). Presented nennt einen angegebenen,
unbekannten SQL-Anmeldenamen, nie einen Schlüssel. Client ist die Art des Programms aus der
Kopfzeile User-Agent (Power Query, WinHTTP, PowerShell, Browser oder das erste Wort); leer
ohne Kopfzeile.
Grundlage der Entscheidung
Jedes Anmeldeereignis, das die Anmelderichtlinie beurteilt hat, nennt in Rules die Regeln, nach
denen entschieden wurde, und in Signals die Signale, nach denen geurteilt wurde – beides als
JSON-Text. Ein Ereignis ohne gültigen ersten Faktor (falscher Schlüssel, falsches Kennwort) erreicht
die Richtlinie nicht; dort sind beide Felder leer.
Rules ist eine Liste; leer ([]), wenn keine Regel zutraf. Jeder Eintrag nennt:
| Feld | Bedeutung |
|---|---|
id | Kennung der Regel: ihre Id in SignInRules, bei einer Regel der Konfigurationsdatei file:n (n-te Regel) – das kommt nur ohne Verwaltungsspeicher vor, also nie in Ereignissen, die die Verwaltungsschnittstelle liest. floor steht für die Untergrenze der Verwaltung (RequireAdminTotp), die keine Regel ist |
version | Fingerabdruck dessen, was die Regel tut: 16 Hexadezimalziffern, die sich ändern, sobald sich Teil, Verfahren, Wirkung, eine Bedingung oder eine Ausnahme mit ihrem Ende ändert – nicht bei einer bloßen Änderung des Vermerks. Gleiche Regeln haben überall dieselbe Version |
effect | deny oder requireTotp |
label | Vermerk der Regel zum Zeitpunkt der Anmeldung |
spared | true, wenn eine Ausnahme die Regel für diese Anmeldung aufgehoben hat; fehlt sonst |
mode | report für eine Regel in Probe: Sie hat nichts entschieden, der Eintrag zeigt, was sie getan hätte; fehlt bei einer durchgesetzten Regel |
exception | Vermerk der Ausnahme, die die Regel aufgehoben hat |
Signals nennt, was über die Spalten des Ereignisses hinaus in die Entscheidung einging:
| Feld | Bedeutung | Verlässlichkeit |
|---|---|---|
country | Land, nach dem die Regeln geurteilt haben; fehlt, wenn es unbekannt war | beobachtet, aus der Adresse abgeleitet (VPN und Mobilfunk verschieben es) |
countryAsked | ob eine Regel Länder nennt und das Land deshalb nachgeschlagen wurde | – |
secondFactor | valid (gültige Sitzung vorgelegt), missing (verlangt, nicht vorgelegt oder abgelaufen), notRequired, exchange (die Anfrage eröffnet eine Sitzung oder richtet den Faktor ein), notChecked (vorher abgewiesen) | nachgewiesen |
totpExempt | ob der persönliche Schlüssel vom zweiten Faktor befreit ist | nachgewiesen |
operator | Netzbetreiber, nach dem die Regeln geurteilt haben (AS3320); fehlt, wenn er unbekannt war oder keine Regel Netzbetreiber nennt | beobachtet, aus der Adresse abgeleitet |
client | Programmart, die die Anfrage angab; fehlt ohne User-Agent | behauptet |
trust | je genanntem Signal sein Niveau: observed, claimed oder verified (für secondFactor) | – |
Verfahren, Person, Schlüssel und Herkunft stehen in den Spalten des Ereignisses (Method, Person,
KeyId, Origin, NetClass, NetName); Client ist eine Behauptung des Programms und entscheidet
nur, wenn eine Regel nach dem Programm fragt. Gleiche Ablehnungen einer Minute fasst das Protokoll nur zusammen, wenn
dieselben Regeln in derselben Version entschieden haben. Gesucht wird mit contains, etwa
$filter=contains(Rules,'"id":"7"') für alle Anmeldungen, an denen die Regel 7 beteiligt war.
Recht sign-in-events. Eine Regel muss die Ressource wörtlich nennen, zusätzlich zu admin;
weder * noch admin allein gewähren sie. Adresse und Person je Anmeldung sind personenbezogene
Daten; das Recht trennt, wer Zählungen und Muster sieht, von wer einzelne Personen verfolgen kann.
Es gilt für SignInEvents, für die Personenfelder von SignInFindings und SignInAdvice und für
`sign-in-profile`. Ohne das Recht sind diese Felder leer, bevor der Filter einer
Anfrage greift; eine Bedingung auf sie trifft dann nichts.
Befunde der Anmelderegeln
Bei jedem Schreiben wendet der Dienst die Anmelderegeln auf die neuen Ereignisse an und speichert,
was eine Regel erkennt, als Befund. Ein Befund wird einmal geschrieben und nie neu bewertet; er nennt
die Regel mit ihren Schwellen (RuleVersion) und den Vergleichszeitraum (BaselineDays), unter
denen er entstand. Dieselbe Lage – Regel, Herkunft oder Person, Stunde oder Tag – ergibt nur einen
Befund.
Seit dem letzten Blick. Id steigt mit jedem Befund und wird nie wiederverwendet. Ein Programm
merkt sich die höchste gelesene Id und fragt danach mit $filter=Id gt 4711 nur Neues ab. Mit
einer Bedingung auf Id liest der Dienst die ältesten passenden Befunde zuerst; so liest ein
Programm auch mehr als 20 000 neue Befunde vollständig in Schritten. Ohne eine solche Bedingung
liefert er die neuesten.
Stufen. act verlangt Handeln: ein Angriffsmuster oder ein weitergegebener Schlüssel. ask ist
eine Frage an die Person wert: Etwas ist für sie neu oder ungewöhnlich. Wohin der Dienst Befunde
meldet, regelt `Gateway:SignInLog:Alerts`.
| Regel | Stufe | Erkannt, wenn |
|---|---|---|
failureWave | act | mindestens 20 abgewiesene Anmeldungen von einer Adresse innerhalb einer Stunde |
spray | act | Abweisungen für mindestens 5 verschiedene Namen oder Schlüssel von einer Adresse innerhalb einer Stunde |
distributed | act | Abweisungen für denselben Namen von mindestens 10 Adressen innerhalb einer Stunde |
impossibleTravel | act | zwei erfolgreiche Anmeldungen einer Person mindestens 500 km auseinander, schneller als 1 000 km/h |
keyManyOrigins | act | ein persönlicher Schlüssel aus mindestens 5 öffentlichen Netzen an einem Tag |
newCountry | ask | ein Land, aus dem die Person in den 90 Tagen davor nicht angemeldet war |
newClient | ask | eine Programmart, die die Person in den 90 Tagen davor nicht benutzt hat |
newNetworkOperator | ask | ein Netzbetreiber, über den die Person in den 90 Tagen davor nicht angemeldet war |
newMethod | ask | ein Anmeldeverfahren, das die Person in den 90 Tagen davor nicht benutzt hat |
unusualHour | ask | eine Stunde, in der und neben der die Person in den 90 Tagen davor nie angemeldet war |
Die Regeln über Neues sprechen erst, wenn die Person in den 90 Tagen davor mindestens 5 erfolgreiche
Anmeldungen hatte, unusualHour erst ab 20; vorher wäre alles neu. Tage und Stunden zählen in der
Zeitzone von Gateway:SignInLog:TimeZone. Observed ist der beobachtete Wert (ein Land, eine
Anzahl, eine Strecke), Usual sind die üblichen Werte der Person zum Vergleich. Der Text eines
Befunds nennt nie eine Person.
Anmeldeprofil
sign-in-profile zeigt, was für eine Person bei der Anmeldung üblich ist – etwa, um einen Befund
newCountry einzuordnen. Rumpf:
{ "person": "Frank Mustermann", "days": 365 }
days zählt ab heute zurück, heute eingeschlossen, in der Zeitzone von Gateway:SignInLog:TimeZone;
Vorgabe 90, erlaubt 1 bis 3 650. Die Antwort nennt person, days, from, until, total
(erfolgreiche Anmeldungen) und activeDays sowie unter dimensions je Merkmal – country,
network, operator, client, method, hour (Stunde 00 bis 23) – die Werte mit value,
count, firstDay und lastDay, die häufigsten zuerst. Ein Ausschnitt:
{ "person": "Frank Mustermann", "days": 365, "from": "2025-10-21", "until": "2026-10-20",
"total": 1184, "activeDays": 213,
"dimensions": { "country": [ { "value": "DE", "count": 1170, "firstDay": "2025-10-21", "lastDay": "2026-10-20" },
{ "value": "IT", "count": 14, "firstDay": "2026-07-28", "lastDay": "2026-08-08" } ] } }
Es zählen nur erfolgreiche Anmeldungen. Das Profil stammt aus Tageswerten je Person, die in der
Protokolldatei länger bleiben als die einzelnen Ereignisse. Es braucht das Recht sign-in-events
(sonst 403) und gilt immer für genau eine Person; eine Liste über Personen gibt es nicht.
Unbekannte Person: 404; days außerhalb des Bereichs: 400.
set-geoip-source. Rumpf:
{ "source": "maxmind", "account": "123456", "licenseKey": "…", "updateDays": 7 }
source ist Pflicht: none (nur Dateien per upload-geoip), dbip-lite oder maxmind. updateDays
0 bis 90: nach so vielen Tagen lädt der Dienst von selbst neu (0 = nur auf Befehl); nach einem
gescheiterten Versuch versucht er es nach sechs Stunden wieder. licenseKey wird mit dem
Hauptschlüssel versiegelt gespeichert; fehlt er im Rumpf, bleibt der gespeicherte,
"removeLicenseKey": true entfernt ihn. maxmind ohne Kontonummer oder Lizenzschlüssel: 422. Das
Änderungsprotokoll nennt nur, ob ein Schlüssel gesetzt ist.
update-geoip. Ohne Rumpf. Lädt beide Dateien und nimmt sie in Gebrauch, ohne Neustart. Antwort
files mit je kind, databaseType, buildDate, bytes, changed, attribution; eine Datei mit
demselben Erstellungsdatum wie die vorhandene bleibt unverändert (changed: false). Abgelehnt mit
422 und Grund: keine Quelle gewählt, Quelle lehnt ab oder liefert nichts, Datei unbrauchbar. Läuft
schon ein Laden: 422.
upload-geoip. Rumpf ist die Datei selbst (.mmdb, .mmdb.gz oder das .tar.gz von MaxMind),
Content-Type: application/octet-stream. Abfrageoptionen: source=dbip-lite|maxmind bestimmt die
Namensnennung, force=true übernimmt auch eine ältere oder gleich alte Datei. Die Art (City,
Asn) erkennt der Dienst am Inhalt. Antwort wie ein Eintrag von update-geoip. Größer als
GeoIp:MaxMegabytes: 429 (LIMIT_EXCEEDED) wie jeder zu große Rumpf; unbrauchbar oder älter als
die vorhandene: 422.
Quellen der Geodatenbank
| Quelle | Bezug | Lizenz und Namensnennung |
|---|---|---|
dbip-lite | DB-IP Lite, monatlich, ohne Konto; der Dienst lädt dbip-city-lite-JJJJ-MM.mmdb.gz und dbip-asn-lite-JJJJ-MM.mmdb.gz von download.db-ip.com (zu Monatsbeginn notfalls den Vormonat) | CC BY 4.0; Namensnennung Pflicht: „IP Geolocation by DB-IP (https://db-ip.com)“ |
maxmind | MaxMind GeoLite2 City und ASN mit Kontonummer und Lizenzschlüssel eines kostenlosen MaxMind-Kontos | GeoLite2-Lizenz von MaxMind; Hinweis „This product includes GeoLite2 data created by MaxMind“ |
Ausgehend ist nur das Laden der Dateien; eine Adresse eines Aufrufers verlässt den Server nie.
Lastprotokoll
Das Lastprotokoll hält fest, was jede Form einer Anfrage kostet (Einstellungen unter
`Gateway:LoadLog`, Vorgehen in
So finden Sie heraus, was den Dienst und die Datenbank belastet).
Eine Form ist eine Anfrage ohne ihre Werte: Weg, Alias, Art, Objekt, verglichene Felder,
Sortierung und Fenster, jeder Wert als ?. Anfragen, die sich nur in Werten unterscheiden, sind
eine Form. Der Dienst schreibt die gesammelten Zahlen einmal in der Minute in die
Protokolldatei; eine Anfrage erscheint also bis
zu einer Minute später. Alle Mengen sind nur lesbar. LoadFindings liest gespeicherte Hinweise, die
übrigen Mengen werden beim Lesen berechnet.
| Menge | Schlüssel | Inhalt |
|---|---|---|
LoadShapes | Id | Je Form der letzten sieben Tage: Path, Alias, Kind, Resource, Text (die Form), Template (die erzeugte Anweisung ohne Werte), FirstAt, LastAt, Requests, TotalMs, AverageMs, P95Ms, MaxMs, DataMs, Rows, AverageRows, Bytes, Errors, Refused, Persons, Advice (Codes aus LoadAdvice, durch Komma getrennt) |
LoadHours | Hour, ShapeId | Je Stunde (UTC, auf die volle Stunde) und Form: Requests, TotalMs, DataMs, MaxMs, P95Ms, Rows, Bytes, Errors, Refused, dazu Weekday (1 = Montag) und HourOfDay nach der Uhr des Servers. Ohne Person; bleibt HourDays Tage |
LoadPersons | Day, ShapeId, Person | Je Tag (UTC), Form und Aufrufer: Requests, TotalMs, Rows. Nur mit dem Recht load-persons, sonst 403; bleibt PersonDays Tage |
SlowRequests | Id | Einzelne Anfragen ab SlowMilliseconds, die neuesten 20 000: At, ShapeId, Ms, DataMs, Rows, Bytes, Status, CorrelationId; Person und KeyId nur mit dem Recht load-persons, sonst leer. Bleibt SlowDays Tage |
LoadFindings | Id | Die Hinweise aus LoadAdvice, einmal in der Stunde gespeichert, Id aufsteigend und beständig: At (wann der Hinweis entstand), Code, ShapeId, Alias, Resource, Text, Advice, Requests und AverageDataMs (Zahlen der sieben Tage, auf denen er beruht). Je Code und Form ein Hinweis; eine Form, die teuer bleibt, erscheint nicht erneut. Ohne Person. Je Abruf höchstens 20 000; Id gt wie bei den Befunden der Anmelderegeln |
LoadAdvice | Id | Formen der letzten sieben Tage mit einer bekannten Abhilfe, beim Lesen berechnet, Id nur innerhalb eines Abrufs: Code, ShapeId, Alias, Resource, Text, Advice, Requests, AverageDataMs. Der Text nennt nie eine Person |
Werte. Path: odata (OData-Oberfläche), cf (eigene Routen unter /api/v1/cf/),
checked (geprüfte Anweisung), passthrough (Anweisung mit dem Recht sql), admin
(Verwaltung), other (zweiter Faktor, Selbstbedienung, Downloads). Kind: read, count,
aggregate, call, insert, update, delete, statement, describe (Anweisung nur
beschrieben), service (Anfrage ohne Daten: Metadaten, Abweisung, Verwaltung). Den Text einer
Anweisung führt der Dienst ohne Literale und Kommentare; lange Wertelisten werden zu ?, …
zusammengelegt. Übersprungene Zeilen ($skip) erscheinen in Text nur als Stufe, nie mit ihrer
Zahl: skip=small (unter 1 000), skip=medium (unter 10 000), skip=large (ab 10 000).
Zeiten und Mengen. TotalMs ist die ganze Zeit der Anfrage im Dienst, DataMs der Teil ab
dem Öffnen der Verbindung zur Datenquelle bis zur letzten gelesenen Zeile, einschließlich des
Wechsels auf die Identität der Person. Das 95. Perzentil (P95Ms) ist die Obergrenze der
Zeitstufe, in die es fällt (Stufen 5, 10, 25, 50, 100, 250, 500, 1 000, 2 500, 5 000, 10 000,
30 000 ms; darüber MaxMs). Bytes zählt die Antwort vor der Komprimierung. Errors zählt
Antworten 5xx, Refused Antworten 4xx. Person ist der Name der Person, ohne Person die
Kennung des Aufrufers wie im Audit. Summen von Zeit, Zeilen und Bytes sind Gleitkommazahlen, damit
ein Programm wie Access, das ganze Zahlen mit 32 Bit liest, nicht überläuft.
Hinweise (LoadAdvice). Jeder Code ist ein Muster, das die Datenbank unabhängig von den Daten
belastet:
| Code | Erkannt, wenn | Naheliegende Abhilfe |
|---|---|---|
wholeSet | Lesen ohne Filter mit im Mittel mindestens 1 000 Zeilen je Anfrage | Filter setzen, nur benötigte Spalten wählen |
unindexedFilter | Filter über eine Tabelle mit mindestens 10 000 Zeilen (Schätzung des Katalogs), keine der verglichenen Spalten führt einen Index an | Index mit der ersten Filterspalte anlegen lassen |
deepPaging | $skip ab 10 000 Zeilen | Mit Filter eingrenzen oder über den Folgelink lesen |
countLarge | Gesamtzahl ($count) mit im Mittel mindestens 500 ms in der Datenschicht | Gesamtzahl nur bei Bedarf anfordern |
aggregateWhole | Summen ohne Filter mit im Mittel mindestens 500 ms in der Datenschicht | Eingrenzen oder in der Datenbank vorberechnen |
expensiveStatement | Eigene Anweisung mit im Mittel mindestens 1 000 ms in der Datenschicht oder 95. Perzentil ab 2 500 ms | Im Query Store prüfen, als Prozedur oder indizierte Sicht ablegen |
refreshLoop | Dieselbe Form mindestens 600-mal in einer Stunde, von höchstens drei Aufrufern | Automatische Aktualisierung prüfen |
Recht load-persons. Eine Regel muss die Ressource wörtlich nennen, zusätzlich zu admin;
weder * noch admin allein gewähren sie. Die Last je Person zeigt, wer wann wie viel gearbeitet
hat; das Recht trennt, wer die Last des Dienstes sieht, von wer Personen vergleichen kann.
export-load. Rumpf optional:
{ "days": 30 }
days 1 bis HourDays, Vorgabe 30. Die Antwort ist eine JSON-Datei
wkl-load-JJJJMMTT-hhmmss.json im Format wkl-load-export/1 mit shapes, hours, advice und
den Grenzen der Zeitstufen (histogramBounds). Sie enthält die Namen von Aliassen, Objekten und
Spalten, aber keine Personen, Schlüssel, Adressen, Korrelationskennungen und keine Werte; die
Tageszahlen je Person und die langsamen Einzelanfragen werden dafür gar nicht gelesen. Das Recht
ist das Lesen der Verwaltung; der Abruf steht im Änderungsprotokoll.
Zuordnung in SQL Server
Der Dienst kennzeichnet jede Anweisung, die er selbst erzeugt, mit der Kennung ihrer Form als
Kommentar hinter dem ersten Schlüsselwort, etwa SELECT /* wkl:5b9b83f10953 */ TOP (1) … oder
UPDATE /* wkl:8b7d7b280081 */ [dbo].[Probe] SET …. Dort bleibt er auch im Query Store erhalten,
der einen Kommentar vor oder hinter einer Anweisung nicht speichert. Bei einem Prozeduraufruf steht
die Kennung vor dem Aufruf; der Query Store führt dann die Anweisungen der Prozedur ohne Kennung. Mit dieser Kennung findet
eine Datenbankverwaltung die Form im Plancache, im Query Store oder in Extended Events und kommt
von dort zu LoadShapes. Geprüfte Anweisungen tragen die Kennung ebenso; eine Anweisung mit dem
Recht sql läuft unverändert, ohne Kennung. Die Verbindungen nennen sich gegenüber SQL Server
Wkl.Data.Gateway (program_name), sofern die Verbindungszeichenfolge der Datenquelle keinen
eigenen Application Name setzt.
Datenbank-Updates
Das Update-Modul des Dienstes spielt signierte Datenbank-Updates (Pakete
<Produkt>-schema-<Version>.zip) in die Datenbanken ein, für die der Ordner
`Gateway:Updates` eine geschützte Konfiguration hat. Es sieht alle
PollSeconds Sekunden nach: Pakete im Eingang, deren Kopie seit mindestens 5 Sekunden abgeschlossen
ist, und angekündigte Pakete, deren Frist abgelaufen ist. Die Befehle zeigen den Stand und führen die Entscheidungen der Verwaltung aus.
Rechte. update-databases, update-status und update-history lesen (Regel mit admin, auch nur
lesend). Jede Entscheidung braucht das Recht zum Ändern und steht im Änderungsprotokoll mit Datenbank,
Paket und Ergebnis.
update-databases. Ohne Rumpf. Die Datenbanken mit vertrauenswürdiger Konfiguration und ihren
Einstellungen, dazu die Dateien, die das Modul auslässt:
{
"folder": "C:\\ProgramData\\Wkl.Data.Gateway\\updates",
"databases": [ {
"id": "demo-prod", "gatewayAlias": "demo", "product": "Demo", "dialect": "sqlserver",
"connection": { "string": "Server=sql01;Database=Demo;User ID=wkl_update;Encrypt=True",
"secret": "file:C:\\ProgramData\\Wkl.Data.Gateway\\secrets\\demo-update.txt" },
"keys": [ "9c0f6d1e…" ],
"inbox": "D:\\Updates\\Demo", "archive": "D:\\Updates\\Demo-Archiv",
"backup": { "mode": "native", "target": "D:\\Sicherung" }, "starterSource": null,
"settings": { "expand": "automatic", "contract": "confirm", "noticeMinutes": 60, "sessionWindowMinutes": 15 }
} ],
"problems": [ { "file": "alt.json", "code": 9, "message": "…" } ]
}
| Feld | Inhalt |
|---|---|
id | Schlüssel der Datenbank in allen anderen Befehlen (database) |
connection.string | Verbindung für Strukturänderungen und Sicherungen, ohne Kennwort |
connection.secret | Verweis auf das Kennwort (env:NAME oder file:PFAD); der Wert wird nie ausgegeben |
keys | Schlüssel der Herausgeber als SHA-256-Fingerabdruck |
settings | Betriebseinstellungen: expand und contract (automatic oder confirm), Vorlauf einer Ankündigung (noticeMinutes), Zeitfenster, in dem eine Anfrage über den Dienst als offene Sitzung zählt (sessionWindowMinutes). Geändert werden sie in der Menge `UpdateSettings`; sie gelten ab dem nächsten Durchlauf. Lassen sie sich nicht lesen, wartet die Datenbank, statt mit den Vorgaben zu laufen |
problems | Ausgelassene Dateien mit Code und Grund; 9 heißt: nicht vertrauenswürdig (für andere als Administratoren und SYSTEM änderbar, Kennwort in der Verbindung, connection.secret fehlt, Dateiname ≠ id) |
update-status. Rumpf { "database": "demo-prod" }. Antwort: das Statusobjekt der
Datenbank-Updates (Format 2), dasselbe wie wkl-db status --json:
{
"format": 2, "id": "demo-prod", "product": "Demo", "schema": 13,
"packages": [ { "id": 41, "file": "Demo-schema-14.zip", "schema": 14, "kind": "contract",
"status": "announced", "reason": null, "message": null, "deadlineUtc": "2026-11-02T18:00:00Z" } ],
"sessions": [ { "product": "Demo", "version": "2.3.1", "path": "gateway", "host": "10.1.2.17", "user": "FIRMA\\mustermann", "lastSeenUtc": "2026-11-02T16:40:12Z" } ],
"drift": []
}
packages enthält die Pakete, die nicht eingespielt (done) oder verworfen (dismissed) sind:
waiting (wartet auf Bestätigung), announced (Ankündigung läuft, deadlineUtc ist die Frist),
running, failed, refused. reason ist der Code, der den Status erklärt (siehe unten).
sessions nennt Sitzungen älterer Anwendungen, die ein angekündigtes Paket aufhalten, und Sitzungen
ohne Angabe von Produkt und Version, die nicht aufhalten; Sitzungen über den Dienst erkennt der Dienst am Kopf `X-Wkl-Application`. drift
nennt Objekte, die seit dem letzten Paket außerhalb eines Pakets geändert wurden, je Eintrag mit
object (Name mit Schema), type (Art des Objekts, etwa table oder view) und change
(changed, missing; dateChanged ist bei einem verschlüsselten Modul nur ein Hinweis und hält
kein Paket auf).
update-history. Rumpf { "database": "demo-prod", "skip": 0, "take": 20 } (take 1 bis 100,
Vorgabe 20). Abgeschlossene Pakete (done, failed, refused, dismissed), neueste zuerst, mit
id, file, schema, kind, status, reason, message, receivedUtc, confirmedBy,
confirmedUtc, sha256 und steps: je Schritt phase (predeploy, pre, model, post,
postdeploy, baseline), version, path, sha256, appliedUtc, appliedBy, durationMs,
result (ok, failed, rolledback) und message.
Entscheidungen. Rumpf { "database": "demo-prod", "package": 41 }; package ist die Nummer
(id) aus Status oder Verlauf.
| Befehl | Für Status | Wirkung |
|---|---|---|
confirm-update | waiting | expand: läuft sofort. contract: wird geprüft und angekündigt (Hinweis an die Anwendungen, Frist nach dem Vorlauf des Pakets oder noticeMinutes) und läuft zur Frist, sobald keine ältere Anwendung mehr offen ist. Als Bestätigende steht gateway:<Name> (<Kennung>) in der Datenbank |
dismiss-update | failed, refused | Wird dismissed und verschwindet aus dem Status; die Datei bleibt im Ordner failed |
retry-update | failed, refused | Nimmt die unveränderte Datei aus failed erneut, mit allen Prüfungen, als neues Paket; das alte wird dismissed. Ein geändertes Paket braucht eine neue Datei |
move-update-deadline | announced | Rumpf zusätzlich "deadlineUtc": "2026-11-03T06:00:00Z"; nicht in der Vergangenheit. Anwendungen lesen die neue Frist bei ihrer nächsten Abfrage |
cancel-update-notice | announced | Die Ankündigung wird abgebrochen, das Paket wartet wieder (reason 7) und braucht eine neue Bestätigung, auch bei contract = automatic |
upload-update. POST /admin/v1/upload-update?database=demo-prod&file=Demo-schema-14.zip,
Rumpf = die ZIP-Datei, höchstens 64 MiB. Der Dienst legt sie in den Eingang und nimmt sie sofort an,
wie eine dort abgelegte Datei. Der Dateiname ist ein reiner Name ohne Pfad.
Antwort einer Entscheidung oder eines Hochladens. message beschreibt, was geschah; package
nennt, was aus dem Paket wurde, wenn es lief oder angekündigt wurde (sonst null):
{ "message": "Angekündigt bis 2026-11-02 18:00:00Z.",
"package": { "file": "Demo-schema-14.zip", "exitCode": 0, "outcome": "announced", "message": "Angekündigt bis 2026-11-02 18:00:00Z." } }
outcome ist done (eingespielt, Datei im Archiv und in done), announced, waiting (wartet
weiter, etwa auf Bestätigung oder auf Sitzungen) oder failed (abgelehnt oder gescheitert, Datei in
failed). exitCode und reason verwenden dieselben Codes:
| Code | Bedeutung |
|---|---|
0 | In Ordnung |
2 | Paket ungültig oder nicht vertrauenswürdig (Signatur, Prüfsumme, Herausgeber, Produkt, Art) |
3 | Datenbank passt nicht: höhere Schemaversion, oder eigene Objekte ohne Verlauf |
4 | Ein Skript oder der Bereitstellungsplan ist gescheitert; alles zurückgenommen |
5 | Sicherung misslungen |
6 | Sitzungen älterer Anwendungen noch offen |
7 | Wartet auf Bestätigung; die Meldung nennt den Grund |
10 | Ein anderer Vorgang spielt gerade ein Paket dieses Produkts ein |
11 | Drift: ein Objekt wurde außerhalb eines Pakets geändert |
12 | Migrationen passen nicht zum Verlauf (Prüfsumme, fehlende oder zu alte Migration) |
13 | Der Plan kann zu Datenverlust führen |
| Antwort | Bedeutung |
|---|---|
404 | Keine Datenbank mit dieser id, oder kein Paket mit dieser Nummer |
409 | Das Paket hat einen anderen Status, als der Befehl braucht; beim Wiederholen fehlt die Datei in failed oder wurde geändert; beim Hochladen wartet dasselbe Paket schon |
400 | Pflichtfeld fehlt, Dateiname mit Pfad, Frist in der Vergangenheit |
413 | Datei größer als 64 MiB |
503 | Das Modul spielt gerade ein Paket ein (nach kurzer Zeit erneut versuchen), oder die Datenbank ist nicht erreichbar |
Viele Vorgänge auf einmal
Einen Sammelweg gibt es nicht: weder OData-$batch noch einen Befehl für mehrere Personen oder
Schlüssel. Jede Person, jede Zuordnung, jeder Schlüssel ist eine eigene Anfrage.
| Frage | Verhalten |
|---|---|
| Wie viele Anfragen nacheinander | Die Verwaltung zählt wie jede andere Anfrage gegen die Ratenbegrenzung des Aufrufers: Gateway:Security:RateLimit:PerCaller (Vorgabe 300) je RateLimit:WindowSeconds (Vorgabe 60), siehe Konfiguration. Darüber antwortet der Dienst mit 429; nach Ablauf des Zeitraums geht es weiter. Eine eigene Grenze nur für die Verwaltung gibt es nicht |
| Teilfehler | Jede Anfrage ist eine eigene Transaktion. Scheitert die 300. Anfrage, bleiben die 299 davor gespeichert; nichts wird zurückgenommen. Der Aufrufer setzt bei der gescheiterten fort |
| Wiederholen nach einem Verbindungsabbruch | Mit der Kopfzeile Idempotency-Key auch hier: Sie wirkt bei jeder Methode außer GET und HEAD, also auch beim Anlegen über OData und bei den Befehlen. Regeln und Aufbewahrung (10 Minuten, nur im Arbeitsspeicher, 401/429/5xx nicht gespeichert) beschreibt Wiederholte Schreibanfragen |
| Wiederholen nach einem Neustart oder nach mehr als 10 Minuten | Ohne gespeicherte Antwort. Vorher nachlesen, ob der Satz schon da ist: eine zweite Person mit gleichem Namen weist der Dienst ab, ein zweiter Schlüssel dagegen entstünde wirklich |
Rechenbeispiel: 847 Personen mit je Anlegen, Regelzuordnung und Schlüssel sind gut 2 500
Anfragen, mit der Vorgabe also mindestens neun Minuten. Wer schneller sein muss, hebt PerCaller
für die Dauer der Einrichtung an.
Der Aufwand lässt sich oft vermeiden. Personen braucht nur, wer persönliche Schlüssel, einen
zweiten Faktor oder eine eigene Vorgabe-Identität bekommt. Rechte und Anmelderegeln lassen sich
einer Windows-Gruppe als Ganzem zuordnen (WindowsAccounts, IdentityAssignments,
Merkmal windowsGroups der Anmelderegeln), ohne ihre Mitglieder einzeln einzutragen. Ein zweiter
Faktor gehört dagegen immer einer Person: Verlangt eine Regel ihn für eine Gruppe, wird jedes
Mitglied ohne Personeneintrag mit 403 abgewiesen
(siehe Anmeldung).
Mitglieder einer Windows-Gruppe
Der Dienst liefert keine Mitgliederlisten. Er fragt das Active Directory nie ab: Welche Gruppen ein
Aufrufer hat, entnimmt er bei der Anmeldung dessen Windows-Anmeldung, und Namen löst er beim
Speichern nur zur SID auf. Wer alle Mitglieder einer Gruppe als Personen anlegen will, liest sie
selbst aus, am Arbeitsplatz oder in einem Skript (etwa Get-ADGroupMember bzw. net group /domain),
und legt dann je Mitglied eine Person mit WindowsAccount an. Spätere Ein- und Austritte in der
Gruppe übernimmt der Dienst dabei nicht von selbst; Rechte, die über die Gruppe selbst vergeben
sind, folgen der Mitgliedschaft dagegen ohne Nachpflege.
Siehe auch
- „Administrator-Handbuch für WKL Database Gateway Manager“ (Dokumentation der Verwaltungsanwendung)