Skip to content

Repository files navigation

fusegate

tests PyPI

Claude Code 세션에 다는 정책 엔진. 재귀 스폰, 예산 소진, 반복 루프 같은 에이전트 폭주를 실행되기 전에 차단한다.

이름 그대로다: 퓨즈(fuse)를 단 관문(gate). 과부하가 나면 집 전체가 타기 전에 두꺼비집의 퓨즈가 먼저 끊어진다 — 그 퓨즈를 에이전트 실행 관문에 단다. 세션이 정책 상한을 넘는 순간, 해당 실행만 그 자리에서 끊긴다.

왜 만들었나

2026년 6월, Claude Code에서 서브에이전트가 재귀로 50단계까지 스스로를 복제하며 5분 만에 약 400만 토큰을 태운 사례가 보고됐다(anthropics/claude-code#68619, 아직 OPEN). 같은 시기 AI 에이전트가 프로덕션 DB를 삭제한 사건들도 잇따랐다 — Cursor 에이전트가 Railway API 호출로 DB와 백업을 수 초 만에 지운 건과, Replit 에이전트가 코드 프리즈 중 무승인 파괴를 일으켜 CEO가 공개 사과한 건은 서로 다른 사건이다(언론이 혼동해 보도한 전례가 있어 구분해 적는다 — AI Incident DB #1152, #1469).

피해자들의 결론은 한결같다: 프롬프트에 적은 규칙은 제안일 뿐이다. 강한 제약이 필요하면 시스템 수준의 경계가 필요하다. 모니터링 도구는 많지만 전부 표시 전용이다 — fusegate는 표시하지 않고 강제한다.

무엇을 하나

fusegate init 한 번으로 프로젝트에 훅 4종이 걸리고, 이후 모든 툴 실행 직전(PreToolUse)에 정책을 평가한다.

규칙 내용 스코프
depth 재귀 스폰 깊이 상한 (기본 1 — 아래 실측 근거) 세션
concurrent_session 세션 내 동시 활성 서브에이전트 수 상한 세션
concurrent_global 프로젝트 전역(모든 세션 합산) 동시 수 상한 전역
budget_session 세션 토큰 예산 — 추정 기반(트랜스크립트 파싱) 세션
budget_subagent 서브에이전트 단위 토큰 예산(추정 기반) 에이전트
repeat 동일 툴 호출의 연속 반복 상한 세션
  • enforce / warn 모드 — 규칙별로 고를 수 있다. enforce는 차단하고, warn은 차단 없이 모델이 관측 가능한 경고를 주입해 자가 교정을 유도한다.
  • 예산 위반은 확장만 차단 — Agent 스폰만 막고 Bash·편집 등은 통과시켜, 진행 중 작업의 마무리(커밋·정리)를 잃지 않게 한다.
  • 차단 메시지가 재시도를 막는다 — 차단 시 모델에게 "재시도하거나 다른 방법으로 우회하지 말 것, 정지하고 사용자에게 보고할 것"을 지시한다. 거부→재스폰 패턴(#68619의 악화 경로)을 막는 실측된 문구다.
  • fail-open — 엔진이 어떤 식으로 고장 나도(정책 문법 오류, DB 손상, 버그) 세션은 절대 막히지 않는다. 통과시키고, 경고하고, 기록한다.
  • 위반 텔레메트리 — 모든 판정이 violations 테이블과 events.jsonl에 남는다(차단, 경고, 킬스위치 통과, 엔진 장애 통과를 구분).
  • 관측fusegate status(터미널)와 fusegate dashboard(127.0.0.1 전용, 완전 읽기 전용 로컬 대시보드)로 위반, 활성 에이전트, 예산 추정을 조회한다.
  • 킬스위치FUSEGATE_DISABLE=1 하나뿐이다. 끄는 행위는 항상 명시적이고 기록된다. 몰래 우회하는 경로는 만들지 않았다.

대시보드 화면

위반 목록 타임라인 예산
위반 목록 뷰 타임라인 뷰 예산 뷰

네이티브 상한과 뭐가 다른가

Claude Code에는 이미 전역 env var 상한이 있다: CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS, CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH, CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION. 쓰고 있다면 계속 쓰면 된다 — fusegate는 이 변수들을 건드리지 않고, 둘 다 설정돼 있으면 보수적인 쪽이 먼저 걸린다.

fusegate의 가치는 상한의 유무가 아니라 정책 계층이다: 전역 고정 노브가 아닌 프로젝트별 정책 파일, 규칙별 enforce/warn 선택, 예산과 반복 같은 상한 밖 규칙, 그리고 무엇이 언제 왜 차단됐는지의 위반 텔레메트리 — 벤더가 버그를 고치고 상한을 늘려도 프로젝트마다 다른 "정책"은 남는다.

실측: 무엇이 이 폭주를 실제로 끊나

재귀 스폰 폭주(#68619 서사)를 가드 없음 / depth=2 / depth=1 세 조건으로 대조 재현했다. n=1 실측이며 모든 수치는 추정치다 — 재현 스크립트와 원본 증거는 measurements/에 있다(안전 상한 내장).

스폰 수 차단 중단 지점 토큰(추정)* 비용(추정, CLI 보고)
가드 없음 4 0 자연 종료(안전 상한 내 관측) 783,109 $0.19
depth=2 enforce 5 0 자연 종료 — 차단 실패 1,040,701 $0.25
depth=1 enforce 1 1 정책 차단 310,464 $0.14

* 토큰 수치는 stream-json usage 4개 카테고리 합산이라 캐시 재사용(cache_read)이 포함된 누적 usage다. 신규 처리량이나 절감률 계산의 근거로 읽지 말 것 — 비용 감각은 CLI 보고 USD가 더 정확하다(두 추정 소스의 차이는 measurements/ 문서 참조).

이 실측의 핵심 발견은 차단 성공이 아니라 depth=2의 차단 실패다. 부모-자식 페어링 필드가 훅 페이로드에 없어 깊이를 동시 활성 수로 근사하는데, 부모가 먼저 종료되는 순차 사슬은 이 근사에 걸리지 않는다. 재귀 재스폰을 실제로 막는 설정은 depth=1뿐이었고, 기본값이 1인 이유다. 이 한계는 문서에 공개돼 있다(docs/tracking/findings.md).

설치와 사용

pip install fusegate         # PyPI: https://pypi.org/project/fusegate/
cd <보호할 프로젝트>
fusegate init                # .fusegate/ 생성 + .claude/settings.json 훅 병합(백업, 멱등)

정책은 .fusegate/policy.toml 하나로 조정한다(편집 즉시 적용):

[fusegate]
mode = "enforce"             # 전역 기본: "enforce" | "warn"

[rules.depth]
limit = 1                    # 규칙 테이블을 지우면 그 규칙은 비활성
[rules.budget_session]
limit_tokens = 2_000_000     # 추정 기반
# 모든 규칙에 mode = "warn" 오버라이드 가능

일시 해제: FUSEGATE_DISABLE=1. 위반 확인: .fusegate/events.jsonl.

정직하게 밝혀두는 한계

  • 예산은 추정이다. 트랜스크립트에 기록된 usage를 파싱한 누적치라 실제 청구와 다를 수 있고, 아직 기록되지 않은 사용량만큼 뒤처질 수 있다. 과금 명세로 쓰지 말 것.
  • 깊이는 근사다. 훅 페이로드에 부모-자식을 잇는 필드가 없어 동시 활성 수로 근사한다. limit=1은 정확히 동작하지만("메인의 직속 스폰만 허용"), 2 이상은 순차 사슬형 폭주를 놓칠 수 있다.
  • CLI 2.1.215 실측 스냅샷 기반이다. 훅 계약(이벤트, 필드명, 차단 지점)은 버전업 시 재검증이 필요하다.
  • fail-open이 철학이다. 게이트가 죽으면 보호도 사라진다 — 대신 세션은 절대 막히지 않고, 장애는 반드시 기록·경고로 드러난다.

About

Claude Code 세션 정책 엔진 — 에이전트 폭주(재귀 스폰, 예산 소진, 반복 루프)를 실행 전에 차단

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages