20여 개 코딩 에이전트의 기록을 한 로그로: Asymptote-Labs/agent-beacon의 세션 텔레메트리·CEL 위협 탐지·핸드오프·승인형 메모리 심층 해부와 박스 실측

Claude Code로 오전에 고친 버그를 오후에 Codex가 처음부터 다시 헤매는 일은 흔하다. 세션 기록은 하네스마다 다른 폴더, 다른 형식(JSONL, SQLite, OTLP)으로 흩어져 있고, 누가 어떤 명령을 실행했는지 보안팀이 볼 방법도 마땅치 않다. Asymptote-Labs/agent-beacon(이하 Beacon)은 이 두 문제를 한 바이너리로 묶는다. 20여 개 코딩 에이전트의 세션을 하나의 정규화된 JSONL 런타임 로그로 모으고, 그 위에서 위협 탐지 규칙(CEL), 토큰·비용 집계, 하네스 간 세션 이어받기(handoff), MCP로 검색, 그리고 리뷰를 거친 프로젝트 메모리를 제공한다. 오늘(2026-10-22) GitHub Trending Go 일간 3위(+443)에 올랐다.

Beacon 한눈에: Claude Code·Codex·Cursor 등 23종 코딩 에이전트의 세션 기록을 하나의 정규화된 runtime.jsonl로 모은다
Beacon 한눈에: Claude Code·Codex·Cursor 등 23종 코딩 에이전트의 세션 기록을 하나의 정규화된 runtime.jsonl로 모은다

README의 첫 문장은 “Self-improving memory for coding agents”지만, 바이너리를 직접 돌려 보면 무게중심은 엔드포인트 텔레메트리 에이전트 쪽에 있다. 명령 체계가 beacon endpoint …로 시작하고, 출력은 Wazuh 호환 JSON이며, Splunk·Datadog·Sentinel·CrowdStrike Falcon·S3로 보내는 포워더가 기본 탑재돼 있다. 이 글은 그래서 “메모리 도구”가 아니라 “에이전트 활동을 기록·감사하는 계층”으로 보고 구조와 타협점을 따진다. 박스(리눅스 x64 VM, 8코어)에서 v1.3.45 릴리스 바이너리를 SHA-256 검증 후 받아, 격리 HOME + unshare -rn(네트워크 차단) + API 키 없음 조건으로 설치·백필·규칙 스캔·훅·handoff·MCP·규모 테스트를 직접 돌렸다. 입력은 가짜 AWS 키·GitHub 토큰·프롬프트 인젝션 문구·curl | bash·강제 푸시를 일부러 넣은 합성 Claude Code/Codex 세션이다. 수치는 2026-10-22 17:30~17:40 KST 실측이거나 GitHub API·저장소 문서에서 확인한 값이다.

한눈에 보기: 오늘 기준 스냅숏

항목 값
저장소 github.com/Asymptote-Labs/agent-beacon (생성 2026-05-12 11:49 KST) · 문서 docs.beacon.sh
라이선스 MIT (Copyright 2026 Asymptote Labs). 클라우드(beacon.sh)·엔터프라이즈 강제(enforcement) 기능은 별도
버전 v1.3.45 (2026-10-20 15:02 KST). 같은 날 1.3.43·1.3.44·1.3.45가 연달아 나올 만큼 릴리스가 잦음
GitHub 지표 2,522 stars · 231 forks · 열린 이슈+PR 51 · 커밋 2,660 · 기여자 27명(git shortlog) · 마지막 커밋 808a321(2026-10-22 06:53 KST)
Trending Go 일간 #3(+443, 16:52 KST 스냅숏). 전체 일간 목록에는 없음
코드 규모 Go 약 28.4만 줄(테스트 파일 571개 포함), 플러그인 7종(OpenCode·Cline·Pi·OpenClaw 등) TS/JS, 브라우저 확장, Terraform, 커스텀 OTel Collector
배포물 tar.gz / deb / rpm / macOS pkg(서명 팀 ID 기재) / Windows msi / Homebrew. 리눅스 tar.gz 하나에 바이너리 4개
지원 하네스 Claude Code, Codex CLI, Cursor, OpenCode, Cline, GitHub Copilot CLI, Factory Droid, Grok Build, Hermes, Pi, Gemini, Qwen, Kiro, Devin, Goose, OpenHands 등 share --harness 기준 23종

무엇이 들어 있나: 다섯 개의 얼굴

Beacon을 처음 보면 기능이 너무 많아 무엇이 핵심인지 헷갈린다. 최상위 명령만 24개다. 역할별로 묶으면 다섯 덩어리가 된다.

축 명령 하는 일 네트워크
수집(Collect) endpoint install, endpoint <harness>, ci exec, cloud 하네스 설정에 OTel 환경변수·훅을 넣고, 각 하네스의 로컬 세션 저장소를 폴링·백필해 정규화 JSONL로 기록 로컬 전용 가능
탐지(Detect) scan, rules lint/add/pull CEL로 쓴 위협 규칙을 런타임 로그에 적용. 기본 6개 내장 + 공개 룰팩 77개 읽기 전용, 네트워크 안 씀
회계(Account) token-usage, pricing 모델·세션·저장소·CI run 별 토큰과 추정 비용 로컬
연속성(Continue) handoff list/export/resume, git link, share 세션 요약 브리프 생성, 다른 하네스로 이어받기, 커밋↔세션 연결(git notes), 트레이스 공유 share만 업로드
기억(Remember) memory evaluations/candidates/skills, mcp serve 트레이스를 외부 평가기(Jev/SubQ)로 채점 → 후보 → 사람이 승인 → Agent Skill로 설치. MCP 도구 7개로 검색 평가 단계는 외부 API
핵심 관찰. “스스로 개선되는 메모리”의 실제 흐름은 로그 → 외부 LLM 평가 → 후보 → 사람 승인 → 스킬 파일이다. 자동으로 에이전트 프롬프트에 섞여 들어가는 블랙박스가 아니라, 승인 게이트가 있는 파이프라인이라는 점이 claude-mem류 ‘자동 회상’ 도구와 가장 크게 다르다. 반대로 말하면 외부 평가기 API 키가 없으면 메모리 후보는 생기지 않는다(박스 실측: 후보 0건).
구조: OTLP 수신(beacon-otelcol)·훅(beacon-hooks)·세션 저장소 폴링 세 경로가 한 로그로 합쳐지고, 그 위에서 scan·token-usage·MCP·handoff·memory가 돈다
구조: OTLP 수신(beacon-otelcol)·훅(beacon-hooks)·세션 저장소 폴링 세 경로가 한 로그로 합쳐지고, 그 위에서 scan·token-usage·MCP·handoff·memory가 돈다

아키텍처: 바이너리 네 개와 로그 하나

리눅스 tar.gz(88.2MB)를 풀면 실행 파일 네 개가 나온다. 크기 차이가 설계를 그대로 보여 준다.

바이너리 크기 역할 언제 도나
beacon 44.0MB Go CLI. 설치·백필·스캔·handoff·MCP 서버·대시보드·포워더 관리까지 전부 사용자가 직접 실행
beacon-hooks 10.6MB 하네스 훅 진입점. stdin으로 받은 이벤트를 정규화해 로그에 한 줄 추가 에이전트가 도구 호출마다 실행
beacon-otelcol 30.0MB OpenTelemetry Collector 커스텀 빌드. 127.0.0.1:4317/4318에서 OTLP 수신 → privacy processor → beacon JSON exporter 상주 서비스(systemd/launchd/감독 프로세스)
beacon-vector 148.3MB Vector 기반 포워더. SIEM·오브젝트 스토리지 전송용 포워딩 켤 때만

데이터 경로는 세 갈래가 한 파일로 합쳐진다. ① OTLP 경로: Claude Code·Codex처럼 OpenTelemetry를 내보내는 하네스는 환경변수로 로컬 컬렉터를 가리키게 한다. ② 훅 경로: PreToolUse·PostToolUse·SessionStart 같은 훅에서 beacon-hooks가 직접 로그에 쓴다. ③ 폴링/백필 경로: OTel도 훅도 없는 하네스(Cursor·Cline·OpenCode SQLite 등)는 세션 저장소를 직접 읽는다. 설치 시 지난 30일치를 한 번에 백필한다. 세 경로 모두 같은 스키마(schema_version 1.0, event.action, harness.name, session.id, trace.id)로 ~/.beacon/endpoint/logs/runtime.jsonl에 append되고, 이 파일이 스캔·집계·MCP·handoff의 단일 진실 원천이 된다.

event.action 의미
session.started 세션 시작, 작업 디렉터리·브랜치·하네스 버전
prompt.submitted 사용자 프롬프트(비밀 마스킹 후)
agent.message 모델 응답 텍스트
tool.invoked / command.executed 도구 호출과 셸 명령·출력
file.modified / file.read 파일 변경·읽기(하네스가 Read 도구로 읽었을 때만 file.read)
token.usage 입력·출력·캐시 읽기/생성 토큰
telemetry.enabled 엔드포인트 자체 상태 이벤트

이 설계의 장점은 분명하다. 하네스가 늘어도 탐지 규칙·대시보드·MCP는 그대로이고, 규칙은 “Claude냐 Codex냐”가 아니라 e.event.action == "command.executed" 같은 공통 필드만 본다. 단점은 정규화가 하네스가 무엇을 알려 주느냐에 묶인다는 것이다. 아래 실측에서 보듯 같은 ‘자격 증명 파일 읽기’라도 Read 도구로 읽으면 file.read, Bash cat으로 읽으면 command.executed가 되고, 규칙 매칭 결과가 달라진다.

설치가 실제로 바꾸는 것

엔드포인트 에이전트는 사용자 환경 설정을 직접 고친다. 도입 전 가장 먼저 확인해야 할 부분이라 격리 HOME에서 --dry-run과 실제 설치(--service none --no-start) 후 생성·변경 파일을 모두 뽑았다.

export HOME=$PWD/run/home DO_NOT_TRACK=1
unshare -rn ./beacon endpoint install --harness claude,codex \
  --service none --no-start --no-browser --collector ./beacon-otelcol
# Claude Code hooks installed: ~/.claude/settings.json
# Codex CLI endpoint hooks installed: ~/.codex/hooks.json
# Session backfill: 485 events from the last 30 days (Claude Code 480, Codex CLI 5).
경로 변경 내용 의미
~/.claude/settings.json 훅 10종(PreToolUse·PostToolUse·PostToolUseFailure·PermissionRequest·SessionStart/End·Stop·SubagentStart/Stop·UserPromptSubmit) + env에 CLAUDE_CODE_ENABLE_TELEMETRY=1, OTEL_LOG_USER_PROMPTS=1, OTEL_LOG_TOOL_DETAILS=1 프롬프트·도구 인자가 OTLP로 나감(로컬 컬렉터로)
~/.codex/config.toml [otel] log_user_prompt = true, OTLP gRPC 127.0.0.1:4317 Codex 프롬프트 로깅이 켜짐
~/.codex/hooks.json SessionStart·Stop 등에 beacon-hooks (timeout 10초) 훅 실패 시 하네스 동작은 계속
~/.beacon/endpoint/ config.json, otelcol.yaml, install-manifest.json, hooks/beacon-hooks 사본, state/*.json, logs/runtime.jsonl(0600, 디렉터리 0700) 제거 시 manifest 기준 원복
서비스 systemd/launchd 없으면 감독 프로세스(collector.pid). 인벤토리 하트비트는 스케줄러 없으면 미설치 컨테이너·CI에서 주의
프라이버시 체크포인트. 설치는 하네스의 프롬프트·도구 상세 로깅 플래그를 켠다. 기본 목적지는 로컬 파일이지만, 나중에 endpoint connect나 SIEM 포워딩을 켜면 같은 데이터가 밖으로 나간다. Beacon Cloud에는 ‘Metadata-only’ 모드가 있어 프롬프트·응답·도구 인자·diff를 빼고 보낼 수 있다. 회사 노트북에 깔 거라면 이 모드와 포워딩 대상부터 정책으로 정해 두는 편이 맞다.
박스 실측: 가짜 GitHub 토큰·AWS 키는 런타임 로그·평가 목록·브리프 본문에서 마스킹됐지만 handoff 브리프 제목 줄에는 원문이 남았다
박스 실측: 가짜 GitHub 토큰·AWS 키는 런타임 로그·평가 목록·브리프 본문에서 마스킹됐지만 handoff 브리프 제목 줄에는 원문이 남았다

박스 실측 ①: 비밀 마스킹과 탐지 규칙

합성 세션 20개(Claude Code) + 1개(Codex)를 만들었다. 각 Claude 세션은 첫 프롬프트에 가짜 GitHub 토큰(ghp_…)과 AWS 키(AKIA…EXAMPLE), “ignore previous instructions” 문구를 넣고, 도구 호출 5개(cat ~/.aws/credentials, curl … | bash, .env 편집, npm test, git push --force origin main)를 수행한다. 원본 세션 파일 합계 126,108바이트가 백필 후 런타임 로그 486줄·477,359바이트(약 3.8배)가 됐다.

이벤트 건수 이벤트 건수
agent.message 120 token.usage 120
tool.invoked 101 command.executed 81
prompt.submitted 21 session.started 21
file.modified 20 telemetry.enabled 1
출력물 결과 비고
런타임 로그(JSONL) 0건 원문 노출 (ghp·AKIA 모두 [REDACTED], 접두어만 남김) 프롬프트와 cat 출력 속 키 모두 마스킹
memory evaluations run --dry-run 목록 마스킹된 제목 표시 OK
handoff export 본문 프롬프트·명령 출력 마스킹 OK
handoff export Title 줄 토큰·키 원문 그대로 1건 세션 저장소에서 직접 뽑은 제목이 마스킹을 거치지 않음
handoff list TITLE 열 ghp_12345… 앞부분 원문 긴 제목 잘림 덕에 일부만 보임
발견 사항. 런타임 로그 경로의 마스킹은 견고했지만, handoff 브리프는 “런타임의 원래 세션 저장소”를 읽기 때문에 제목(첫 프롬프트)이 마스킹 없이 브리프 파일(권한 0600)에 들어갔다. 브리프는 다른 에이전트에게 넘기려고 만드는 파일이라, 프롬프트에 토큰을 붙여 넣는 습관이 있다면 이 경로가 유출 지점이 될 수 있다. v1.3.45 기준 동작이며, 합성 키로만 확인했다.

탐지는 내장 기본 규칙 6개만으로 먼저 돌리고, 공개 룰팩(77개, 카테고리 10종)을 rules lint로 검증한 뒤 스토어에 넣어 다시 돌렸다. 린트는 규칙마다 들어 있는 적합성 픽스처 544개를 0 실패로 통과했다(132ms).

룰팩 카테고리 규칙 수
context-exfiltration 13
risky-command 13
prompt-injection 11
sensitive-edit 11
credential-access 9
external-access 5
source-control 5
agent-control 4
approval-abuse 3
resource-consumption 3
규칙 세트 발견 내역 시간
기본 6개 40 curl-pipe-to-shell(high) 20 · ignore-previous-instructions(medium) 20 43ms
기본 + 룰팩 77개 80 위 2개 + git-force-push-to-protected(high) 20 · git-remote-tampering(high) 20 331ms
--fail-on high 종료 코드 1 CI 게이트로 바로 사용 가능 –
합성 행위 결과 이유
curl … | bash 탐지 명령 문자열 정규식
프롬프트 속 “ignore previous instructions” 탐지 프롬프트 인젝션 규칙
git push --force origin main 탐지(2개 규칙 중복) 보호 브랜치 강제 푸시 + remote-tampering 정규식이 push --force도 포함 → 같은 행위가 2건
Bash cat ~/.aws/credentials 미탐 credential-file-read는 file.read 이벤트만 봄. 셸 명령으로 읽으면 command.executed라 빠짐
.env 편집 미탐 sensitive-edit 규칙들은 .envrc·CI·IaC·의존성 매니페스트 등 대상. 일반 .env는 범위 밖

정리하면 규칙 엔진 자체(CEL + 픽스처 내장 + 린트)는 잘 만들어졌지만, 탐지 범위는 이벤트 정규화의 해상도를 넘지 못한다. 자격 증명 파일 읽기처럼 중요한 행위는 Read 도구 경로와 셸 경로 양쪽 규칙을 함께 두는 게 안전하고, 중복 발견은 세션·행위 단위로 묶어 보는 후처리가 필요하다.

박스 실측 ②: 훅 지연, 토큰 집계, MCP

PreToolUse 입력(Bash) 지연 ms(5회) 훅 응답 동작
ls -la 6–8 {} 기록
curl … | bash 5–6 {} 기록(차단 없음)
cat ~/.aws/credentials 5–6 {} 기록(차단 없음)
rm -rf / --no-preserve-root 5–7 {} 기록(차단 없음)

훅 한 번에 5–8ms로 체감 비용은 거의 없다. 대신 오픈소스 판 훅은 관찰 전용이다. 위험 명령에도 빈 JSON을 돌려주고 차단하지 않는다. 실행 전 차단은 endpoint enforcement(“enterprise edition” 표기)로 분리돼 있다. 즉 무료 판은 “사후 탐지 + 감사 로그”이고, 실시간 가드레일을 원하면 하네스 자체 권한 설정이나 다른 도구와 함께 써야 한다. 훅 입력 4종 × 5회 = 20줄이 로그에 추가됐다.

token-usage (합성 20세션) 값
입력 토큰 153,000
출력 토큰 9,100
캐시 읽기 1,090,000
캐시 생성 30,000
추정 비용 $1.035 (claude-sonnet-4-5 가격표 기준, 세션당 $0.05175)
집계 시간 약 70ms(20세션) / 197ms(약 530세션)

MCP 서버(beacon mcp serve, stdio)는 search_activity, summarize_activity, get_activity_event, list_activity_filters, search_memory, get_memory, get_memory_context 7개 도구를 노출했다. 모두 읽기 계열이지만 readOnlyHint 주석은 붙어 있지 않았다. 에이전트가 “지난주에 이 테스트 어떻게 고쳤지?”를 스스로 찾게 하는 통로가 이것이다.

박스 실측: 룰팩 77개+기본 6개로 curl|bash·인젝션 문구·강제 푸시는 잡고 셸 cat 자격 증명 읽기·.env 편집은 놓쳤다. 스캔 20세션 0.33초, 1,000세션 4.6초, 훅 5~8ms
박스 실측: 룰팩 77개+기본 6개로 curl|bash·인젝션 문구·강제 푸시는 잡고 셸 cat 자격 증명 읽기·.env 편집은 놓쳤다. 스캔 20세션 0.33초, 1,000세션 4.6초, 훅 5~8ms

박스 실측 ③: 규모와 백필 한도

합성 세션 수 원본 크기 설치+백필 런타임 로그 스캔 발견 / 시간(83규칙)
20 + 1 126KB 0.27초 486줄 / 0.48MB 80 / 331ms
200 + 1 1.26MB 1.04초 4,806줄 / 4.7MB 800 / 2.03초
1,000 + 1 6.31MB 2.75초 12,778 이벤트(로그 10.5MB 회전 + 2.1MB) 2,129 / 4.64초

1,000세션에서는 “Claude Code: stopped at the backfill size limit”가 뜨고 약 530세션(12,773 이벤트)에서 백필이 멈췄다. 나머지는 beacon endpoint claude sync로 따로 읽으라고 안내한다. 로그는 약 10MB에서 runtime.jsonl.1로 회전했고, 스캔은 회전 파일까지 읽어 발견 수가 세션 수에 비례했다(약 532×4=2,128). 스캔은 이벤트 수에 대략 선형이라 CI에서 세션 로그 하나 검사하는 용도에는 충분히 빠르지만, 수개월치 개발자 PC 로그 전체를 주기적으로 훑는 용도라면 SIEM 쪽 탐지로 넘기는 게 자연스럽다.

설계 타협점

선택 얻는 것 치르는 것
단일 JSONL 로그 단순·이식성·grep 가능, 어떤 SIEM에도 붙음 인덱스 없음 → 검색·스캔이 선형, 10MB 회전
3중 수집(OTLP·훅·폴링) 하네스가 무엇을 지원하든 최대한 모음 같은 행위가 경로마다 다른 action으로 기록, 중복·누락 관리 필요
관찰 전용 훅 5–8ms, 에이전트 동작 안 깨뜨림 차단 없음. 실시간 방어는 유료/외부
CEL 규칙 + 내장 픽스처 규칙이 데이터라 바이너리 안 커짐, 린트로 회귀 방지 규칙 품질이 정규화 해상도에 묶임(셸 cat 미탐)
승인 게이트형 메모리 엉뚱한 기억이 자동 주입되지 않음, 스킬 파일로 투명 외부 평가기 API·사람 리뷰 비용, 오프라인이면 후보 0
하네스 설정 직접 수정 한 명령으로 23종 대응 프롬프트 로깅 플래그를 켬, 하네스 업데이트와 충돌 가능
로컬 우선 + 클라우드 옵션 기본은 아무것도 업로드 안 함 share·connect·포워더를 켜면 정책 관리 필요

비슷한 도구와 위치

도구 초점 범위 맞는 사용자
Beacon 세션 텔레메트리 + 탐지 + handoff + 승인형 메모리 하네스 23종, 로컬 JSONL/SIEM 보안·플랫폼팀, 다중 하네스 팀
claude-mem 계열 세션 요약을 자동 회상 주로 단일 하네스 개인 개발자 연속성
context-mode / headroom 류 컨텍스트 압축·도구 출력 샌드박스 토큰 절감 비용·문맥 최적화
하네스 내장 OTel 각 벤더 텔레메트리 벤더별 형식 단일 도구 조직

도입 체크리스트

항목 확인 내용
포워딩 정책 로컬만 / Metadata-only / 전체 중 무엇인지 먼저 결정
프롬프트 로깅 설치가 켜는 OTEL_LOG_USER_PROMPTS·log_user_prompt를 조직 정책과 대조
handoff 브리프 제목 마스킹 이슈가 고쳐지기 전까지 브리프 공유 전 수동 확인
룰팩 rules lint 통과 후 add, CI에선 scan --fail-on high
셸 경로 보강 자격 증명·.env를 셸로 읽는 경우를 잡는 command.executed 규칙 추가
백필 한도 대량 세션 환경은 설치 후 endpoint claude sync
업데이트 확인 필요 시 BEACON_UPDATE_CHECK로 비활성화, 버전 고정
# 최소 로컬 감사 구성 예
beacon endpoint install --harness claude,codex --no-backfill
beacon rules lint ./rules && beacon rules add ./rules
beacon scan --min-severity medium
beacon token-usage --top 10
beacon handoff export <session-id> --print   # 공유 전 제목 줄 확인

실측하지 않은 것

항목 상태
실제 에이전트 세션 합성 JSONL만 사용. 실제 Claude Code/Codex 실행·OTLP 실시간 수신은 미검증
메모리 평가기 Jev/SubQ API 키 없음 → dry-run(21 트레이스, 추정 $0.00735)까지만
클라우드·SIEM beacon.sh 로그인, share, Splunk/Datadog 포워딩 미실행
다른 OS macOS pkg·Windows msi, Cursor·OpenCode SQLite 폴링 미검증

결론

Beacon은 “에이전트가 배운 걸 기억하게 한다”는 문구보다 “여러 에이전트가 이 컴퓨터에서 무엇을 했는지 한 형식으로 남긴다”에서 진가가 나온다. 23종 하네스를 한 스키마로 묶는 정규화, 픽스처가 딸린 CEL 규칙, 5–8ms짜리 훅, 마스킹된 로컬 로그는 플랫폼·보안팀이 바로 쓸 만한 수준이다. 반면 무료 판 훅은 막지 않고 기록만 하며, 탐지 범위는 이벤트가 얼마나 잘게 정규화되느냐에 묶이고, handoff 제목처럼 마스킹 경로 밖의 출력이 아직 남아 있다. 메모리는 외부 평가기와 사람 승인이 있어야 돈다. 여러 코딩 에이전트를 섞어 쓰는 팀이라면 먼저 로컬 전용 모드로 깔아 scan과 token-usage부터 써 보고, 포워딩과 메모리는 정책이 정해진 다음 켜는 순서를 권한다.