개요
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로 제한합니다. 이를 초과하는 페이로드는 폐기되며 모든 정책이 암묵적으로 allow 처리됩니다.
응답 형식
Deny (PreToolUse):- 종료 코드:
2 - 이유는 stdout이 아닌 stderr에 기록됩니다
- 종료 코드:
0 - stdout 출력 없음
allow(message)를 통해 정책이 작업을 허용하면서도 Claude에게 정보성 컨텍스트를 전달할 수 있습니다. 훅 핸들러는 다음 JSON을 stdout에 씁니다 (설정 파일이 아니라 deny 및 instruct 응답과 마찬가지로 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가 반환된 경우, deny 응답을 출력합니다.instruct반환값이 수집된 경우, 모든 메시지를 합쳐 단일 instruct 응답을 출력합니다.- 그 외의 경우, allow 응답을 출력합니다 (stdout 비움, 종료 코드 0).
내장 정책
src/hooks/builtin-policies.ts가 26개의 내장 정책을 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와 React 서버 컴포넌트 및 서버 액션을 사용하는 Next.js 16 애플리케이션입니다.- 페이지 컴포넌트는
lib/projects.ts와lib/log-entries.ts를 호출하여 파일시스템에서 직접 프로젝트/세션 데이터를 읽습니다 (읽기에는 API 레이어 없음). - 정책 페이지는 모든 변경 작업(토글, 파라미터 업데이트, 설치/제거)에 서버 액션을 사용합니다.
- 세션 뷰어는 Claude의 JSONL 트랜스크립트 형식을 파싱하고 메시지와 툴 호출의 타임라인을 렌더링합니다.
- 데이터베이스 없음 - 모든 영구 상태는 일반 파일에 저장됩니다 (
~/.failproofai/,~/.claude/projects/). - 변경 작업에 서버 액션 사용 - CRUD 작업에 REST API가 필요 없습니다.
- 읽기 페이지에 React 서버 컴포넌트 사용 - 초기 로드 속도 향상, 데이터 패칭을 위한 클라이언트 번들 불필요.
- 상호작용이 필요한 곳에서만 클라이언트 컴포넌트 사용 (정책 토글, 활동 검색, 로그 뷰어).

