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:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
Claude Code ruft dann failproofai --hook PreToolUse vor jedem Tool-Aufruf als Subprozess auf und übergibt eine JSON-Nutzlast über stdin.

Nutzlastformat

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
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):
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
Ablehnen (PostToolUse):
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
Anweisen (jedes Ereignis außer Stop):
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
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):
// Written to stdout by the hook handler process
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
  • 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:
stdin JSON
  → Nutzlast parsen (max. 1 MB)
  → Sitzungsmetadaten extrahieren (session_id, cwd, tool_name, tool_input usw.)
  → readMergedHooksConfig(cwd)    ← Projekt- + lokale + globale Konfiguration zusammenführen
  → aktivierte integrierte Richtlinien mit aufgelösten Parametern registrieren
  → benutzerdefinierte Richtlinien aus customPoliciesPath laden (falls angegeben)
  → benutzerdefinierte Richtlinien in die Richtlinienregistrierung eintragen
  → alle Richtlinien auswerten (erst integrierte, dann benutzerdefinierte)
      → erstes deny bricht sofort ab
      → instruct-Entscheidungen werden gesammelt
      → allow-Nachrichten werden gesammelt
  → JSON-Entscheidung auf stdout schreiben
  → Ereignis in ~/.failproofai/hook-activity.jsonl persistieren
  → beenden
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.
[1] {cwd}/.failproofai/policies-config.json        ← Projekt  (höchste Priorität)
[2] {cwd}/.failproofai/policies-config.local.json  ← lokal
[3] ~/.failproofai/policies-config.json             ← global   (niedrigste Priorität)
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:
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
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:
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
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:
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
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.
app/
  layout.tsx                  ← Root-Layout (Theme, Telemetrie, Navigation)
  projects/page.tsx           ← Server-Komponente: alle Claude-Projekte auflisten
  project/[name]/page.tsx     ← Server-Komponente: Sitzungen in einem Projekt auflisten
  project/[name]/session/
    [sessionId]/page.tsx      ← Server-Komponente: Sitzungsansicht rendern
  policies/page.tsx           ← Client-Komponente: Richtlinienverwaltung + Aktivitätsprotokoll
  actions/
    get-hooks-config.ts       ← Konfiguration + Richtlinienliste lesen
    update-hooks-config.ts    ← Richtlinie aktivieren/deaktivieren
    update-policy-params.ts   ← Richtlinienparameter aktualisieren
    get-hook-activity.ts      ← Aktivitätsprotokoll blättern/durchsuchen
    install-hooks-web.ts      ← Hooks über den Browser installieren/entfernen
  api/
    download/[project]/[session]/route.ts   ← Sitzungsexport pro CLI (JSONL oder JSON)
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

failproofai/
├── bin/
│   └── failproofai.mjs           # CLI-Router (Hook / Dashboard / Install / usw.)
├── src/hooks/
│   ├── handler.ts                # Hook-Ereignis-Pipeline
│   ├── builtin-policies.ts       # 39 Richtliniendefinitionen
│   ├── policy-evaluator.ts       # Richtlinien-Ausführungsengine
│   ├── policy-registry.ts        # Richtlinienregistrierung und -suche
│   ├── policy-types.ts           # TypeScript-Interfaces
│   ├── hooks-config.ts           # Mehrstufiges Konfigurationsladen
│   ├── custom-hooks-registry.ts  # globalThis-basierte Hook-Registrierung
│   ├── custom-hooks-loader.ts    # ESM-Loader für benutzerdefinierte JS-Hooks
│   ├── manager.ts                # Installations-, Entfernungs- und Listenoperationen
│   ├── install-prompt.ts         # Interaktive Richtlinienauswahlabfrage
│   ├── hook-logger.ts            # Protokollierung in hook.log
│   ├── hook-activity-store.ts    # Aktivität in hook-activity.jsonl persistieren
│   └── llm-client.ts             # LLM-API-Client (für KI-gestützte Richtlinien)
├── app/                          # Next.js-Dashboard (Seiten + Server Actions)
├── lib/                          # Gemeinsame Hilfsprogramme
│   ├── projects.ts               # Claude-Projekte aus dem Dateisystem aufzählen
│   ├── log-entries.ts            # Claude-Transkript-JSONL-Format parsen
│   ├── paths.ts                  # Systempfade auflösen
│   └── ...
├── components/                   # Gemeinsame React-UI-Komponenten
├── contexts/                     # React-Context-Provider (Theme, Auto-Refresh, Telemetrie)
├── examples/                     # Beispieldateien für benutzerdefinierte Hooks
└── __tests__/                    # Unit- und E2E-Tests