Skip to main content
Dieses Dokument erklärt, wie failproofai intern funktioniert: wie das Hook-System Agenten-Tool-Aufrufe abfängt, wie die Konfiguration geladen und zusammengeführt wird, wie Richtlinien ausgewertet werden und wie das Dashboard die Agenten-Aktivität überwacht.

Überblick

failproofai besteht aus zwei unabhängigen Subsystemen:
  1. Hook-Handler – Ein schneller CLI-Subprozess, den Claude Code bei jedem Agenten-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 Verwaltung von Richtlinien.
Beide Subsysteme teilen sich Konfigurationsdateien in ~/.failproofai/ und im .failproofai/-Verzeichnis des Projekts, laufen jedoch als separate Prozesse und kommunizieren ausschließlich über das Dateisystem.

Hook-Handler

Integration mit Claude Code

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

Nutzlastformat

Bei PostToolUse-Ereignissen enthält die Nutzlast zusätzlich tool_result mit der Ausgabe des Tools. Der Handler erzwingt ein stdin-Limit von 1 MB. Nutzlasten, die dieses überschreiten, werden verworfen, und alle Richtlinien erlauben implizit.

Antwortformat

Verweigern (PreToolUse):
Verweigern (PostToolUse):
Anweisen (beliebiges Ereignis außer Stop):
Stop-Ereignis instruct:
  • Exit-Code: 2
  • Begründung wird in stderr geschrieben (nicht stdout)
Erlauben:
  • Exit-Code: 0
  • Leeres stdout
Erlauben mit Nachricht: allow(message) ermöglicht es einer Richtlinie, informativen Kontext an Claude zurückzusenden, selbst wenn die Operation erlaubt ist. Der Hook-Handler schreibt folgendes JSON auf stdout (keine Konfigurationsdatei – dies ist die Antwort des Handlers an Claude Code, genau wie deny- und instruct-Antworten oben):
  • Exit-Code: 0 (Operation ist 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 zuvor)

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.

Laden der Konfiguration

src/hooks/hooks-config.ts implementiert das dreistufige Laden der Konfiguration.
Zusammenführungslogik:
  • enabledPolicies – deduplizierte Vereinigung aller drei Dateien
  • policyParams – pro Richtlinienschlü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 legen, um ctx.params zu erzeugen.
  4. policy.fn(ctx) mit dem aufgelösten Kontext aufrufen.
  5. Ist das Ergebnis deny, sofort abbrechen und diese Entscheidung zurückgeben.
  6. Ist das Ergebnis instruct, die Nachricht sammeln und fortfahren.
  7. Ist das Ergebnis allow, zur nächsten Richtlinie übergehen.
Nachdem alle Richtlinien ausgeführt wurden:
  • Wurde ein deny zurückgegeben, die deny-Antwort ausgeben.
  • Wurden instruct-Rückgaben gesammelt, eine einzelne instruct-Antwort mit allen zusammengefügten Nachrichten ausgeben.
  • Andernfalls eine allow-Antwort ausgeben (leeres stdout, Exit-Code 0).

Eingebaute Richtlinien

src/hooks/builtin-policies.ts definiert alle 39 eingebauten Richtlinien als BuiltinPolicyDefinition-Objekte:
Richtlinien, die params akzeptieren, deklarieren ein PolicyParamsSchema mit Typen und Standardwerten für jeden Parameter. Der Richtlinienauswerter fügt aufgelöste Werte in ctx.params ein, bevor fn aufgerufen wird. Richtlinienfunktionen lesen ctx.params ohne Null-Prüfung, da Standardwerte immer zuerst angewendet werden. Die Mustererkennung innerhalb von Richtlinien verwendet geparste Befehlstoken (argv), keine reine Zeichenkettensuche. Dies verhindert Umgehungsversuche durch Shell-Operator-Injektion (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 zum tatsächlichen dist-Pfad umschreiben, damit customPolicies auf dieselbe globalThis-Registrierung verweist.
  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 einem Fehler (Datei nicht gefunden, Syntaxfehler, Import-Fehler) wird der Fehler in ~/.failproofai/hook.log protokolliert und der Loader gibt ein leeres Array zurück. Eingebaute Richtlinien sind nicht betroffen. Benutzerdefinierte Richtlinien werden nach allen eingebauten Richtlinien ausgewertet. Ein deny einer benutzerdefinierten Richtlinie bricht weitere benutzerdefinierte Richtlinien ab (alle eingebauten wurden jedoch zu diesem Zeitpunkt bereits ausgeführt).

Aktivitätsprotokollierung

Nach jedem Hook-Ereignis hängt der Handler eine JSONL-Zeile an ~/.failproofai/hook-activity.jsonl an:
Eine Zeile pro Richtlinie, die eine Nicht-allow-Entscheidung getroffen hat. Allow-Entscheidungen werden nicht protokolliert (um die Datei klein 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 Lesezugriffe).
  • Die Policies-Seite verwendet Server Actions für alle Mutationen (Umschalten, Parameter-Aktualisierung, Installieren/Entfernen).
  • Der Sitzungsbetrachter 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 – kein REST-API für CRUD-Operationen erforderlich.
  • React Server Components für Leseseiten – schnelleres erstes Laden, kein Client-Bundle für das Datenabrufen.
  • Client-Komponenten nur dort, wo Interaktivität benötigt wird (Richtlinien-Umschalter, Aktivitätssuche, Log-Betrachter).

Dateistruktur