Überblick
failproofai besteht aus zwei unabhängigen Subsystemen:- Hook-Handler – Ein schneller CLI-Subprocess, den Claude Code bei jedem Agent-Tool-Aufruf aufruft. Wertet Policies aus und gibt eine Entscheidung zurück.
- Agent Monitor (Dashboard) – Eine Next.js-Webanwendung zur Überwachung von Agentensitzungen und zur Verwaltung von Policies.
~/.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 Siefailproofai policies --install ausführen, schreibt es folgende Einträge in ~/.claude/settings.json:
failproofai --hook PreToolUse als Subprocess vor jedem Tool-Aufruf auf und übergibt dabei einen JSON-Payload über stdin.
Payload-Format
PostToolUse-Events enthält der Payload zusätzlich tool_result mit der Ausgabe des Tools.
Der Handler setzt ein Limit von 1 MB für stdin. Payloads, die dieses Limit überschreiten, werden verworfen und alle Policies erlauben implizit.
Antwortformat
Deny (PreToolUse):- Exit-Code:
2 - Grund wird nach stderr geschrieben (nicht stdout)
- Exit-Code:
0 - Leerer stdout
allow(message) ermöglicht einer Policy, informativen Kontext an Claude zurückzusenden, auch wenn die Operation erlaubt wird. Der Hook-Handler schreibt folgendes JSON nach 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 Policies
allowmit einer Nachricht zurückgeben, werden ihre Nachrichten mit Zeilenumbrüchen zu einem einzigenadditionalContext-String verbunden - Wenn keine Policy eine Nachricht liefert, ist stdout leer (wie zuvor)
Verarbeitungspipeline
src/hooks/handler.ts implementiert die vollständige Pipeline:
Konfiguration laden
src/hooks/hooks-config.ts implementiert das dreistufige Laden der Konfiguration.
enabledPolicies– deduplizierte Vereinigung aller drei DateienpolicyParams– pro Policy-Schlüssel gewinnt die erste Datei, die ihn definiert, vollständigcustomPoliciesPath– die erste Datei, die ihn definiert, gewinntllm– die erste Datei, die ihn definiert, gewinnt
readHooksConfig() (nur global) zum Lesen und Schreiben, da es nicht mit einem Projekt-cwd aufgerufen wird.
Policy-Auswertung
src/hooks/policy-evaluator.ts führt Policies der Reihe nach aus.
Für jede Policy:
- Das
params-Schema der Policy nachschlagen (sofern vorhanden). policyParams[policy.name]aus der zusammengeführten Konfiguration lesen.- Benutzerdefinierte Werte über Schema-Standardwerte zusammenführen, um
ctx.paramszu erzeugen. policy.fn(ctx)mit dem aufgelösten Kontext aufrufen.- Ist das Ergebnis
deny, sofort stoppen und diese Entscheidung zurückgeben. - Ist das Ergebnis
instruct, die Nachricht sammeln und fortfahren. - Ist das Ergebnis
allow, mit der nächsten Policy fortfahren.
- Wenn ein
denyzurückgegeben wurde, die Deny-Antwort ausgeben. - Wenn
instruct-Rückgaben gesammelt wurden, eine einzelne instruct-Antwort mit allen verbundenen Nachrichten ausgeben. - Andernfalls eine Allow-Antwort ausgeben (leerer stdout, Exit 0).
Eingebaute Policies
src/hooks/builtin-policies.ts definiert alle 26 eingebauten Policies als BuiltinPolicyDefinition-Objekte:
params akzeptieren, deklarieren ein PolicyParamsSchema mit Typen und Standardwerten für jeden Parameter. Der Policy-Evaluator injiziert aufgelöste Werte in ctx.params, bevor fn aufgerufen wird. Policy-Funktionen lesen ctx.params ohne Null-Prüfung, da Standardwerte immer zuerst angewendet werden.
Pattern-Matching innerhalb von Policies verwendet geparste Befehlstoken (argv) statt einfachen String-Vergleichen. Dies verhindert Umgehungsversuche durch Shell-Operator-Injection (z. B. kann ein Muster für sudo systemctl status * nicht durch Anhängen von ; rm -rf / an den Befehl umgangen werden).
Benutzerdefinierte Policies
src/hooks/custom-hooks-registry.ts implementiert eine globalThis-basierte Registry:
src/hooks/custom-hooks-loader.ts lädt die Policy-Datei des Benutzers:
customPoliciesPathaus der Konfiguration lesen; überspringen, falls nicht vorhanden.- Zu absolutem Pfad auflösen; prüfen, ob die Datei existiert.
- Alle
from "failproofai"-Imports zum tatsächlichen dist-Pfad umschreiben, damitcustomPolicieszur selbenglobalThis-Registry aufgelöst wird. - Transitive lokale Imports rekursiv umschreiben, um ESM-Kompatibilität sicherzustellen.
- Temporäre
.mjs-Dateien schreiben und die Einstiegsdatei perimport()laden. getCustomHooks()aufrufen, um registrierte Hooks abzurufen.- Alle temporären Dateien in einem
finally-Block bereinigen.
~/.failproofai/hook.log protokolliert und der Loader gibt ein leeres Array zurück. Eingebaute Policies sind davon nicht betroffen.
Benutzerdefinierte Policies werden nach allen eingebauten Policies ausgewertet. Ein deny einer benutzerdefinierten Policy unterbricht dennoch weitere benutzerdefinierte Policies (alle eingebauten Policies sind zu diesem Zeitpunkt jedoch bereits ausgeführt worden).
Aktivitätsprotokollierung
Nach jedem Hook-Event hängt der Handler eine JSONL-Zeile an~/.failproofai/hook-activity.jsonl an:
Dashboard-Architektur
Das Dashboard ist eine Next.js 16-Anwendung, die den App Router mit React Server Components und Server Actions verwendet.- Seitenkomponenten rufen
lib/projects.tsundlib/log-entries.tsauf, 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, Parameter-Aktualisierung, Installieren/Entfernen).
- Der Sitzungs-Viewer parst das JSONL-Transkriptformat von Claude und rendert einen Zeitstrahl von Nachrichten und Tool-Aufrufen.
- Keine Datenbank – der gesamte persistente Zustand liegt in einfachen Dateien (
~/.failproofai/,~/.claude/projects/). - Server Actions für Mutationen – kein REST-API für CRUD-Operationen erforderlich.
- React Server Components für Lese-Seiten – schnelleres initiales Laden, kein Client-Bundle für Datenabruf.
- Client-Komponenten nur dort, wo Interaktivität benötigt wird (Policy-Umschalter, Aktivitätssuche, Log-Viewer).

