You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
deriveGrantSignerState (clients): the grant-state check a connected-apps
surface runs over a recorded delegation's signers against the enrolled
clients' current signing keys, returning active, orphaned, or unknown
(GrantSignerState). A signer is judged against the account document only
when the document could have listed it, its DID being the account DID or a
did:key. A client-annex per-visit signer (a transient session's grant) derives
as unknown rather than orphaned: the document never lists it, and whether
its chain under the generation delegation is still alive is the revocation's
to settle.
isWasLinkPayload (space): the structural recognizer for a was-link QR
payload, handed to @interop/wallet-request's input classifier as a
recognizer.
Changed
ARCHITECTURE.md is a short map again: module map, overviews, the key
hierarchy, the ceremony inventory, wire constants, glossary. Each area's full
account moved to a topic doc under docs/architecture/, with history, roadmap
ids, and restated rules dropped along the way.
publishUnlockKey refuses to re-bind a standing credential member under
another ladder: a bind whose rung-0 hash differs from the member's ladderCommitment throws LadderAttributionError with nothing written, on
the merged entry and on either half of a split bind. It used to re-add the
member naming the new hash, which left the member unclaimable by every
seedless reader for the rest of its standing run and the first ladder's VM and
commitment as orphans. The converging re-run is the one holding the seed that
bound the member. A consumer whose retry mints a fresh seed for a member the
account already lists now sees the refusal instead of the orphans.
attributeUnlockLadderInventory (the removal edit and the retirement
pre-flight) walks from both anchors a ceremony holds, the registry's recorded
update key and the member's own ladderCommitment, and cross-checks them: the
registry-anchored inventory must be contained in the member-anchored one, the
member's reading is what the strike acts on, and anchors resolving to
different ladders refuse with LadderAttributionError. With a ladder seed in
hand the seed's rung-0 hash must be the member's named commitment. The
member-anchored walk starts at rung 0 and needs no backward climb, so a ladder
VM the last-client transition reinstalled is now struck seedlessly after a
later self-enrollment advanced the recorded anchor past the acting rung (the
gap WC-158 named).
Fixed
The ladder walk's reveal branch no longer claims the hashes of an entry that
installs another credential's inventory: a committed rung revealing itself in
the ladder-signed bind of a sibling credential took the sibling's rung-0
commitment as its own next claim, and once a later self-enrollment spent the
rung the acting credential's retirement struck that commitment, leaving the
sibling standing in the document with no update authority. The narrowing the
signed-while-revealed branch already applied now covers the reveal itself.
The seedless ladder walk (attributeLadderInventory, read backwards by recoverEarlierRungs) no longer recovers the acting credential's rung as the
bound credential's from a ladder-signed bind entry. A committed rung that
reveals itself in the publishUnlockKey entry it signs met every gate of the
climb rule, so a seedless retirement of the credential it bound (from an
enrolled client, or the retirement gate's pre-flight) struck the acting
ladder's revealed rung from updateKeys, leaving that credential unable to
extend the log. The climb now stops at an entry that installs the credential's
inventory (its keyAgreement member, or a ladder VM not standing before)
unless the signer's own hash is newly committed there, the ladder-anchored
genesis shape.
A credential member's ladderCommitment is adopted as the ladder's anchor
(credentialLadderAnchor) only when the log committed that hash for the
member: newly added to nextKeyHashes by the introducing entry, by a later
entry of the member's standing run, or by an earlier entry whose signer the
introducing entry retires (a recovery continuation's reveal entry). A member
restating a hash that already stood -- an enrolled client's staged hash --
names no anchor and is reported unclaimed. And survivingClientKeyProtection
(whose parameter is now derivedLatentHashes) never lets a walk-derived claim
prune the positional successor of a surviving client's update-key hash, so a
walk anchored on a client's staged hash can no longer strike it from nextKeyHashes on a recovery spend.
serializedAppUrl (request) refuses an appUrl whose origin is opaque
(chrome-extension:, file:, any non-special scheme). Such an origin
serializes as the string "null" and is same-origin only with itself, so the
equality check accepted any two opaque-origin values as same-origin. The rule
stays the spec's: origin equality alone, with no scheme constraint, so a
same-origin ws: appUrl still passes. The wallet-onboarding host validator
keeps its mirror shape (scheme check, no origin), and both are now covered on
that asymmetry by tests.
Changed
hasAppConnectQuery(queries) (request) is the one presence test for an AppConnectQuery: processRequest's App Connect gate runs it, and every type comparison in the query predicates (isAppConnectQuery, isWalletOnboardingQuery, exclusiveQueryOf's match) goes through one
private helper, so the type string is compared in one place. processRequest
gains its own test file.
The "one mental model per exchange" exclusion is one shared set
(EXCLUSIVE_QUERY_TYPES in request, currently AppConnectQuery and WalletOnboardingQuery): exclusiveQueryOf derives each type's exclusion
list from it, so a third exclusive type is one entry and is refused from every
side. It replaces singletonQueryOf, which took a per-caller exclusion
predicate and message; the two refusal messages are unchanged.
Breaking:KEYRING_KDF (keyring) is now Argon2id (64 MiB memory, 3
passes, parallelism 1, 32-byte output) under the salt freewallet/keyring/unlock/argon2id/v1, passphrase version 2. The memory and
pass counts follow RFC 9106's second recommended option; the parallelism is a
deliberate departure from that option's four lanes, since noble is
single-threaded. The PBKDF2-600k set is replaced outright, with no try-both
locate: a passphrase account bound before this release cannot be entered by
passphrase, since its unlock Space is addressed by the PBKDF2-derived
identity. Passkeys (HKDF over the PRF output) and recovery codes (their own
HKDF) are unaffected.
UnlockKdf gains an Argon2id arm (memory in KiB, passes, parallelism, salt; no hash member), and deriveUnlockSeed derives it over @noble/hashes/argon2.js. The PBKDF2 arm is removed with it: no shipped
parameter set names it. The KEYRING_RECORD_VERSION frame version is
unchanged: the KDF's own version records the parameter set.
Breaking: every credential-class keyAgreement verification method in the
account did:webvh document now carries ladderCommitment, a plain JSON member
holding hash(rung 0) of the credential's ladder in the multihash form nextKeyHashes already carries. unlockKeyVerificationMethod (unlock) is
the one builder every bind site writes it through, now requires the property,
and returns the typed CredentialKeyAgreementMethod; an enrolled client's
marked key-agreement twin never carries it. credentialLadderAnchor
(clientAnnex) anchors a credential's seedless ladder walk by reading the
property off the entry that introduced the member (a member re-bound after a
retirement anchors on its fresh ladder; one whose value changes while it
stands is refused as retargeted) instead of inferring an anchor from the shape
of that entry; it now returns { anchorHash } only. The three retired
bind-shape readings and the keyAgreement relation-order rule they depended
on are removed. A credential-class member with no ladderCommitment names no
anchor and is reported unclaimed. There is no fallback for a log written
before this change.
Removed
The request subpath and its request/matching leaf subpath move to @interop/wallet-request 0.1.0. The @interop/vc, @interop/data-integrity-proof, and @interop/security-document-loader
dependencies go with it. A wallet now hands the new package's classifier isWasLinkPayload (space) and isConnectCode (enrollment) as
recognizers.