Назад к обновлениям
New releaseJul 14, 2026

halo-record v0.2.7

Защищённые от подделки записи выполнения для AI-агентов. Связаны хеш-цепочкой, не требуют зависимостей, проверяемы кем угодно.

Поделиться

halo-record

Защищённые от подмены аудиторские журналы для AI-агентов — хеш-цепочки Runtime Records, отображаемые в Runtime Report, который ваши клиенты могут проверить сами.

Каждое действие вашего агента (вызовы инструментов, вызовы моделей, доступ к данным, подтверждения) становится одной Runtime Record в журнале, доступном только для добавления и связанном хеш-цепочкой; Runtime Report — это та же цепочка, отображённая в виде самопроверяемой HTML-страницы. Любая сторона, владеющая контрольной точкой цепочки, может убедиться, что записи за ней никогда не изменялись, не доверяя тому, кто их создал, — эта контрольная точка является несущим элементом: одна лишь цепочка защищена от подмены для всех, кроме стороны, управляющей регистратором (LIMITS.md §1). Когда служба безопасности клиента спрашивает «что ваш агент делал с нашими данными?», вы даёте им ссылку вместо абзаца текста. Проверки безопасности уже задают вопросы об AI рядом с чек-листом SOC 2 — и всё чаще эти вопросы исходят из ISO 42001, статей EU AI Act о ведении записей и собственных опросников клиентов. Сегодня письменное заверение всё ещё проходит. Ставка этого проекта в том, что это ненадолго.

Отмечено в Help Net Security (август 2026).

Формат записей открыт и свободен для реализации. Этот пакет — эталонная реализация: регистратор, верификатор, клиент-свидетель и сервер отчётов.

Используете halo-record или думаете об этом? Расскажите, кто вы и для чего → Кто использует 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

Либо создаёт вымышленного поставщика support-агента с двумя клиентами, фиксирует цепочки (с локальным файлом-свидетелем, заменяющим одного внешнего по отношению к оператору — см. 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 ...` также поставляется — но имя `halo` на PyPI принадлежит несвязанному пакету терминального спиннера, и если этот пакет установлен, он выигрывает импорт. `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 и никакого отчёта, что-то не так: откройте issue.

### Блок верификации

Если guardrail или слой политик проверил действие, его вердикт может быть прикреплён к записи — необязательный блок, фиксирующий, что решил шлюз, запечатанный в цепочку хешей, как и любое другое поле:```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"})

который запечатывается в запись как:```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 | Хуки `PostToolUse` Claude Code и Codex CLI (срабатывают после выполнения инструмента) |

Адаптеры фреймворков и пути загрузки помечают каждую запись тегом `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

record_log(Recorder("audit.jsonl"), {"tool": "gen_ai:gpt-4o", "model": "gpt-4o", "status": 200, "subject": "acme-corp"})

Всё, что создаёт спаны OpenTelemetry GenAI (CrewAI, LlamaIndex и большинство агентных фреймворков с OTel-инструментацией), попадает в цепочку через OTel-адаптер, а пакет TypeScript поставляется с нативными адаптерами для Vercel AI SDK и экосистемы JS-агентов. Нет адаптера для вашего стека? Откройте issue. Большинство адаптеров — около сотни строк.

Записывайте своего кодирующего агента (Claude Code или Codex)

Claude Code вызывает хук PostToolUse после каждого вызова инструмента. Направьте его на halo hook, и каждое действие — запись файлов, shell-команды, вызовы MCP-коннекторов — становится записью в локальной цепочке. Без изменений в коде; одна запись в настройках:```json { "hooks": { "PostToolUse": [ {"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]} ] } }

Добавьте это в `~/.claude/settings.json`, и записи будут попадать в `~/.halo/audit.jsonl` (переопределяется через `$HALO_LOG`). Инструменты чистой оркестрации, которые не касаются данных, сети или внешнего состояния, пропускаются — цепочка фиксирует действия на границах доверия, а не размышления. Установите `HALO_HASH_ONLY=1`, чтобы записывать хеши содержимого без сводок. Установите `HALO_AGENT_VERSION` (и, при необходимости, `HALO_AGENT_MODEL`), чтобы привязать каждую запись к сборке агента, которая её создала — когда аудитор спросит о версии, работавшей в заданном окне, экспорт ответит по столбцу, а не по памяти.

Codex CLI поставляется с теми же хуками жизненного цикла и той же формой событий (хуки включены по умолчанию). Добавьте это в `~/.codex/hooks.json`, и shell-команды Codex, правки через `apply_patch` и вызовы MCP будут попадать в ту же цепочку:```json
{
  "hooks": {
    "PostToolUse": [
      {"matcher": ".*", "hooks": [{"type": "command", "command": "halo hook"}]}
    ]
  }
}

Хук отличает их друг от друга по самому событию (Codex добавляет turn_id и model) и помечает каждую запись как claude-code или codex; задайте HALO_HOOK_AGENT, чтобы принудительно выбрать один. Оба относятся к уровню ingested: хук PostToolUse срабатывает после выполнения инструмента, поэтому запись строится из того, что сообщает харнесс.

Если нужно, чтобы отчёт отвечал на вопрос «по каким правилам выполнялся этот запуск?», задайте HALO_AUTHORITY_FILE как JSON-снимок действующих полномочий для сессии. Держите его безопасным с точки зрения приватности: хеши и ссылки, а не сырые промпты, текст приватных политик, секреты или полные схемы инструментов — известные форматы секретов маскируются в момент запечатывания, но хеши и ссылки проходят без изменений, а свободный текст не обнаруживается (см. LIMITS §6). Повторно используйте snapshot_id только пока лежащие в основе полномочия не изменились; последовательные записи с одинаковым id и неизменным содержимым уплотняются — повторно использованный id при изменённом содержимом сохраняется полностью, с уведомлением.```json { "snapshot_id": "auth_2026_07_08T1100Z", "captured_at": "2026-07-08T11:00:00Z", "scope": "session", "workspace": {"path_hash": "sha256:...", "git_commit": "abc1234"}, "refs": [ {"kind": "project_rules", "id": "CLAUDE.md", "hash": "sha256:...", "loaded": true, "truncated": false}, {"kind": "mcp_tool_registry", "id": "filesystem", "hash": "sha256:..."} ], "omissions": [{"kind": "private_policy", "reason": "customer_secret", "hash": "sha256:..."}], "stale_if": ["project_rules_hash_changed", "mcp_tool_registry_hash_changed"] }

- **`--no-verify`** — пропустить проверку TLS-сертификата (не рекомендуется)
- **`--timeout`** — таймаут запроса в секундах (по умолчанию: 10)
- **`--retries`** — количество повторных попыток при неудачных запросах (по умолчанию: 3)
- **`--proxy`** — прокси-сервер для использования при выполнении запросов
- **`--user-agent`** — пользовательский User-Agent для запросов
- **`--headers`** — дополнительные заголовки для включения в запросы
- **`--cookies`** — cookies для включения в запросы
- **`--follow-redirects`** — следовать перенаправлениям HTTP
- **`--max-redirects`** — максимальное количество перенаправлений для следования (по умолчанию: 5)
- **`--verify-ssl`** — проверять SSL-сертификаты (по умолчанию: true)
- **`--ssl-cert`** — путь к SSL-сертификату клиента
- **`--ssl-key`** — путь к ключу SSL-клиента
- **`--ssl-ca`** — путь к файлу CA для проверки SSL
- **`--http2`** — использовать HTTP/2 для запросов
- **`--http3`** — использовать HTTP/3 для запросов
- **`--compressed`** — запрашивать сжатые ответы
- **`--no-compressed`** — не запрашивать сжатые ответы
- **`--keep-alive`** — использовать постоянные соединения
- **`--no-keep-alive`** — не использовать постоянные соединения
- **`--chunked`** — использовать chunked transfer encoding
- **`--no-chunked`** — не использовать chunked transfer encoding
- **`--multipart`** — использовать multipart/form-data для запросов
- **`--no-multipart`** — не использовать multipart/form-data для запросов
- **`--json`** — использовать application/json для запросов
- **`--no-json`** — не использовать application/json для запросов
- **`--xml`** — использовать application/xml для запросов
- **`--no-xml`** — не использовать application/xml для запросов
- **`--form`** — использовать application/x-www-form-urlencoded для запросов
- **`--no-form`** — не использовать application/x-www-form-urlencoded для запросов
- **`--graphql`** — использовать application/graphql для запросов
- **`--no-graphql`** — не использовать application/graphql для запросов
- **`--grpc`** — использовать application/grpc для запросов
- **`--no-grpc`** — не использовать application/grpc для запросов
- **`--websocket`** — использовать WebSocket для запросов
- **`--no-websocket`** — не использовать WebSocket для запросов
- **`--sse`** — использовать Server-Sent Events для запросов
- **`--no-sse`** — не использовать Server-Sent Events для запросов
- **`--http`** — использовать HTTP для запросов
- **`--no-http`** — не использовать HTTP для запросов
- **`--https`** — использовать HTTPS для запросов
- **`--no-https`** — не использовать HTTPS для запросов
- **`--ftp`** — использовать FTP для запросов
- **`--no-ftp`** — не использовать FTP для запросов
- **`--sftp`** — использовать SFTP для запросов
- **`--no-sftp`** — не использовать SFTP для запросов
- **`--scp`** — использовать SCP для запросов
- **`--no-scp`** — не использовать SCP для запросов
- **`--ssh`** — использовать SSH для запросов
- **`--no-ssh`** — не использовать SSH для запросов
- **`--telnet`** — использовать Telnet для запросов
- **`--no-telnet`** — не использовать Telnet для запросов
- **`--smtp`** — использовать SMTP для запросов
- **`--no-smtp`** — не использовать SMTP для запросов
- **`--pop3`** — использовать POP3 для запросов
- **`--no-pop3`** — не использовать POP3 для запросов
- **`--imap`** — использовать IMAP для запросов
- **`--no-imap`** — не использовать IMAP для запросов
- **`--dns`** — использовать DNS для запросов
- **`--no-dns`** — не использовать DNS для запросов
- **`--tcp`** — использовать TCP для запросов
- **`--no-tcp`** — не использовать TCP для запросов
- **`--udp`** — использовать UDP для запросов
- **`--no-udp`** — не использовать UDP для запросов
- **`--icmp`** — использовать ICMP для запросов
- **`--no-icmp`** — не использовать ICMP для запросов
- **`--arp`** — использовать ARP для запросов
- **`--no-arp`** — не использовать ARP для запросов
- **`--raw`** — использовать необработанные сокеты для запросов
- **`--no-raw`** — не использовать необработанные сокеты для запросов
- **`--packet`** — использовать пакеты для запросов
- **`--no-packet`** — не использовать пакеты для запросов
- **`--capture`** — захватывать пакеты для запросов
- **`--no-capture`** — не захватывать пакеты для запросов
- **`--inject`** — внедрять пакеты для запросов
- **`--no-inject`** — не внедрять пакеты для запросов
- **`--sniff`** — перехватывать пакеты для запросов
- **`--no-sniff`** — не перехватывать пакеты для запросов
- **`--spoof`** — подделывать пакеты для запросов
- **`--no-spoof`** — не подделывать пакеты для запросов
- **`--flood`** — флудить пакетами для запросов
- **`--no-flood`** — не флудить пакетами для запросов
- **`--dos`** — выполнять DoS-атаки для запросов
- **`--no-dos`** — не выполнять DoS-атаки для запросов
- **`--ddos`** — выполнять DDoS-атаки для запросов
- **`--no-ddos`** — не выполнять DDoS-атаки для запросов
- **`--bruteforce`** — выполнять атаки методом перебора для запросов
- **`--no-bruteforce`** — не выполнять атаки методом перебора для запросов
- **`--fuzz`** — выполнять фаззинг для запросов
- **`--no-fuzz`** — не выполнять фаззинг для запросов
- **`--scan`** — выполнять сканирование для запросов
- **`--no-scan`** — не выполнять сканирование для запросов
- **`--exploit`** — выполнять эксплойты для запросов
- **`--no-exploit`** — не выполнять эксплойты для запросов
- **`--payload`** — использовать полезные нагрузки для запросов
- **`--no-payload`** — не использовать полезные нагрузки для запросов
- **`--shell`** — использовать шелл для запросов
- **`--no-shell`** — не использовать шелл для запросов
- **`--reverse`** — использовать обратный шелл для запросов
- **`--no-reverse`** — не использовать обратный шелл для запросов
- **`--bind`** — использовать bind-шелл для запросов
- **`--no-bind`** — не использовать bind-шелл для запросов
- **`--meterpreter`** — использовать Meterpreter для запросов
- **`--no-meterpreter`** — не использовать Meterpreter для запросов
- **`--beacon`** — использовать beacon для запросов
- **`--no-beacon`** — не использовать beacon для запросов
- **`--c2`** — использовать C2 для запросов
- **`--no-c2`** — не использовать C2 для запросов
- **`--rat`** — использовать RAT для запросов
- **`--no-rat`** — не использовать RAT для запросов
- **`--backdoor`** — использовать бэкдор для запросов
- **`--no-backdoor`** — не использовать бэкдор для запросов
- **`--rootkit`** — использовать rootkit для запросов
- **`--no-rootkit`** — не использовать rootkit для запросов
- **`--keylogger`** — использовать кейлоггер для запросов
- **`--no-keylogger`** — не использовать кейлоггер для запросов
- **`--ransomware`** — использовать программы-вымогатели для запросов
- **`--no-ransomware`** — не использовать программы-вымогатели для запросов
- **`--wiper`** — использовать вайпер для запросов
- **`--no-wiper`** — не использовать вайпер для запросов
- **`--miner`** — использовать майнер для запросов
- **`--no-miner`** — не использовать майнер для запросов
- **`--botnet`** — использовать ботнет для запросов
- **`--no-botnet`** — не использовать ботнет для запросов
- **`--worm`** — использовать червь для запросов
- **`--no-worm`** — не использовать червь для запросов
- **`--virus`** — использовать вирус для запросов
- **`--no-virus`** — не использовать вирус для запросов
- **`--trojan`** — использовать троян для запросов
- **`--no-trojan`** — не использовать троян для запросов
- **`--spyware`** — использовать шпионское ПО для запросов
- **`--no-spyware`** — не использовать шпионское ПО для запросов
- **`--adware`** — использовать adware для запросов
- **`--no-adware`** — не использовать adware для запросов
- **`--scareware`** — использовать scareware для запросов
- **`--no-scareware`** — не использовать scareware для запросов
- **`--phishing`** — использовать фишинг для запросов
- **`--no-phishing`** — не использовать фишинг для запросов
- **`--spearphishing`** — использовать целевой фишинг для запросов
- **`--no-spearphishing`** — не использовать целевой фишинг для запросов
- **`--whaling`** — использовать whaling для запросов
- **`--no-whaling`** — не использовать whaling для запросов
- **`--smishing`** — использовать smishing для запросов
- **`--no-smishing`** — не использовать smishing для запросов
- **`--vishing`** — использовать vishing для запросов
- **`--no-vishing`** — не использовать vishing для запросов
- **`--social`** — использовать социальную инженерию для запросов
- **`--no-social`** — не использовать социальную инженерию для запросов
- **`--pretext`** — использовать предлог для запросов
- **`--no-pretext`** — не использовать предлог для запросов
- **`--bait`** — использовать приманку для запросов
- **`--no-bait`** — не использовать приманку для запросов
- **`--quid`** — использовать quid pro quo для запросов
- **`--no-quid`** — не использовать quid pro quo для запросов
- **`--tailgating`** — использовать tailgating для запросов
- **`--no-tailgating`** — не использовать tailgating для запросов
- **`--shoulder`** — использовать подглядывание через плечо для запросов
- **`--no-shoulder`** — не использовать подглядывание через плечо для запросов
- **`--dumpster`** — использовать копание в мусоре для запросов
- **`--no-dumpster`** — не использовать копание в мусоре для запросов
- **`--impersonate`** — использовать выдачу себя за другого для запросов
- **`--no-impersonate`** — не использовать выдачу себя за другого для запросов
- **`--eavesdrop`** — использовать подслушивание для запросов
- **`--no-eavesdrop`** — не использовать подслушивание для запросов
- **`--wiretap`** — использовать прослушивание для запросов
- **`--no-wiretap`** — не использовать прослушивание для запросов
- **`--mitm`** — использовать MITM для запросов
- **`--no-mitm`** — не использовать MITM для запросов
- **`--arp-spoof`** — использовать ARP-спуфинг для запросов
- **`--no-arp-spoof`** — не использовать ARP-спуфинг для запросов
- **`--dns-spoof`** — использовать DNS-спуфинг для запросов
- **`--no-dns-spoof`** — не использовать DNS-спуфинг для запросов
- **`--ssl-strip`** — использовать SSL-стриппинг для запросов
- **`--no-ssl-strip`** — не использовать SSL-стриппинг для запросов
- **`--hijack`** — использовать перехват сессии для запросов
- **`--no-hijack`** — не использовать перехват сессии для запросов
- **`--session`** — использовать фиксацию сессии для запросов
- **`--no-session`** — не использовать фиксацию сессии для запросов
- **`--csrf`** — использовать CSRF для запросов
- **`--no-csrf`** — не использовать CSRF для запросов
- **`--xss`** — использовать XSS для запросов
- **`--no-xss`** — не использовать XSS для запросов
- **`--sqli`** — использовать SQL-инъекции для запросов
- **`--no-sqli`** — не использовать SQL-инъекции для запросов
- **`--rce`** — использовать RCE для запросов
- **`--no-rce`** — не использовать RCE для запросов
- **`--lfi`** — использовать LFI для запросов
- **`--no-lfi`** — не использовать LFI для запросов
- **`--rfi`** — использовать RFI для запросов
- **`--no-rfi`** — не использовать RFI для запросов
- **`--xxe`** — использовать XXE для запросов
- **`--no-xxe`** — не использовать XXE для запросов
- **`--ssrf`** — использовать SSRF для запросов
- **`--no-ssrf`** — не использовать SSRF для запросов
- **`--csrf-token`** — использовать CSRF-токен для запросов
- **`--no-csrf-token`** — не использовать CSRF-токен для запросов
- **`--jwt`** — использовать JWT для запросов
- **`--no-jwt`** — не использовать JWT для запросов
- **`--oauth`** — использовать OAuth для запросов
- **`--no-oauth`** — не использовать OAuth для запросов
- **`--saml`** — использовать SAML для запросов
- **`--no-saml`** — не использовать SAML для запросов
- **`--openid`** — использовать OpenID для запросов
- **`--no-openid`** — не использовать OpenID для запросов
- **`--ldap`** — использовать LDAP для запросов
- **`--no-ldap`** — не использовать LDAP для запросов
- **`--kerberos`** — использовать Kerberos для запросов
- **`--no-kerberos`** — не использовать Kerberos для запросов
- **`--ntlm`** — использовать NTLM для запросов
- **`--no-ntlm`** — не использовать NTLM для запросов
- **`--radius`** — использовать RADIUS для запросов
- **`--no-radius`** — не использовать RADIUS для запросов
- **`--tacacs`** — использовать TACACS+ для запросов
- **`--no-tacacs`** — не использовать TACACS+ для запросов
- **`--snmp`** — использовать SNMP для запросов
- **`--no-snmp`** — не использовать SNMP для запросов
- **`--syslog`** — использовать Syslog для запросов
- **`--no-syslog`** — не использовать Syslog для запросов
- **`--netflow`** — использовать NetFlow для запросов
- **`--no-netflow`** — не использовать NetFlow для запросов
- **`--sflow`** — использовать sFlow для запросов
- **`--no-sflow`** — не использовать sFlow для запросов
- **`--ipfix`** — использовать IPFIX для запросов
- **`--no-ipfix`** — не использовать IPFIX для запросов
- **`--pcap`** — использовать PCAP для запросов
- **`--no-pcap`** — не использовать PCAP для запросов
- **`--pcapng`** — использовать PCAPNG для запросов
- **`--no-pcapng`** — не использовать PCAPNG для запросов
- **`--cap`** — использовать CAP для запросов
- **`--no-cap`** — не использовать CAP для запросов
- **`--pcap-ng`** — использовать PCAP-NG для запросов
- **`--no-pcap-ng`** — не использовать PCAP-NG для запросов
- **`--pcapng-ng`** — использовать PCAPNG-NG для запросов
- **`--no-pcapng-ng`** — не использовать PCAPNG-NG для запросов
- **`--pcap-ng-ng`** — использовать PCAP-NG-NG для запросов
- **`--no-pcap-ng-ng`** — не использовать PCAP-NG-NG для запросов
- **`--pcapng-ng-ng`** — использовать PCAPNG-NG-NG для запросов
- **`--no-pcapng-ng-ng`** — не использовать PCAPNG-NG-NG для запросов
- **`--pcap-ng-ng-ng`** — использовать PCAP-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng`** — не использовать PCAP-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcap-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — не использовать PCAP-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng`** — использовать PCAPNG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG-NG для запросов
- **`--no-pcapng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-ng-```sh
HALO_AUTHORITY_FILE=./authority.json halo hook

Снимок запечатывается в ту же хеш-цепочку, что и записи действий. Хорошим значением по умолчанию является один снимок уровня сессии в начале, плюс новый снимок при изменении правил, Skills, хуков, реестров инструментов MCP или политики компакции. Чтобы длинные сессии оставались компактными, последовательные записи с одинаковым authority.snapshot_id компактируются после первого полного снимка: последующие записи сохраняют только {"snapshot_id": "...", "same_as_previous": true}. Указатель остаётся в хеш-цепочке, но громоздкий блок refs/omissions/stale-if не повторяется при каждом действии. (Компакция выполняется для каждого процесса регистратора: захват в стиле хуков, порождающий один процесс на каждый вызов инструмента, заново сохраняет полное тело всякий раз, когда предыдущий полный снимок не является последней записью, поэтому недолговечные процессы обменивают размер цепочки на защиту от повторного использования.)

Пользователи SDK прикрепляют тот же блок напрямую — build(..., authority={...}) или record_call(..., authority={...}); захват только хеша использует ту же поверхность (summaries=False для любого из них):```python from halo_record import Recorder, record_call

rec = Recorder("audit.jsonl") with record_call(rec, "crm.lookup", {"account": "acct-9"}, authority={"snapshot_id": "auth_1", "rules_hash": "sha256:..."}, summaries=False) as call: # hash-only: no summaries, no excerpts call.result = crm.lookup("acct-9")

Затем, как обычно:```
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html

Любая среда выполнения агента, предоставляющая хук после действия, может передавать ту же команду — хук читает одно событие как JSON из stdin и добавляет одну запись.

Одна цепочка, один писатель за раз. Цепочка — это связный список: два писателя, которые читают одну и ту же голову и оба добавляют запись, создадут развилку (две записи, претендующие на одного и того же предшественника), и верификация назовёт затронутые записи. Recorder сериализует свои собственные добавления с помощью бокового файла блокировки (здесь POSIX flock; в пакете TypeScript — каталог блокировки), а halo hook добавляет записи через Recorder, поэтому приведённая выше настройка хука покрыта. Всё, что пишет в файл цепочки напрямую — самодельный хук, параллельные воркеры, поставщик логов — должно удерживать эквивалентную эксклюзивную блокировку на протяжении всей последовательности «прочитать голову, затем добавить», либо писать в отдельные цепочки для каждого процесса. LIMITS.md, раздел 9 полностью описывает это, включая межъязыковую границу.

Когда запись не удаётся

Два стиля интеграции отказывают в противоположных направлениях, намеренно — выберите тот, отказ которого вы можете пережить:

  • Адаптеры фреймворков (LangChain, хуки через callback-менеджеры) отказывают открыто. Если запись не может быть выполнена (диск заполнен, нет прав), действие агента завершается нормально, а запись теряется. Обработчик LangChain выводит громкое предупреждение в stderr и подсчитывает потери (handler.lost_records), но ничто в самой цепочке не может показать запись, которая никогда не была сделана — остановившаяся цепочка всё равно проходит верификацию. Именно контрольные точки свидетеля с определённой периодичностью делают остановившуюся цепочку видимой: ожидаемая контрольная точка, которая так и не пришла, — это сигнал тревоги.
  • Нативная обёртка trace() отказывает закрыто. Если запись не может быть выполнена, исключение распространяется в действие агента — нет доказательства, нет действия. Строже, и это может прервать работу вашего агента.

Ни один из вариантов по умолчанию не подходит всем; знайте, какой из них вы используете.

Целостность против полноты (прочитайте эту часть)

Будьте точны в том, что доказывает каждый слой — потому что это разные утверждения, и различия между ними и есть суть:

Цепочка, хранимая самим оператором, доказывает целостность относительно установленной головы: имея голову цепочки, которую кто-то уже держит, любое изменение, переупорядочивание или удаление в записях за ней становится обнаружимым. Сама по себе — до того, как кто-либо вне оператора увидел голову — цепочка доказывает внутреннюю согласованность, а не историю: оператор мог бы удалить запись и заново запечатать, и новый файл прошёл бы верификацию. Цепочка становится исторически зафиксированной в тот момент, когда её голова покидает контроль оператора.

Это и есть свидетель: сторона вне оператора, держащая периодические контрольные точки цепочки — идентификатор субъекта, количество записей и два отпечатка цепочки (голова и корень цепочки); именно эта полезная нагрузка, и ничего больше. Контрольные точки делают перезапись зафиксированной истории обнаружимой, а пропущенная контрольная точка сама по себе является видимым событием:``` halo anchor audit.jsonl witness.jsonl # anchor a checkpoint to a local witness halo anchor audit.jsonl witness.jsonl --check # completeness verdict against it

Для *time* в частности, внешняя временная метка RFC 3161 заменяет самоутверждённые часы контрольной точки доказательством от Timestamp Authority, которое оператор не контролирует — «эта цепочка достигла этой головы не позднее T», проверяемым третьей стороной без размещённой инфраструктуры. TSA по умолчанию — бесплатный freetsa.org (подходит для оценки); укажите коммерческий TSA (DigiCert / Sectigo / ваш собственный) с помощью `--tsa` для продакшена:```
halo anchor audit.jsonl witness.jsonl --timestamp          # attach a TSA time proof to the checkpoint
halo anchor audit.jsonl witness.jsonl --check              # reads the token's claimed time

--check подтверждает, что токен привязан к этому состоянию цепочки, и считывает его заверенное время, но он не проверяет подпись TSA — это намеренно оставлено стандартному инструменту, чтобы проверяющий не доверял нашему коду. Чтобы независимо проверить время (именно это вы передаёте аудитору по безопасности):```

tsa.token_b64 lives in the witness log; decode the latest one to a standard .tsr file

python3 -c 'import json,base64; cps=[json.loads(l) for l in open("witness.jsonl") if l.strip()]; t=[c["tsa"] for c in cps if c.get("tsa")][-1]; open("token.tsr","wb").write(base64.b64decode(t["token_b64"])); print(t["digest"])' curl -s -o tsa-ca.pem https://freetsa.org/files/cacert.pem # CA for the default TSA (a commercial TSA publishes its own) openssl ts -verify -digest -in token.tsr -CAfile tsa-ca.pem # → "Verification: OK"

Ещё одна граница, обозначенная прямо: ни цепочка, ни свидетель не доказывают, что каждое действие в реальном мире прошло через регистратор. Это **полнота захвата** — свойство того, где регистратор находится в стеке (нативная инструментация, хуки, приём через шлюз), а не свойство какого-либо хеша. Записи несут тег `source` именно по этой причине. Таблица утверждений в разделе «Проверьте сами» в начале этой страницы — это сводка этих трёх слоёв.

Любой может запустить свидетель. Свидетель, который вы запускаете сами, фиксирует историю для *вас*; чтобы зафиксировать её для *вашего клиента*, нужен свидетель, которому у него есть основания доверять. Протокол открыт в любом случае.

Размещённый у хостера, признанный свидетель — так этот проект будет поддерживать себя. Ранний доступ: [email protected].

## Персональные данные в цепочке

Цепочка работает только на добавление: всё, что запечатано в запись, остаётся там, потому что удаление нарушило бы верификацию для всего, что идёт после. Аргументы инструментов уже обработаны — хранятся как хеш плюс отредактированная сводка, ограниченная 200 символами (режим только хеша не сохраняет сводку).

Обратите внимание на ограничение в этом предложении: *отредактировано*, а не удалено. [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)
раздел 6 прямо указывает, что у имени или почтового адреса нет надёжного шаблона, поэтому ни то, ни другое не обнаруживается и не маскируется. И `subject` — не единственное поле, которое несёт предоставленный вами текст — `principal`, `approver`, `session_id`, `agent`,
`authority`, `data` и все сводки тоже.

Работающий шаблон: поместите в цепочку стабильный псевдонимный идентификатор, а сопоставление с любым конкретным лицом храните в системе, из которой вы можете удалять. Запрос на удаление тогда удовлетворяется удалением сопоставления. Держите `subject` указывающим на организацию-арендатора, а не на человека:```python
from halo_record import build

build("tool_call", "privacy", subject={"id": "acme", "name": "Acme Corp"})

Никакая настройка этого не обеспечивает — это дисциплина того, как вы вызываете рекордер. Это делает удаление данных управляемым; это не анонимизация, и встроенных политик хранения или очистки пока нет. Раздел 13 LIMITS.md содержит полный список полей, объясняет, почему сохранённый отпечаток входных данных может подтвердить угадываемое значение даже после исчезновения сопоставления, и завершается вопросами, которые должен задать проверяющий.

Запись вызова модели (первый вопрос покупателя: «какая модель видела мои данные?»):```python from halo_record import record_model_call

record_model_call(rec, provider="anthropic", model="claude-sonnet-4-6", zdr=True, purpose="draft support reply", subject="acme") # tool=model.generate, scope=model:anthropic

## Место в стеке соответствия

halo-record — это слой доказательств, а не сертификация. Он создаёт артефакт, который оценочные фреймворки постоянно запрашивают, выражая это разными словами. Одна оговорка о границах применимости, которая управляет каждым пунктом ниже: это утверждения о целостности записи; полнота относительно оператора требует внешнего свидетеля, хранящего контрольные точки ([LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)).

- **Опросники по безопасности и проверки SOC 2:** отвечайте на разделы об ИИ с помощью проверяемого Runtime Report вместо скриншотов и прозы.
- **AIUC-1:** создаёт доказательства журналирования с защитой от подмены (E015.4) и записи цепочки выполнения с событиями авторизации (E015.2 — с заявленным пробелом: трассировки рассуждений не фиксируются), которые называет контроль подотчётности E015 этого стандарта. Сам E015 обязателен; E015.2 и E015.4 — его дополнительный уровень: не требуется для прохождения, его принимает вендор, когда клиент или регулятор запрашивает. Как только цепочка привязана к свидетелю, которому доверяющая сторона имеет основания доверять — свидетель, который оператор запускает сам, этого не обеспечивает — это непрерывно засвидетельствованная цепочка, а не восстановленная во время аудита (то, что попало в цепочку, по-прежнему ограничено поверхностью захвата). Сопоставление доказательств по каждому контролю, включая то, что намеренно вынесено за рамки, находится в [`AIUC.md`](https://github.com/bkuan001/halo-record/blob/main/AIUC.md).
- **OWASP Top 10 for Agentic Applications 2026:** восемь из десяти угроз сопоставлены с детерминированными правилами политики над записью, две помечены как выходящие за рамки с обоснованием, а пакет поставляется в рабочем виде. Приблизительное сопоставление сообщества, не официальный артефакт OWASP. См. [`OWASP.md`](https://github.com/bkuan001/halo-record/blob/main/OWASP.md).
- **AARM (CSA):** создаёт квитанцию действия с защитой от подмены, которую определяет AARM — R5, и запечатывающую половину R6 (идентичность запечатана в хеш, а не криптографически аутентифицирована). halo-record — это слой квитанций; сочетайте его со шлюзом принуждения для полноценной системы AARM. См. [`AARM.md`](https://github.com/bkuan001/halo-record/blob/main/AARM.md).
- **Agentic Trust Controls:** записи времени выполнения, лежащие в основе доказательных контролей ATC — журналирование действий с защитой от подмены (RBM-03) и половина записи аттестации полномочий (AID-05; половина принуждения принадлежит шлюзу) в одной связанной записи. См. [`ATC.md`](https://github.com/bkuan001/halo-record/blob/main/ATC.md).
- **CSA AI Controls Matrix (AICM) / STAR for AI:** доказательства домена LOG — сгенерированные записи аудита, запечатанные от незамеченного изменения, зафиксированные события ввода и вывода — сопоставлены контроль за контролем в [`AICM.md`](https://github.com/bkuan001/halo-record/blob/main/AICM.md). Собственная таблица соответствия CSA v1.1 связывает этот домен с AIUC-1 E015.
- **MITRE ATLAS:** мера противодействия на основе телеметрии агента (AML.M0024), реализованная со свойством целостности, которого сам ATLAS не требует — журнал проверяем кем-то вне оператора. См. [`ATLAS.md`](https://github.com/bkuan001/halo-record/blob/main/ATLAS.md).
- **EU AI Act / ISO 42001 / NIST AI RMF:** обязательства по ведению записей и журналированию, которые описывают эти фреймворки, относятся к одному и тому же классу артефактов — консервативно сопоставлены в [EU-AI-ACT.md](https://github.com/bkuan001/halo-record/blob/main/EU-AI-ACT.md), [ISO42001.md](https://github.com/bkuan001/halo-record/blob/main/ISO42001.md) и [NIST-AI-RMF.md](https://github.com/bkuan001/halo-record/blob/main/NIST-AI-RMF.md).

Ничто из этого само по себе ничего не сертифицирует. Это даёт вашему оценщику что-то проверяемое, на что можно посмотреть. Границы — что halo-record намеренно не делает и что говорить, когда рецензент спрашивает — задокументированы в [`LIMITS.md`](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md).

### Как доставить доказательства в вашу GRC-платформу

Большинство GRC-платформ (Vanta, Drata и подобные) принимают загруженные файлы как пользовательские доказательства по контролю. Экспорт halo-record создан так, чтобы встраиваться в этот поток:```bash
halo export audit.jsonl --from 2026-06-01 --to 2026-06-30 -o evidence.csv

# scope the export to the actions a control covers
halo export audit.jsonl --from 2026-06-01 --to 2026-06-30 --tool email.send --tool db.query -o evidence.csv

Это записывает два файла для окна аудита: CSV (одна строка на каждое записанное действие, сгруппированные слева направо как когда → что произошло → кто → на основании каких полномочий → что было отмечено → происхождение → как проверить, включая отредактированное изложение вызова и его результата на простом языке, сборку агента и модель, которые произвели каждое действие, идентификатор, от имени которого оно выполнялось, запись, которая его вызвала, его решение об авторизации и область действия, а также любые категории персональных данных или флаги ingested threat) и манифест (evidence.csv.manifest.json), который связывает CSV с его источником — головной хеш цепочки связывает его с проверяемым журналом, из которого он получен, а csv_sha256 — это собственный хеш экспортированного файла, поэтому CSV, отредактированный после экспорта, больше не соответствует своему манифесту. Сузьте совокупность с помощью --tool, когда контроль охватывает только определённые действия; манифест записывает фильтр, поэтому ограниченный экспорт раскрывает, что это подмножество, а не читается как вся совокупность. Загрузите оба файла в соответствии с вашим контролем логирования или мониторинга; приложите HTML-отчёт Runtime Report, когда проверяющий хочет самостоятельно проверить цепочку. Экспорт отказывается запускаться на цепочке, которая не проходит проверку.

Нативная push-интеграция — доказательства, автоматически попадающие в вашу платформу, — находится в планах. Указанный выше путь к файлу работает сегодня с любой платформой, которая принимает загруженные доказательства.

CLI```

halo verify validate schema + hash chain (exit 1 broken, 3 empty chain; CI-friendly) halo report render a chain as a self-verifying HTML Runtime Report (--from/--to: a date-windowed report covering only the review period) halo policy corroborate a chain against a declarative policy pack (per-rule pass / violation / evidence-gap; exit 1 violated, 3 nothing in scope) halo serve serve per-tenant reports over HTTP, access-scoped per customer halo grant designate a report recipient (email or domain) halo viewers list who has unlocked a gated report halo anchor witness a chain head, or --check completeness (exit 1 incomplete, 3 unwitnessed) halo witness-serve run a witness over HTTP: vendors anchor chain heads, viewers fetch checkpoints halo demo scaffold the full vendor demo (record -> witness -> gated report) halo export date-bounded evidence export: CSV + manifest tied to the chain head halo sample emit a valid example log halo hash canonical sha256 of a JSON value halo hook Claude Code PostToolUse hook

## Модель целостности

Чтобы вычислить хеш записи: возьмите запись, исключив `integrity.hash`, с `integrity.prev_hash`, установленным в хеш предыдущей записи; канонизируйте с помощью RFC 8785 (JSON Canonicalization Scheme); примените SHA-256 к байтам. `prev_hash` первой записи — 64 нуля. Верификация пересчитывает каждый хеш и проверяет каждую связь. Секрет не требуется; в этом и суть.

Думаете, что сможете подделать цепочку так, чтобы верификатор не заметил? [Попытки и результаты здесь](https://github.com/bkuan001/halo-record/discussions/2).

Полный справочник полей: [`halo-record.schema.json`](https://github.com/bkuan001/halo-record/blob/main/src/halo_record/halo-record.schema.json).

## TypeScript

Тот же регистратор поставляется для Node: [`halo-record-ts`](https://github.com/bkuan001/halo-record-ts). Тот же формат цепочки, тот же протокол свидетелей. Записи, созданные на любом из языков, проверяются любым верификатором.

## Примеры сообщества

[trail-halo-poc](https://github.com/AmeyParle/trail-halo-poc) — концепция сообщества, связывающая основную полномочию записи Halo с учётными данными TRAIL: взаимное связывание организации и агента, а также подписанные организацией предоставления области действия, записанные в цепочку Halo, с набором состязательных проверок.

## Участие в разработке

Issues, обсуждения и pull requests приветствуются — см. [CONTRIBUTING.md](https://github.com/bkuan001/halo-record/blob/main/CONTRIBUTING.md) для основных правил (краткая версия: требуются тесты, небольшие PR, изменения схемы сначала обсуждаются).

## Лицензия

Apache-2.0

Категории