chore: release 0.6.0 — affinidi-did-common 0.4 + pre-release audit fixes - #50
Merged
Conversation
affinidi-did-common 0.4.0 promotes alsoKnownAs to a typed Document::also_known_as field. The change is additive and this crate needed no code changes: it neither constructs Document by struct literal nor re-exports it from its public API. Also pins affinidi-data-integrity to 0.7.7. It is a transitive consumer of affinidi-did-common and, left at "0.7", resolves to the published 0.7.6 which still requires "0.3" -- putting a second copy of affinidi-did-common in the dependency graph. This release must reach crates.io before affinidi-did-common 0.4.0 propagates to consumers. Per affinidi-tdk-rs ADR 0003 section 3, a minor bump of affinidi-did-common invalidates the [patch.crates-io] redirect held by any external consumer still requiring "0.3"; leaving this crate on "0.3" puts two copies of affinidi-did-common in the graph and breaks downstream builds with duplicate-type errors. Cargo.lock is intentionally left untouched: it cannot be regenerated until affinidi-did-common 0.4.0 and affinidi-data-integrity 0.7.7 are published. Signed-off-by: Glenn Gore <glenn.gore@gmail.com>
Promotes the release from 0.5.8 to 0.6.0: the fixes below add `#[non_exhaustive]` to already-published public enums, which is breaking for downstream code matching them exhaustively. Breaking: - `DIDWebVHError`, `URLType`, `LogEntryValidationStatus` are now `#[non_exhaustive]`, so future variants no longer require a major bump. - Removed the `affinidi_secrets_resolver` whole-crate re-export, deprecated since 0.5.0 and documented for removal in 0.6.0. Fixed: - `update_did()` no longer silently drops `portable` on the migrate path. `do_migrate` had a copy-pasted parameter overlay missing the `portable` arm, so `.migrate_to(..).disable_portability()` discarded the change. Both paths now share `apply_param_overrides`. - The successor-version check no longer overflows on a `versionId` of `u32::MAX` — a debug panic, and a release wrap to 0 that would let a chain restart its numbering. Extracted as `expected_next_version_id`. - The interactive CLI update flow no longer panics on an empty `active_update_keys` (deactivated DID, or all keys deselected). Changed: - `WebVHURL` derives `Debug`/`PartialEq`/`Eq`; `URLType` derives `Eq`. - Regenerated `Cargo.lock` now that affinidi-did-common 0.4.0 and affinidi-data-integrity 0.7.7 are published; refreshed direct deps. - Dropped four stale `--ignore` flags from the CI audit job (the reqwest 0.11 chain they covered is gone). - Gated the nightly bench behind a `nightly` feature so `--all-targets` works on stable. Docs: - Fixed README examples that could not compile (missing `.await`, `pub(crate)` field access, a `Multibase` type error), the MSRV badge, a dead LICENSE link, stale versions, and the prelude listing. - Converted all 22 `ignore` doctests to `no_run`: 24/24 now compile. This caught a documented builder method that does not exist. Tests: - `tests/revoked.rs` now uses `TempDir`. `save_to_file` appends, and the fixed paths were only cleaned up after the assertion, so a failed run poisoned every subsequent run — the source of the intermittent failure. - `witness-update` interop vector un-ignored and inverted to assert rejection. The vector expects an entry that lowers its own witness threshold to be accepted; didwebvh 1.0 requires proofs from the "then active" witnesses, and accepting it would let a compromised update key self-approve. Interop suite is now 13/13 with nothing ignored. Signed-off-by: Glenn Gore <glenn@affinidi.com>
stormer78
force-pushed
the
did-common-0.4
branch
from
July 19, 2026 10:37
610ac22 to
f8005af
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Bumps
affinidi-did-commonfrom"0.3"to"0.4"andaffinidi-data-integrityfrom
"0.7"to"0.7.7", plus a pre-release audit pass over dependencies,documentation, and code quality.
Releases as
0.6.0, not0.5.8— see Why the version moved.Both upstream dependencies are now published, so CI should be green and
Cargo.lockhas been regenerated (this PR originally left it untoucheddeliberately, because it could not be regenerated honestly until they landed).
Why the version moved
The audit fixes include
#[non_exhaustive]on public enums that shipped through0.5.7. That breaks downstream code matching them exhaustively, so shipping it in
a patch release would violate semver. Doing it now — rather than deferring —
means future variants of
DIDWebVHErrorno longer force a major bump each time.This also triggers removal of the
affinidi_secrets_resolverre-export, whichwas deprecated in 0.5.0 with
"removed in 0.6.0"in both its doc comment andits
#[deprecated]note.Breaking changes
DIDWebVHError,URLTypeandLogEntryValidationStatusare now#[non_exhaustive]. Downstreammatchexpressions need a_ =>arm.didwebvh_rs::affinidi_secrets_resolverre-export is removed. Replacewith
didwebvh_rs::prelude::Secret, or depend onaffinidi-secrets-resolverdirectly. README carries a migration note.
Fixed
update_did()silently discardedportableon the migrate path.do_migratecarried its own copy-pasted parameter-overlay block that omittedthe
portablearm, so.migrate_to(..).disable_portability()built and ransuccessfully while dropping the portability change. Both paths now share a
single
apply_param_overrideshelper. The regression test was verified tofail without the fix.
u32overflow in the successor-version check.versionIdis parsed fromthe untrusted log and
verify_log_entryis public, so a bareid + 1wasreachable with attacker-controlled input: a panic in debug builds, and a
silent wrap to
0in release builds that would let a chain restart itsnumbering.
active_update_keys[0]was indexed unconditionally; a deactivated DID (empty
update_keys) ordeselecting every key in the prompt produced an index-out-of-bounds panic
instead of an error.
Conformance:
witness-updateis now asserted to failThe
witness-updateinterop test was#[ignore]d with the reason "witnessproof signature on entry 2 fails verification". That diagnosis was wrong — the
signature verifies fine. The actual failure is a witness threshold mismatch,
and it is correct behaviour.
Entry 2 of that vector lowers its own witness config from
{threshold: 2, witnesses: [A, B]}to{threshold: 1, witnesses: [A]}andsupplies one proof. The committed
resolutionResult.jsonexpects it to resolve,i.e. the generator evaluated the entry against the new config it declares.
didwebvh 1.0 says otherwise:
The then-active threshold for entry 2 is 2; one proof is present; it is
rejected. Accepting the fixture's reading would make witnessing bypassable —
an attacker holding a compromised update key could publish
{threshold: 1, witnesses: [attacker]}, sign the single required proofthemselves, and have it accepted. Bounding exactly that compromise is the point
of the witness mechanism.
So rather than weakening the verifier to match the fixture, the test is
un-ignored and inverted:
witness_update_rejects_self_lowered_thresholdpinsthe rejection. If it ever starts passing by resolving, that is a security
regression, not fixture drift. The discrepancy should be raised against
didwebvh-test-suite.
Interop suite is now 13/13 with nothing ignored.
Documentation
create_did()shown without.await;CreateDIDResultshown accessingdid/log_entry/witness_proofsas fields when they arepub(crate);update_keysbuiltfrom a bare
Stringinstead of aMultibase.LICENSE-APACHElink, staleversions in four install snippets, two rustdoc intra-doc links that render
broken in plain Markdown, and an incomplete prelude listing.
```ignoredoctests to```no_run. Doctests wentfrom 2 passing / 22 ignored to 24 passing / 0 ignored. This is what let the
README bugs above survive; the conversion immediately surfaced a documented
builder method (
.authorization_secrets()/.witness_secrets()) that doesnot exist — the real API is a single
.secrets().witness-updatedivergence for anyone comparing implementations.
Tests
tests/revoked.rs.LogEntry::save_to_fileappends, the tests wrote to fixed paths under the git-tracked
tests/test_vectors/, and cleanup ran only after the assertion — so anyfailed or interrupted run left a file the next run appended to, producing a
corrupt chain. Self-perpetuating once triggered. Each test now gets its own
TempDir, removed on drop including on panic.portable-on-migrate fix, the other overlayfields on the migrate path, and the
u32::MAXboundary.Dependencies & CI
async-trait0.1.91,serde1.0.229,thiserror2.0.19,
tokio1.53.0, plusanyhow/clapdev-side.tempfileadded as adev-dependency.
--ignoreflags from the CI audit job(
RUSTSEC-2026-0098/0099/0104,RUSTSEC-2025-0134). Those were rooted in thereqwest 0.11 chain, which disappeared when this crate moved to reqwest 0.13; a
current audit does not report them. Stale ignores silently mask an advisory if
the dependency ever returns.
nightlyfeature, socargo check/clippy --all-targetsnow works on stable instead of failing withE0554.
Publish ordering
✅ publishedaffinidi-did-common 0.4.0✅ publishedaffinidi-data-integrity 0.7.7didwebvh-rs 0.6.0Verification
cargo fmt --checkcleancargo clippy --all-features --tests --examples -- -D warningscleancargo auditclean under the trimmed ignore list--no-default-featuresbuilds;--all-targetsbuilds on stableNot done, deliberately
ResolveOptionswas not made#[non_exhaustive]. It is a config struct withpublic fields, and
non_exhaustiveforbids struct-literal constructiondownstream entirely — even with
..Default::default().examples/resolve.rsuses exactly that pattern, so it is the documented public idiom. The right
version of that change is
non_exhaustiveplus a builder, which is a largerAPI addition than belongs in this release.