[0.3.0] - 2026-05-29
This release closes the failures reported by the
did:webvh interop test suite
against the v0.2.0 line (see issue #2).
Added
- Negative interop test vectors vendored from the upstream
did:webvh test suite under
didwebvh-core/src/test/resources/interop/
(basic-create/python,basic-update/ts, fulljava-eecclog
set,pre-rotation-consume/{rust,java-eecc},
witness-update/rust,witness-threshold/rust, and
negative-cross-did-witness-replay/ts), each exercised by a
dedicated JUnit test underdidwebvh-core/.../interop/. - Implicit
#filesand#whoisservices in resolved DID
Documents (spec §3.8 and §3.9): the resolver now emits a
relativeRef#filesservice and a
LinkedVerifiablePresentation#whoisservice unless the
controller has already declared services with the same id. The
shared logic lives indidweb.ImplicitServicesand is also
reused byDidWebPublisher.
Fixed
- Cross-DID witness-proof replay (spec §3.7.5, lines 884-889):
when thewitnessparameter is set to{}while witnesses were
active, the transition entry MUST itself be witnessed by the
prior witnesses.WitnessValidatorwas merging the empty config
in first and skipping the entry as inactive, which let an
attacker disable witnessing and replay a stale (or cross-DID)
proof for an earlier entry. The validator now tracks the prior
config and, on a witness-off transition, requires approval from
the prior witnesses. witness: {}round-trip across implementations: Python and
TS serialise an empty"witness": {}object in parameters when
no witnesses are configured. Gson was instantiating
WitnessConfigviaUnsafe.allocateInstance, bypassing the
constructor and leavingwitnessesnull — every call to
isActive()/getWitnesses()then NPE'd on the first
Python/TS log entry. Java was also re-serialising the empty
config as{"threshold":0,"witnesses":[]}instead of{},
producing a different JCS canonical form for SCID, entry-hash
and proof computation. Added a no-arg constructor (so Gson uses
Constructor.newInstance) and aWitnessConfigTypeAdapterthat
round-trips the empty-object form.- Pre-rotation entries authorized against the wrong key set
(spec §3.7.5): when the previous entry committed a
nextKeyHashes, the active updateKeys for the current entry are
the current entry's ownupdateKeys, not the previous entry's.
LogChainValidatorwas unconditionally using the previous
entry, failing everypre-rotation-consumelog from rust and
java-eecc.DeactivateDidOperationmade the same mistake when
emitting its intermediate pre-rotation-consuming entry; it now
signs that entry withnextRotationSigner. Regenerated
pre-rotation-log.jsonlunder the corrected rules. - Witness-proof pruning and bare-multikey witness ids (spec
§3.7.8): a witness proof at versionId V implicitly approves all
prior log entries, and the DID Controller SHOULD prune
did-witness.jsonto retain only the latest proof per witness.
WitnessValidatorrequired an exact-versionId proof per entry
and failed with "missing witness proof" against the Rust pruned
files. It also compared witness ids asdid:key:<multikey>
against the Rust implementation's bare-multikey form, yielding 0
authorized proofs. The validator now pre-verifies all proofs
once, counts distinct authorized witnesses per entry, and
matches witness ids by multikey portion to accept both
did:key:z6Mk…and barez6Mk…forms. - Pre-rotation and portable-SCID negative-test gaps closed for
negative-pre-rotation-omit-updatekeysand
negative-portable-scid-swap. - Release auth: auto-detect and decode base64-encoded
user:passOSSRH_TOKENvalues so Sonatype publishing succeeds
with either token form.
Changed
- CI: GitHub Actions upgraded to versions compatible with
Node.js 24, and Node.js 24 is forced in both the CI and release
workflows to silence the deprecation warning emitted by older
actions on the GHA runners. - Release notes: the GitHub Release body is now generated
directly from the matching## [VERSION]section of this
CHANGELOG (with an appended Maven Central coordinate), and the
release attaches only the self-containeddidwebvh-wizard.jar
uber-jar — library modules are consumed from Maven Central. - Test coverage: raised
didwebvh-corefrom ~77% to ~82% on
the Codecov metric (line coverage 93%) by covering real branches
inLogChainValidator,LogProcessor,DidResolver,
MigrateDidOperation,CreateDidOperation,DidWebVhUrl, and
the file fetcher — malformed versionId/versionTime, future
versionTime rejection, witness threshold bounds, method-version
downgrade, query-param parsing, PROACTIVE / WHEN_REQUIRED witness
fetch branches, migrate guards (null/empty inputs, deactivated,
newPath, alsoKnownAs dedup), controller-list array vs string
forms, and URL port / IPv6 / empty-domain rejection.
Artifacts
Maven Central (recommended for library users):
<dependency>
<groupId>io.github.decentralized-identity</groupId>
<artifactId>didwebvh-java</artifactId>
<version>0.3.0</version>
</dependency>The aggregate didwebvh-java artifact transitively pulls in didwebvh-core and didwebvh-signing-local. Depend on didwebvh-core directly if you plan to supply your own Signer implementation.
<!-- Core only (BYO Signer): -->
<dependency>
<groupId>io.github.decentralized-identity</groupId>
<artifactId>didwebvh-core</artifactId>
<version>0.3.0</version>
</dependency>Browse all modules: https://central.sonatype.com/namespace/io.github.decentralized-identity
CLI wizard uber-jar is attached to this release below (not published to Maven Central).
Full changelog: https://github.com/decentralized-identity/didwebvh-java/blob/v0.3.0/CHANGELOG.md