Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
halo-record — Rastros de auditoría a prueba de manipulaciones para agentes de IA: Runtime Records encadenados por hash, sin dependencias, verificables por cualquiera. | Kitploit
Herramientas/GitHubGitHub/bkuan001/halo-record
Análisis ForenseSeguridad de Cadena de SuministroRespuesta a IncidentesSeguridad de IA
GitHubbkuan001/halo-record

halo-record

Rastros de auditoría a prueba de manipulaciones para agentes de IA: Runtime Records encadenados por hash, sin dependencias, verificables por cualquiera.

Ver Repositorio
636102hace 24 díasRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Sitio web
Compartir
# halo-record

**Rastros de auditoría a prueba de manipulación para agentes de IA** — Registros de Runtime encadenados por hash, renderizados como un Informe de Runtime que tus clientes pueden verificar por sí mismos.

Cada acción que tu instrumentación captura (llamadas a herramientas, llamadas a modelos, acceso a datos, aprobaciones) se convierte en un **Registro de Runtime** en un log de solo anexado y encadenado por hash; el **Informe de Runtime** es esa cadena renderizada como una página HTML que recalcula la cadena de hash en el navegador del lector y renderiza lo que muestra a partir de los registros que verificó (para una comprobación que no dependa del propio código de la página, ejecuta `halo verify` sobre el JSONL). Cualquier parte que posea un checkpoint de la cadena puede verificar que los registros hasta ese checkpoint no han sido alterados desde que se tomó, 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](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)). 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 de 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](https://www.helpnetsecurity.com/2026/08/31/halo-record-open-source-ai-agent-audit-trail/)** (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?](https://github.com/bkuan001/halo-record/discussions/7)

## Compruébalo tú mismo

Te están pidiendo que pongas 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-record` instala exactamente un paquete.
- **Sin llamadas de red**, excepto cuatro opcionales — anclaje a un testigo (envía el id del sujeto, un recuento de registros y dos huellas 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), la marca de tiempo RFC 3161 (envía solo el hash de estado de un checkpoint a una Autoridad de Sellado de Tiempo) y `halo serve --verify-email` (envía un código de un solo uso y el nombre del sujeto del informe, por SMTP a través del servidor que configures en `$HALO_SMTP_HOST`, a la dirección que introduce un visor del informe). Todas están desactivadas a menos que las invoques; ningún otro contenido de los registros sale de tu infraestructura.
- **Los argumentos de herramientas en bruto se hashean, con un resumen redactado al lado.** 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 un comodín de entropía): trátala como defensa en profundidad, no como una garantía. Los campos de resultado que proporciones más allá de `summary` se escanean y enmascaran con los mismos patrones, pero no se recortan a 200 caracteres (LIMITS §13).
- **Lo suficientemente pequeño para auditar.** ~8.700 líneas de Python, incluidos el JavaScript y CSS incrustados en la página del informe (líneas de código, sin contar líneas en blanco, comentarios y docstrings). Léelo todo en un día.
- **Apache-2.0.**
- **El papeleo es de primera clase.** [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md) (lo que la cadena no puede probar), [PRIVACY.md](https://github.com/bkuan001/halo-record/blob/main/PRIVACY.md) (qué contienen los registros y qué sale de tu máquina), [RETENTION.md](https://github.com/bkuan001/halo-record/blob/main/RETENTION.md) (operar bajo una política de retención) y [REVIEWERS.md](https://github.com/bkuan001/halo-record/blob/main/REVIEWERS.md) — la comprobació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](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)): 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 la reescritura de historial comprometido | — | ✔ | ✔ |
| 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 Informe de Runtime de ejemplo](https://bkuan001.github.io/halo-record/sample-report.html) — datos ficticios, cadena real, y su script recalcula la cadena de hash de los registros incrustados en tu navegador cuando lo abres.

## Demo de 60 segundos

No se requiere agente. Con [uv](https://docs.astral.sh/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 testigo local que representa a uno fuera del operador — véase [LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)), 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 terminal-spinner 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 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 vinculan 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 -> HTML that re-verifies in the browser
halo serve ./records --port 8721          # all tenants, gated per customer
```

¿Actualizar un directorio servido por primera vez por 0.2.45? `halo serve` vuelve a leer cada concesión anterior al iniciarse. Una concesión sobre un archivo cuyos registros no llevan ningún id de sujeto (un archivo `Recorder` simple como `agent.jsonl`) se pone en cuarentena y se imprime en cada inicio con el comando `halo grant` que la vuelve a emitir — véase [LIMITS.md §17, "Serving a directory first served by 0.2.45"](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#17-one-chain-file-per-subject).

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 va mal: abre un issue.

### El bloque de verificación

Si una capa de guardarraíl o de política comprobó 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 hash 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"})
```

Una nota sobre nomenclatura: el paquete exporta una *función* `record` (el decorador), que oculta el *módulo* `halo_record.record` en el objeto del paquete. Importa directamente desde la ruta del módulo cuando quieras sus elementos internos — `from halo_record.record import build` — en lugar de `import halo_record.record as record`.

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 de los hechos; 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 SDK de OpenAI Agents | Cualquier registro de gateway / proxy inverso |
| Hook del SDK de Claude Agent | 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 para cada llamada de herramienta, independientemente del framework que lo impulse:

```python
from mcp import Client   # mcp 2.x
from halo_record import Recorder
from halo_record.integrations.mcp import instrument_client_session

async def run(server):   # an MCPServer, a server URL, or stdio parameters
    async with Client(server) as client:
        instrument_client_session(client, Recorder("audit.jsonl"), server="stripe")
        await client.call_tool("charge", {"customer": "cus_123", "amount": 500})   # recorded
```

Cada `call_tool()` en el cliente envuelto se registra, y una herramienta que devuelve un resultado de error se sella como `error` (el adaptador lee tanto `isError` como `is_error` de mcp 2.x). En mcp 1.x, pasa el `ClientSession` inicializado en lugar de `Client`; el wrapper es el mismo.

Para registros de gateway o proxy (Cloudflare AI Gateway, Portkey, nginx delante del modelo), asigna 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"})
```

Cualquier cosa que emita 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](https://github.com/bkuan001/halo-record-ts) 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 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 aterrizan en `~/.halo/audit.jsonl` (sobrescribe con `$HALO_LOG`). Las herramientas de pura orquestación que no tocan datos, red ni estado externo se omiten — la cadena registra acciones en límites de confianza, no pensamientos. 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 a la compilación del agente que lo produjo — cuando un auditor pregunte sobre 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 evento (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 aterrizan 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. Mantenlo seguro para la privacidad: hashes y refs, no prompts en bruto, texto de políticas privadas, secretos ni esquemas de herramientas completos — los formatos conocidos de secretos y datos personales se enmascaran en el momento del sellado, pero los hashes y las refs pasan intactos y el texto de forma libre no se detecta (véase LIMITS §6). Reutiliza un `snapshot_id` solo mientras la autoridad subyacente no haya cambiado; 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"]
}
```

```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 la bifurcarán (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 el archivo de cadena directamente — un hook hecho a mano, trabajadores en paralelo, un transportador de logs — debe mantener un bloqueo exclusivo equivalente a lo largo de la secuencia de leer-cabeza-luego-añadir, o escribir en cadenas por proceso. [LIMITS.md sección 9](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#9-single-writer-chains) cubre esto en su totalidad, incluida la frontera entre lenguajes.

## Cuando falla el registro

Los registros se escriben después de que se ejecuta la acción. Halo no bloquea una acción cuando su registro no puede escribirse; escribir el registro antes de la acción no se ofrece.

- **Predeterminado: el resultado de la acción pasa sin cambios.** Si un registro no puede escribirse (disco lleno, permisos, un tiempo de espera de bloqueo), `trace()`, `record_call`, `@record`, los adaptadores de framework (OpenAI Agents, LangChain, LiteLLM, OpenTelemetry), el wrapper MCP y `halo hook` devuelven el propio resultado de la acción sin cambios, o vuelven a lanzar la propia excepción de la acción si esta falló. Una advertencia en stderr nombra el registro faltante: `halo-record: FAILED to record '<tool>' (<error>); the action ran, but its record is NOT in the evidence log. ...` (sin un stderr utilizable, va al logger `halo_record`). El manejador de LangChain y el exportador de OTel también cuentan las pérdidas (`lost_records`). Nada en la cadena puede mostrar un registro que nunca se escribió, por lo que los registros restantes aún verifican. Un revisor que observa la cadencia del punto de control testigo ve una cadena que se ha estancado; un único registro faltante entre los posteriores solo se muestra en la línea de stderr.
- **El modo estricto también lanza.** Pasa `strict_recording=True` (a `trace`, `record_call`, `@record`, `instrument_client_session`, o al constructor de un adaptador), o establece `HALO_STRICT_RECORDING=1`, y se escribe la misma línea de stderr, luego se lanza el error de registro. Llega al llamador desde `trace`, `record_call`, `@record`, el wrapper MCP, los hooks de OpenAI Agents y el manejador de LangChain (que establece el `raise_error` de LangChain). No puede llegar al llamador desde el exportador de OTel o el logger de LiteLLM: esos frameworks capturan y registran lo que lanza un exportador o callback, por lo que allí la línea de stderr es la señal en ambos modos (LIMITS.md sección 18). La acción ya se ha ejecutado cuando se lanza el error, por lo que un llamador que lo trate como "la acción falló" y reintente repetirá los efectos secundarios de la acción (un reembolso emitido dos veces). Usa el modo estricto solo donde un registro faltante sea peor que ese riesgo.
- `record_call` y `@record` sin un recorder (ninguno pasado, ninguno vinculado por `trace`) lanzan `RuntimeError` antes de que se ejecute el código envuelto.
- `@record` y `trace()` aceptan funciones `async def`. El wrapper es a su vez `async def`: espera la función, luego registra lo que devolvió o lanzó (una cancelación se registra como un error). `trace()` mantiene su recorder vinculado mientras se espera el cuerpo, por lo que una herramienta `record_call` o `@record` dentro de un agente asíncrono aterriza en la cadena trazada, y los agentes concurrentes mantienen cada uno la suya. Una función simple que devuelve un awaitable se registra cuando devuelve. Una función generadora asíncrona se registra cuando se llama, con el objeto generador como su resultado; lo que produce no se registra.

## 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 **históricamente comprometida** en el momento en que su cabeza sale del control del operador.

Ese es el testigo: una parte fuera del operador que posee puntos de control 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 puntos de control hacen detectable la reescritura de la historia comprometida, y un punto de control 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 sello de tiempo RFC 3161 externo reemplaza el reloj autoafirmado del checkpoint con una prueba de una Autoridad de Sellado de Tiempo fuera del control del operador — "esta cadena alcanzó esta cabeza no más tarde de T", verificable por un tercero sin infraestructura alojada. La TSA predeterminada es la gratuita freetsa.org (adecuada para evaluación); para producción, apunte `--tsa` a una TSA fuera del control del operador (p. ej. DigiCert / Sectigo, o una que designe una parte confiante):

```
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 <the digest printed above> -in token.tsr -CAfile tsa-ca.pem   # → "Verification: OK"
```

Otra frontera, dicha sin rodeos: ni la cadena ni el testigo demuestran que toda acción del mundo real haya pasado por el registrador. Eso es la **exhaustividad de captura** — una propiedad de dónde se sitúa el registrador en la pila (instrumentación nativa, hooks, ingesta por 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 resumen; en los campos id enmascara un token con un formato de secreto conocido como en los resúmenes y reemplaza cualquier otro token con forma de secreto por `sha256:`
más 16 dígitos hexadecimales — todos salvo `subject.id`, que enruta el registro a la cadena de su inquilino).

Nótese el límite en esa frase: *redactado*, no eliminado. [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)
sección 6 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 lleva 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](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#13-personal-data-and-erasure) 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 haya desaparecido, y termina con preguntas que un revisor debería hacer.

Registrar una llamada a un modelo (la primera pregunta del comprador: "¿qué modelo vio mis datos?"):

```python
from halo_record import Recorder, record_model_call

rec = Recorder("audit.jsonl")
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
```

El modelo también se sella como `agent.model` y los términos de retención como `data.zdr`,
para que la columna de modelo del export y las reglas de política puedan leerlos, y ambos sobreviven
`summaries=False`. Pasa `messages=` y la entrada del registro lleva cuántos había — un recuento, nunca su contenido.

## 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 pidiendo 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 custodie checkpoints ([LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)).

- **Cuestionarios de seguridad y revisiones SOC 2:** responde a las secciones de IA con un Runtime Report verificable en lugar de capturas de pantalla y prosa.
- **AIUC-1:** produce la evidencia de logging 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 Accountability E015 del estándar. E015 en sí es obligatorio; E015.2 y E015.4 son su nivel suplementario: no se exigen para aprobar, es el que adopta un proveedor cuando un cliente o un 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 ofrece esto —, eso es una cadena continuamente atestiguada en lugar de una reconstruida en el momento de la auditoría (lo que entra en la cadena sigue estando acotado 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 se marcan fuera de alcance con motivos, y el pack se incluye en el paquete, ejecutable por nombre (`halo policy chain.jsonl owasp-starter`). Un mapeo comunitario aproximado, no un artefacto oficial de OWASP. Véase [`OWASP.md`](https://github.com/bkuan001/halo-record/blob/main/OWASP.md).

---

[Read more](https://github.com/bkuan001/halo-record)
Descargar herramienta