
halo-record v0.2.42
AI 에이전트를 위한 변조 방지 감사 추적: 해시 체인 방식의 런타임 레코드, 의존성 없음, 누구나 검증 가능.
halo-record
AI 에이전트를 위한 변조 감지 감사 추적 — 해시 체인으로 연결된 Runtime Record를 고객이 직접 확인할 수 있는 Runtime Report로 렌더링합니다.
에이전트가 수행하는 모든 작업(도구 호출, 모델 호출, 데이터 접근, 승인)은 추가 전용(append-only) 해시 체인 로그의 하나의 Runtime Record가 되며, Runtime Report는 그 체인을 자체 검증 가능한 HTML 페이지로 렌더링한 것입니다. 체인의 체크포인트를 보유한 누구든지, 그것을 생성한 주체를 신뢰하지 않고도 그 뒤의 레코드가 변경되지 않았음을 검증할 수 있습니다 — 그 체크포인트가 핵심 요소입니다. 체인 자체만으로는 레코더를 운영하는 주체를 제외한 모든 사람에 대해 변조 감지가 가능합니다(LIMITS.md §1). 고객의 보안 팀이 "귀하의 에이전트가 우리 데이터로 무엇을 했습니까?"라고 물을 때, 문단 대신 링크 하나를 건네주면 됩니다. 보안 검토는 이미 SOC 2 체크리스트 옆에서 AI 관련 질문을 던지고 있으며, 점점 더 그 질문들은 ISO 42001, EU AI Act의 기록 보존 조항, 그리고 고객 자체 설문지에서 나옵니다. 오늘날에는 서면 보증서만으로도 통과됩니다. 이 프로젝트의 배팅은 그것이 오래가지 않으리라는 것입니다.
Help Net Security 에 소개됨 (2026년 8월).
레코드 형식은 개방되어 있으며 자유롭게 구현할 수 있습니다. 이 패키지는 레코더, 검증기, 위트니스 클라이언트, 리포트 서버를 포함한 참조 구현입니다.
halo-record를 사용 중이거나 고려 중이신가요? 누구이며 무엇을 위해 사용하는지 알려주세요 → Who's using halo-record?
직접 확인하세요
에이전트 내부에 레코더를 설치하라는 요청을 받고 있습니다. 그것을 믿음으로 받아들여서는 안 됩니다:
- 런타임 의존성 제로. 표준 라이브러리만 사용합니다.
pip install halo-record는 정확히 하나의 패키지만 설치합니다. - 네트워크 호출 없음, 단 세 가지 옵트인 호출은 예외입니다 — 위트니스에 앵커링(주체 ID, 레코드 수, 두 개의 체인 지문 — 헤드와 체인 루트 — 를 전송), 위트니스의 체크포인트 다시 읽기(주체 ID 전송), 그리고 RFC 3161 타임스탬프(체크포인트의 상태 해시만 Timestamp Authority에 전송). 모두 직접 호출하지 않는 한 비활성화되어 있으며, 레코드 내용은 절대 인프라를 벗어나지 않습니다.
- 원시 도구 인수는 해시 처리되며, 함께 마스킹된 요약이 제공됩니다. 인수는 정규화된 해시와 요약으로 저장됩니다: 알려진 비밀 및 PII 패턴이 마스킹된 인수 텍스트이며, 최대 200자로 제한됩니다. 어떤 패턴에도 일치하지 않는 짧은 입력은 요약에 전체가 나타나며, 해시 전용 모드(
summaries=False)는 요약을 전혀 유지하지 않습니다. 마스킹은 최선의 노력(일반적인 비밀 및 PII 형식에 대한 정규식과 엔트로피 포괄 규칙)이므로, 보장이 아닌 심층 방어로 취급하십시오.summary외에 제공하는 결과 필드는 주어진 그대로 봉인됩니다(LIMITS §13). - 감사하기에 충분히 작습니다. 약 5,300줄의 Python(공백과 주석을 제외한 코드 줄 수). 오후 한나절이면 전부 읽을 수 있습니다.
- Apache-2.0.
- 문서가 일급 시민입니다. LIMITS.md(체인이 증명할 수 없는 것), PRIVACY.md(레코드에 포함되는 내용과 머신을 떠나는 것), RETENTION.md(보존 정책 하에서의 운영), 그리고 REVIEWERS.md — 네 개의 명령으로 이루어진 독립 검사와 검토 결과를 위한 인용 형식.
각 계층이 증명하는 것 — 이 프로젝트의 핵심 구분(LIMITS.md §1): 직접 보유한 체인은 이미 누군가가 보유한 헤드를 기준으로 레코드가 편집되지 않았음을 증명합니다. 운영자 외부에 보관된 체크포인트만이 어떤 레코드도 제거되지 않았음을 증명합니다. 그리고 어떤 해시도 모든 작업이 포착되었음을 증명하지 못합니다.
| 주장 | 자체 보유 체인 | + 외부 체크포인트 | + 신뢰할 수 있는 포착 |
|---|---|---|---|
| 확립된 아티팩트에 대한 편집 감지 | ✔ | ✔ | ✔ |
| 커밋된 이력의 재작성 감지 | — | ✔ | ✔ |
| 누락/지연된 체크포인트 감지 | — | ✔ (합의된 주기) | ✔ |
| 모든 작업이 기록되었음을 증명 | — | — | 포착 경계에 따라 다름 |
설치 전에 하나 보기: 샘플 Runtime Report — 가상의 데이터, 실제 체인, 그리고 지켜보는 동안 브라우저에서 스스로 재검증됩니다.
60초 데모
에이전트가 필요하지 않습니다. uv를 사용하면 설치할 것이 없습니다:``` uvx --from halo-record halo demo --serve
또는 고전적인 방식으로:```
pip install halo-record
halo demo --serve
둘 중 하나는 두 고객을 둔 가상의 지원 에이전트 벤더를 스캐폴딩하고, 체인을 목격하며(운영자 외부의 한 곳을 대신하는 로컬 witness 파일 포함 — LIMITS.md §1 참조), 게이트된 Runtime Reports를 제공하고, 브라우저에서 운영자 콘솔을 엽니다. 그런 다음 변조 테스트를 시도해 보세요: .jsonl 파일 중 하나에서 한 줄을 삭제하고 다시 로드하세요. 보고서가 이를 잡아냅니다.
자신만의 에이전트 기록하기
경계에서 한 줄:```python from halo_record import trace
agent = trace(run_my_agent, profile="my-agent", log="audit.jsonl") # wraps your entrypoint; records the run boundary to ./audit.jsonl — add record_call() or a framework adapter at each tool boundary to capture individual calls
`from halo import ...` 편의 심(shim)도 함께 제공되지만, PyPI의 `halo` 이름은 무관한 터미널 스피너 패키지에 속해 있으며, 해당 패키지가 설치되어 있으면 임포트에서 우선권을 가진다. `halo_record`는 모호하지 않으므로 예제에서는 이를 사용한다.
`log=`가 없으면 레코드는 `~/.halo/my-agent.jsonl`로 간다(에이전트당 체인 하나). 래퍼는 실행 경계를 봉인하며, 증거는 호출별 레코드에 남는다. 이를 프레임워크 어댑터(아래 매트릭스)로 캡처하거나 — 또는 명시적으로 캡처할 수 있으며, 이는 위임 링크가 어떻게 연결되는지도 보여준다:```python
from halo_record import Recorder, record_call
rec = Recorder("audit.jsonl")
with record_call(rec, "crm.lookup", {"account": "acct-9"}) as call: # one sealed record per tool call
call.result = crm.lookup("acct-9")
with record_call(rec, "payments.refund", {"amount": 120},
parent_id=rec.last_record_id()) as call: # child links to the action that spawned it
call.result = payments.refund(120)
그런 다음 보고서를 렌더링합니다:``` halo report audit.jsonl -o report.html # one chain -> self-verifying HTML halo serve ./records --port 8721 # all tenants, gated per customer
quickstart는 브라우저에서 자신의 에이전트 Runtime Report를 보고 있을 때 끝납니다. JSONL 파일만 받고 보고서를 받지 못했다면 뭔가 잘못된 것입니다: 이슈를 열어 주세요.
### 검증 블록
가드레일이나 정책 계층이 해당 작업을 검사했다면, 그 판정이 레코드에 실릴 수 있습니다 — 게이트가 무엇을 결정했는지 기록하는 선택적 블록으로, 다른 모든 필드와 마찬가지로 해시 체인에 봉인됩니다:```python
from halo_record import build
build("tool_call", "security", tool="payments.refund",
verification={"status": "allowed", "verifier": "gate/1.2",
"policy_ref": "sha256:1f3a...",
"checked_at": "2026-08-01T12:00:00Z"})
한 가지 명명 관련 참고 사항: 이 패키지는 record 함수(데코레이터)를 내보내며, 이는 패키지 객체에서 halo_record.record 모듈을 가립니다. 내부 요소를 사용하려면 import halo_record.record as record 대신 모듈 경로에서 직접 가져오세요 — from halo_record.record import build.
이는 다음과 같이 레코드에 봉인됩니다:```json "verification": {"status": "allowed", "verifier": "gate/1.2", "policy_ref": "sha256:1f3a...", "checked_at": "2026-08-01T12:00:00Z"}
`record_call(...)`은 동일한 `verification=` 키워드를 받습니다. `status`는 블록 내에서 필수이며, `verifier`, `policy_ref`, `checked_at`은 선택 사항입니다. 각 상태의 의미는 다음과 같습니다:
| 상태 | 게이트가 보고하는 내용 | 작업이 실행되었는가? |
|---|---|---|
| `allowed` | 작업을 허용함 | 예 — 작업이 진행됨 |
| `blocked` | 작업을 거부함 | 이 필드가 아니라 통합에 의해 결정됨 — 레코드가 여전히 결과를 담고 있을 수 있으며, 차단 자체만으로 미실행이 증명되지는 않음 |
| `modified` | 실행 전에 작업을 변경함 — `action.input`은 수정 후 **실행된 그대로**의 작업을 설명함 | 예, 변경된 형태로 |
| `unverified` | 실행되었으나(또는 참조되었으나) 판단을 내리지 않음 — 검증 주장이 전혀 이루어지지 않았음을 의미하는 블록 부재와는 구별됨 | 예 — 판정 없이 작업이 진행됨 |
이 블록은 운영자의 통합 코드에 의해 제공되며, 게이트가 말했다고 보고하는 내용을 기록합니다 — `principal`과 동일한 신뢰 태세입니다([LIMITS](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#11-verification-status-is-the-gates-report-not-halos-finding) 참조). 봉인은 상태가 사후에 편집되지 않았음을 증명하지만, 검사가 발생했는지, 판정이 올바랐는지, 또는 차단된 작업이 실행되지 않았는지는 증명하지 않습니다. 이는 독립적인 검증이 아닙니다.
`policy_ref`를 증거로 사용할 수 있으려면 규칙 집합의 콘텐츠 해시를 사용하고 규칙 집합 아티팩트를 보관하십시오 — 해석할 수 없는 레이블은 해당 필드를 장식적으로 만듭니다.
## 이미 실행 중인 것에 연결하기
| 경계에서 캡처됨 | 기존 텔레메트리에서 수집됨 |
|---|---|
| 네이티브 레코더 (`from halo_record import trace`) | OpenTelemetry GenAI 스팬 |
| MCP 인터셉터 | LiteLLM 콜백 |
| LangChain / LangGraph 콜백 | Langfuse 내보내기 |
| OpenAI Agents SDK 훅 | 모든 게이트웨이 / 리버스 프록시 로그 |
| Claude Agent SDK 훅 | Claude Code 및 Codex CLI `PostToolUse` 훅 (도구 실행 후 발화) |
프레임워크 어댑터와 수집 경로는 각 레코드에 `source` 태그를 찍으므로, 보고서는 각 증거가 어떻게 수집되었는지 공개합니다. 캡처된 레코드와 수집된 레코드는 동일한 체인에 존재합니다.
LangChain / LangGraph의 경우, 이는 콜백 핸들러입니다:```python
from halo_record import Recorder
from halo_record.integrations.langchain import HaloCallbackHandler
recorder = Recorder("audit.jsonl")
result = my_chain.invoke(inputs, config={"callbacks": [HaloCallbackHandler(recorder)]}) # every tool call becomes a record
MCP의 경우, 하나의 호출이 클라이언트 세션을 감싸며 — 그러면 어떤 MCP를 사용하는 에이전트든, 어떤 프레임워크가 구동하든 관계없이 모든 도구 호출에 대해 레코드를 내보냅니다:```python from halo_record.integrations.mcp import instrument_client_session
instrument_client_session(session, Recorder("audit.jsonl"), server="stripe") # every session.call_tool() is now recorded
게이트웨이 또는 프록시 로그(Cloudflare AI Gateway, Portkey, 모델 앞단의 nginx)의 경우, 로그 행을 체인에 매핑합니다 — 경계에서 캡처된 것이 아니라 수집된 것으로 명시적으로 태그합니다:```python
from halo_record.integrations.gateway import record_log