Ihr Prüfer bittet um Nachweise darüber, was Ihre KI-Agenten im letzten Quartal getan haben. Sie übergeben ihnen einen CSV-Export des Audit-Protokolls. Sie stellen eine Frage: „Können Sie beweisen, dass dies nicht nachträglich bearbeitet wurde?“ Die meisten Teams können das nicht. Das Protokoll befindet sich in einer veränderbaren Datenbank, die von demselben System exportiert wird, das es geschrieben hat. Der Prüfer muss die gesamte Spur des Glaubens verfolgen, was genau die Eigenschaft ist, die ihn zu keinem Beweis macht.
In diesem Beitrag wird erklärt, wie die Plattform Prüfungsbeweise erstellt, die ohne Glauben Bestand haben. In einem vorherigen Beitrag wurde erläutert, warum die Identität pro Agent und ein manipulationssicheres Ledger für Claude Code- und MCP-Bereitstellungen wichtig sind. Dieser geht tiefer auf die drei Eigenschaften ein, die die Beweise unabhängig überprüfbar machen: Hash-Verkettung, kryptografische Signierung pro Ereignis und Export in ein maschinenlesbares Compliance-Format, das die Tools eines Prüfers bereits verstehen.
Die Hash-Kette: Jedes Ereignis verpflichtet sich zu allen vorherigen Ereignissen
Das Ledger kann nur angehängt werden und ist pro Mandant mit einer Hash-Kette versehen. Jedes Ereignis trägt den SHA-256-Hash des vorherigen Ereignisses in seinem prev_hash-Feld. Der Ketten-Hash von Ereignis N wird über ein kanonisches binäres Vorbild berechnet, das Folgendes umfasst: Die eigenen Felder von Ereignis N (Mandant, Sequenznummer, Zeitstempel, Akteur, Aktion, Ziel, Metadaten-Digest, Nutzlast-Hash), verkettet mit dem prev_hash von Ereignis N-1. Bei Sequenz 1 besteht prev_hash ausschließlich aus Nullen – dem Genesis-Anker.
Die kritische Eigenschaft: Das Ändern, Einfügen oder Löschen eines Ereignisses in der Mitte der Kette ändert seinen Hash, wodurch die prev_hash-Verknüpfung des nächsten Ereignisses unterbrochen wird, wodurch die Verknüpfung des nächsten Ereignisses unterbrochen wird, und so weiter bis zur Spitze. Eine einzelne Bearbeitung lässt sich erkennen, indem man die Kette durchläuft und jeden Hash neu berechnet.
Das Vorbild ist eine feste Binärkodierung mit vorangestellter Länge und einem versionierten Domänentrennzeichen – nicht JSON. Dies ist eine bewusste Entscheidung gegen drei Angriffsflächen, die ein JSON-basierter Hash offenlassen würde:
- Schlüsselreihenfolge: Die Reihenfolge der JSON-Objektschlüssel wird von den meisten Serialisierern nicht garantiert. Eine andere Schlüsselreihenfolge erzeugt selbst für semantisch identische Daten einen anderen Hash, was zu falschen Kettenunterbrechungen führt – oder, schlimmer noch, dazu führt, dass ein Angreifer die Schlüssel neu anordnen kann, um einen passenden Hash zu fälschen.
- Leerzeichen und Zahlenformatierung:
{"seq": 1}und{"seq":1}und{"seq": 1.0}sind semantisch äquivalente JSON-Dateien, erzeugen jedoch unterschiedliche SHA-256-Digests. - Verkettungsfälschung: Ohne Längenpräfixe können zwei benachbarte kurze Felder zusammengeführt werden, um ein drittes langes Feld mit demselben Hash zu fälschen. Dies wird durch das Voranstellen der Länge jedes Felds mit seiner Byteanzahl (4-Byte-Big-Endian) abgeschlossen.
Die Implementierung in core/internal/store/canon/canon.go ist die einzige Quelle der Wahrheit. Sowohl Append (Schreiben) als auch Verify (Rücklesen) rufen dieselbe EventHash-Funktion auf. Es gibt keine zweite Implementierung zum Driften:
func EventHash(e Event) []byte {
var buf []byte
buf = lps(buf, domainEvent) // "olivares.audit.v1"
buf = lps(buf, e.TenantID)
var seq [8]byte
binary.BigEndian.PutUint64(seq[:], uint64(e.Seq))
buf = append(buf, seq[:]...)
buf = lps(buf, e.OccurredAt)
buf = lps(buf, e.Actor)
buf = lps(buf, e.ActorKind)
buf = lps(buf, e.Action)
buf = lps(buf, e.TargetKind)
buf = lps(buf, e.TargetID)
buf = append(buf, fixed(e.MetaDigest)...)
buf = append(buf, fixed(e.PayloadHash)...)
buf = append(buf, fixed(e.PrevHash)...)
sum := sha256.Sum256(buf)
return sum[:]
}
Der Domänentrenner "olivares.audit.v1" bindet den Hash an seinen Zweck und seine Version. Ein Hash aus einer anderen Domäne (einem Prüfpunkt, einer Nutzlast, einem Metadaten-Digest) kann niemals mit einem Ereignis-Hash kollidieren, selbst wenn die Rohbytes zufällig übereinstimmen.
Ed25519 Signierung: Jedes Ereignis ist sein eigener Anker
Eine Hash-Kette beweist interne Konsistenz, aber nicht Authentizität. Ein Angreifer mit unformatiertem Datenbank-Schreibzugriff könnte die gesamte Kette mit veränderten Ereignissen von Grund auf neu berechnen und eine gültige Kette erstellen – anders als das Original, aber intern konsistent. Hash-Ketten erkennen Manipulationen; Sie beweisen nicht die Herkunft.
Ereignisbezogene Ed25519-Signaturen schließen diese Lücke. Jedes an das Ledger angehängte Ereignis wird zum Zeitpunkt des Schreibens signiert. Die Signatur umfasst ein domänengetrenntes Vorabbild des Mandanten, der Sequenznummer und des Ketten-Hashs des Ereignisses:
domain ("olivares.audit.event.v1") || tenant || seq (8 bytes, big-endian) || hash
Die Signatur wird für das Ereignis gespeichert, ist jedoch konstruktionsbedingt vom Chain-Hash-Preimage ausgeschlossen. Das ist kein Zufall: Wäre die Signatur im Hash enthalten, würde das Signieren eines Ereignisses den Hash ändern, den es bezeugen soll. Die Signatur beglaubigt den Hash, ohne ihn zu verändern.
Ein externer Verifizierer, der nur über den öffentlichen Schlüssel verfügt, kann jedes Ereignis einzeln bestätigen: Er berechnet den Ketten-Hash aus den Feldern des Ereignisses neu, rekonstruiert das Vorbild und überprüft die Ed25519-Signatur. Wenn ein Ereignis nach dem Signieren geändert wurde, schlägt die Signaturprüfung für dieses bestimmte Ereignis fehl – der Prüfer muss dem System, das den Beweis erbracht hat, nicht vertrauen.
Die Plattform unterstützt auch die Schlüsselrotation. Eine Kette, deren Signaturschlüssel sich mitten im Leben geändert hat, führt eine End-to-End-Verifizierung durch, indem sie den aktuellen Schlüssel und die öffentlichen Schlüssel der vorherigen Generationen anheftet. Die Verifizierungsfunktion akzeptiert eine Reihe von Kandidatenschlüsseln und betrachtet ein Ereignis als gültig, wenn ein Kandidat es überprüft.
Zusätzlich zu den Signaturen pro Ereignis beglaubigen regelmäßige Kontrollpunkte die Kettenspitze unter einer separaten Signaturdomäne (olivares.audit.checkpoint.v1). Für Organisationen, die sich gegen Kompromittierungen auf Host-Ebene (nicht nur auf Datenbankebene) verteidigen müssen, können Prüfpunkte mit einem Off-Box-Schlüssel KMS/HSM (AWS KMS, GCP Cloud KMS, Azure Key Vault) signiert werden, wobei der private Schlüssel niemals auf dem Host gespeichert ist. Ereignisspezifische Signaturen behandeln den Nur-DB-Angreifer; Off-Box-Checkpoints handhaben den Host-Kompromiss-Angreifer. Die beiden Bedrohungsmodelle sind unterschiedlich; Keine der Unterschriften allein deckt beides ab.
Der Ledgervertrag: in derselben Transaktion besiegelt
Ein häufiger Fehler in Prüfsystemen ist die eventuelle Konsistenz zwischen der Statusmutation und dem Prüfdatensatz. Der Status ändert sich, der Audit-Schreibvorgang wird in die Warteschlange gestellt oder gestapelt, und wenn der Audit-Schreibvorgang fehlschlägt, wurde die Statusänderung bereits festgeschrieben. Das Ergebnis: ungeprüfte Mutationen, die im System existieren, aber nicht in den Beweisen.
Die Plattform erzwingt einen stärkeren Vertrag. Sowohl die Zustandsänderung als auch die Ledgerversiegelung erfolgen in derselben Datenbanktransaktion. Wenn das Siegel fehlschlägt, wird der gesamte Übergang rückgängig gemacht – die Zustandsmutation wird nie festgeschrieben. Dies ist kein Best-Effort; es ist deny-closed.
Das Session-Laufzeit-Ledger veranschaulicht dies. Wenn eine Claude Code-Sitzung den Status wechselt (erstellt, gestartet, gestoppt, gestoppt, fehlgeschlagen), zeichnet appendRunEvent den Übergang atomar an zwei Stellen auf:
- Das globale Hash-verkettete Audit-Ledger über
sc.Audit().Append– die manipulationssichere Kette, die durch einenPayloadHashverankert ist. - Das pro Sitzung abfragbare Ledger – eine Nur-Anhänge-Projektion, die über
audit_seqmit der globalen Kette verknüpft ist.
Beide Schreibvorgänge erfolgen innerhalb der Mutate-Transaktion des Aufrufers. Der Codekommentar in runtime_ledger.go gibt die Entwurfsabsicht direkt an: „Das Ledger ist das Aufzeichnungssystem. Wenn also das Siegel fehlschlägt, wird der gesamte Übergang zurückgesetzt – es handelt sich NICHT um eine Best-Effort-Methode.“
Der PayloadHash selbst verpflichtet sich nur zu kanonischen, nicht sensiblen Übergangsfakten – der Laufreferenz, der Sequenz, dem Ereignistyp, dem Zustandsübergang und dem Zeitstempel. Es enthält niemals Transkriptinhalte, Eingabeaufforderungen, Umgebungswerte oder Geheimnisse. Das Ledger beweist, was passiert ist; Es speichert nicht, was gesagt wurde.
Das gleiche Muster gilt für Arbeitsbereichsdateimutationen in workspace_ledger.go. Das Schreiben, Markieren, Verschieben oder Löschen einer Datei wird versiegelt, bevor der Dateisystemvorgang ausgeführt wird. Wenn die Beweise nicht angehängt werden können, wird die Mutation nicht ausgeführt. Das Siegel trägt den Operationstyp, den Pfad und einen SHA-256 des geschriebenen Inhalts – niemals die Inhaltsbytes selbst.
OSCAL-Export: maschinenlesbare Beweise, die von den Werkzeugen Ihres Prüfers aufgenommen werden
Ein manipulationssicheres Ledger ist für einen Wirtschaftsprüfer notwendig, aber nicht ausreichend. Wenn die Beweise in einem proprietären Format vorliegen, ist der Prüfer bei der Interpretation immer noch auf Ihre Werkzeuge angewiesen. OSCAL – die von NIST verwaltete Open Security Controls Assessment Language – ist das Format, das diese Lücke schließt.
Die Plattform exportiert versiegelte Beweispakete als OSCAL-Paket mit drei Modellen:
- Komponentendefinition: Die Fähigkeiten der Kontrollebene, ausgedrückt als implementierte Anforderungen anhand eines Compliance-Frameworks (NIST SP 800-53, ISO 27001, EU AI Act und andere). Jede implementierte Anforderung trägt die Kontroll-ID, die Fähigkeitsschlüssel, die sie belegen, und den tatsächlichen Status als benutzerdefinierte Eigenschaft.
- Bewertungsergebnisse: Befunde pro Kontrolle mit einem OSCAL-konformen Status. Das Ziel jedes Ergebnisses trägt
satisfiedodernot-satisfied, wobei der genaue Produktstatus im Feld „Grund“ gespeichert ist. - Steuerungszuordnung: ein Übergang von Framework-Steuerungen zum Fähigkeitsreferenzmodell der Plattform unter Verwendung des Steuerungszuordnungsmodells OSCAL 1.2.0. Die Beziehung ist immer
intersects-with– die Fähigkeiten adressieren einen Teil eines Steuerelements. Es behauptet niemals Konformität; Diese Behauptung lebt nur in Bewertungsergebnissen, die auf tatsächlichen betrieblichen Beweisen basieren.
Es lohnt sich, die Ehrlichkeitsbeschränkung beim OSCAL-Export ausdrücklich zu erwähnen. Die Ergebnisstatus-Enumeration von OSCAL hat genau zwei Werte: satisfied und not-satisfied. Es gibt kein „teilweise“ oder „beabsichtigt“. Ein Steuerelement, das teilweise implementiert, entwurfsbedingt adressiert, mit Lücken versehen oder nicht zugeordnet ist, ist OSCAL not-satisfied zugeordnet, wobei der tatsächliche Produktstatus in status.reason und eine benutzerdefinierte Eigenschaft unter dem plattformeigenen Namensraum (https://olivares.ai/ns/oscal) gespeichert ist. Der Export führt niemals eine teilweise erfüllte Kontrolle in satisfied durch. Nur Kontrollen, die durch Live-Betriebsnachweise zum Zeitpunkt der Versiegelung gestützt werden, erhalten OSCAL satisfied.
Jedes OSCAL-Dokument trägt Ledger-Ankereigenschaften: den Manifest-Hash, die Ledger-Sequenznummer zum Zeitpunkt der Versiegelung, den Ledger-Hash und das Ergebnis der Integritätsüberprüfung. Diese bilden die Brücke zwischen dem OSCAL-Dokument, das der Prüfer in seinem GRC-Tool liest, und der zugrunde liegenden Manipulationssicherheitskette, die er unabhängig überprüfen kann.
Was Offline-Verifizierung konkret bedeutet
„Offline-Verifizierung“ ist keine Marketingphrase. Es beschreibt ein spezifisches technisches Verfahren: Der Verifizierer nimmt die exportierten Beweise, führt ein Verifizierungstool auf einer Air-Gap-Maschine aus und bestätigt die Integrität der Beweise ohne Netzwerkzugriff auf das System, das sie erstellt hat.
Der Archivexport der Plattform schreibt einen Verzeichnisbaum aus JSONL-Segmenten (eine Zeile pro Ereignis, kanonischer JSON) sowie ein Manifest pro Segment. Das Manifest zeichnet den Sequenzbereich des Segments, die Ereignisanzahl, die ersten und letzten Ketten-Hashes, einen SHA-256 der Ereignisdatei und den letzten Hash des vorherigen Segments für segmentübergreifende Kontinuität auf.
Der Offline-Verifizierer (VerifyArchiveDir) führt dann Folgendes aus, vollständig im konstanten Speicher und ohne Netzwerkaufrufe:
- Laden Sie Manifeste und koppeln Sie sie mit Ereignisdateien. Eine vereinzelte Ereignisdatei (kein Manifest) oder ein Manifest, dessen Ereignisdatei fehlt, ist ein Fehler. Die Beweiseinheit ist das Paar.
- Streamen Sie jede Ereignisdatei Zeile für Zeile. Leiten Sie für jedes Ereignis den Ketten-Hash aus den archivierten Feldern erneut ab, indem Sie dieselbe
EventHash-Funktion verwenden, die das Live-System verwendet. Vergleichen Sie es mit dem gespeicherten Hash. Überprüfen Sie dieprev_hash-Verknüpfung und die Lückenfreiheit der Sequenz. - Überprüfen Sie die Kanonizität. Stellen Sie jede analysierte Zeile erneut zusammen und bestätigen Sie, dass sie eine byteidentische Ausgabe für die Bytes auf der Festplatte erzeugt. Dies verhindert den Schmuggel unbekannter Felder oder Angriffe mit doppelten Schlüsseln, die zwar eine Hash-Prüfung bestehen, aber verborgene Daten enthalten.
- Überprüfen Sie die Ed25519-Signaturen pro Ereignis. Rekonstruieren Sie für jedes Nicht-Checkpoint-Ereignis das Signatur-Vorbild und überprüfen Sie es anhand der angehefteten öffentlichen Schlüssel.
- Checkpoint-Signaturen überprüfen. Überprüfen Sie für jedes Checkpoint-Ereignis die Signatur unter der Checkpoint-Domäne anhand der angehefteten Checkpoint-Schlüssel. Wenn nur Ereignisschlüssel angeheftet sind (kein Prüfpunktschlüssel), wird ein Prüfpunktereignis als „nicht überprüfbar“ markiert – verweigert-geschlossen, nicht überspringbar.
- Überprüfen Sie die segmentübergreifende Kontinuität. Stellen Sie sicher, dass die erste Sequenz jedes Segments der letzten Sequenz des vorherigen Segments plus eins folgt und dass der letzte Hash des vorherigen Segments mit dem
prev_segment_last_hashdes aktuellen Segments übereinstimmt. - Überprüfen Sie den Digest der Ereignisdatei. Der SHA-256 der während des Streamings berechneten Ereignisdatei muss mit dem
events_sha256des Manifests übereinstimmen.
Der Prüfer meldet die erste gefundene Inkonsistenz mit der spezifischen Sequenznummer und einem maschinenlesbaren Grund: hash-mismatch, prev-mismatch, seq-gap, event-sig-invalid, event-sig-missing, checkpoint-sig-invalid, count-mismatch, events-sha256-mismatch oder segment-link-mismatch.
Eine ehrliche Einschränkung: Der Offline-Verifizierer attestiert genau den Bereich, den er überprüft hat, und nichts darüber hinaus. Ein entferntes Präfix oder Ende ist offline nicht erkennbar – das Verzeichnis gibt nicht an, wo die Kette begann oder endete. Der Überprüfungsbericht enthält ein mandantenspezifisches Ranges-Feld mit einem StartsMidChain-Flag, sodass der Prüfer genau weiß, was attestiert wurde. Die in der Kette signierten Kontrollpunkte des Live-Systems decken den Schwanz ab; Der Offline-Export deckt den archivierten Bereich ab. Zusammen bilden sie die vollständige Bescheinigung.
| Schicht | Was es beweist | Was es nicht beweist |
|---|---|---|
| Hash-Kette | Interne Konsistenz; Jede Bearbeitung unterbricht die Kette | Herkunft (Wer hat die Ereignisse geschrieben) |
| Pro Ereignis Ed25519 | Herkunft; Jede Veranstaltung wurde vom Schlüsselinhaber unterzeichnet | Abwehr von Kompromissen auf Host-Ebene |
| Off-Box-Kontrollpunkt | Host-Kompromittierungsresistenz (KMS/HSM-Schlüssel nie auf dem Host) | Granularität pro Ereignis (deckt nur Prüfpunkte ab) |
| OSCAL-Export | Maschinenlesbarer, Framework-abgebildeter Compliance-Nachweis | Dass jede Kontrolle vollständig erfüllt ist (nur Live-Beweise zählen) |
| Archivüberprüfung | Offline-Neuableitung aller oben genannten Punkte | Ereignisse vor oder nach dem exportierten Bereich |
Der Codepfad: von der Zustandsmutation zum versiegelten Beweis
Der Ablauf von einer Claude Code-Sitzungsstatusänderung bis hin zu überprüfbaren Beweisen umfasst drei Ebenen. In runtime_ledger.go erstellt die Funktion appendRunEvent einen PayloadHash, indem SHA-256 die kanonischen Übergangsfelder mit Längenpräfix hasht:
func runEventPayloadHash(runRef string, seq int64, event, from, to, detail, atTS string) [32]byte {
h := sha256.New()
for _, part := range []string{
runRef, strconv.FormatInt(seq, 10), event, from, to, detail, atTS,
} {
_, _ = h.Write([]byte(strconv.Itoa(len(part))))
_, _ = h.Write([]byte{':'})
_, _ = h.Write([]byte(part))
}
var sum [32]byte
copy(sum[:], h.Sum(nil))
return sum
}
Dieser Hash wird dann an sc.Audit().Append übergeben, der die nächste Sequenznummer pro Mandant zuweist, eine Verknüpfung zum Hash des vorherigen Ereignisses herstellt, den Ketten-Hash dieses Ereignisses über EventHash berechnet, ihn mit Ed25519 signiert und das versiegelte Ereignis einfügt – alles innerhalb der Transaktion des Aufrufers.
Das Ergebnis: Wenn die Transaktion festgeschrieben wird, bleiben die Statusänderung der Sitzung und ihr manipulationssicherer Prüfdatensatz entweder bestehen oder werden zurückgesetzt. Es gibt kein Fenster, in dem sich der Zustand geändert hat, die Beweise jedoch nicht.
Links
- Produkt: Audit – der Audit-Trail und der Beweisexport
- Produkt: Compliance – Rahmenbewertungen und OSCAL
- Sicherheitsmodell – das Vertrauensmodell, das dem Ledger zugrunde liegt