> ## 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.

# Architecture

title: Architettura
description: "Come funzionano internamente il gestore hook, il caricamento della configurazione e la valutazione delle policy"
icon: sitemap
-------------

Questo documento spiega come failproofai funziona internamente: come il sistema di 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 richiama per ogni chiamata allo 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 i 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`, vengono scritte voci come questa in `~/.claude/settings.json`:

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

Claude Code quindi richiama `failproofai --hook PreToolUse` come sottoprocesso prima di ogni chiamata allo 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 applica un limite di 1 MB per stdin. 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 un file di configurazione — questa è la risposta del gestore a Claude Code, proprio come le risposte deny e instruct precedenti):

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

* Codice di uscita: `0` (l'operazione è consentita)
* Quando più policy restituiscono `allow` con un messaggio, i loro messaggi vengono uniti con interruzioni di riga in una singola stringa `additionalContext`
* Se nessuna policy fornisce un messaggio, stdout è vuoto (come 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 in tre ambiti.

```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 chiave di policy, il primo file che la definisce vince completamente
* `customPoliciesPath` - il primo file che la definisce vince
* `llm` - il primo file che la definisce vince

Il dashboard web utilizza `readHooksConfig()` (solo globale) per la lettura e la scrittura, poiché non viene richiamato con un cwd di progetto.

***

## Valutazione delle policy

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

Per ogni policy:

1. Cerca lo schema `params` della policy (se ne ha uno).
2. Leggi `policyParams[policy.name]` dalla configurazione unita.
3. Unisci i valori forniti dall'utente sui valori predefiniti dello schema per produrre `ctx.params`.
4. Chiama `policy.fn(ctx)` con il contesto risolto.
5. Se il risultato è `deny`, fermarti immediatamente e restituisci quella decisione.
6. Se il risultato è `instruct`, accumula il messaggio e continua.
7. Se il risultato è `allow`, continua alla policy successiva.

Dopo l'esecuzione di tutte le policy:

* Se è stata restituita una qualsiasi `deny`, emetti la risposta deny.
* Se sono state raccolte risposte `instruct`, emetti una singola risposta instruct con tutti i messaggi uniti.
* Altrimenti, emetti una risposta allow (stdout vuoto, exit 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 uno `PolicyParamsSchema` con tipi e valori predefiniti 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 protezioni null perché i valori predefiniti vengono sempre applicati prima.

La corrispondenza dei pattern all'interno delle policy utilizza token di comando analizzati (argv), non la 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. Leggi `customPoliciesPath` dalla configurazione; salta se assente.
2. Risolvi al percorso assoluto; controlla che il file esista.
3. Riscrivi tutti gli import `from "failproofai"` al percorso dist effettivo in modo che `customPolicies` si risolva nello stesso registro `globalThis`.
4. Riscrivi ricorsivamente gli import locali transitivi per assicurare la compatibilità ESM.
5. Scrivi file `.mjs` temporanei e esegui l'import del file di entry.
6. Chiama `getCustomHooks()` per recuperare gli hook registrati.
7. Pulisci tutti i file temp in un blocco `finally`.

In caso di errore (file non trovato, errore di sintassi, fallimento dell'import), l'errore viene registrato in `~/.failproofai/hook.log` e il caricatore restituisce un array vuoto. Le policy integrate non sono interessate.

Le policy personalizzate vengono valutate dopo tutte le policy integrate. Una policy personalizzata `deny` interrompe ancora ulteriormente le policy personalizzate (ma a quel punto tutte le policy integrate sono già state eseguite).

***

## 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 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 di dati:**

* I componenti della pagina chiamano `lib/projects.ts` e `lib/log-entries.ts` per leggere i dati di progetto/sessione direttamente dal filesystem (nessun livello API per le letture).
* La pagina Policies utilizza Server Actions per tutte le mutazioni (attiva/disattiva, aggiornamento params, installa/rimuovi).
* Il visualizzatore di sessione analizza il formato di trascrizione JSONL di Claude e renderizza una timeline di messaggi e chiamate allo strumento.

**Decisioni di progettazione chiave:**

* Nessun database - tutto lo stato persistente è in file ordinari (`~/.failproofai/`, `~/.claude/projects/`).
* Server Actions per mutazioni - nessuna API REST necessaria per operazioni CRUD.
* React Server Components per pagine di lettura - caricamento iniziale più veloce, nessun bundle client per il recupero dati.
* Componenti client solo dove è necessaria l'interattività (attiva/disattiva 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
```
