> ## Documentation Index
> Fetch the complete documentation index at: https://exosphere-auto-translate-docs-20260623-1106.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Architettura

> Come il gestore hook, il caricamento della configurazione e la valutazione delle policy funzionano internamente

Questo documento spiega come failproofai funziona internamente: come il sistema hook intercetta le chiamate agli strumenti dell'agente, come viene caricata e unita la configurazione, come vengono valutate le policy e come il dashboard monitora l'attività dell'agente.

***

## Panoramica

failproofai ha due sottosistemi indipendenti:

1. **Gestore hook** - Un veloce sottoprocesso CLI che Claude Code invoca su ogni chiamata dello strumento dell'agente. Valuta le policy e restituisce una decisione.
2. **Agent Monitor (Dashboard)** - Un'applicazione web Next.js per monitorare le sessioni dell'agente e gestire le policy.

Entrambi i sottosistemi condividono file di configurazione in `~/.failproofai/` e nella directory `.failproofai/` del progetto, ma vengono eseguiti come processi separati e comunicano solo attraverso il filesystem.

***

## Gestore hook

### Integrazione con Claude Code

Quando esegui `failproofai policies --install`, scrive voci come questa in `~/.claude/settings.json`:

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

Claude Code quindi invoca `failproofai --hook PreToolUse` come sottoprocesso prima di ogni chiamata dello strumento, passando un payload JSON su stdin.

### Formato del payload

```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" }
}
```

Per gli eventi `PostToolUse`, il payload contiene anche `tool_result` con l'output dello strumento.

Il gestore impone un limite di stdin di 1 MB. I payload che superano questo limite vengono scartati e tutte le policy consentono implicitamente.

### Formato della risposta

**Nega (PreToolUse):**

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

**Nega (PostToolUse):**

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

**Istruisci (qualsiasi evento eccetto Stop):**

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

**Istruisci evento Stop:**

* Codice di uscita: `2`
* Motivo scritto su stderr (non stdout)

**Consenti:**

* Codice di uscita: `0`
* Stdout vuoto

**Consenti con messaggio:**

`allow(message)` consente a una policy di inviare un contesto informativo a Claude anche quando l'operazione è consentita. Il gestore hook scrive il seguente JSON su **stdout** (non su un file di configurazione — questa è la risposta del gestore a Claude Code, proprio come le risposte deny e instruct sopra):

```json theme={null}
// Scritto su stdout dal processo del gestore hook
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
```

* Codice di uscita: `0` (operazione consentita)
* Quando più policy restituiscono `allow` con un messaggio, i loro messaggi vengono uniti con newline in una singola stringa `additionalContext`
* Se nessuna policy fornisce un messaggio, stdout è vuoto (stesso di prima)

### Pipeline di elaborazione

`src/hooks/handler.ts` implementa la pipeline completa:

```text theme={null}
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
```

L'intero processo viene eseguito in meno di 100ms per payload tipici senza chiamate LLM.

***

## Caricamento della configurazione

`src/hooks/hooks-config.ts` implementa il caricamento della configurazione a tre livelli.

```text theme={null}
[1] {cwd}/.failproofai/policies-config.json        ← project  (highest priority)
[2] {cwd}/.failproofai/policies-config.local.json  ← local
[3] ~/.failproofai/policies-config.json             ← global   (lowest priority)
```

Logica di unione:

* `enabledPolicies` - unione deduplicate tra tutti e tre i file
* `policyParams` - per policy, il primo file che lo definisce vince completamente
* `customPoliciesPath` - il primo file che lo definisce vince
* `llm` - il primo file che lo definisce vince

Il dashboard web utilizza `readHooksConfig()` (solo globale) per leggere e scrivere, poiché non viene invocato con un cwd di progetto.

***

## Valutazione delle policy

`src/hooks/policy-evaluator.ts` esegue le policy in ordine.

Per ogni policy:

1. Cercare lo schema `params` della policy (se ne ha uno).
2. Leggere `policyParams[policy.name]` dalla configurazione unita.
3. Unire i valori forniti dall'utente sui default dello schema per produrre `ctx.params`.
4. Chiamare `policy.fn(ctx)` con il contesto risolto.
5. Se il risultato è `deny`, fermarsi immediatamente e restituire quella decisione.
6. Se il risultato è `instruct`, accumulare il messaggio e continuare.
7. Se il risultato è `allow`, continuare alla policy successiva.

Dopo che tutte le policy vengono eseguite:

* Se è stato restituito un `deny`, emettere la risposta deny.
* Se sono stati raccolti ritorni `instruct`, emettere una singola risposta instruct con tutti i messaggi uniti.
* Altrimenti, emettere una risposta allow (stdout vuoto, uscita 0).

***

## Policy integrate

`src/hooks/builtin-policies.ts` definisce tutte le 39 policy integrate come oggetti `BuiltinPolicyDefinition`:

```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;
}
```

Le policy che accettano `params` dichiarano un `PolicyParamsSchema` con tipi e default per ogni parametro. Il valutatore di policy inietta i valori risolti in `ctx.params` prima di chiamare `fn`. Le funzioni di policy leggono `ctx.params` senza protezione null perché i default vengono sempre applicati per primo.

La corrispondenza di pattern all'interno delle policy utilizza token di comando analizzati (argv), non corrispondenza di stringhe grezze. Questo previene il bypass tramite iniezione di operatori shell (ad es. un pattern per `sudo systemctl status *` non può essere bypassato aggiungendo `; rm -rf /` al comando).

***

## Policy personalizzate

`src/hooks/custom-hooks-registry.ts` implementa un registro supportato da `globalThis`:

```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` carica il file di policy dell'utente:

1. Leggere `customPoliciesPath` dalla configurazione; saltare se assente.
2. Risolvere al percorso assoluto; controllare che il file esista.
3. Riscrivere tutti gli import `from "failproofai"` al percorso dist effettivo in modo che `customPolicies` si risolva nello stesso registro `globalThis`.
4. Riscrivere ricorsivamente gli import locali transitivi per garantire la compatibilità ESM.
5. Scrivere file `.mjs` temporanei e `import()` il file di ingresso.
6. Chiamare `getCustomHooks()` per recuperare gli hook registrati.
7. Pulire tutti i file temporanei in un blocco `finally`.

Su qualsiasi errore (file non trovato, errore di sintassi, errore di importazione), l'errore viene registrato in `~/.failproofai/hook.log` e il loader restituisce un array vuoto. Le policy integrate non sono interessate.

Le policy personalizzate vengono valutate dopo tutte le policy integrate. Un `deny` di una policy personalizzata ancora cortocircuita ulteriori policy personalizzate (ma tutte le politiche integrate sono già state eseguite a quel punto).

***

## Registrazione dell'attività

Dopo ogni evento hook, il gestore aggiunge una riga JSONL a `~/.failproofai/hook-activity.jsonl`:

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

Una riga per ogni policy che ha preso una decisione non-allow. Le decisioni allow non vengono registrate (per mantenere il file piccolo).

***

## Architettura del dashboard

Il dashboard è un'applicazione **Next.js 16** che utilizza l'App Router con React Server Components e Server Actions.

```text theme={null}
app/
  layout.tsx                  ← Root layout (theme, telemetry, nav)
  projects/page.tsx           ← Server component: list all Claude projects
  project/[name]/page.tsx     ← Server component: list sessions in a project
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: render session viewer
  policies/page.tsx           ← Client component: policy management + activity log
  actions/
    get-hooks-config.ts       ← Read config + policy list
    update-hooks-config.ts    ← Toggle policy on/off
    update-policy-params.ts   ← Update policy parameters
    get-hook-activity.ts      ← Paginate/search activity log
    install-hooks-web.ts      ← Install/remove hooks from the browser
  api/
    download/[project]/[session]/route.ts   ← Per-CLI session export (JSONL or JSON)
```

**Flusso dei dati:**

* I componenti della pagina chiamano `lib/projects.ts` e `lib/log-entries.ts` per leggere i dati del progetto/sessione direttamente dal filesystem (nessun livello API per le letture).
* La pagina Policies utilizza Server Actions per tutte le mutazioni (toggle, aggiornamento parametri, installazione/rimozione).
* Il visualizzatore della sessione analizza il formato di trascritto JSONL di Claude e visualizza una timeline di messaggi e chiamate ai strumenti.

**Decisioni di progettazione chiave:**

* Nessun database - tutto lo stato persistente si trova in file semplici (`~/.failproofai/`, `~/.claude/projects/`).
* Server Actions per le mutazioni - nessuna API REST necessaria per le operazioni CRUD.
* React Server Components per le pagine di lettura - caricamento iniziale più veloce, nessun bundle client per il recupero dei dati.
* Componenti client solo dove è necessaria l'interattività (toggle policy, ricerca attività, visualizzatore log).

***

## Layout dei file

```text theme={null}
failproofai/
├── bin/
│   └── failproofai.mjs           # CLI router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook event pipeline
│   ├── builtin-policies.ts       # 39 policy definitions
│   ├── policy-evaluator.ts       # Policy execution engine
│   ├── policy-registry.ts        # Policy registration and lookup
│   ├── policy-types.ts           # TypeScript interfaces
│   ├── hooks-config.ts           # Multi-scope config loading
│   ├── custom-hooks-registry.ts  # globalThis-backed hook registry
│   ├── custom-hooks-loader.ts    # ESM loader for user JS hooks
│   ├── manager.ts                # install / remove / list operations
│   ├── install-prompt.ts         # Interactive policy selection prompt
│   ├── hook-logger.ts            # Logging to hook.log
│   ├── hook-activity-store.ts    # Persist activity to hook-activity.jsonl
│   └── llm-client.ts             # LLM API client (for AI-powered policies)
├── app/                          # Next.js dashboard (pages + server actions)
├── lib/                          # Shared utilities
│   ├── projects.ts               # Enumerate Claude projects from filesystem
│   ├── log-entries.ts            # Parse Claude transcript JSONL format
│   ├── paths.ts                  # Resolve system paths
│   └── ...
├── components/                   # Shared React UI components
├── contexts/                     # React context providers (theme, auto-refresh, telemetry)
├── examples/                     # Example custom hook files
└── __tests__/                    # Unit and E2E tests
```
