Zum Hauptinhalt springen
Dieses Dokument erklärt, wie failproofai intern arbeitet: wie das Hook-System Agent-Tool-Aufrufe abfängt, wie die Konfiguration geladen und zusammengeführt wird, wie Richtlinien ausgewertet werden und wie das Dashboard die Agentenaktivität überwacht.

Übersicht

failproofai besteht aus zwei unabhängigen Subsystemen:
  1. Hook-Handler – Ein schneller CLI-Subprozess, den Claude Code bei jedem Agent-Tool-Aufruf aufruft. Er wertet Richtlinien aus und gibt eine Entscheidung zurück.
  2. Agent Monitor (Dashboard) – Eine Next.js-Webanwendung zur Überwachung von Agentensitzungen und zur Verwaltung von Richtlinien.
Beide Subsysteme teilen Konfigurationsdateien in ~/.failproofai/ und im .failproofai/-Verzeichnis des Projekts, laufen aber als separate Prozesse und kommunizieren ausschließlich über das Dateisystem.

Hook-Handler

Integration mit Claude Code

Wenn Sie failproofai policies --install ausführen, werden folgende Einträge in ~/.claude/settings.json geschrieben:
Claude Code ruft dann failproofai --hook PreToolUse vor jedem Tool-Aufruf als Subprozess auf und übergibt eine JSON-Nutzlast über stdin.

Nutzlastformat

Bei PostToolUse-Ereignissen enthält die Nutzlast zusätzlich tool_result mit der Ausgabe des Tools. Der Handler begrenzt die stdin-Eingabe auf 1 MB. Nutzlasten, die diesen Wert überschreiten, werden verworfen, und alle Richtlinien erlauben die Ausführung implizit.

Antwortformat

Ablehnen (PreToolUse):
Ablehnen (PostToolUse):
Anweisen (jedes Ereignis außer Stop):
Stop-Ereignis anweisen:
  • Exit-Code: 2
  • Begründung wird in stderr (nicht stdout) geschrieben
Erlauben:
  • Exit-Code: 0
  • Leere stdout-Ausgabe
Erlauben mit Nachricht: allow(message) ermöglicht es einer Richtlinie, informativen Kontext an Claude zurückzusenden, auch wenn die Operation erlaubt ist. Der Hook-Handler schreibt das folgende JSON auf stdout (keine Konfigurationsdatei – dies ist die Antwort des Handlers an Claude Code, genau wie Ablehnen- und Anweisen-Antworten oben):
  • Exit-Code: 0 (die Operation wird erlaubt)
  • Wenn mehrere Richtlinien allow mit einer Nachricht zurückgeben, werden ihre Nachrichten mit Zeilenumbrüchen zu einem einzigen additionalContext-String zusammengefügt
  • Wenn keine Richtlinie eine Nachricht liefert, ist stdout leer (wie bisher)

Verarbeitungspipeline

src/hooks/handler.ts implementiert die vollständige Pipeline:
Der gesamte Prozess läuft bei typischen Nutzlasten ohne LLM-Aufrufe in unter 100 ms ab.

Konfigurationsladung

src/hooks/hooks-config.ts implementiert das Laden der Konfiguration aus drei Bereichen.
Zusammenführungslogik:
  • enabledPolicies – deduplizierte Vereinigung aus allen drei Dateien
  • policyParams – pro Richtlinien-Schlüssel gewinnt die erste Datei, die ihn definiert, vollständig
  • customPoliciesPath – die erste Datei, die diesen Wert definiert, gewinnt
  • llm – die erste Datei, die diesen Wert definiert, gewinnt
Das Web-Dashboard verwendet readHooksConfig() (nur global) zum Lesen und Schreiben, da es nicht mit einem Projekt-cwd aufgerufen wird.

Richtlinienauswertung

src/hooks/policy-evaluator.ts führt Richtlinien der Reihe nach aus. Für jede Richtlinie:
  1. Das params-Schema der Richtlinie nachschlagen (falls vorhanden).
  2. policyParams[policy.name] aus der zusammengeführten Konfiguration lesen.
  3. Benutzerdefinierte Werte über die Schema-Standardwerte zusammenführen, um ctx.params zu erzeugen.
  4. policy.fn(ctx) mit dem aufgelösten Kontext aufrufen.
  5. Wenn das Ergebnis deny ist, sofort abbrechen und diese Entscheidung zurückgeben.
  6. Wenn das Ergebnis instruct ist, die Nachricht sammeln und fortfahren.
  7. Wenn das Ergebnis allow ist, zur nächsten Richtlinie übergehen.
Nachdem alle Richtlinien ausgeführt wurden:
  • Falls ein deny zurückgegeben wurde, die Ablehnungsantwort ausgeben.
  • Falls instruct-Rückgaben gesammelt wurden, eine einzelne Anweisungsantwort mit allen zusammengefügten Nachrichten ausgeben.
  • Andernfalls eine Erlauben-Antwort ausgeben (leere stdout-Ausgabe, Exit 0).

Integrierte Richtlinien

src/hooks/builtin-policies.ts definiert alle 39 integrierten Richtlinien als BuiltinPolicyDefinition-Objekte:
Richtlinien, die params akzeptieren, deklarieren ein PolicyParamsSchema mit Typen und Standardwerten für jeden Parameter. Der Richtlinien-Evaluator injiziert aufgelöste Werte in ctx.params, bevor er fn aufruft. Richtlinienfunktionen lesen ctx.params ohne Null-Prüfung, da Standardwerte immer zuerst angewendet werden. Musterabgleiche innerhalb von Richtlinien verwenden geparste Befehls-Tokens (argv) statt einfachem String-Matching. Dadurch werden Umgehungen durch Shell-Operator-Injection verhindert (z. B. kann ein Muster für sudo systemctl status * nicht durch Anhängen von ; rm -rf / an den Befehl umgangen werden).

Benutzerdefinierte Richtlinien

src/hooks/custom-hooks-registry.ts implementiert eine globalThis-basierte Registrierung:
src/hooks/custom-hooks-loader.ts lädt die Richtliniendatei des Benutzers:
  1. customPoliciesPath aus der Konfiguration lesen; überspringen, falls nicht vorhanden.
  2. Auf absoluten Pfad auflösen; prüfen, ob die Datei existiert.
  3. Alle from "failproofai"-Importe auf den tatsächlichen dist-Pfad umschreiben, sodass customPolicies auf dieselbe globalThis-Registrierung zeigt.
  4. Transitive lokale Importe rekursiv umschreiben, um ESM-Kompatibilität sicherzustellen.
  5. Temporäre .mjs-Dateien schreiben und die Einstiegsdatei per import() laden.
  6. getCustomHooks() aufrufen, um registrierte Hooks abzurufen.
  7. Alle temporären Dateien in einem finally-Block bereinigen.
Bei jedem Fehler (Datei nicht gefunden, Syntaxfehler, Import-Fehler) wird der Fehler in ~/.failproofai/hook.log protokolliert und der Loader gibt ein leeres Array zurück. Integrierte Richtlinien sind davon nicht betroffen. Benutzerdefinierte Richtlinien werden nach allen integrierten Richtlinien ausgewertet. Ein deny einer benutzerdefinierten Richtlinie unterbricht zwar weitere benutzerdefinierte Richtlinien (alle integrierten wurden zu diesem Zeitpunkt jedoch bereits ausgeführt).

Aktivitätsprotokollierung

Nach jedem Hook-Ereignis fügt der Handler eine JSONL-Zeile an ~/.failproofai/hook-activity.jsonl an:
Pro Richtlinie, die eine Nicht-Erlauben-Entscheidung getroffen hat, wird eine Zeile geschrieben. Erlauben-Entscheidungen werden nicht protokolliert (um die Dateigröße gering zu halten).

Dashboard-Architektur

Das Dashboard ist eine Next.js 16-Anwendung, die den App Router mit React Server Components und Server Actions verwendet.
Datenfluss:
  • Seitenkomponenten rufen lib/projects.ts und lib/log-entries.ts auf, um Projekt-/Sitzungsdaten direkt aus dem Dateisystem zu lesen (keine API-Schicht für Lesevorgänge).
  • Die Policies-Seite verwendet Server Actions für alle Mutationen (Umschalten, Parameteraktualisierung, Installieren/Entfernen).
  • Der Sitzungs-Viewer parst das JSONL-Transkriptformat von Claude und rendert eine Zeitleiste mit Nachrichten und Tool-Aufrufen.
Wichtige Designentscheidungen:
  • Keine Datenbank – alle persistenten Zustände liegen in einfachen Dateien (~/.failproofai/, ~/.claude/projects/).
  • Server Actions für Mutationen – keine REST-API für CRUD-Operationen erforderlich.
  • React Server Components für Leseseiten – schnelleres initiales Laden, kein Client-Bundle für das Datenabrufen.
  • Client-Komponenten nur dort, wo Interaktivität benötigt wird (Richtlinien-Umschalter, Aktivitätssuche, Log-Viewer).

Dateistruktur