Registro y checkpoints
Qué se registra, cómo se firman los checkpoints y cómo se detectan retrocesos.
El registro
logs/log.jsonl es un registro de solo añadir con una línea JSON por evento (time, level, event, message y detalles). Su vista legible está en la página de transparencia. Los eventos llevan solo cifras: nunca nombres ni hallazgos de reglas en cuarentena.
Checkpoints
Un checkpoint es un texto de tres líneas: el origen macguard-rules-log v1, el número de entradas y la raíz en base64. publish.yml lo firma con Sigstore (checkpoint.sigstore.json) y lo adjunta a la release (checkpoint.txt).
Dibujando el diagrama…
sequenceDiagram autonumber participant W as publish.yml participant T as tlog.py participant CO as cosign participant SE as seal.py W->>T: event release.approved W->>T: checkpoint (origin, size, root) W->>CO: sign-blob checkpoint CO-->>W: checkpoint.sigstore.json W->>T: verify (every checkpoint, consistency, signatures) W->>SE: write manifest.json (rules_root and log head) W->>CO: sign-blob manifest.json CO-->>W: manifest.sigstore.json W->>T: release-proof (inclusion proof for the candidate.accepted entry) T-->>W: inclusion-proof.json
Descripción del diagrama
publish.yml añade el evento de aprobación, escribe un checkpoint sin firmar con tlog.py, lo firma con cosign usando la identidad del workflow, verifica de nuevo todo el registro, sella el manifiesto que incluye la cabeza del registro, lo firma y escribe la prueba de inclusión que une la entrada de la release con ese checkpoint.
Código Mermaid
sequenceDiagram
autonumber
participant W as publish.yml
participant T as tlog.py
participant CO as cosign
participant SE as seal.py
W->>T: event release.approved
W->>T: checkpoint (origin, size, root)
W->>CO: sign-blob checkpoint
CO-->>W: checkpoint.sigstore.json
W->>T: verify (every checkpoint, consistency, signatures)
W->>SE: write manifest.json (rules_root and log head)
W->>CO: sign-blob manifest.json
CO-->>W: manifest.sigstore.json
W->>T: release-proof (inclusion proof for the candidate.accepted entry)
T-->>W: inclusion-proof.jsonDibujando el diagrama…
flowchart LR C20["checkpoint<br/>size 20<br/>00000020.txt"]:::sec C75["checkpoint<br/>size 75<br/>00000075.txt"]:::sec CN["next checkpoint<br/>size N"]:::sec C20 -->|consistency.json<br/>proof 20 to 75| C75 C75 -->|consistency proof| CN RB["client sees size smaller<br/>than the one it trusts"]:::qua -.->|rollback refused| C75 FK["client sees another root<br/>at the same size"]:::qua -.->|fork refused| C75 classDef src fill:#cfe3ff,stroke:#2f6df0,color:#0b1b33 classDef val fill:#ffe9b8,stroke:#b8860b,color:#2b1d00 classDef sec fill:#cdeedd,stroke:#1b6e3c,color:#08210f classDef pub fill:#e4d7ff,stroke:#6a3fd1,color:#1b0b3b classDef qua fill:#ffd4cc,stroke:#c0392b,color:#33100b classDef gate fill:#ffffff,stroke:#111111,stroke-width:3px,color:#111111
Descripción del diagrama
Cada checkpoint firmado compromete un tamaño y una raíz del registro. Los posteriores extienden a los anteriores. Una prueba de consistencia demuestra que el árbol del tamaño anterior es prefijo del posterior, es decir, solo se añadieron entradas. Un cliente que recuerda el checkpoint anterior puede rechazar un árbol menor (rollback) o una raíz distinta con el mismo tamaño (bifurcación).
Código Mermaid
flowchart LR
C20["checkpoint<br/>size 20<br/>00000020.txt"]:::sec
C75["checkpoint<br/>size 75<br/>00000075.txt"]:::sec
CN["next checkpoint<br/>size N"]:::sec
C20 -->|consistency.json<br/>proof 20 to 75| C75
C75 -->|consistency proof| CN
RB["client sees size smaller<br/>than the one it trusts"]:::qua -.->|rollback refused| C75
FK["client sees another root<br/>at the same size"]:::qua -.->|fork refused| C75
classDef src fill:#cfe3ff,stroke:#2f6df0,color:#0b1b33
classDef val fill:#ffe9b8,stroke:#b8860b,color:#2b1d00
classDef sec fill:#cdeedd,stroke:#1b6e3c,color:#08210f
classDef pub fill:#e4d7ff,stroke:#6a3fd1,color:#1b0b3b
classDef qua fill:#ffd4cc,stroke:#c0392b,color:#33100b
classDef gate fill:#ffffff,stroke:#111111,stroke-width:3px,color:#111111El manifiesto firmado incluye la cabeza del registro (log.origin, log.size, log.root) y debe coincidir con el checkpoint adjunto. En la última release el tamaño es 75 y la raíz del registro 1bab9bb9364b3a42f76835ae138bffad33eaa3694d0021afd208f5fd54e63d96.
Eventos
Estos son los códigos de evento que emiten los workflows y scripts. Una prueba de la documentación comprueba que cada uno aparece en el fichero indicado.
| Código | Nivel | Cuándo se registra | Origen |
|---|---|---|---|
sources.fetched | INFO | Se descargaron las fuentes upstream en commits fijados. | scripts/tlog.py |
candidate.classified | INFO | Resumen de la clasificación: examinados, aceptados, en cuarentena, rechazados y obsoletos (solo cifras). | scripts/tlog.py |
candidate.accepted | INFO | El conjunto aprobado superó todos los controles obligatorios; incluye su rules_root candidato. | scripts/tlog.py |
candidate.not_publishable | WARNING | Esta ejecución no produce release; se indican los bloqueos. | scripts/tlog.py |
rules.withheld | WARNING | Ficheros ya publicados que ya no pasan los controles y se retienen hasta resolverse. | scripts/tlog.py |
file.skipped | WARNING | Un fichero de la fuente se omitió al preparar el candidato (tamaño, binario, ruta no permitida). | scripts/tlog.py |
rule.rejected | ERROR | Evento heredado del flujo anterior: un fichero falló la validación del compilador. | scripts/tlog.py |
gate.rule_error | ERROR | Evento heredado: un fichero falló al cargarse en la app. | scripts/tlog.py |
candidate.rejected | ERROR | Evento heredado: candidato bloqueado completo por ficheros que fallaban. | scripts/tlog.py |
candidate.changes | INFO | Hay cambio efectivo respecto a la última release; se solicita la publicación. | .github/workflows/daily.yml |
candidate.unchanged | INFO | Sin cambio efectivo: no hay release ni solicitud de aprobación. | .github/workflows/daily.yml |
run.failed | ERROR | La ejecución diaria falló antes de poder decidir; no se publicó nada. | .github/workflows/daily.yml |
release.approved | INFO | El candidato se aprobó para firma (automáticamente o por un humano según el entorno) y se firma la cabeza del registro. | .github/workflows/publish.yml |
release.published | INFO | La release firmada se publicó en GitHub y se solicitó el despliegue del sitio. | .github/workflows/publish.yml |
release.failed | ERROR | Fallo al firmar o publicar; no se publicó nada nuevo. | .github/workflows/publish.yml |
site.github_pages_deployed | INFO | Sitio desplegado en GitHub Pages. | .github/workflows/site.yml |
site.github_pages_failed | ERROR | Falló el despliegue en GitHub Pages. | .github/workflows/site.yml |
site.live_verified | INFO | La URL pública se verificó con los bytes que sirve (paquete, ficheros, firmas, pruebas, enlaces). | .github/workflows/site.yml |
site.live_failed | ERROR | La verificación de la URL pública falló. | .github/workflows/site.yml |
site.build_failed | ERROR | No se pudo construir o verificar el sitio. | .github/workflows/site.yml |
site.cloudflare_deployed | INFO | Sitio desplegado automáticamente en Cloudflare Pages desde el workflow. | .github/workflows/site.yml |
site.cloudflare_skipped | WARNING | No hay credenciales de Cloudflare configuradas en el repositorio; no se intentó desplegar. | .github/workflows/site.yml |
site.cloudflare_blocked | ERROR | La comprobación previa de la credencial de Cloudflare falló; no se intentó desplegar. | .github/workflows/site.yml |
site.cloudflare_failed | ERROR | El despliegue en Cloudflare Pages falló; sigue el sitio anterior. | .github/workflows/site.yml |