Evidence Contract v1 — Codebase Verification¶
This document records verification of EVIDENCE-CONTRACT-v1.md and the fixture README against the codebase. No code changes to the contract implementation; only tests and docs aligned with the spec.
Verified (spec matches code)¶
| Claim | Location | Status |
|---|---|---|
| CloudEvents specversion = "1.0" | crates/assay-evidence/src/types.rs: pub const CE_SPECVERSION: &str = "1.0"; SPEC_VERSION remains a backward-compatible alias. | ✓ |
| Assay Evidence Spec version = "1.0" | crates/assay-evidence/src/types.rs: pub const ASSAY_EVIDENCE_SPEC_VERSION: &str = "1.0" | ✓ |
| Bundle schema_version only 1 accepted | crates/assay-evidence/src/bundle/writer.rs: verify_bundle_with_limits rejects m.schema_version != 1 with ContractSchemaVersion (lines 811–817). Reader uses same verify path. | ✓ |
| specversion "1.0" enforced | writer.rs: after deserializing event, if event.specversion != "1.0" → reject (lines 941–946). | ✓ |
| Assay requires time | EvidenceEvent.time is DateTime<Utc> (required). CloudEvents does not require it; Assay does. | ✓ |
| Assay-required flattened fields | types.rs: assayrunid, assayseq, assayproducer, assayproducerversion, assaycontenthash, datacontenttype, data present and used in verify. | ✓ |
| Unknown fields ignored | EvidenceEvent has no #[serde(deny_unknown_fields)]. Deserialization ignores unknown keys. | ✓ |
| Verify does not reject solely for unknown | Verify uses validate_json_strict (duplicate keys, lone surrogates, limits) then serde_json::from_str::<EvidenceEvent>. Unknown keys are not checked; they are dropped by serde. Rejection only for security/integrity (duplicate keys, bad UTF-8, hash mismatch, etc.). | ✓ |
| Canonicalization: JCS (RFC 8785) | crates/assay-evidence/src/crypto/jcs.rs: uses serde_jcs; module doc references RFC 8785. | ✓ |
| Content hash: SHA-256, lowercase hex, "sha256:" prefix | crates/assay-evidence/src/crypto/id.rs: Sha256::digest, format!("sha256:{}", hex::encode(hash)). The v1 hash input is specversion, type, datacontenttype, optional subject, and data, not the full envelope. | ✓ |
| Pinned hashes location | crates/assay-evidence/tests/determinism_test.rs, test test_golden_hash: manifest content hash, events content hash, container hash (lines 272–296). | ✓ |
| test-bundle.tar.gz generation | crates/assay-evidence/tests/generate_fixture.rs: writes to tests/fixtures/evidence/test-bundle.tar.gz; command as in spec. | ✓ |
| Fixtures path | tests/fixtures/evidence/test-bundle.tar.gz and README.md exist. | ✓ |
Pack evidence_schema_version¶
- Spec: “For v1 freeze: exact match on 1.0”. Packs declare
evidence_schema_version: "1.0"(e.g.packs/eu-ai-act-baseline.yaml,crates/assay-evidence/packs/mandate-baseline.yaml). - Code:
lint/packs/schema.rshasevidence_schema_version: Option<String>. The pack loader warns when the field is absent and assumes v1.0 for backward compatibility. - Conclusion: Contract is defined in this spec; enforcement (e.g. rejecting a pack run when bundle schema_version or event spec does not match) may be added in a future release. No change required for doc-only freeze.
Golden fixture: determinism vs file fixture¶
- determinism_test.rs
test_golden_hashpins the format using an in-memory bundle generated bygenerate_bundle(1)withcreate_deterministic_event(typeassay.determinism.test, fixed payload). Those pinned values are not the hashes oftest-bundle.tar.gz(which uses different events fromgenerate_fixture). - test-bundle.tar.gz is a separate smoke fixture (mixed event types) for CI and manual verify/lint/explore. Regenerating it does not change the determinism_test pins; the spec correctly says “update pinned hashes” only when the writer or event format changes (which would be reflected in the determinism_test bundle).
Normative fixture: unknown optional fields¶
- Spec: “verify MUST accept an event with extra unknown top-level and payload keys. Tests and fixture MUST exist to enforce this.”
- Code:
crates/assay-evidence/tests/verify_strict_test.rsdefinestest_verify_accepts_unknown_optional_fields: it builds a raw bundle whoseevents.ndjsonline contains an extra top-level key (unknown_extension) and assertsverify_bundlesucceeds. This enforces the contract; a separate fixture file is optional.
Type naming (URN vs identifier)¶
- Spec: “Event type is a stable, namespaced string identifier (dot-separated; e.g. assay.profile.started).” No “URN” for the type field.
- Code:
types.rsdoc comment fortype_previously said “Event Type URN”. Aligned to “Event type (dot-separated identifier)” to match the spec.
Last verified: 2026-02 (codebase state at verification).