Skip to main content
Este documento explica como o failproofai funciona internamente: como o sistema de hooks intercepta chamadas de ferramentas do agente, como a configuração é carregada e mesclada, como as políticas são avaliadas e como o dashboard monitora a atividade do agente.

Visão geral

O failproofai possui dois subsistemas independentes:
  1. Hook handler - Um subprocesso CLI rápido que o Claude Code invoca a cada chamada de ferramenta do agente. Avalia as políticas e retorna uma decisão.
  2. Agent Monitor (Dashboard) - Uma aplicação web Next.js para monitorar sessões de agentes e gerenciar políticas.
Ambos os subsistemas compartilham arquivos de configuração em ~/.failproofai/ e no diretório .failproofai/ do projeto, mas são executados como processos separados e se comunicam apenas pelo sistema de arquivos.

Hook handler

Integração com o Claude Code

Quando você executa failproofai policies --install, ele grava entradas como esta no ~/.claude/settings.json:
O Claude Code então invoca failproofai --hook PreToolUse como subprocesso antes de cada chamada de ferramenta, passando um payload JSON via stdin.

Formato do payload

Para eventos PostToolUse, o payload também contém tool_result com a saída da ferramenta. O handler impõe um limite de 1 MB para o stdin. Payloads que excedam esse limite são descartados e todas as políticas implicitamente permitem a operação.

Formato da resposta

Negar (PreToolUse):
Negar (PostToolUse):
Instruir (qualquer evento exceto Stop):
Instruir no evento Stop:
  • Código de saída: 2
  • Motivo gravado em stderr (não stdout)
Permitir:
  • Código de saída: 0
  • stdout vazio
Permitir com mensagem: allow(message) permite que uma política envie contexto informacional de volta ao Claude mesmo quando a operação é permitida. O hook handler grava o seguinte JSON no stdout (não em um arquivo de configuração — esta é a resposta do handler ao Claude Code, assim como as respostas de deny e instruct acima):
  • Código de saída: 0 (a operação é permitida)
  • Quando múltiplas políticas retornam allow com uma mensagem, suas mensagens são concatenadas com quebras de linha em uma única string additionalContext
  • Se nenhuma política fornecer uma mensagem, o stdout fica vazio (como antes)

Pipeline de processamento

src/hooks/handler.ts implementa o pipeline completo:
Todo o processo é executado em menos de 100ms para payloads típicos, sem chamadas a LLMs.

Carregamento de configuração

src/hooks/hooks-config.ts implementa o carregamento de configuração em três escopos.
Lógica de mesclagem:
  • enabledPolicies - união deduplicada dos três arquivos
  • policyParams - por chave de política, o primeiro arquivo que a define prevalece inteiramente
  • customPoliciesPath - o primeiro arquivo que o define prevalece
  • llm - o primeiro arquivo que o define prevalece
O dashboard web usa readHooksConfig() (somente global) para leitura e escrita, pois não é invocado com um cwd de projeto.

Avaliação de políticas

src/hooks/policy-evaluator.ts executa as políticas em ordem. Para cada política:
  1. Busca o schema de params da política (se houver).
  2. policyParams[policy.name] da configuração mesclada.
  3. Mescla os valores fornecidos pelo usuário sobre os padrões do schema para produzir ctx.params.
  4. Chama policy.fn(ctx) com o contexto resolvido.
  5. Se o resultado for deny, para imediatamente e retorna essa decisão.
  6. Se o resultado for instruct, acumula a mensagem e continua.
  7. Se o resultado for allow, continua para a próxima política.
Após a execução de todas as políticas:
  • Se algum deny foi retornado, emite a resposta de negação.
  • Se algum retorno instruct foi coletado, emite uma única resposta de instrução com todas as mensagens concatenadas.
  • Caso contrário, emite uma resposta de permissão (stdout vazio, exit 0).

Políticas integradas

src/hooks/builtin-policies.ts define todas as 39 políticas integradas como objetos BuiltinPolicyDefinition:
Políticas que aceitam params declaram um PolicyParamsSchema com tipos e valores padrão para cada parâmetro. O avaliador de políticas injeta os valores resolvidos em ctx.params antes de chamar fn. As funções de política leem ctx.params sem verificações de nulo, pois os padrões são sempre aplicados primeiro. A correspondência de padrões dentro das políticas usa tokens de comando analisados (argv), não correspondência de string bruta. Isso evita contornar via injeção de operadores shell (por exemplo, um padrão para sudo systemctl status * não pode ser contornado adicionando ; rm -rf / ao comando).

Políticas personalizadas

src/hooks/custom-hooks-registry.ts implementa um registro baseado em globalThis:
src/hooks/custom-hooks-loader.ts carrega o arquivo de políticas do usuário:
  1. customPoliciesPath da configuração; ignora se ausente.
  2. Resolve para caminho absoluto; verifica se o arquivo existe.
  3. Reescreve todas as importações from "failproofai" para o caminho real de dist, de modo que customPolicies resolva para o mesmo registro globalThis.
  4. Reescreve recursivamente as importações locais transitivas para garantir compatibilidade com ESM.
  5. Grava arquivos .mjs temporários e importa o arquivo de entrada com import().
  6. Chama getCustomHooks() para recuperar os hooks registrados.
  7. Remove todos os arquivos temporários em um bloco finally.
Em caso de qualquer erro (arquivo não encontrado, erro de sintaxe, falha de importação), o erro é registrado em ~/.failproofai/hook.log e o carregador retorna um array vazio. As políticas integradas não são afetadas. As políticas personalizadas são avaliadas após todas as políticas integradas. Um deny de uma política personalizada ainda interrompe o processamento das demais políticas personalizadas (mas todos os integrados já foram executados nesse ponto).

Log de atividade

Após cada evento de hook, o handler adiciona uma linha JSONL em ~/.failproofai/hook-activity.jsonl:
Uma linha por política que tomou uma decisão diferente de allow. Decisões de allow não são registradas (para manter o arquivo pequeno).

Arquitetura do dashboard

O dashboard é uma aplicação Next.js 16 que usa o App Router com React Server Components e Server Actions.
Fluxo de dados:
  • Os componentes de página chamam lib/projects.ts e lib/log-entries.ts para ler dados de projetos/sessões diretamente do sistema de arquivos (sem camada de API para leituras).
  • A página de Políticas usa Server Actions para todas as mutações (ativar/desativar, atualização de parâmetros, instalar/remover).
  • O visualizador de sessão analisa o formato de transcrição JSONL do Claude e renderiza uma linha do tempo de mensagens e chamadas de ferramentas.
Principais decisões de design:
  • Sem banco de dados — todo o estado persistente está em arquivos simples (~/.failproofai/, ~/.claude/projects/).
  • Server Actions para mutações — nenhuma API REST necessária para operações CRUD.
  • React Server Components para páginas de leitura — carregamento inicial mais rápido, sem bundle de cliente para busca de dados.
  • Client components apenas onde há necessidade de interatividade (toggles de políticas, pesquisa de atividade, visualizador de log).

Estrutura de arquivos