개요
failproofai는 두 개의 독립적인 서브시스템으로 구성됩니다:- 훅 핸들러 - Claude Code가 에이전트 도구 호출마다 실행하는 빠른 CLI 서브프로세스입니다. 정책을 평가하고 결정을 반환합니다.
- 에이전트 모니터 (대시보드) - 에이전트 세션을 모니터링하고 정책을 관리하기 위한 Next.js 웹 애플리케이션입니다.
~/.failproofai/와 프로젝트의 .failproofai/ 디렉토리에 있는 설정 파일을 공유하지만, 별도의 프로세스로 실행되며 파일 시스템을 통해서만 통신합니다.
훅 핸들러
Claude Code와의 통합
failproofai policies --install을 실행하면 ~/.claude/settings.json에 다음과 같은 항목이 작성됩니다:
failproofai --hook PreToolUse를 서브프로세스로 실행하고, stdin으로 JSON 페이로드를 전달합니다.
페이로드 형식
PostToolUse 이벤트의 경우, 페이로드에 도구의 출력 결과가 담긴 tool_result도 포함됩니다.
핸들러는 stdin을 1 MB로 제한합니다. 이를 초과하는 페이로드는 무시되며, 모든 정책은 암묵적으로 허용됩니다.
응답 형식
거부 (PreToolUse):- 종료 코드:
2 - 이유는 stdout이 아닌 stderr에 기록됩니다
- 종료 코드:
0 - stdout은 비어 있습니다
allow(message)를 사용하면 작업이 허용된 경우에도 정책이 Claude에게 정보성 컨텍스트를 전달할 수 있습니다. 훅 핸들러는 다음 JSON을 stdout에 씁니다 (설정 파일이 아닌, 위의 거부 및 지시 응답과 동일하게 Claude Code에 대한 핸들러의 응답입니다):
- 종료 코드:
0(작업이 허용됨) - 여러 정책이 메시지와 함께
allow를 반환하는 경우, 해당 메시지들은 줄바꿈으로 연결되어 하나의additionalContext문자열이 됩니다 - 어떤 정책도 메시지를 제공하지 않으면 stdout은 비어 있습니다 (기존 동작과 동일)
처리 파이프라인
src/hooks/handler.ts가 전체 파이프라인을 구현합니다:
설정 로딩
src/hooks/hooks-config.ts가 세 가지 범위의 설정 로딩을 구현합니다.
enabledPolicies- 세 파일 전체에서 중복을 제거한 합집합policyParams- 정책별 키 기준, 먼저 정의한 파일이 전체를 결정customPoliciesPath- 먼저 정의한 파일이 결정llm- 먼저 정의한 파일이 결정
readHooksConfig() (글로벌 전용)를 사용합니다.
정책 평가
src/hooks/policy-evaluator.ts가 순서대로 정책을 실행합니다.
각 정책에 대해:
- 정책의
params스키마를 조회합니다 (있는 경우). - 병합된 설정에서
policyParams[policy.name]을 읽습니다. - 사용자 제공 값을 스키마 기본값에 덮어써서
ctx.params를 생성합니다. - 해석된 컨텍스트와 함께
policy.fn(ctx)를 호출합니다. - 결과가
deny이면 즉시 중단하고 해당 결정을 반환합니다. - 결과가
instruct이면 메시지를 누적하고 계속 진행합니다. - 결과가
allow이면 다음 정책으로 넘어갑니다.
deny가 반환된 경우, 거부 응답을 출력합니다.instruct반환이 수집된 경우, 모든 메시지를 합쳐 단일 지시 응답을 출력합니다.- 그 외에는 허용 응답을 출력합니다 (stdout 비움, 종료 코드 0).
내장 정책
src/hooks/builtin-policies.ts는 39개의 내장 정책을 BuiltinPolicyDefinition 객체로 정의합니다:
params를 허용하는 정책은 각 파라미터의 타입과 기본값이 담긴 PolicyParamsSchema를 선언합니다. 정책 평가기는 fn을 호출하기 전에 해석된 값을 ctx.params에 주입합니다. 기본값이 항상 먼저 적용되므로, 정책 함수는 null 검사 없이 ctx.params를 읽을 수 있습니다.
정책 내부의 패턴 매칭은 원시 문자열 매칭이 아닌 파싱된 명령어 토큰(argv)을 사용합니다. 이를 통해 셸 연산자 주입을 이용한 우회를 방지합니다 (예: sudo systemctl status * 패턴은 명령어에 ; rm -rf /를 추가해도 우회할 수 없습니다).
커스텀 정책
src/hooks/custom-hooks-registry.ts는 globalThis 기반의 레지스트리를 구현합니다:
src/hooks/custom-hooks-loader.ts는 사용자의 정책 파일을 로드합니다:
- 설정에서
customPoliciesPath를 읽고, 없으면 건너뜁니다. - 절대 경로로 변환하고 파일 존재 여부를 확인합니다.
customPolicies가 동일한globalThis레지스트리로 해석될 수 있도록 모든from "failproofai"임포트를 실제 dist 경로로 재작성합니다.- ESM 호환성을 보장하기 위해 전이적인 로컬 임포트도 재귀적으로 재작성합니다.
- 임시
.mjs파일을 작성하고 엔트리 파일을import()합니다. getCustomHooks()를 호출하여 등록된 훅을 가져옵니다.finally블록에서 모든 임시 파일을 정리합니다.
~/.failproofai/hook.log에 기록되고 로더는 빈 배열을 반환합니다. 내장 정책은 영향을 받지 않습니다.
커스텀 정책은 모든 내장 정책이 실행된 후에 평가됩니다. 커스텀 정책의 deny는 이후 커스텀 정책을 즉시 중단하지만, 이 시점에서 내장 정책은 이미 모두 실행된 상태입니다.
활동 로깅
각 훅 이벤트 이후, 핸들러는~/.failproofai/hook-activity.jsonl에 JSONL 한 줄을 추가합니다:
대시보드 아키텍처
대시보드는 App Router를 사용하는 Next.js 16 애플리케이션으로, React Server Components와 Server Actions를 활용합니다.- 페이지 컴포넌트는
lib/projects.ts와lib/log-entries.ts를 호출하여 파일 시스템에서 직접 프로젝트/세션 데이터를 읽습니다 (읽기에는 API 레이어 없음). - 정책 페이지는 모든 변경 작업(토글, 파라미터 업데이트, 설치/제거)에 Server Actions를 사용합니다.
- 세션 뷰어는 Claude의 JSONL 트랜스크립트 형식을 파싱하고 메시지 및 도구 호출 타임라인을 렌더링합니다.
- 데이터베이스 없음 - 모든 영속 상태는 일반 파일(
~/.failproofai/,~/.claude/projects/)에 저장됩니다. - 변경 작업에 Server Actions 사용 - CRUD 작업에 REST API가 필요 없습니다.
- 읽기 페이지에 React Server Components 사용 - 초기 로딩이 빠르고 데이터 페칭을 위한 클라이언트 번들이 없습니다.
- 상호작용이 필요한 경우에만 클라이언트 컴포넌트 사용 (정책 토글, 활동 검색, 로그 뷰어).

