Ir al contenido
synsema ¿por qué?
← Todos los casos
Caso 5 · Medido el 2026-10-16 · Synsema 0.6.44 · mismos datos y mismo modelo de los dos lados

Datos de entrenamiento que no se pueden esconder

Si alguien suma datos al entrenamiento sin documentarlos, ¿queda registrado?

1 de 2

archivos de datos que registró el manifiesto habitual. Synsema registró 2 de 2.

Alguien sumó datos al entrenamiento sin documentarlos. El manifiesto de Python no lo vio. El recibo firmado de Synsema, sí.

1/2Python, manifiesto manual2/2Synsema, lineage del motor
Archivos de datos registrados, de los 2 que el entrenamiento usó de verdadEl mismo entrenamiento, la misma lectura no documentada de extra_data.csv (80 filas).

El problema

Desde agosto de 2026 la ley de IA de la UE pide a los sistemas de alto riesgo documentar de dónde salieron los datos de entrenamiento y cómo se prepararon, y poder reconstruir qué produjo cada modelo.

Hoy eso es un logger, MLflow o DVC, más la disciplina de que todos lo usen siempre. Una lectura directa por fuera del helper y el registro queda incompleto, sin que nadie mienta.

Qué construimos

  • El clasificador de géneros de otro caso, entrenado dos veces: Python con el manifiesto manual habitual (un helper de lectura que calcula el hash de cada archivo) y Synsema con el lineage del motor y un recibo firmado.
  • En los dos, un descuido realista: además de los datos documentados, alguien carga extra_data.csv directamente.
  • Un verificador independiente que no necesita clave privada: firma, resultado declarado, hash del modelo y hash de cada entrada.

Resultados

El mismo entrenamiento, dos registros
Python, manifiesto manualSynsema, lineage + recibo
Filas de entrenamiento498 (418 + 80 extra)498 (418 + 80 extra)
Archivos que lista el registro1: la lectura extra no pasó por el helper2: el motor registra toda lectura
Quién escribe el registroel programa, si se acuerdael motor de Synsema
Firmaninguna, salvo que se agregue a manoed25519, W3C Data Integrity, emisor did:key
Ficha de datosa manogenerada desde el lineage
Pruebas de manipulación (verificador, sin clave privada)
CasoResultado
Todo tal cualVERIFICADO: firma, resultado, modelo y datos coinciden
Cambiar una etiqueta en el archivo de entrenamientodetectado, NO VERIFICADO: el archivo cambió
Editar el recibo para ocultar extra_data.csvdetectado, NO VERIFICADO: firma inválida

El código, lado a lado

Lo habitual
Python: el registro depende de que todos usen el helperLa lectura de extra_data.csv no pasa por read() y nunca queda registrada.
def read(path):
    """Records the read in the manifest. It only works if EVERYONE uses it, every time."""
    b = pathlib.Path(path).read_bytes()
    MANIFEST.append({"file": pathlib.Path(path).as_posix(), "bytes": len(b), "sha256": hashlib.sha256(b).hexdigest()})
    return pd.read_csv(path)

labels = read(D / "data" / "teacher_labels.csv")
labels = labels[labels["teacher"].notna()].sample(frac=1, random_state=11)
cut = int(len(labels) * 0.8)
train, test = labels.iloc[:cut], labels.iloc[cut:]

extra = pd.read_csv(D / "data" / "extra_data.csv")       # <- the off-the-books read: it does not go through read()
cases/05-ai-act-traceability/python/train.py · desde la línea 13
Synsema
Synsema: la misma lectura, registrada por el motorEl programa no escribe el registro. Lo hace el motor.
-- 2. The off-the-books read: someone added data without documenting it. In Python it escapes the manual
--    manifest; here the engine records it anyway, because the program cannot drop entries from the lineage.
let extra be csv_parse(read_file(`{D}/data/extra_data.csv`))
cases/05-ai-act-traceability/train.syn · desde la línea 23
Synsema: el recibo firmadoLa firma cubre el hash del resultado y la lista de lecturas.
let k be secret("RECEIPT_SEED")
let did be did_key_encode(ed25519_pubkey(k))
let vm be did + "#" + did_key_decode(did).multibase
let result be {"model_sha256": slice(hex(sha256(model_json)), 2), "metrics": metrics, "classes": CLASSES}
let rec be receipt({"sign": k, "verification_method": vm, "result": result})
cases/05-ai-act-traceability/train.syn · desde la línea 36

Por qué Synsema

  • lineage() lo escribe el motor, no el programa. Cada lectura de afuera queda registrada con su sha256, y el programa no puede agregar, editar ni quitar entradas.
  • receipt() lo convierte en una Verifiable Credential firmada con ed25519. Cualquiera la verifica con el DID público del emisor.
  • La ficha de datos se genera a partir de lo que pasó de verdad, no de lo que alguien se acordó de registrar.

Lo que no resuelve

  • El lineage registra lo que el programa lee, no lo que escribe. El hash del modelo se agrega a mano al resultado firmado.
  • Probar que un archivo no cambió no prueba que fuera lícito usarlo (licencia, consentimiento). Eso sigue siendo de quien arma el dataset.
  • La clave de firma de la demo es de prueba. En producción vive en un gestor de secretos y se publica su DID.

Reproducirlo

git clone https://github.com/synsema/why-synsema && cd why-synsema
# cases/05-ai-act-traceability
python -c "import secrets; print(secrets.token_hex(32))" > cases/05-ai-act-traceability/signing_key.hex   # test key, never committed
RECEIPT_SEED=$(tr -d '\r\n' < cases/05-ai-act-traceability/signing_key.hex) synsema run cases/05-ai-act-traceability/train.syn
synsema run cases/05-ai-act-traceability/verify.syn
uv run --with pandas --with scikit-learn python cases/05-ai-act-traceability/python/train.py

github.com/synsema/why-synsema/tree/main/cases/05-ai-act-traceability

Compartir