Vai al contenuto principale
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:
Claude Code quindi invoca failproofai --hook PreToolUse come sottoprocesso prima di ogni chiamata dello strumento, passando un payload JSON su stdin.

Formato del payload

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):
Nega (PostToolUse):
Istruisci (qualsiasi evento eccetto Stop):
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):
  • 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:
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.
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:
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:
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:
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.
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