Ir al contenido

Los artefactos .froga/*

✅ Estable

El directorio .froga/ del repositorio contiene los artefactos de evidencia firmados que el motor escribe tras ejecutar los subcomandos correspondientes. Cada artefacto es un fichero JSON en un sobre versionado (FrogaArtifact<T>) acompañado de un fichero de firma (.sig). La plataforma cloud lee estos ficheros desde el repositorio y los verifica antes de renderizar cualquier informe.


Todos los artefactos .froga/* siguen el mismo contrato de envelope definido en crates/froga-core/src/froga_artifact.rs. El sobre es un wrapper versionado con serialización determinista (sin timestamps, orden de campos estable):

Forma del envelope FrogaArtifact<T>
{
"schema_version": 1,
"kind": "conformance",
"system": "loan",
"payload": { ... }
}
CampoTipo RustDescripción
schema_versionu32Versión incremental del esquema del payload (por tipo de artefacto). Actualmente 1 para todos los tipos.
kindStringTipo de artefacto: "conformance" o "reconstruct".
systemStringNombre del sistema al que pertenece el artefacto (ancla legible; el ancla técnica es la referencia git externa).
payloadTContenido específico del artefacto (varía por kind).

La serialización es determinista (JSON pretty, orden de declaración de campos) — los mismos bytes se firman y se almacenan. No hay timestamps en el envelope; la trazabilidad temporal la aporta el historial git.


Cada artefacto <nombre>.json tiene un fichero hermano <nombre>.json.sig. La firma sigue el mismo esquema que el bundle principal:

  • Algoritmo: ECDSA sobre la curva NIST P-256 con SHA-256, firmas deterministas (RFC 6979); firma en formato crudo r‖s (64 bytes) dentro de un sobre DSSE (Dead Simple Signing Envelope)
  • Statement: in-toto Statement v1 (tipo https://in-toto.io/Statement/v1)
  • Payload canónico: JCS (RFC 8785) para determinismo byte-a-byte
  • Subject: SHA-256 de los bytes del artefacto — vincula firma y contenido
  • keyid: hex(SHA-256(pubkey SEC1 sin comprimir, 65 B)) — identifica la clave verificadora sin transportarla

El backend de firma es enchufable (variable de entorno FROGA_SIGNING_BACKEND): local (escalar EC P-256 de 32 bytes en hex en FROGA_SIGNING_KEY; sin él, escalar de desarrollo fijo, solo integridad, sin no-repudio) o scaleway-kms (firma delegada al Scaleway Key Manager, alg EC-P256-SHA256; la clave privada nunca sale del KMS). froga pubkey exporta la pubkey verificadora (SEC1, 65 B hex) para registrarla en la nube por conexión (ScmConnection.signerPubkey).

Ver Firmar y verificar evidencia para el flujo completo y el estado actual de la cadena de confianza.


Fichero .froga/FirmaComando que lo escribeFlag requeridokind
bundle.jsonbundle.json.sigfroga run(siempre)(bundle, no FrogaArtifact)
reconstruct.jsonreconstruct.json.sigfroga reconstruct--out"reconstruct"
conformance/<slug>.jsonconformance/<slug>.json.sigfroga conformance--out"conformance"
risk-projection/<slug>.jsonrisk-projection/<slug>.json.sigfroga risk-projection(siempre, bajo demanda)"risk-projection"

bundle.json no usa el envelope FrogaArtifact<T>: es un EvidenceBundle firmado directamente. Es el artefacto central que los demás referencian. Su firma se verifica con froga verify.

El slug del fichero de conformidad se genera a partir del id del estándar reemplazando / y @ por _:

eu/pren-18228@2026 → .froga/conformance/eu_pren-18228_2026.json
iso/23894@2023 → .froga/conformance/iso_23894_2023.json

Esta transformación la realiza la función standard_slug en froga_artifact.rs.

El slug del fichero de proyección de riesgo se genera a partir del id del marco de la misma forma:

iso/14971@2019 → .froga/risk-projection/iso_14971_2019.json
eu/pren-18228@2026 → .froga/risk-projection/eu_pren-18228_2026.json
iso/23894@2023 → .froga/risk-projection/iso_23894_2023.json

La función write_froga_artifact en crates/froga-cli/src/util/artifact.rs sigue este patrón para todos los artefactos:

Patrón de escritura (de util/artifact.rs)
// 1. Construir el envelope
let art = FrogaArtifact::new(1, "conformance", &system, payload);
// 2. Serializar a JSON determinista
let json = artifact.to_json()?;
// 3. Firmar los bytes (ECDSA-P256; el backend lo elige FROGA_SIGNING_BACKEND
// internamente vía signer_from_env: LocalSigner o ScalewayKmsSigner)
let proof = EcdsaDsseSigner.sign(json.as_bytes())?;
// 4. Escribir <rel>.json y <rel>.json.sig
std::fs::write(&path, &json)?;
std::fs::write(sig_path, &proof)?;

Los subdirectorios se crean automáticamente (p. ej. .froga/conformance/ para los informes de conformidad).


La plataforma cloud (TypeScript, cloud/lib/froga/) reproduce la verificación de froga verify sin llamar al CLI:

  1. Lee los bytes del artefacto (.json) y la firma (.sig).
  2. Parsea el sobre DSSE e in-toto Statement del fichero .sig.
  3. Verifica la firma ECDSA-P256 (raw r‖s) sobre el PAE (Pre-Authentication Encoding DSSE) con @noble/curves.
  4. Comprueba que el keyid del sobre coincide con hex(SHA-256(pubkey SEC1)) de la pubkey esperada de ESA conexión (ScmConnection.signerPubkey), de forma fail-loud: sin pubkey registrada, la nube no verifica en silencio sino que falla.
  5. Verifica que el subject.sha256 del Statement coincide con el SHA-256 de los bytes del artefacto.

La nube no calcula evidencia: solo lee y verifica lo que el motor ha committeado. La fuente de verdad es siempre el repositorio git.

Verificar la firma del bundle desde el CLI
froga verify --repo /ruta/al/proyecto

froga compile → genera oscal.assessment_plan (no es un FrogaArtifact)
froga run → escribe .froga/bundle.json + bundle.json.sig
froga reconstruct --out → escribe .froga/reconstruct.json + .sig
froga conformance --out → escribe .froga/conformance/<slug>.json + .sig
froga risk-projection → escribe .froga/risk-projection/<slug>.json + .sig
[git commit -A .froga/] → los artefactos quedan versionados y trazables

Los artefactos no se commitean automáticamente; el commit es un acto humano deliberado que constituye el “poner en mercado” del estado de conformidad.

Ver también: Referencia del CLI froga, Firmar y verificar evidencia.