The NYT sued OpenAI. Getty sued Stability AI. Every AI company with a copyright problem is now asking the same question: can we prove what was and wasn't in our training set?
Until now, the honest answer was no. Training runs are one-way operations — you can say "we used Common Crawl" but you can't issue a cryptographic proof that a specific document was excluded.
CompletenessManifest is a Python library that changes that. It's part of Cathedral-Constraint-Field, and it lets you build training-data manifests where absence is provable, not just claimed.
The core idea: proving a negative
Standard Merkle trees prove membership — "this item is in the set." To prove non-membership you normally need a different structure.
Sorted Merkle trees solve this via adjacency. If your tree is built over a sorted list of items, and you want to prove "private_diary.txt" ∉ corpus, you present the two adjacent items in sorted order that bracket it — say "press_release_2024.pdf" and "quarterly_report.pdf" — each with a Merkle path to the root. If those items are genuinely adjacent in the sorted tree, there's no gap for "private_diary.txt" to hide in.
The leaf hash formula binds all three: SHA-256(json({"item": item, "index": i, "total": n})). This prevents forged-index attacks — you can't rearrange items to manufacture a false adjacency.
Show me the code
pip install cathedral-constraint-field
from cathedral_constraint_field import CompletenessManifest, SortedMerkle
# During training data collection
manifest = CompletenessManifest("training-corpus-v1")
for doc_id in training_pipeline:
manifest.add(doc_id, category="training", metadata={"source": doc_id.split("/")[0]})
# heartbeat periodically for temporal provenance
if len(manifest.entries) % 10_000 == 0:
manifest.heartbeat()
root = manifest.seal() # finalise — no further entries allowed
print(f"Corpus root: {root}")
# → Corpus root: 3a7f2c1e9b...
Now at any future point — months or years later — you can issue a non-membership proof:
# Someone claims their private messages were in your training set
proof = manifest.prove_non_membership("user_dm_archive_2024.jsonl")
if proof is None:
print("That document IS in the training set")
else:
import json
print("Non-membership proof:")
print(json.dumps(proof, indent=2))
# Share this JSON — the recipient can verify it independently
assert SortedMerkle.verify_non_membership(proof)
The proof is plain JSON. No manifest needed to verify it. Anyone with the root can check it.
Heartbeats: temporal provenance
The harder problem isn't proving absence now — it's proving absence at training time. Did you add the document later? Did you modify the manifest after the fact?
CompletenessManifest solves this with a heartbeat chain:
# Each heartbeat anchors the current Merkle root + chain tip to a timestamp
hb = manifest.heartbeat()
print(f"Epoch {hb.epoch}: root={hb.commitment_root[:16]}..., chain_tip={hb.chain_tip[:16]}...")
Heartbeats are hash-chained to each other (same pattern as the entry chain). You can't insert a heartbeat retroactively without breaking the chain. A verifier can check:
- Does the entry chain verify? (tamper-evident append-only log)
- Does the heartbeat chain verify?
- Was
Xprovably absent at heartbeat epoch N?
Cathedral integration: BCH on-chain anchoring
If you're using the Cathedral memory API, CathedralBridge now wires this directly to Bitcoin Cash anchoring:
from cathedral_constraint_field import CathedralBridge
bridge = CathedralBridge(api_key="cathedral_...", agent_id="my-training-pipeline")
manifest.seal()
bridge.store_manifest(manifest) # persist Merkle root to Cathedral memory
bridge.snapshot_manifest(manifest) # BCH-anchor via Cathedral snapshot (OP_RETURN)
The snapshot call writes the manifest root into Cathedral's snapshot note, which Cathedral anchors to BCH via OP_RETURN. You now have a blockchain-timestamped record of your corpus root — externally verifiable, not self-signed.
Why this matters now
Three converging pressures:
GDPR Article 17 (right to erasure): If a data subject requests deletion, can you prove their data was never in your training set? Or that you scrubbed it? Right now most companies just say "we'll try." A manifest lets you say "here's a cryptographic proof."
EU AI Act (Art. 53): High-risk AI systems must maintain documentation of training data sources. "Comprehensive" is the word used. A verifiable manifest is the difference between a compliance checkbox and an auditable record.
NYT v. OpenAI pattern: The legal theory is that copyrighted works were memorised. A non-membership proof doesn't win the case, but it moves the conversation from "we don't think so" to "we can prove it wasn't there."
What it doesn't solve
Be honest about limitations:
- Hashing items: the manifest hashes document IDs or content hashes, not semantic content. You can exclude a filename but still include a verbatim copy under a different name. The manifest is only as honest as your ingestion pipeline.
- Seal timing: the value depends on when you sealed. An unsealed manifest can still have items added. Frequent heartbeats + external anchoring narrows the window.
- Not a legal opinion: this is a cryptographic tool. What it means in court depends on jurisdiction, judge, and the specifics of your ingestion pipeline.
The library
pip install cathedral-constraint-field # v0.3.1
- Pure stdlib crypto (SHA-256, no external deps for the manifest module)
- Sorted Merkle tree with O(log n) non-membership proofs
- Append-only entry chain + heartbeat chain (both independently verifiable)
- Full export/reload round-trip
- 67 tests
If you're building training pipelines and want to discuss compliance tooling, I'm interested in early adopters. Drop a comment or open an issue.
Top comments (0)