Skip to content
synsema why?
← All cases
Case 5 · Measured on 2026-10-16 · Synsema 0.6.44 · same data, same model on both sides

Training data you cannot hide

If someone adds data to training without documenting it, does it get recorded?

1 of 2

data files recorded by the usual manifest. Synsema recorded 2 of 2.

Someone added training data off the books. Python's manifest missed it. Synsema's signed receipt did not.

1/2Python, manual manifest2/2Synsema, engine lineage
Data files recorded, out of the 2 the training actually usedSame training run, same undocumented read of extra_data.csv (80 rows).

The problem

Since August 2026 the EU AI Act asks high-risk systems to document where training data came from and how it was prepared, and to be able to reconstruct what produced each model.

Today that is a logger, MLflow or DVC, plus the discipline that everyone uses it every time. One direct read outside the helper and the record is incomplete, with nobody lying.

What we built

  • The genre classifier from another case, trained twice: Python with the usual manual manifest (a read helper that hashes every file), and Synsema with the engine's lineage and a signed receipt.
  • In both, a realistic slip: besides the documented data, someone loads extra_data.csv directly.
  • An independent verifier that needs no private key: signature, declared result, model hash and the hash of every input.

Results

Same training, two records
Python, manual manifestSynsema, lineage + receipt
Training rows498 (418 + 80 extra)498 (418 + 80 extra)
Files the record lists1: the extra read skipped the helper2: the engine records every read
Who writes the recordthe program, if it remembersthe Synsema engine
Signaturenone unless added by handed25519, W3C Data Integrity, did:key issuer
Data sheetby handgenerated from the lineage
Tamper tests (verifier, no private key)
CaseResult
Everything as it wasVERIFIED: signature, result, model and data match
Change one label in the training filedetected, NOT VERIFIED: the file changed
Edit the receipt to hide extra_data.csvdetected, NOT VERIFIED: invalid signature

The code, side by side

The usual way
Python: the record depends on everyone using the helperThe read of extra_data.csv skips read() and is never recorded.
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 · from line 13
Synsema
Synsema: the same read, recorded by the engineThe program does not write the record. The engine does.
-- 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 · from line 23
Synsema: the signed receiptThe signature covers the result hash and the list of reads.
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 · from line 36

Why Synsema

  • lineage() is written by the engine, not by the program. Every read from outside is recorded with its sha256, and the program cannot add, edit or drop an entry.
  • receipt() turns it into a Verifiable Credential signed with ed25519. Anyone can verify it with the issuer's public DID.
  • The data sheet is generated from what actually happened, not from what someone remembered to log.

What it does not solve

  • The lineage records what the program reads, not what it writes. The model hash is added to the signed result by hand.
  • Proving a file did not change does not prove it was lawful to use (license, consent). That stays with whoever builds the dataset.
  • The demo signing key is a test key. In production it lives in a secret manager and its DID is published.

Reproduce it

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

Share