원문 캡처 · github.com
원문 캡처 · github.com

Mnestiq는 AI 에이전트가 무엇을 보고, 판단하고, 실행했는지 변조 여부를 확인할 수 있는 서명된 증거로 남기는 도구다. 대시보드 관찰보다 사고 대응에 초점을 맞췄으며, 에이전트 안에 SDK를 넣거나 프록시를 통해 외부에서 기록할 수 있다.

에이전트가 고객 목록을 공격자에게 이메일로 보냈을 때 관찰 추적은 그런 일이 일어났다는 사실을 보여준다. Mnestiq의 증거 기록은 어떤 실행(run)의 어느 단계에서 일이 벌어졌는지, 지시가 사용자 대신 웹 페이지에서 왔는지, 자동 승인 정책이 실행을 통과시켰는지 확인하고, 사후에 기록이 편집되지 않았음을 검증하는 데 쓰인다.

저장소의 예시는 방화벽 로그에 있는 연결을 에이전트의 실행 기록과 연결한다. 방화벽 로그에는 10.0.4.17:53120 -> 198.51.100.9:25 (50 bytes)가 나오고 에이전트 식별 정보는 없지만, Mnestiq 기록에는 agent=support-bot, run=c7831ff7, step=6, sandbox=container:7f3a9c, operator=acme-helpdesk가 남는다. 앞선 4단계에서 fetch_page를 통해 신뢰할 수 없는 입력이 들어왔고, 수신자를 고객 도메인과 대조하지 않은 email-auto-approve 정책이 작업을 승인했다는 내용도 포함한다.

기록과 증거 검증

각 이벤트에는 run_id, step_id, parent_run_id, agent_id, operator_id, sandbox_id가 포함된다. 컨테이너와 Kubernetes 파드는 샌드박스 식별 정보를 자동으로 감지한다. 외부 HTTP 요청에는 X-Mnestiq-Run-Id와 X-Mnestiq-Egress-Id 헤더를 붙이며, 모든 외부 요청과 원시 TCP 연결(SMTP, 데이터베이스, 소켓)을 로컬·원격 IP 및 포트와 함께 기록한다. 이 값으로 흐름 로그와 방화벽 로그를 대조할 수 있다.

기록 시점에 모델 문맥의 각 항목에는 system, user, model, tool_output, retrieved_doc, web, agent_msg, memory 중 출처를 표시한다. 이 출처 정보는 나중에 프롬프트 인젝션을 분석할 때 활용한다.

기록은 RFC 8785 정규 JSON과 SHA-256을 사용해 해시 체인으로 연결하고, 주기적으로 RFC 9162 머클 트리 체크포인트를 Ed25519 서명으로 봉인한다. 수정·삭제·삽입·순서 변경은 검증에 실패한다. 감사자에게 증거를 전달하기 전에 프롬프트, 도구 인수, 도구 출력을 제거할 수도 있으며, 이때도 체인 검증은 유지된다.

Anthropic 및 OpenAI 클라이언트를 동기·비동기 방식으로 감싸는 SDK 캡처 기능을 제공한다. Chat Completions와 Responses API를 지원한다. 기록에 실패해도 에이전트 실행을 중단하지 않는 것이 기본 동작이다. 코드 변경을 원하지 않으면 mnestiq proxy를 사용해 에이전트 외부에서 모델 호출을 기록할 수 있다. 언어나 프레임워크와 관계없이 모델 API 기본 URL을 프록시로 지정하는 방식이다.

설치와 사용

Anthropic 연동을 설치하는 빠른 시작 예시는 다음과 같다. OpenAI 연동은 mnestiq[openai]를 설치하면 된다.

python
pip install "mnestiq[anthropic]" # or mnestiq[openai]
mnestiq keygen --out keys

import anthropic
from mnestiq import FileSink, Recorder
from mnestiq.keys import load_private_key
from mnestiq.egress import instrument_egress
from mnestiq.integrations.anthropic import instrument_anthropic

recorder = Recorder(FileSink("evidence.jsonl"), agent_id="support-bot",
 operator_id="acme-helpdesk", signing_key=load_private_key("keys/signing.key"))

@recorder.tool(source="web") # output of this tool is untrusted web content
def fetch_page(url: str) -> str: ...

client = instrument_anthropic(anthropic.Anthropic(), recorder)
instrument_egress() # stamp + record outbound connections during runs

with recorder.run(effective_permissions={"email": "send"}):
 client.messages.create(model="claude-sonnet-5-5", max_tokens=1024, messages=[...])

recorder.close() # writes the final signed checkpoint

mnestiq verify evidence.jsonl --trusted-key keys/signing.pub
mnestiq inspect evidence.jsonl
mnestiq redact evidence.jsonl evidence.redacted.jsonl

테스트할 때는 에이전트가 읽을 수 있는 키 파일을 사용해도 된다. 프로덕션에서는 에이전트가 키를 읽지 못하도록 해야 한다. 다른 계정으로 실행하는 mnestiq signer serve와 signer=SignerClient.from_env()를 사용하거나 signer=AzureKeyVaultSigner(...)를 지정할 수 있다. 자세한 내용은 서명 문서를 참조하면 된다.

어떤 언어로 작성된 에이전트든 외부 프록시로 기록하려면 다음 명령과 환경 변수를 사용한다.

bash
mnestiq proxy evidence.jsonl --agent support-bot --key keys/signing.key
export OPENAI_BASE_URL=http://127.0.0.1:8787/openai/v1 # and/or
export ANTHROPIC_BASE_URL=http://127.0.0.1:8787/anthropic

에이전트 코드는 바꾸지 않아도 된다. 에이전트가 보내는 모든 모델 호출은 evidence.jsonl에 서명된 상태로 기록된다. 프록시가 볼 수 있는 정보와 볼 수 없는 정보, 에이전트가 프록시를 우회하지 못하도록 배치하는 방법은 프록시 문서에 설명돼 있다.

API 키 없이 시연을 먼저 해볼 수도 있다. 스크립트로 구성한 지원 에이전트가 악성 웹 페이지를 읽고 고객 데이터를 이메일로 내보내며, 이후 남겨진 증거를 조사하는 예시다.

bash
git clone https://github.com/mnestiq/mnestiq && cd mnestiq
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e python httpx
python examples/injection_incident.py --out evidence
mnestiq dashboard evidence --trusted-key evidence/incident.pub

대시보드는 다음 명령으로 실행한다.

bash
mnestiq dashboard evidence/ --trusted-key keys/signing.pub

명령이 출력하는 http://127.0.0.1:8765/?key=... 주소에서 로컬 조사 화면을 연다. 주소의 키는 같은 컴퓨터의 다른 계정이 화면에 접근하지 못하게 한다. 화면에는 실행과 하위 실행, 모델 문맥의 출처를 색으로 구분한 단계별 시간순 기록, 발견 사항, 각 기록의 검증 상태(봉인됨·봉인되지 않음·변조됨)가 표시된다. 방화벽이나 흐름 로그의 ip:port를 입력하면 해당 연결을 연 에이전트 단계로 이동하는 연결 검색 기능도 있다. 에이전트가 기록을 추가하면 대시보드가 실시간으로 갱신된다.

발견 규칙과 대시보드의 한계

대시보드는 다음 여섯 가지 규칙에 해당하는 상황을 발견 사항으로 표시한다.

규칙이름발견 조건
MNQ-001신뢰할 수 없는 인수도구 호출 인수에 이메일 주소, URL, 계좌 번호 또는 긴 숫자가 포함되고, 그 값이 신뢰할 수 없는 콘텐츠에는 있지만 시스템 프롬프트나 사용자 메시지에는 없는 경우다.
MNQ-002자동 승인신뢰할 수 없는 콘텐츠가 모델에 전달된 뒤 사람의 승인 없이 작업이 승인되고, 승인된 호출에 해당 콘텐츠의 값이 포함된 경우다. 일상적인 답변을 승인하는 정책은 단서가 아니다.
MNQ-003오염된 입력 뒤의 외부 연결신뢰할 수 없는 콘텐츠가 모델에 전달된 뒤 루프백이 아닌 외부 연결이 발생한 경우다.
MNQ-004신뢰할 수 없는 입력에 따른 민감 작업자격 증명, 권한, 삭제, 취소, 계정 변경, 결제, 예약 등 민감 작업의 인수가 사용자나 시스템의 말에는 없고 신뢰할 수 없는 콘텐츠에 있는 값으로 실행된 경우다. 도구에 @recorder.tool(sensitive=True)를 표시할 수 있으며, 표시하지 않은 도구는 이름을 기준으로 판단한다.
MNQ-005답변의 신뢰할 수 없는 링크모델 답변에 이미지 또는 쿼리 데이터가 포함된 링크가 있고, 해당 호스트가 사용자나 시스템의 말에는 없지만 신뢰할 수 없는 콘텐츠에는 있는 경우다. 답변을 표시하는 즉시 이미지가 불러와지므로 도구 호출 없이도 데이터가 외부로 나갈 수 있다.
MNQ-006내부 데이터의 새 수신자 전송도구가 회사 시스템의 데이터(출처가 internal로 표시된 도구 출력)를 해당 시스템이나 시스템 프롬프트에 나오지 않은 주소 또는 호스트로 보낸 경우다. 예를 들면 누군가 ‘새 고객’이라고 하며 가격표를 요청하는 상황으로, 프롬프트 인젝션이 없어도 발생할 수 있다.

MNQ-001은 탈취 공격의 전형적인 징후다. 주입된 지시가 공격자의 주소, 계좌 또는 URL을 도구 호출에 넣어야 하기 때문이다. 목적지를 지정하지 않는 공격에는 MNQ-004가 적용될 수 있다. 예를 들어 “비밀번호를 …로 바꾸기”, “파일 13 삭제”, “주문 4100 취소” 같은 지시다. 답변 자체를 통한 유출, 예를 들어 고객 이메일 주소를 전달하는 주입된 이미지 링크는 MNQ-005가 다룬다. 단순히 요청한 사람에게 데이터를 넘기는 경우는 MNQ-006이 포착한다.

발견 사항은 관찰된 일을 설명할 뿐, 판정이나 결론을 뜻하지 않는다. 예를 들어 동료가 올린 링크를 에이전트가 방문해도 MNQ-001이 발생할 수 있다. 같은 규칙은 Python의 mnestiq.findings에서도 사용할 수 있다.

대시보드는 로컬에서만 제공된다. 루프백 인터페이스에만 바인딩하고 로컬이 아닌 Host 헤더는 거부한다. 증거는 엄격히 텍스트로 렌더링하며, 콘텐츠 보안 정책(CSP)은 페이지가 로컬 서버 이외의 대상으로 로드하거나 연결하지 못하게 한다.

API 키 없이 실시간 데이터를 보려면 스크립트 에이전트를 실행한 뒤 대시보드를 연다.

bash
python examples/live_agent.py --out evidence # keeps a scripted agent running
mnestiq dashboard evidence --trusted-key evidence/live.pub

examples/injection_incident.py에는 전체 사고 예시가 있다. 주입된 웹 페이지가 원시 TCP 연결을 통한 데이터 유출로 이어지고, 방화벽 로그 항목을 정확한 에이전트 단계까지 추적하며, 변조된 승인을 찾아내고, 비식별화한 사본을 공유하는 과정을 다룬다.

프로덕션 운영 시 동작

레코더는 에이전트 내부에서 실행되며, 에이전트 동작을 중단시키지 않도록 설계됐다. 기본 동작과 운영 조건은 다음과 같다.

상황동작
기록 실패(디스크 부족, 닫힌 저장 대상, 예상 밖의 값)에이전트는 계속 실행한다. 실패 횟수는 recorder.failures에 집계하고 mnestiq 로거에 기록하며 경고는 한 번 표시한다. 기록되지 않은 작업보다 실패가 더 나쁜 경우에는 strict=True를 전달해 예외를 발생시킬 수 있다.
쓰기 도중 프로세스 종료다시 시작하면 FileSink가 끝부분의 불완전한 줄을 <file>.torn-<time>으로 옮기며 삭제하지 않는다. 체인은 마지막으로 완전한 기록부터 이어지고, 복구 사실을 note 이벤트로 남긴다. 중간에 실패한 쓰기는 잘라내므로 파일에 반쪽짜리 기록이 남지 않는다.
지나치게 큰 프롬프트나 도구 출력기본값이 1 MiB인 max_content_bytes를 넘는 값은 해시만 저장하고 x-omitted에 표시한다. 체인 검증은 계속 가능하다.
작업자 스레드스레드는 활성 실행 정보를 상속하지 않는다. run.bind(fn)으로 작업을 감싼다. 예를 들면 pool.submit(run.bind(fetch), url)을 사용한다.
서명 서비스 또는 Key Vault 중단기록은 계속 저장한다. 실패 횟수를 집계하고 retry_after초 뒤 서명을 다시 시도해 마지막 체크포인트 이후의 내용을 처리한다.
레코더 중단 또는 강제 종료Recorder(..., checkpoint_interval=300)을 설정하면 5분 동안 서명된 기록이 없을 때 heartbeat를 작성하고 서명한다. 레코더가 멈추면 공백이 드러난다. verify는 하트비트가 허용하는 시간을 넘는 침묵에 경고하며, HeadFile이나 Evidence Vault처럼 체크포인트를 보내는 위치에서 중단 중 경보를 낼 수 있다.
타임스탬프 권한 기관 중단체크포인트는 서명해 기록한다. RFC 3161 토큰 발급만 건너뛰고 경고를 표시한다.
정전FileSink(path, fsync=True)를 사용하면 에이전트가 계속 진행하기 전에 각 기록을 디스크에 강제로 기록한다.
누군가 파일 끝부분 삭제Recorder(..., on_checkpoint=HeadFile("/other/disk/agent.head.json"))로 최신 체크포인트를 다른 위치에도 보관한다. mnestiq verify evidence.jsonl --head /other/disk/agent.head.json을 실행하면 파일이 과거 체크포인트 시점으로 잘렸는지 보고한다.
민감한 콘텐츠Linux와 macOS에서는 새 증거 파일을 소유자만 읽고 쓸 수 있게 만든다. Windows에서는 파일이 폴더의 접근 제어 목록을 따르므로 에이전트 계정만 읽을 수 있는 폴더에 증거를 보관한다. 공유하기 전에는 mnestiq redact를 사용한다.
대시보드에서 큰 증거 파일 열기대시보드는 파일 전체를 검증하지만 최근 20,000개 기록만 보여준다.

저장소 구성은 증거 형식과 해시·검증 규칙, 기록용 JSON Schema(2020-12), 레코더·검증기·CLI, 실행 가능한 데모로 나뉜다.

보안 모델과 현재 상태

증거 형식은 기록이 작성된 뒤 변조 여부를 확인하게 해준다. 하지만 침해된 호스트가 기록 시점에 진실을 말하도록 보장하지는 않는다. 별도 조치가 없으면 파일 끝부분을 과거의 어떤 체크포인트까지 잘라도 탐지하지 못할 수 있다. 이를 막으려면 최신 체크포인트를 HeadFile로 다른 곳에 보관하고 --head로 검증해야 한다.

서명 키는 에이전트가 접근하지 못하도록 서명 서비스나 Azure Key Vault에 보관해야 한다. 그래야 키를 복사해 기록 이력을 다시 서명하는 일을 막을 수 있다. --trusted-key로 레코더의 공개 키를 고정해야 서명이 의미를 가진다. 그렇지 않으면 파일 전체를 다시 작성한 사람이 새로 서명할 수 있다. 키 변경은 서명된 인계 절차를 통해서만 이뤄진다. 자세한 내용은 SPEC.md의 2절과 6절, 위협 모델에서 확인할 수 있다. 위협 모델에 적힌 각 공격은 테스트로 구성돼 있으며, 공격 실험 키트에서 직접 시도할 수 있다.

현재 상태는 v0.2 초안이다. 완료된 항목은 명세, 검증기, Python 레코더, Anthropic·OpenAI 기록, httpx·requests·원시 TCP의 외부 연결 식별 정보 전달, 로컬 대시보드, 에이전트 외부 서명(서명 서비스 및 Azure Key Vault)과 서명된 키 인계, 하트비트, 공개 기관에서 발급하고 검증기가 확인하는 RFC 3161 타임스탬프, 모든 언어의 에이전트를 외부에서 기록하는 모델 API 프록시다.

앞으로의 계획은 외부 체크포인트 헤드 고정과 S3 Object Lock 저장 대상, 실행기까지 자동으로 실행 정보를 전달하는 기능(현재는 run.bind 사용)과 aiohttp·하위 프로세스 외부 연결 지원이다. OpenAI Agents SDK 및 LangGraph 캡처와 스트리밍 출력 캡처도 계획에 포함됐다. TypeScript SDK, MCP 프록시, OpenTelemetry (GenAI) 추적 수신기, 다른 구현을 위한 증거 테스트 벡터, 선택적 공개를 위한 머클 포함 증명도 예정돼 있다.

개발 환경 설정과 검사 명령은 다음과 같다.

bash
cd python
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev,anthropic,openai,azure,timestamps]" httpx requests ruff mypy
pytest
ruff check src tests
mypy src

테스트는 이 컴퓨터 밖으로 네트워크에 연결하지 않는다. tests/_netguard.py가 루프백이 아닌 연결과 DNS 조회를 차단하므로, 외부에 접속하려는 테스트는 인터넷에 조용히 연결되는 대신 실패한다.

취약점은 비공개로 신고해야 한다. 안내는 SECURITY.md에 있다. 라이선스는 Apache-2.0이며 자세한 내용은 LICENSE에서 확인할 수 있다.

부록: 원문 예시

text
firewall: 10.0.4.17:53120 -> 198.51.100.9:25 (50 bytes)   [no agent identity] mnestiq:  agent=support-bot run=c7831ff7 step=6 sandbox=container:7f3a9c operator=acme-helpdesk           untrusted input earlier in the run: step 4: web via fetch_page           authorized by policy:email-auto-approve (policy): recipient not checked against customer domain