Skip to content

feat(attestation): verify() — offline consumer for v0.1.1 envelopes#74

Open
ColonistOne wants to merge 1 commit into
mainfrom
feat/attestation-verify
Open

feat(attestation): verify() — offline consumer for v0.1.1 envelopes#74
ColonistOne wants to merge 1 commit into
mainfrom
feat/attestation-verify

Conversation

@ColonistOne

Copy link
Copy Markdown
Collaborator

What

Adds the consumer half of the attestation envelope. v1.20.0 (#73) shipped the producer; this adds attestation.verify() so colony_sdk both mints and verifies v0.1.1 envelopes in one place — closing the produce↔verify loop integrators were otherwise pointed at another repo for.

API

  • verify(envelope, *, now=None) -> VerificationResult — deterministic, network-free verification:
    1. structural (required fields, envelope_version == "0.1", non-empty evidence + sigchain)
    2. ed25519 peel-and-verify each signature over JCS(envelope with sigchain = sigchain[0..i-1]) (the spec's peel-not-replace rule)
    3. validity window (time_bounded / perpetual / revocation_checked)
    4. issuer did:key binding (sigchain[0].key_id == issuer.id)
  • VerificationResultok (truthy via __bool__), issuer_bound (kept separate from ok: only did:key issuers close the binding in v0.1; other schemes verify but are UNBINDABLE → "key K signed this"), reasons, notes.
  • did_key_to_public_key() — inverse of public_key_to_did_key().

Scope

Evidence resolution and revocation are out of scope by designverify() never makes a network call (no phone-home to the issuer). Callers resolve evidence[].uri / check content_hash / query revocation_uri themselves if their trust model needs it. This mirrors the spec reference verifier's --offline subset.

Checks (local, mirrors CI)

  • ruff check / ruff format --check / mypy src/
  • pytest844 passed, 147 skipped, 100% coverage (attestation.py 310/310).
  • Round-trips: producer output (export_attestation/attest_post) verifies ok=True, issuer_bound=True; tampered payloads, expired/not-yet-valid windows, bad alg/role/key_id, and UNBINDABLE issuers all behave as specified.

Bumps to 1.21.0. Non-breaking, additive. Routing through @jackparnell to merge (TheColonyCC repo).

🤖 Generated with Claude Code

v1.20.0 shipped the producer; this adds the consumer half so the SDK both
mints and verifies attestation envelopes in one place.

- verify(envelope, *, now=None) -> VerificationResult: deterministic, network-
  free verification — structural checks, ed25519 peel-and-verify of the sigchain
  over JCS(envelope with sigchain[0..i-1]), validity window, did:key issuer
  binding. Mirrors the spec's reference verifier offline subset.
- VerificationResult: ok (truthy via __bool__), issuer_bound (separate — only
  did:key closes the binding in v0.1; other schemes are valid-but-UNBINDABLE),
  reasons, notes.
- did_key_to_public_key(): inverse of public_key_to_did_key().

Evidence resolution + revocation are out of scope by design (no network calls).
Same optional extra as signing. Bump to 1.21.0. 100% coverage; 844 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant