Skip to content

feat(registry): spec 026 contract signing + revoked.json lifecycle marker (#621) - #625

Merged
enricopiovesan merged 1 commit into
mainfrom
claude/issue-621-contract-signing
Oct 1, 2026
Merged

enricopiovesan merged 1 commit into
mainfrom
claude/issue-621-contract-signing

Conversation

@enricopiovesan

Copy link
Copy Markdown
Contributor

Summary

Implements spec 026-model-rights-compliance FR-012 and FR-013 (decision-log entries 127/128).

Spec amendment (owner decision, decision-log 128)

Spec 026 v1.0.0 said the contract signature covers the hash of canonical JSON. Published contracts contain 91,239 floats (embedding examples), and Python (1e-05), JavaScript (0.00001) and Rust (1e-5) serialize those differently, so traverse-runtime could not reproduce the hash reliably. The owner chose to sign the raw committed contract.json bytes instead. Contracts are immutable, and the index already publishes this exact hash as contract_digest (spec 009), so no canonicalization is needed. Spec 026 moves to 1.1.0 in both approved-specs.json copies; no other requirement changed.

Contract signing (FR-012)

  • sign_artifacts.py: every new signature.json adds contract_sha256 (SHA-256 of the committed contract bytes) and contract_signature_hex (Ed25519, same key, over the 32 digest bytes). Revoked versions are skipped, like deprecated ones.
  • capability_validation.py:
    • Any signature that carries the fields has its digest recomputed and its Ed25519 signature verified (signature.contract_digest_mismatch, signature.bad_contract_signature, signature.missing_contract_signature when only one field is present). Verification fails closed when cryptography is missing (signature.verifier_unavailable).
    • A newly added signature.json must carry both fields (check_new_signatures_cover_contract).
    • Pre-026 signatures stay valid untouched.
  • CI: the capability-validation job now installs cryptography (the sign jobs already did).

revoked.json (FR-013)

  • Whole-tree shape validation: {reason, evidence_url (https), revoked_at (ISO-8601 UTC)}, which must sit next to a contract.json (revocation.invalid, revocation.orphaned).
  • Immutable once merged: added to check_immutability (capabilities.revocation_modified). The contract and artifact are already immutable.
  • Excluded from signing completeness, like deprecated.json.
  • The index carries a revocation record next to status: "revoked" (Implement spec 026 model rights: ModelRef schema, CI gates, index, crate types #620 added the status) and fails the build on an unreadable revoked.json.
  • Crate traverse-registry 0.27.0 gains PublicRevocation and PublicRegistryCapabilityRecord.revocation (serde(default)).

Docs

  • docs/artifact-signing.md: contract-signature section plus a verification snippet.
  • docs/model-rights.md: a "Signing and revocation" section that describes how to revoke.

Canonicalization rule for traverse-runtime (cross-repo traverse-framework/traverse#1598): none. Hash the bytes fetched from contract_url and verify contract_signature_hex over that digest with public_key_hex.

Governing Spec

  • 026-model-rights-compliance
  • 010-crate-publish-pipeline
  • 001-registry-foundation

Project Item

Closes #621

Definition of Done

  • A contract-hash signature verifies, and changing any contract byte breaks it (SC-003): test_changing_any_contract_byte_breaks_it and test_contract_signature_covers_exact_committed_bytes
  • A revoked fixture indexes as revoked with its full rights record and revocation record
  • The canonicalization rule is documented precisely enough for traverse-runtime to reproduce: raw bytes, so none is needed
  • Existing signatures and contracts pass unchanged

Validation

  • test_capability_validation.py: 12 new tests (contract signature and revocation)
  • test_build_index.py: 1 new test plus an extended status test
  • test_sign_artifacts.py: 2 new tests
  • cargo test -p traverse-registry --locked, cargo clippy ... -D warnings
  • bash scripts/ci/pre_pr_check.sh

🤖 Generated with Claude Code

…rker (#621)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@enricopiovesan
enricopiovesan enabled auto-merge (squash) October 1, 2026 22:27
@enricopiovesan
enricopiovesan merged commit d51a927 into main Oct 1, 2026
11 checks passed
@enricopiovesan
enricopiovesan deleted the claude/issue-621-contract-signing branch October 1, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spec 026: contract-hash signing + revoked.json lifecycle marker

1 participant