> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Architektur

> Wie der Hook-Handler, das Laden der Konfiguration und die Richtlinienauswertung intern funktionieren

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`:

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
```

Claude Code ruft dann `failproofai --hook PreToolUse` als Subprozess vor jedem Tool-Aufruf auf und übergibt dabei eine JSON-Nutzlast über stdin.

### Nutzlastformat

```json theme={null}
{
  "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 erzwingt ein stdin-Limit von 1 MB. Nutzlasten, die dieses überschreiten, werden verworfen, und alle Richtlinien erlauben implizit.

### Antwortformat

**Verweigern (PreToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
```

**Verweigern (PostToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
```

**Anweisen (beliebiges Ereignis außer Stop):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
```

**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):

```json theme={null}
// Written to stdout by the hook handler process
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
```

* 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:

```text theme={null}
stdin JSON
  → Nutzlast parsen (max. 1 MB)
  → Sitzungsmetadaten extrahieren (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← führt Projekt- + lokale + globale Konfiguration zusammen
  → aktivierte eingebaute Richtlinien mit aufgelösten Parametern registrieren
  → benutzerdefinierte Richtlinien aus customPoliciesPath laden (falls gesetzt)
  → benutzerdefinierte Richtlinien in die Richtlinienregistrierung eintragen
  → alle Richtlinien auswerten (eingebaute zuerst, dann benutzerdefinierte)
      → erstes deny bricht ab
      → instruct-Entscheidungen werden gesammelt
      → allow-Nachrichten werden gesammelt
  → JSON-Entscheidung auf stdout schreiben
  → Ereignis in ~/.failproofai/hook-activity.jsonl speichern
  → beenden
```

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.

```text theme={null}
[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 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:

```typescript theme={null}
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 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:

```typescript theme={null}
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 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:

```json theme={null}
{
  "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
}
```

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.

```text theme={null}
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 seitenweise abrufen/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 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

```text theme={null}
failproofai/
├── bin/
│   └── failproofai.mjs           # CLI-Router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook-Ereignis-Pipeline
│   ├── builtin-policies.ts       # 39 Richtliniendefinitionen
│   ├── policy-evaluator.ts       # Richtlinienausführungs-Engine
│   ├── policy-registry.ts        # Richtlinienregistrierung und -nachschlage
│   ├── policy-types.ts           # TypeScript-Schnittstellen
│   ├── hooks-config.ts           # Mehrstufiges Laden der Konfiguration
│   ├── custom-hooks-registry.ts  # globalThis-basierte Hook-Registrierung
│   ├── custom-hooks-loader.ts    # ESM-Loader für benutzerdefinierte JS-Hooks
│   ├── manager.ts                # Installieren / Entfernen / Auflisten
│   ├── install-prompt.ts         # Interaktive Richtlinienauswahl-Eingabeaufforderung
│   ├── hook-logger.ts            # Protokollierung in hook.log
│   ├── hook-activity-store.ts    # Aktivität in hook-activity.jsonl speichern
│   └── llm-client.ts             # LLM-API-Client (für KI-gestützte Richtlinien)
├── app/                          # Next.js-Dashboard (Seiten + Server Actions)
├── lib/                          # Gemeinsam genutzte Hilfsprogramme
│   ├── projects.ts               # Claude-Projekte aus dem Dateisystem aufzählen
│   ├── log-entries.ts            # Claude-Transkript-JSONL-Format parsen
│   ├── paths.ts                  # Systempfade auflösen
│   └── ...
├── components/                   # Gemeinsam genutzte React-UI-Komponenten
├── contexts/                     # React-Kontextanbieter (Theme, Auto-Refresh, Telemetrie)
├── examples/                     # Beispiel-Dateien für benutzerdefinierte Hooks
└── __tests__/                    # Unit- und E2E-Tests
```
