
halo-record v0.2.7
Registros de tiempo de ejecución a prueba de manipulaciones para agentes de IA. Encadenados con hash, sin dependencias, verificables por cualquiera.
halo-record
Rastros de auditoría a prueba de manipulación para agentes de IA — Runtime Records encadenados por hash, representados como un Runtime Report que tus clientes pueden verificar por sí mismos.
Cada acción que realiza tu agente (llamadas a herramientas, llamadas a modelos, acceso a datos, aprobaciones) se convierte en un Runtime Record en un registro de solo anexado y encadenado por hash; el Runtime Report es esa cadena representada como una página HTML autoverificable. Cualquier parte que posea un checkpoint de la cadena puede verificar que los registros que respaldan nunca fueron alterados, sin confiar en quien los produjo — ese checkpoint es la pieza fundamental: la cadena por sí sola es a prueba de manipulación frente a todos excepto la parte que opera el registrador (LIMITS.md §1). Cuando el equipo de seguridad de un cliente pregunta "¿qué hizo tu agente con nuestros datos?", les entregas un enlace en lugar de un párrafo. Las revisiones de seguridad ya hacen preguntas sobre IA junto a la lista de verificación SOC 2 — y cada vez más esas preguntas provienen de ISO 42001, los artículos de mantenimiento de registros del Reglamento de IA de la UE y los propios cuestionarios de los clientes. Hoy una garantía por escrito todavía pasa. La apuesta detrás de este proyecto es que no lo hará por mucho tiempo.
Destacado en Help Net Security (agosto de 2026).
El formato de registro es abierto y libre de implementar. Este paquete es la implementación de referencia: registrador, verificador, cliente testigo y servidor de informes.
¿Usas halo-record o lo estás considerando? Cuéntame quién eres y para qué → ¿Quién usa halo-record?
Compruébalo tú mismo
Se te está pidiendo que coloques un registrador dentro de tu agente. No deberías aceptarlo por fe:
- Cero dependencias en tiempo de ejecución. Solo biblioteca estándar.
pip install halo-recordinstala exactamente un paquete. - Sin llamadas de red, excepto tres opcionales — anclaje a un testigo (envía el id del sujeto, un recuento de registros y dos huellas digitales de la cadena — la cabeza y la raíz de la cadena), lectura de los checkpoints de un testigo (envía el id del sujeto) y la marca de tiempo RFC 3161 (envía solo el hash de estado de un checkpoint a una Autoridad de Sellado de Tiempo). Todas están desactivadas a menos que las invoques; el contenido de los registros nunca sale de tu infraestructura.
- Los argumentos de herramientas sin procesar se hashean, con un resumen redactado junto a ellos. Los argumentos se almacenan como un hash canónico más un resumen: el texto del argumento con patrones conocidos de secretos y PII enmascarados, limitado a 200 caracteres. Una entrada corta que no coincide con ningún patrón aparece completa en el resumen; el modo solo hash (
summaries=False) no conserva ningún resumen. La redacción es de mejor esfuerzo (regex sobre formatos comunes de secretos y PII más una captura general por entropía): trátala como defensa en profundidad, no como una garantía. Los campos de resultado que proporciones más allá desummaryse sellan tal como se dan (LIMITS §13). - Lo suficientemente pequeño para auditar. ~5.300 líneas de Python (líneas de código, sin contar líneas en blanco ni comentarios). Léelo todo en una tarde.
- Apache-2.0.
- La documentación es de primera clase. LIMITS.md (lo que la cadena no puede probar), PRIVACY.md (qué contienen los registros y qué sale de tu máquina), RETENTION.md (operar bajo una política de retención) y REVIEWERS.md — la verificación independiente de cuatro comandos más un formato de cita para hallazgos de revisión.
Lo que prueba cada capa — la distinción fundamental en este proyecto (LIMITS.md §1): una cadena que tú mismo posees prueba que los registros no fueron editados, en relación con una cabeza que alguien ya posee; solo los checkpoints mantenidos fuera del operador prueban que ninguno fue eliminado; y ningún hash prueba que cada acción fue capturada.
| Afirmación | Cadena propia | + Checkpoints externos | + Captura confiable |
|---|---|---|---|
| Detectar ediciones a un artefacto establecido | ✔ | ✔ | ✔ |
| Detectar reescritura de historial confirmado | — | ✔ | ✔ |
| Detectar checkpoints faltantes/tardíos | — | ✔ (cadencia acordada) | ✔ |
| Probar que cada acción fue registrada | — | — | depende del límite de captura |
Ve uno antes de instalar: un Runtime Report de ejemplo — datos ficticios, cadena real, y se reverifica a sí mismo en tu navegador mientras observas.
Demo de 60 segundos
No se requiere agente. Con uv, nada que instalar:``` uvx --from halo-record halo demo --serve
o de la forma clásica:```
pip install halo-record
halo demo --serve
Cualquiera de los dos crea un proveedor ficticio de agentes de soporte con dos clientes, es testigo de las cadenas (con un archivo de testigo local que representa a uno fuera del operador — véase LIMITS.md §1), sirve sus Runtime Reports restringidos y abre la consola del operador en tu navegador. Luego prueba la prueba de manipulación: elimina una línea de uno de los archivos .jsonl y recarga. El informe lo detecta.
Registra tu propio agente
Una línea en el límite:```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
Un shim de conveniencia `from halo import ...` también se incluye — pero el nombre `halo` en PyPI pertenece a un paquete de spinner de terminal no relacionado, y si ese paquete está instalado, gana la importación. `halo_record` no es ambiguo, por lo que los ejemplos lo usan.
Sin `log=`, los registros van a `~/.halo/my-agent.jsonl` (una cadena por agente). El wrapper sella el límite de la ejecución; la evidencia vive en los registros por llamada. Captúralos con un adaptador de framework (matriz a continuación) — o explícitamente, lo que también muestra cómo se enlazan las delegaciones:```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)
Luego renderiza el informe:``` halo report audit.jsonl -o report.html # one chain -> self-verifying HTML halo serve ./records --port 8721 # all tenants, gated per customer
El inicio rápido termina cuando estás viendo el Runtime Report de tu propio agente en un navegador. Si obtuviste un archivo JSONL y ningún informe, algo está mal: abre un issue.
### El bloque de verificación
Si una capa de guardrail o de políticas verificó la acción, su veredicto puede viajar en el registro — un bloque opcional que registra lo que decidió la puerta, sellado en la cadena de hashes como cualquier otro campo:```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"})
que se sella en el registro como:```json "verification": {"status": "allowed", "verifier": "gate/1.2", "policy_ref": "sha256:1f3a...", "checked_at": "2026-08-01T12:00:00Z"}
`record_call(...)` acepta la misma palabra clave `verification=`. `status` es obligatorio dentro del bloque; `verifier`, `policy_ref` y `checked_at` son opcionales. Qué significa cada estado:
| Estado | Qué reporta el gate | ¿Se ejecutó la acción? |
|---|---|---|
| `allowed` | permitió la acción | sí — la acción procedió |
| `blocked` | denegó la acción | determinado por la integración, no por este campo — un registro aún puede llevar un resultado, y un bloqueo por sí solo no prueba la no ejecución |
| `modified` | alteró la acción antes de la ejecución — `action.input` describe la acción **tal como se ejecutó**, tras la modificación | sí, en la forma alterada |
| `unverified` | se ejecutó (o fue consultado) pero no emitió una determinación — distinto de un bloque ausente, que significa que no se hizo ninguna afirmación de verificación en absoluto | sí — la acción procedió sin un veredicto |
El bloque es proporcionado por el código de integración del operador y registra lo que este reporta que dijo el gate — la misma postura de confianza que `principal` (ver [LIMITS](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#11-verification-status-is-the-gates-report-not-halos-finding)). El sellado prueba que el estado no fue editado después del hecho; no prueba que la comprobación ocurrió, que el veredicto fue correcto, o que una acción bloqueada no se ejecutó. Esto no es verificación independiente.
Para que `policy_ref` sea utilizable como evidencia, use un hash de contenido del conjunto de reglas y conserve el artefacto del conjunto de reglas — una etiqueta no resoluble hace que el campo sea decorativo.
## Conecte con lo que ya ejecuta
| Capturado en el límite | Ingerido desde telemetría existente |
|---|---|
| Grabador nativo (`from halo_record import trace`) | Spans GenAI de OpenTelemetry |
| Interceptor MCP | Callbacks de LiteLLM |
| Callback de LangChain / LangGraph | Exportación de Langfuse |
| Hooks del OpenAI Agents SDK | Cualquier log de gateway / proxy inverso |
| Hook del Claude Agent SDK | Hooks `PostToolUse` de Claude Code y Codex CLI (se disparan después de que la herramienta se ejecutó) |
Los adaptadores de frameworks y las rutas de ingesta marcan cada registro con una etiqueta `source`, de modo que el informe revela cómo se recopiló cada pieza de evidencia. Los registros capturados e ingeridos viven en la misma cadena.
Para LangChain / LangGraph, es un manejador de callbacks:```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
Para MCP, una llamada envuelve la sesión del cliente — y luego cualquier agente que use MCP emite registros por cada llamada de herramienta, independientemente del framework que lo impulse:```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
Para registros de gateway o proxy (Cloudflare AI Gateway, Portkey, nginx delante del modelo), asigne una fila de registro a la cadena — etiquetada explícitamente como ingerida, no capturada en el límite:```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"})
Todo lo que emite spans de OpenTelemetry GenAI (CrewAI, LlamaIndex y la mayoría de los frameworks de agentes con instrumentación OTel) llega a la cadena a través del adaptador OTel, y el paquete de TypeScript incluye adaptadores nativos para el Vercel AI SDK y el ecosistema de agentes de JS. ¿Falta un adaptador para tu stack? Abre un issue. La mayoría de los adaptadores tienen alrededor de cien líneas.
Registra tu agente de programación (Claude Code o Codex)
Claude Code dispara un hook PostToolUse después de cada llamada a una herramienta. Apúntalo a halo hook y cada acción —escrituras de archivos, comandos de shell, llamadas a conectores MCP— se convierte en un registro en una cadena local. Sin cambios en el código; una entrada en la configuración:```json
{
"hooks": {
"PostToolUse": [
{"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]}
]
}
}
Añade eso a `~/.claude/settings.json` y los registros se guardan en `~/.halo/audit.jsonl` (se puede sobrescribir con `$HALO_LOG`). Las herramientas de pura orquestación que no tocan datos, red ni estado externo se omiten — la cadena registra acciones en los límites de confianza, no el pensamiento. Establece `HALO_HASH_ONLY=1` para registrar hashes de contenido sin resúmenes. Establece `HALO_AGENT_VERSION` (y opcionalmente `HALO_AGENT_MODEL`) para vincular cada registro con la build del agente que lo produjo — cuando un auditor pregunte por la versión que estaba en ejecución en una ventana determinada, la exportación responde por columna en lugar de por recuerdo.
Codex CLI incluye los mismos hooks de ciclo de vida con la misma forma de eventos (los hooks están activados por defecto). Añade esto a `~/.codex/hooks.json` y los comandos de shell de Codex, las ediciones de `apply_patch` y las llamadas MCP se registran en la misma cadena:```json
{
"hooks": {
"PostToolUse": [
{"matcher": ".*", "hooks": [{"type": "command", "command": "halo hook"}]}
]
}
}
El hook distingue a los dos del propio evento (Codex añade turn_id y model) y etiqueta cada registro como claude-code o codex; establece HALO_HOOK_AGENT para forzar uno. Ambos son la capa ingerida: un hook PostToolUse se dispara después de que la herramienta se ejecutó, por lo que el registro se construye a partir de lo que reporta el harness.
Si necesitas que el informe responda "¿bajo qué reglas ocurrió esta ejecución?", establece HALO_AUTHORITY_FILE con una instantánea JSON de la autoridad efectiva para la sesión. Mantenla segura para la privacidad: hashes y refs, no prompts en bruto, texto de políticas privadas, secretos ni esquemas de herramientas completos — los formatos de secretos conocidos se enmascaran en el momento del sellado, pero los hashes y las refs pasan sin modificarse y el texto de forma libre no se detecta (véase LIMITS §6). Reutiliza un snapshot_id solo mientras la autoridad subyacente no cambie; los registros consecutivos con el mismo id y contenido sin cambios se compactan — un id reutilizado sobre contenido modificado se almacena en su totalidad, con un aviso.```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"]
}
- **`-p, --port`** - Puerto para el servidor web (por defecto: 8888)
- **`-b, --bind`** - Dirección de enlace para el servidor web (por defecto: 127.0.0.1)
- **`-d, --debug`** - Habilitar modo de depuración
- **`-w, --workers`** - Número de procesos trabajadores (por defecto: 1)
- **`--timeout`** - Tiempo de espera de solicitud en segundos (por defecto: 30)
- **`--max-size`** - Tamaño máximo de carga útil en bytes (por defecto: 10485760)
- **`--no-auth`** - Deshabilitar autenticación
- **`--auth-file`** - Ruta al archivo de credenciales
- **`--tls-cert`** - Ruta al certificado TLS
- **`--tls-key`** - Ruta a la clave TLS
- **`--log-file`** - Ruta al archivo de registro
- **`--log-level`** - Nivel de registro (debug, info, warning, error, critical)
- **`--version`** - Mostrar información de versión y salir
- **`-h, --help`** - Mostrar mensaje de ayuda y salir```sh
HALO_AUTHORITY_FILE=./authority.json halo hook
El snapshot se sella en la misma cadena de hash que los registros de acciones. Un valor predeterminado adecuado es un snapshot a nivel de sesión al inicio, más un nuevo snapshot cuando cambian las reglas, los Skills, los hooks, los registros de herramientas MCP o la política de compactación. Para mantener ligeras las sesiones largas, los registros consecutivos con el mismo authority.snapshot_id se compactan después del primer snapshot completo: los registros posteriores conservan solo {"snapshot_id": "...", "same_as_previous": true}. El puntero permanece encadenado por hash, pero el voluminoso bloque de refs/omissions/stale-if no se repite en cada acción. (La compactación es por proceso de grabación: la captura estilo hook que genera un proceso por llamada de herramienta vuelve a almacenar el cuerpo completo siempre que el snapshot completo anterior no sea el registro final, por lo que los procesos de corta duración intercambian tamaño de cadena por la protección de reutilización).
Los usuarios del SDK adjuntan el mismo bloque directamente — build(..., authority={...}) o record_call(..., authority={...}); la captura solo de hash es la misma superficie (summaries=False en cualquiera de ellos):```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")
Luego, lo habitual:```
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html
Cualquier entorno de ejecución de agente que exponga un hook posterior a la acción puede alimentar el mismo comando — el hook lee un evento como JSON en stdin y añade un registro.
Una cadena, un escritor a la vez. Una cadena es una lista enlazada: dos escritores que leen la misma cabeza y ambos añaden bifurcarán la cadena (dos registros que reclaman el mismo predecesor), y la verificación nombrará los registros afectados. Recorder serializa sus propias adiciones con un bloqueo sidecar (POSIX flock aquí; un directorio de bloqueo en el paquete TypeScript), y halo hook añade a través de Recorder, por lo que la configuración del hook anterior está cubierta. Cualquier cosa que escriba directamente en el archivo de la cadena — un hook hecho a mano, trabajadores en paralelo, un transportador de logs — debe mantener un bloqueo exclusivo equivalente durante toda la secuencia de leer-cabeza-y-luego-añadir, o escribir en cadenas por proceso. LIMITS.md sección 9 cubre esto en su totalidad, incluida la frontera entre lenguajes.
Cuando el registro falla
Los dos estilos de integración fallan en direcciones opuestas, a propósito — elige el que tenga un fallo con el que puedas vivir:
- Los adaptadores de framework (LangChain, hooks vía gestores de callback) fallan abiertos. Si un registro no se puede escribir (disco lleno, permisos), la acción del agente se completa normalmente y el registro se pierde. El manejador de LangChain imprime una advertencia llamativa en stderr y cuenta la pérdida (
handler.lost_records), pero nada en la propia cadena puede mostrar un registro que nunca se escribió — una cadena estancada aún verifica. Los checkpoints testigo con una cadencia son lo que hace visible una cadena estancada: un checkpoint esperado que nunca llega es la alarma. - El wrapper nativo
trace()falla cerrado. Si el registro no se puede escribir, la excepción se propaga a la acción del agente — sin evidencia, sin acción. Más estricto, y puede interrumpir tu agente.
Ninguno de los dos comportamientos por defecto es adecuado para todos; sabe cuál estás ejecutando.
Integridad vs. completitud (lee esta parte)
Sé preciso sobre lo que prueba cada capa — porque son afirmaciones diferentes, y las diferencias son el punto:
Una cadena auto-mantenida prueba integridad relativa a una cabeza establecida: dada una cabeza de cadena que alguien ya posee, cualquier edición, reordenación o eliminación en los registros detrás de ella se vuelve detectable. Por sí sola — antes de que alguien fuera del operador haya visto una cabeza — una cadena prueba consistencia interna, no historia: un operador podría eliminar un registro y volver a sellar, y el nuevo archivo verificaría. La cadena se vuelve comprometida históricamente en el momento en que su cabeza sale del control del operador.
Ese es el testigo: una parte fuera del operador que posee checkpoints periódicos de la cadena — el id del sujeto, un recuento de registros y dos huellas digitales de la cadena (la cabeza y la raíz de la cadena); la carga útil exacta, y nada más. Los checkpoints hacen detectable la reescritura de la historia comprometida, y un checkpoint perdido es en sí mismo un evento visible:``` halo anchor audit.jsonl witness.jsonl # anchor a checkpoint to a local witness halo anchor audit.jsonl witness.jsonl --check # completeness verdict against it
Para *time* específicamente, un timestamp externo RFC 3161 reemplaza el reloj autoafirmado del checkpoint con una prueba de una Timestamp Authority que el operador no controla — "esta cadena alcanzó esta cabeza no más tarde que T", verificable por un tercero sin infraestructura alojada. La TSA predeterminada es la gratuita freetsa.org (adecuada para evaluación); apunta a una TSA comercial (DigiCert / Sectigo / la tuya propia) con `--tsa` para producción:```
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 confirma que el token vincula este estado de la cadena y lee su tiempo atestiguado, pero no valida la firma del TSA — eso se deja deliberadamente a una herramienta estándar para que un revisor no confíe en ningún código nuestro. Para verificar el tiempo de forma independiente (esto es lo que se entrega a un revisor de seguridad):```
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"
Otra frontera, dicha con claridad: ni la cadena ni el testigo demuestran que toda acción del mundo real haya pasado por el registrador. Eso es la **completitud de captura** — una propiedad de dónde se sitúa el registrador en la pila (instrumentación nativa, hooks, ingesta en la pasarela), no de ningún hash. Los registros llevan una etiqueta `source` precisamente por esta razón. La tabla de afirmaciones bajo «Compruébalo tú mismo» al principio de esta página es el resumen de estas tres capas.
Cualquiera puede ejecutar un testigo. Un testigo que ejecutas tú mismo compromete el historial ante *ti*; comprometerlo ante *tu cliente* requiere un testigo en el que tenga razones para confiar. El protocolo está abierto en cualquier caso.
Un testigo alojado y reconocido es la forma en que este proyecto se sostendrá. Acceso anticipado: [email protected].
## Datos personales en la cadena
La cadena es de solo anexado: cualquier cosa sellada en un registro permanece allí, porque eliminarla rompería la verificación de todo lo posterior. Los argumentos de las herramientas ya están gestionados — se almacenan como un hash más un resumen redactado limitado a 200 caracteres (el modo solo-hash no conserva ningún resumen).
Nótese el límite en esa frase: *redactado*, no eliminado. La sección 6 de [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md) es explícita en que un nombre o una dirección postal no tienen un patrón fiable, por lo que ni se detectan ni se enmascaran. Y `subject` no es el único campo que contiene texto que tú proporcionas — `principal`, `approver`, `session_id`, `agent`, `authority`, `data` y los resúmenes también lo hacen.
El patrón que funciona: poner un id pseudónimo estable en la cadena y mantener la correspondencia con cualquier individuo en un sistema del que puedas eliminar. Una solicitud de borrado se satisface entonces eliminando la correspondencia. Mantén `subject` apuntando a la organización inquilina, no a una persona:```python
from halo_record import build
build("tool_call", "privacy", subject={"id": "acme", "name": "Acme Corp"})
Ninguna configuración impone esto — es una disciplina en cómo llamas al grabador. Hace que el borrado sea manejable; no es anonimización, y todavía no hay retención ni depuración integradas. LIMITS.md sección 13 tiene la lista completa de campos, explica por qué la huella de entrada almacenada puede confirmar un valor adivinable incluso después de que el mapeo desaparezca, y termina con preguntas que un revisor debería hacer.
Registra una llamada a un modelo (la primera pregunta del comprador: "¿qué modelo vio mis datos?"):```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
## Dónde encaja esto en una pila de cumplimiento
halo-record es una capa de evidencia, no una certificación. Produce el artefacto que los marcos de evaluación siguen solicitando con distintas palabras. Una nota de alcance que rige cada punto a continuación: estas son afirmaciones de integridad sobre el registro; la completitud frente al operador requiere un testigo externo que mantenga puntos de control ([LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)).
- **Cuestionarios de seguridad y revisiones SOC 2:** responda las secciones de IA con un Runtime Report verificable en lugar de capturas de pantalla y prosa.
- **AIUC-1:** produce la evidencia de registro a prueba de manipulación (E015.4) y los registros de cadena de ejecución con eventos de autorización (E015.2 — con una brecha declarada: las trazas de razonamiento no se capturan) que nombra el control de Responsabilidad E015 del estándar. E015 en sí es obligatorio; E015.2 y E015.4 son su nivel complementario: no se requieren para aprobar, es el que un proveedor adopta cuando un cliente o regulador lo solicita. Una vez que la cadena está anclada a un testigo en el que la parte confiante tiene razones para confiar — un testigo que el propio operador ejecuta no proporciona esto — eso es una cadena continuamente atestiguada en lugar de una reconstruida en el momento de la auditoría (lo que entró en la cadena sigue estando limitado por la superficie de captura). Un mapeo de evidencia control por control, incluido lo que queda deliberadamente fuera de alcance, está en [`AIUC.md`](https://github.com/bkuan001/halo-record/blob/main/AIUC.md).
- **OWASP Top 10 for Agentic Applications 2026:** ocho de las diez amenazas se mapean a reglas de política deterministas sobre el registro, dos están marcadas fuera de alcance con razones, y el paquete se distribuye ejecutable. Un mapeo comunitario aproximado, no un artefacto oficial de OWASP. Véase [`OWASP.md`](https://github.com/bkuan001/halo-record/blob/main/OWASP.md).
- **AARM (CSA):** produce el recibo de acción a prueba de manipulación que especifica AARM — R5, y la mitad de sellado de R6 (la identidad se sella en el hash, no se autentica criptográficamente). halo-record es la capa de recibos; combínelo con una puerta de enlace de aplicación para obtener un sistema AARM completo. Véase [`AARM.md`](https://github.com/bkuan001/halo-record/blob/main/AARM.md).
- **Agentic Trust Controls:** los registros en tiempo de ejecución detrás de los controles de evidencia del ATC — registro de acciones a prueba de manipulación (RBM-03) y la mitad de registro de la atestación de autoridad (AID-05; la mitad de aplicación pertenece a la puerta) en un único registro encadenado. Véase [`ATC.md`](https://github.com/bkuan001/halo-record/blob/main/ATC.md).
- **CSA AI Controls Matrix (AICM) / STAR for AI:** la evidencia del dominio LOG — registros de auditoría generados, sellados contra modificación no detectada, eventos de entrada y salida registrados — mapeada control por control en [`AICM.md`](https://github.com/bkuan001/halo-record/blob/main/AICM.md). El propio crosswalk v1.1 de CSA vincula ese dominio con AIUC-1 E015.
- **MITRE ATLAS:** la mitigación de telemetría de agentes (AML.M0024) implementada con una propiedad de integridad que ATLAS en sí no solicita — el registro es verificable por alguien fuera del operador. Véase [`ATLAS.md`](https://github.com/bkuan001/halo-record/blob/main/ATLAS.md).
- **EU AI Act / ISO 42001 / NIST AI RMF:** las obligaciones de mantenimiento de registros y registro de eventos que describen estos marcos son la misma clase de artefacto — mapeadas de forma conservadora en [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) y [NIST-AI-RMF.md](https://github.com/bkuan001/halo-record/blob/main/NIST-AI-RMF.md).
Nada de esto certifica nada por sí solo. Le da a su evaluador algo verificable que examinar. Los límites — lo que halo-record deliberadamente no hace, y qué decir cuando un revisor pregunta — están documentados en [`LIMITS.md`](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md).
### Cómo llevar la evidencia a su plataforma de GRC
La mayoría de las plataformas de GRC (Vanta, Drata y similares) aceptan archivos cargados como evidencia personalizada contra un control. La exportación de halo-record está diseñada para integrarse en ese flujo:```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
Esto escribe dos archivos para la ventana de auditoría: el CSV (una fila por acción registrada, agrupada de izquierda a derecha como cuándo → qué ocurrió → quién → bajo qué autoridad → qué se marcó → procedencia → cómo verificar, incluyendo un resumen en lenguaje sencillo y redactado de la llamada y su resultado, la compilación del agente y el modelo que produjo cada una, la identidad en cuyo nombre se ejecutó, el registro que la causó, su decisión de autorización y alcance, y cualquier categoría de datos personales o indicadores de amenaza ingeridos) y un manifiesto (evidence.csv.manifest.json) que vincula el CSV con su origen — el hash de cabecera de la cadena lo enlaza con el registro verificable del que proviene, y csv_sha256 es el hash del propio archivo exportado, de modo que un CSV editado después de la exportación ya no coincide con su manifiesto. Restrinja la población con --tool cuando un control solo cubra ciertas acciones; el manifiesto registra el filtro, de modo que una exportación con alcance limitado revela que es un subconjunto en lugar de leerse como la población completa. Suba ambos contra su control de registro o monitoreo; adjunte el HTML del Runtime Report cuando un revisor quiera verificar la cadena por sí mismo. La exportación se niega a ejecutarse sobre una cadena que no supera la verificación.
Una integración push nativa — evidencia que llega automáticamente a su plataforma — está en la hoja de ruta. La ruta de archivo anterior funciona hoy con cualquier plataforma que acepte evidencia subida.
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
## Modelo de integridad
Para calcular el hash de un registro: toma el registro excluyendo `integrity.hash`, con `integrity.prev_hash` establecido al hash del registro anterior; canoniza con RFC 8785 (JSON Canonicalization Scheme); aplica SHA-256 a los bytes. El `prev_hash` del primer registro son 64 ceros. La verificación recalcula cada hash y comprueba cada enlace. No se requiere ningún secreto; de eso se trata.
¿Crees que puedes manipular una cadena sin que el verificador lo note? [Los intentos y resultados están aquí](https://github.com/bkuan001/halo-record/discussions/2).
Referencia completa de campos: [`halo-record.schema.json`](https://github.com/bkuan001/halo-record/blob/main/src/halo_record/halo-record.schema.json).
## TypeScript
El mismo grabador se distribuye para Node: [`halo-record-ts`](https://github.com/bkuan001/halo-record-ts). Mismo formato de cadena, mismo protocolo de testigos. Los registros escritos en cualquiera de los dos lenguajes se verifican con cualquiera de los dos verificadores.
## Ejemplos de la comunidad
[trail-halo-poc](https://github.com/AmeyParle/trail-halo-poc) — prueba de concepto de la comunidad que vincula la autoridad principal de un registro Halo a credenciales TRAIL: un vínculo recíproco organización–agente y concesiones de alcance firmadas por la organización registradas en una cadena Halo, con una suite de verificación adversarial.
## Contribuir
Issues, discusiones y pull requests son bienvenidos — consulta [CONTRIBUTING.md](https://github.com/bkuan001/halo-record/blob/main/CONTRIBUTING.md) para las reglas básicas (versión corta: se requieren tests, PRs pequeños, los cambios de esquema se discuten primero).
## Licencia
Apache-2.0