Skip to content

Releases: interop-alliance/wallet-core

v0.73.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 10 Sep 21:57

Added

  • userKeySigningSeed, userKeyRecordSigner, userKeySigningKeyMultibase, and
    USER_KEY_SALT (/keys): the user key's Ed25519 signing half, derived from
    the key-agreement half's raw 32-byte secret (HKDF-SHA256, salt
    freewallet/keys/user-key/v1, info signing). Every holder of the user key
    can now sign and verify a record proof with nothing server-served in hand, so
    a record sealed to the vault KAK carries authenticity as well as
    confidentiality.
  • recordSignerFromSeed (/keyring): the record signer seam over a raw 32-byte
    Ed25519 seed, for a signing key derived on demand rather than held by an
    agent.
  • verified-registry in EVIDENCE (/menders): a registry record whose proof
    verified under the user key the reader holds, before it was decrypted.

Changed

  • recordSignerFromSeed, updateKeySigner, and the ladder VM signer take their
    signer from @interop/ed25519-verification-key 8.2.0's didKeySigner()
    instead of each naming the key pair by hand before asking for one.

Removed

  • Breaking: UserKey.signingSeed and the client-key record's
    userKey.signingSeed member. The signing half derives from the key-agreement
    secret, so it is neither minted nor stored: mintUserKey returns
    { id, secret }, and encodeClientKeyRecord / decodeClientKeyRecord
    neither write nor read the member.

v0.72.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 10 Sep 19:25

Removed

  • Breaking: getUnlockKeyringWithCapability,
    putUnlockKeyringWithCapability, and deleteUnlockSpaceWithCapability
    (/keyring). getUnlockKeyring, putUnlockKeyring, and deleteUnlockSpace
    take an optional capability instead, so every unlock Space operation has one
    request shape whether it is a root invocation or rides the delegated
    management zcap.
  • Breaking: isSoleEnrolledClient (/clientAnnex), replaced by
    isLastEnrolledClient({ doc, signingVmId }) (/webvh).

Added

  • clientAdditionFields (/webvh): the add-side twin of clientRemovalFields.
    One enrolled client's marked verification-method pair and its relation
    membership (all four signing relations plus keyAgreement) as one bundle,
    which the genesis assembly, the enrollment add entry, the self-enrollment add
    entry, and the recovery continuation now take instead of restating the five
    relations each.

  • enrolledClientVmIds and isLastEnrolledClient (/webvh): the one read of
    which methods are enrolled clients, and the last-client rule over it. The
    plain forget, the last-client transition, the enrolled-client listing, and the
    disconnect policy's count all decide on it.

  • The /testing subpath, test fixtures only: recordedGrant (a delegated zcap
    as the delegation suite records it, the chain in the proof with a parent
    delegation embedded as its last link) and accountSignerCheck over
    SIGNER_FIXTURE (one enrolled client and one ladder VM under
    capabilityDelegation, a pointed and an old annex generation). The grant
    revocation tests here and in freewallet import them in place of two
    hand-written copies. Consumers keep it out of production code by lint
    restriction, as with @interop/vh-resource-log/testing.

  • The /menders subpath: the mender registry's shared half. It carries the
    InvariantDeclaration and Registration types, the closed vocabularies
    (AUTHORITIES, TRIGGERS, CHAIN_TRIGGERS, ACCOUNT_SHAPES, EVIDENCE,
    MEND_OUTCOMES, GAP_KINDS) and the INVARIANT_IDS census, the
    menderRegistry readers (all, byId, sites, admits, dueAt) with
    heldAuthorities and its ResolvedAuthority kind type, and the derived-set
    helpers transientReachableInvariants, deriveGaps, undeclaredGaps, and
    undeclaredInvariants. menderRegistry checks its table and sites once at
    construction and throws a TypeError on a duplicated declaration id, a site
    reporting an undeclared invariant, or a guardedBy on a site that is not a
    login-routing one (RegistrationSite is a union on trigger, so the type
    forbids the third). A detector is not a mender: an invariant no site reports
    derives a none gap whether or not it declares holdsWhen, and
    transientReachableInvariants counts an unreported declaration only as a
    detector checked at a trigger a transient visit fires, under the ladder held
    set on the chain. A wallet declares its own table and its own registrations;
    nothing here executes a ceremony.

  • The mender runner on /menders: runMenderBlock runs one chain trigger's
    registrations in list order, filtered by the held authorities and by each
    reported declaration's when(route) predicate. An optional seed registration
    runs first and its failure aborts the block; past it, a registration that
    throws warns once per reported invariant with that declaration's own warn
    string through the wallet-supplied Logger, reports failed carrying
    err.name alone, and the block continues. A registration whose entries do not
    match the invariants it reports warns and is normalized to failed entries
    carrying MEND_REPORT_SHAPE_ERROR. onOutcome is the single place an entry
    is reported. An optional registrations list overrides which registrations
    the block runs, under the same authority and route tests, for a wallet that
    runs one trigger's list in parts (a settle point partway through); a seed or
    override registration listed under another trigger is refused with a
    TypeError. A converge-free site the registry indexes beside its
    registrations is never executed, whichever list the block runs from.

  • mendReportAccumulator (/menders): the report collector a wallet creates
    ahead of session assembly, so a routing site's entries and the chain's entries
    assemble into one MendReport. report doubles as runMenderBlock's
    onOutcome, and settled resolves with the assembled report at the first
    settle().

  • errorNameOf (/menders): the name a mend report carries for a thrown value
    of any shape, so an app-side report site does not read .name off a
    non-Error throw.

  • LoginRoute (/menders): the route type InvariantDeclaration.when reads,
    { popup: boolean } today.

  • addHistoryProfileCreated, addHistoryCollectionShared, and
    addHistoryCollectionUnshared (/space), completing the wallet-activity
    builder set. Their type / summary bytes match what the wallets were
    writing inline, so existing records are unaffected.

  • isDelegationExpired and the RevokeGenerationDelegationOutcome type
    (/clientAnnex): the expiry check and the outcome union
    revokeTreatingAlreadyRevokedAsSuccess now returns.

  • revokeRecordedGrant, classifyGrantRevocationRefusal,
    embeddedParentCapability, isClientAnnexDid, and the AccountSignerCheck,
    GrantRevocationOutcome, and GrantRevocationRefusal types (/clientAnnex):
    revoking one recorded app or agent grant under the same policy
    revokeTreatingAlreadyRevokedAsSuccess applies to a generation delegation.
    The one local skip is a grant expired beyond REVOCATION_CLOCK_SKEW_MS;
    everything else is POSTed; AlreadyRevokedError is success; a plain
    ValidationError is read against the verified account document as expired,
    orphaned (a root-delegated grant whose signer left the document),
    signer-gone (an embedded parent delegation whose proof key left the
    document), or generation-swapped (a parent generation delegation naming a
    generation the document no longer points at), and rethrown when the client
    cannot say why. It composes delegationExpired, delegationAtExpiry, and
    delegationSignerGone (/webvh) with deriveGrantSignerState (/clients).

Changed

  • The byoe service-entry convention is read and written through one predicate in
    /clientAnnex: the delegated-clients pointer and the generation delegation
    each match on the type IRI and the endpoint shape (a string, a map), for the
    readers and the in-place re-point alike. A malformed entry of the right type
    is now skipped by both, where the writer used to re-point it in place while
    the reader could not see it.

  • decisions/0002, 0004, 0006, 0008, 0010, and 0011 say durable
    only of server-backed state: their client, session, login, and recovery-spend
    uses take the enrolled and remembered words, prose only.

  • Every topic doc under docs/architecture/ now states its ceremony's pivot
    write, which of the ceremony's other writes sit before and after it, and the
    invariants a torn run can leave violated, numbered as in INVARIANT_IDS.
    did-webvh-account-log.md gains an update-key rotation section for the one
    ceremony that had no account of its own.

  • ARCHITECTURE.md: a mender registry subsection beside "Ceremonies and cascades"
    names the registry's unit, the authority values, and the trigger values, and
    holds the four menders (the Space-controller promotion, the
    credential-anchored mend, the transient readiness ensure, and the login-time
    roster sweep) with their invariant ids. The ceremony table keeps only
    ceremonies, each row now carrying its CeremonyId, and gains the
    recovery-code issuance and revocation rows.

  • menderRegistry's dueAt takes an optional route. With one, a registration
    is due only when every reported declaration either declares no when
    predicate or admits that route; without one, the route test does not apply and
    the reader behaves as before.

  • Breaking: deleteUnlockSpace returns
    { outcome: 'deleted' | 'not-found' } in both forms (a 404 is reported, not
    decided), where the root-invoked form previously resolved void.

  • fetchKeyringRecord's kdf parameter is required. It no longer defaults to
    KEYRING_KDF, so a caller names its unlock method's parameter set and a
    method that omits it fails to compile instead of deriving the
    passphrase-salted unlock identity and reading the wrong unlock Space.

  • CapabilityAgent now comes from @interop/capability-agent; the
    @interop/webkms-client dependency is dropped. The unlock identity derivation
    is unchanged.

  • The account-document relation reader memoizes its verificationMethod index
    per document, so the resource-log controller no longer rebuilds it for each
    relation it resolves per log entry.

  • publishUnlockKey's fresh-bind check (a recorded update key that is not rung
    0 of the ladder seed handed in refuses with LadderAttributionError, nothing
    written) is documented as the checked half of the ladderCommitment contract
    on unlockKeyVerificationMethod. The seedless bind and the remembered
    recovery continuation's replacement member are named as the two emitters that
    still take the value on trust.

  • mendCredentialAnchoredAccount hands the account log its roster-mint
    preconditions verified to the registry arm instead of reading and verifying it
    a second time.

  • Breaking: revokeTreatingAlreadyRevokedAsSuccess (/clientAnnex) no
    longer reads every was-client ValidationError as success, and now takes the
    locally verified account document (accountDoc). It skips the POST only for a
    delegation whose own expires is past by more than REVOCATION_CLOCK_SKEW_MS
    (ten minutes; an absent or unparseable expires is not treated as expired),
    and otherwise POSTs whatever the caller's document says about the signer. It
    reads was-client ...

Read more

v0.71.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 09 Sep 20:44

Removed

  • The ./identity subpath. The WAS identity derivation (agentsFromSecret /
    agentsFromSeed / agentsFromKeyAgent, ProfileAgents, singleKeyResolver,
    zcapClientForSigner, the BOOTSTRAP_HANDLE / BOOTSTRAP_KEY_NAME handles)
    moved byte-for-byte to @interop/was-client/identity 0.55.0, with
    zcapClientForSigner also at was-client's root entry. Consumers repoint their
    imports from @interop/wallet-core/identity to
    @interop/was-client/identity.

Changed

  • @interop/was-client dependency bumped to ^0.55.0.
  • The identity fixture test (test/node/identity.test.ts, pinning the
    seed/secret derivation to its did:key and KAK multibase values) now runs
    against the was-client home.

v0.70.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 09 Sep 19:27

Added

  • 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.

v0.69.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 08 Sep 20:53

Added

  • collectionDescriptorLogStore (keys): the log-governed, sealable descriptor
    store over ONE encrypted collection's governing history log -- the
    collection's own meta/log sub-resource, whose verified head state is its
    encryption descriptor. It takes the collection handle, a controller
    resolver, a chain-head pin store, and the same ResourceLogSigner the
    roster's store takes; it derives its own pin slot from the handle, and carries
    the collection-descriptor log class.
  • ResourceLogClass / controllerForLogClass (resourceLog): the log-class
    dispatch a log-governed store applies to every controller view it resolves, so
    read-side verification and write-side admission run under one rule. The
    ceremony-tail license binds the user-key-roster class; a
    collection-descriptor log admits a ladder-signed append on assertionMethod
    membership at the anchored version alone (decisions/0013, app-connect-spec
    decisions/0003).
  • COLLECTION_HISTORY_LOG_SUBRESOURCE (space), 'meta/log': where every
    encrypted wallet collection's governing history log lives.
  • collectionStoreFor, required on ensureAccountGenesis
    (({ did }) => (collectionId) => store) and on
    ensureCredentialAnchoredAccountGenesis /
    establishCredentialAnchoredAccount / mendCredentialAnchoredAccount
    (({ did, log }) => (collectionId) => store). Breaking: every caller of those
    four supplies it. The mend also takes an optional collectionStore
    ((collectionId) => store), the per-collection sibling of rosterStore that
    its post-promotion roster-and-epochs arm lands epochs through; the arm runs
    only when both are supplied.
  • controller on cascadeCollectionsToUserKey: the ceremony's post-edit
    controller view, set as every sealable collection store's minimum controller
    version before that collection's first append. Absent, each store's injected
    resolver decides its own freshness.

Changed

  • isResourceLogRefusal (resourceLog) is now @interop/vh-resource-log's
    (0.5.0), re-exported from resourceLog/errors.ts in place of the local copy.
  • The descriptors subpath shrinks to the log-governed descriptor source
    (logGovernedDescriptorSource, collectionDescriptorLogPinId). The
    acquisition and unknown-epoch refresh policy moved to
    @interop/was-client/edv (0.54.0) unchanged: acquireDescriptor /
    acquireDescriptors, wasDescriptorSource, the EncryptionDescriptorSource
    / EncryptionDescriptorCache seams, DescriptorRefreshPolicy,
    createRefreshingEdvDocCipher, and isKeyUnwrapError. Breaking: import them
    from there; this subpath re-exports none of them. The truth table is
    unchanged: integrity refusals and every continuity reason but rollback, with
    the license class deliberately outside the set.
  • Followed @interop/was-client's ./sync port contract update: the server's
    ETag is opaque and can no longer be rebuilt from a bare revision number.
    SyncedRow gains an etag field persisted alongside version;
    SyncStore.markPushed / markDeletedPushed take the write's acked etag
    (from its WriteAck) and record it; runPush builds every conditional
    write's ifMatch from the stored etag string instead of formatting one from
    version. Pull ingestion and adoptLatest record etag (and metaEtag,
    where a collection syncs metadata) from MasterState / WireDoc.
  • The log-governed descriptor store moved down a layer:
    @interop/was-client/edv now owns the generic logGovernedDescriptorStore
    (verified read, signed replace / create, the conflict translation,
    seal()), the readGovernedEpochConfiguration helper, the
    WasEpochConfiguration state type constant, and toEpochConfigurationState.
    The roster's logGovernedDescriptorStore (keys/rosterLogStore.ts) is now
    that store wrapped with the post-edit minimum controller version, and keeps
    its SealableEncryptionDescriptorStore shape; descriptors/logSource.ts
    re-exports the two names it exported before. No behavior change. Requires
    @interop/was-client 0.52.0.
  • Every edv roster collection is now created bare and declared by its own
    governing history log. provisionWalletSpace creates such a collection with
    encryption: 'governed', so its Description carries no client-written
    descriptor, and the epoch[0] install is the guarded create
    (If-None-Match: *) that both declares the collection governed and lands its
    first epoch. The server derives the Description's encryption member from the
    log head, so the wallet writes the log alone, and a Description already
    carrying a client-written descriptor cannot be governed. Breaking for a caller
    that relied on provisioning to declare the descriptor. An account whose
    collections were provisioned with a client-written descriptor is refused by
    every provisioning re-run rather than adopted (the declared descriptor is
    immutable server-side, so no conversion exists); such an account is
    re-provisioned from scratch.
  • The provision-time epoch install is store-shaped rather than handle-shaped,
    the lookup shape the rotation cascade already took. ensureIndexedFirstEpoch
    takes store, ensureWalletSpaceEpochs and walletSpaceProvisioner take
    storeFor: (collectionId) => EncryptionDescriptorStore, and
    ensureRosterDeliveredEpochs takes the same storeFor. Each store's own
    wiring decides the authority its requests ride and, on a governed collection,
    the signer its genesis append is proved by. Breaking. spaceId on
    ensureWalletSpaceEpochs is now optional and names the Space in the
    per-collection failure messages alone.
  • logGovernedDescriptorStore (keys/rosterLogStore.ts) takes a required
    logClass, stated once at construction, and narrows every controller view it
    resolves through controllerForLogClass. A store cannot be built without
    saying which class of log it governs. Breaking.
  • collectionDescriptorLogPinId resolves to
    space/<spaceId>/<collectionId>/meta/log, over @interop/vh-resource-log's
    collectionLogPinId, following the log's move out of the key-map collection
    and into the subtree of the collection it governs. The read capability a share
    grantee or a connected app already holds covers the log.
  • logGovernedDescriptorSource takes a resolveController returning a
    WebvhResourceLogController (was ResourceLogController) and reads under the
    collection-descriptor class, so a reader no longer inherits the roster's
    license and refuses a served log its own wallet wrote. Breaking for a caller
    passing a bare controller view.
  • anchorRosterStoreAt returns the controller view it built. Both cascade entry
    points (rotateRosterToDocumentAndCascade, retireRosterRecipientAndCascade)
    thread that view into the collection fan-out, so each log-governed collection
    store takes the same post-edit minimum controller version the roster store
    takes. Breaking for a caller typed on the void return.
  • userKeyRosterLogSigner also signs the per-collection descriptor logs; the
    name says which log it was built for first.
  • cascadeCollectionsToUserKey and ensureWalletSpaceEpochs carry a
    verified-log refusal (isResourceLogRefusal) in failed verbatim, the
    refusal itself as the entry's error rather than the wrapped per-collection
    message, so a caller reading the report tells it by err.name. The fan-out
    never throws for it: the ceremonies past their pivot keep their later stages
    and their resumable-success contract, and the settled collections still reach
    walletSpaceProvisioner's onSettled.
  • ensureWalletSpaceEpochs and walletSpaceProvisioner refuse a missing
    storeFor with a synchronous TypeError before any write, the way the
    geneses and the mend refuse a missing collectionStoreFor. A caller on the
    former handle-shaped signature fails loudly instead of landing every
    collection in failed on a resolving call.
  • controllerForLogClass is an exhaustive switch that throws a TypeError on a
    class it does not name, so a caller outside the type system never gets the
    membership-only rule on the user key roster log.
  • The mend's completion probe and roster-mint precondition decide epoch presence
    from each collection's VERIFIED governing log (through collectionStore)
    rather than from the Description's server-derived encryption member. The
    Description read stays, as the readability check under the caller's authority.
    A host omitting the derived member can no longer license a fresh roster
    genesis over collections keyed under the real user key.
  • Requires @interop/vh-resource-log 0.5.0 (collectionLogPinId,
    isResourceLogRefusal) and @interop/was-client 0.54.0 (the 'governed'
    provisioning value, the meta/log transport seam, and the moved descriptor
    policy). was-client 0.54.0 is unpublished today.

Removed

  • formatEtag, re-exported from push.ts / the sync subpath barrel. A
    SyncStore implementation now persists the opaque etag string itself rather
    than reconstructing a validator from version.
  • readGovernedEpochConfiguration and EPOCH_CONFIGURATION_STATE_TYPE from the
    descriptors barrel, and logGovernedDescriptorStore and
    EPOCH_CONFIGURATION_STATE_TYPE from the keys barrel. Every governed store
    states its log class at construction, so the class-stating builders
    (userKeyRosterDescriptorStore, collectionDescriptorLogStore,
    logGovernedDescriptorSource) are the public surface; a bare-controller
    reader or a caller-chosen-class wrapper would read or write a collection log
    under the roster's license. The functions themselves stay, module-internal.
  • was and capability on ensureWalletSpaceEpochs, collection on
    ensureIndexedFirstEpoch, capability on walletSpaceProvisioner, and was
    and capability on ensureRosterDeliveredEpochs. Each collection's transport
    and invocation authorit...
Read more

v0.68.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 07 Sep 18:35

Changed

  • The account log's chain-head pin is a property of the store rather than an
    optional argument of every ceremony. WebvhIdStore gains a required
    pin: { store, logId } member; wasWebvhIdStore, wasWebvhLogStore,
    delegatedWebvhLogStore, and clientAnnexLogStore take a required pinStore
    at construction and derive the slot themselves. readPublishedLog /
    readPublishedLogOrThrow check and advance the store's pin on every read, and
    putLogResource (so publishEntryPinned and every ceremony entry's publish)
    advances it on every write. Enrollment approval, client revocation, credential
    retirement, and self-enrollment therefore refuse a served truncated prefix of
    the log before any entry publishes, where the approver's and the revoker's
    reads used to run unpinned.

Removed

  • The pinStore / logId options of every account-log and annex-log ceremony
    (signAccountEntry, ladderSignedAccountEntry, enrollWebvhClient,
    revokeWebvhClient, publishUnlockKey / removeUnlockKey,
    rotateWebvhUpdateKey, ensureDidWebvh, the recovery continuations, the
    annex ceremonies, and their orchestrators) and the half-supplied TypeError,
    together with advanceLogPin. verifyAccountLog keeps its optional
    pinStore, since it fetches by URL and holds no store. A ceremony that builds
    stores from a client still takes a pinStore, now required.
  • The pinStore option of the annex orchestrators that already take an
    idStore (establishCredentialAnchoredAccount,
    ensurePointedClientAnnexGeneration, mendCredentialAnchoredAccount,
    ensureCredentialClientAnnexGeneration, runClientAnnexGc,
    swapClientAnnexGeneration): the annex logs pin in the account-log store's
    pin store, read off idStore.pin.store, so the two log families can no longer
    be handed two stores.
  • clientAnnexLogPinId. Every log store derives its slot through the new
    logResourcePinId({ spaceId, collectionId }) in ./webvh, which
    accountLogPinId is now defined over.
  • ensureAccountGenesis, ensureCredentialAnchoredAccountGenesis, and
    selfEnrollClientCore's accountLogPinStore option, and
    ensurePointedClientAnnexGeneration's accountSpaceId option (it only named
    a pin slot the store now derives).

Fixed

  • The three reads that tell a dead or absent annex generation from a live one
    (the transient readiness ensure's pointed read, the last-client transition's
    generation stage, the GC's orphan and pointed reads) go through
    readClientAnnexLogOrAbsent: an absent log under a pin this client still
    holds is read as absence, so a collected generation is re-pointed, skipped as
    log-unreadable, or collected rather than refused as a rollback, while a
    served prefix of a pinned generation log stays refused. The helper is exported
    from ./clientAnnex, and it is one read: readPublishedLog gains an
    absentUnderPin: 'absent' mode instead of the helper re-fetching after a
    rollback refusal.
  • swapClientAnnexGeneration resolves to { clientAnnexDid, revoke } rather
    than the bare DID: revoke reports whether the old generation's delegation
    was revoked, or skipped because the old log embeds no-delegation or is
    log-absent, so a swap that could not revoke is no longer reported as one
    that did.

v0.67.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 06 Sep 02:08

Removed

  • isSyncConflictError, isSyncNotFoundError, isUnknownEpochError and the
    SyncStatus type from ./sync. They ship from @interop/was-client/sync,
    beside the classes whose name they match, and are imported from there.
    Nothing is re-exported here: one owner per name. isKeyUnwrapError stays in
    ./descriptors, since it classifies a roster-membership failure the wallet
    layer owns and no sync driver dispatches on it.

Changed

  • The @interop/was-client dependency now requires ^0.49.0, for the
    predicates and SyncStatus.
  • deriveUnlockIdentity and unlockIdentityFromSeed state their return type
    explicitly, and UnlockIdentity is an interface rather than an alias over the
    inferred return. Same shape; the emitted declarations now name this package's
    own copy of the agent types.
  • decisions/0009 amended (revisit criterion 1 fired) and decisions/0021
    added: the engine and the RxDB driver stay two algorithms.

Removed

  • The three stages that re-sealed a SIBLING credential's unlock record. Every
    record's frame proof is signed by its own credential's unlock identity key,
    and its bridge and delegatedClients sibling delegations by that credential's
    own ladder VM (decisions/0019), so no ceremony owes a re-seal; each stage
    also planted the acting session's key as the sibling record's frame signer,
    which a later strike of that key turns into a lock-out no self-heal reaches.
    Gone with them:
    • remintRecoveryDelegations, RecordRemintOutcome and
      RecoveryDelegationEntry (/recovery) -- the shared re-mint core.
      delegateLogWrite, recordedDelegationFields, zcapExpiring,
      ZCAP_RENEWAL_WINDOW_MS and RECOVERY_DELEGATION_TTL_MS stay: a
      credential's own login still refreshes its own bridge on those axes.
    • revokeAccountClient's remintRecoveryDelegations option and the
      recovery member of ClientRevocationResult (/clients). The
      remintGenerationDelegation option is unaffected and is now stage 4.
    • retireUnlockCredential's remintDependentRecords option and the
      dependentRecords member of UnlockCredentialRetirementResult (/unlock).
      The ceremony now starts at the document edit, which is its only account-log
      read; the read-only retirement gate (preflightUnlockCredentialRetirement)
      is unchanged.
    • forgetLastEnrolledClient's unlockMethods option, the
      UnlockMethodsRemintReach type, RecordRemintFailedError, and the
      unlockMethods member of LastEnrolledClientForgetResult (/clientAnnex).
      The onBeforeRemoval seam, which re-binds the LOGIN credential's own
      record, is unchanged and is now stage 5 of 6.
  • removeUnlockKey's expectedLadderVmIds option and
    LadderInventoryDriftError (/unlock): the cross-check tied the edit's
    ladder attribution to a pre-edit read only the removed re-mint stage made.
  • revokeAccountClient's ownSigningKeyMultibase option (/clients). Its only
    effect was the wording of the self-revocation refusal, which the document edit
    enforces from the signer's own seeds regardless; an app that omitted it saw
    different prose for the same refusal. The edit's refusal ("a client cannot
    revoke itself") is now the one wording, and the self eligibility refusal in
    clients/policy.ts remains the surface's rule.

Added

  • recoverySpendRetirementFromLog and RecoverySpendRetirement (/recovery):
    the retirement report of a recovery spend whose add-and-retire entry already
    stands, read back off the log from the successor's public halves --
    retiredCredentialVmIds, struckRungHashes, unclaimedCredentialVmIds, the
    same members both continuations return. The continuations' completed branch
    now calls it, so a resume that never re-enters the continuation (freewallet's
    remembered spend resume) consumes one definition of what the spend retired
    rather than a document-membership test of its own. A log that does not
    authorize the successor key reports nothing retired.

  • walletSpaceProvisioner and WalletSpaceProvisioningError (/keys): builds
    the sync engine's ensureProvisioned closure for a wallet Space --
    provisionWalletSpace then ensureWalletSpaceEpochs, single-flight across
    concurrent callers, throwing when a collection was left without its first key
    epoch so the engine never memoizes a torn run. An optional onSettled hook
    receives every run's epoch report, a partial one included, ahead of that
    refusal, so an eager minter still gets the descriptors that did settle.

  • collectionDescriptorLogPinId (/descriptors): the pin-slot key for a
    collection's governing descriptor log,
    space/<spaceId>/key-map/<collectionId>.jsonl.

  • isAppConnectQuery / isWalletOnboardingQuery (/request), beside
    isZcapQuery: the one place each query type's type string is matched.

  • advanceLogPin (/webvh), concludeUnchangedAccountEntry (/webvh),
    isSoleEnrolledClient and ladderVmSigners (/clientAnnex): the shared
    forms of four blocks the ceremonies used to restate per site.

  • RecoveryCredentialStandingError (/recovery): the recovery continuations
    refuse, before their reveal entry, a fresh credential whose keyAgreement id
    already stands in the document (the same passphrase re-typed). The entry
    retires every pre-recovery credential, and one it re-bound instead would keep
    its old rung commitments under a struck ladder VM. The error carries the
    standing ids; nothing is published.

  • AccountEntrySigner (/webvh): signAccountEntry signs under a third,
    seam-only arm, { kind: 'committed', updateSeed } -- a bare update key the
    published log commits or already authorizes, revealing itself with the ladder
    arm's unions. AccountLogSigner, what a ceremony body accepts, is unchanged.

  • introducedCredentialKeys / retiredCredentialKeys
    (resourceLog/document.ts, surfaced through /webvh): the credential-class
    keyAgreement ids one entry introduces and retires, the one definition the
    anchor rule and the resumed spend's report share.

  • mergeVerificationMethods and RelationMembership (/webvh): the one merge
    of added verification methods into a published document across the five
    relations -- same-id replace into verificationMethod, per-relation dedupe,
    an optional retirement predicate over the existing document only. The
    enrollment add entry, the self-enrollment add entry, and the recovery
    add-and-retire entry build through it instead of three inline copies.

Fixed

  • Eight catch sites that classify a rejection on its name read it through an
    optional chain (clientAnnex/heal.ts, clientAnnex/rosterDeliveredEpochs.ts,
    clientAnnex/forgetLast.ts, clientAnnex/log.ts, keys/spaceEpochs.ts,
    webvh/revokeClient.ts). A seam rejecting with a nullish reason -- an
    app-injected store's bare Promise.reject() -- now propagates as it is
    instead of raising a TypeError from inside the catch. Every site's other
    branch rethrows, so no nullish rejection is adopted into a re-run or a skip.
  • isDIDAuthOnlyRequest (/request) reads the query set through queriesOf,
    as classification does, so a body carrying a null or untyped query entry
    beside its DIDAuthentication query is DID-Auth-only to both predicates
    rather than to one.
  • The credential-anchored establishment's stage-3 pointer entry no longer fails
    on a lost race that retired its signing rung. The entry used to retry with a
    pair fixed before the retry, so a sibling self-enrollment spending rung 0
    between the read and the PUT ended the run on an untyped not-authorized error
    after the annex Space and generation were minted (the stage-3 orphan residue).
    The entry is now moved as the ladder: every attempt attributes the ladder's
    current rung, reveals it when only its hash stands committed, and signs with
    it, landing the pointer on the winner's head with no second mint. The registry
    records the rung the entry was signed with, or, when the document already
    pointed, the ladder's currently attributed rung. The stage-3 primitive
    attributes the rung before anything is minted, so a ladder the account log no
    longer anchors refuses with no annex Space or generation minted.
  • A second recovery now retires the credentials the first recovery introduced.
    The recovery add-and-retire entry is read as a bind shape by the anchor rule
    (credentialLadderAnchor, /clientAnnex): the fresh credential anchors on
    the successor key the entry authorized, and the replacement code on the reveal
    entry's last committed hash, paired by the keyAgreement relation's order
    (decisions/0014, amended). A later recovery therefore strikes the first
    recovery's passphrase rung (standing authorized in updateKeys until now) and
    its replacement code's commitment, and reports neither on
    unclaimedCredentialVmIds; a client removal's derived latent-hash set claims
    a recovery replacement's commitment without the caller vouching for it. The
    two anchors are independent: a replacement lookup that refuses leaves the
    fresh credential anchored. A transient continuation torn at its seam more than
    once (a fresh ladder seed each time, the same replacement) still anchors both,
    the replacement's hash read past every resumed reveal to the one attempt that
    committed it. A remembered continuation resumed with a different replacement
    code than its reveal entry committed is refused an anchor rather than guessed;
    a transient one anchors the replacement the document carries, and refuses only
    when two attempts committed different ones.

Changed

  • The "install collection epochs only under the key the user-key roster
    delivers" gate is one place: ensureWalletSpaceEpochs (/keys) takes an
    optional rosterDescriptor and refuses the whole fan-out when the roster's
    current epoch is not the given user key, reporting it on the result's new
    skipped member (...
Read more

v0.66.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 04 Sep 20:54

Added

  • issueAppKeyCredential (/request): the caller-supplied-seed app-key
    credential issuer under mintAppKeyCredential and reissueAppKeyCredential
    is now exported, so an application's own self-issue path signs the same
    credential shape instead of maintaining a copy. It takes an optional
    documentLoader (defaulting to the shared security loader) and an optional
    description (defaulting to the wallet's consent sentence).
  • retireRosterRecipientAndCascade (/keys): the recipient-naming twin of
    rotateRosterToDocumentAndCascade, for a ceremony that rotates the roster
    before its own document edit. It retires one named roster recipient, reads the
    fresh key back through a caller-supplied key-agreement key, and runs the same
    collection fan-out, with no seal backstop.
  • anchorRosterStoreAt (/keys): sets a log-governed roster store's minimum
    controller version from a given account log; shared by both cascade entry
    points.
  • rosterWrapsRecipient (/keys): the shared "does the current epoch wrap to
    this recipient" probe, now also used by addUserKeyRosterRecipient.
  • UserKeyAdoptedHook (/keys): the named type for the roster-adoption
    persistence callback both cascade entry points take.

Fixed

  • The enrollment wrap (addUserKeyRosterRecipient, /keys) now refuses a
    roster whose currentEpoch names no epoch in its own list, instead of
    proceeding to addRecipient. The collection cascade's per-collection op
    raises UserKeyRosterIntegrityError for the same shape rather than a bare
    Error. The new currentEpochOf (/keys) is the one implementation both
    sites, and the roster read, now share.

Changed

  • The seeded ladder-rung scans (/clientAnnex, attributeLadderRung and
    attributeLadderInventory) derive the scanned range concurrently rather than
    one rung per await. Each rung is an HKDF expansion plus an Ed25519 keygen,
    and the default range is 128 rungs. The full range is still scanned, the
    classification order is unchanged, and no refusal changes.
  • The client-annex generation minters (/clientAnnex,
    mintClientAnnexGeneration and mintCredentialClientAnnexGeneration) return
    the published head of the genesis log they just wrote -- the log, the DID and
    document, the effective update-key parameters, and the PUT's own ETag when the
    store hands one back -- rather than the log and document alone.
  • The credential-anchored establishment's generation-delegation install stands
    on that head instead of re-fetching and re-resolving the log the mint wrote a
    moment earlier: one fewer serialized request on every signup. The GC
    generation swap and the annex heal's fresh-generation arm do the same.
  • A head carrying no ETag is not carried forward, since the install's entry
    publishes under a compare-and-swap and a head with no validator would degrade
    that to an unconditional write. The install then reads for itself, as before.
    Either way its own publish advances the generation's chain-head pin.
  • The fresh-generation block the credential-anchored paths share -- mint the
    generation, install its generation delegation, append the account document's
    pointer entry -- is one helper, mintPointedClientAnnexGeneration
    (/clientAnnex), with the pointer write injected: the establishment's two
    arms sign it as a client, the transient visit's fresh-generation arm moves it
    as the ladder. The establishment's bootstrap arm runs its controller flip
    through the helper's beforePointerEntry seam, between the install and the
    pointer entry, as before.
  • rebindCredentialAnchoredRecord and credentialAnchoredStandingFields
    (/clientAnnex) are the one builder of the ladder-VM-signed bridge, sibling,
    and record re-bind, and the one builder of the standing fields a registry
    entry records; the establishment's stage 4 and the mend's record-downgrade
    re-bind and registry arm now share them.
  • The annex heal's two pre-flight attributions merge into
    attributePointerEntryRung (/clientAnnex), the one place a
    LadderAttributionError maps onto ClientAnnexGenerationUnavailableError;
    pointerEntryUpdateKeys is a wrapper over it that still refuses a
    committed-only rung.
  • The controller flip's error-handling asymmetry is now stated in code: the
    establishment's bootstrap arm swallows an authorization-class refusal because
    its Space may be one a concurrent run already flipped, while the annex heal's
    fresh-Space flip acts on a Space id minted a moment ago that no other run can
    hold, so it propagates every failure.
  • forgetEnrolledClient and forgetLastEnrolledClient (/clientAnnex) now run
    their roster rotation and collection fan-out through
    retireRosterRecipientAndCascade instead of restating it, so both forgets set
    the roster store's minimum controller version the same way the revocation
    cascade does.
  • BREAKING: forgetLastEnrolledClient takes a
    rosterStore: EncryptionDescriptorStore option instead of a
    rosterStoreFor: ({ did, log }) => store factory. The ceremony now anchors
    the roster store's minimum controller version itself -- at the pre-transition
    head for the opening read, at the post-reinstall head for the rotation -- so a
    store wired over a cached pre-transition view still anchors the transition's
    one ladder-signed roster append past the reinstall entry. Callers building a
    rosterStoreFor factory must pass one store instead. The store must be
    anchorable (SealableEncryptionDescriptorStore); one without
    setMinimumControllerVersion is refused with a TypeError before any read,
    since the strike-and-reinstall pair would otherwise publish two entries per
    attempt ahead of a rotation the ceremony-tail license refuses every time.
  • isSealableDescriptorStore (/keys) probes both interface members (seal
    and setMinimumControllerVersion) rather than seal alone.

v0.65.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 04 Sep 16:05

Added

  • The ceremony-tail license gains a third admitted shape (/resourceLog,
    assertLadderAppendLicensed). A ladder-signed roster append is now also
    admitted at a document version whose enrolled-client set differs from the
    previous version's, in either direction, AND whose entry was signed by a rung
    of the appending ladder. One-shot per version under the same head comparison
    as shape 2, and under the same single-ladder-proof rule; shapes 1 and 2 are
    unchanged. It is what lets a client enrollment or a client disconnect
    published by a standing credential's ladder rotate the roster as its own tail.
  • The signer conjunct is what keeps the shape out of the
    any-keyAgreement-change predicate the clause rejects: a client's own
    enrollment or revocation entry is client-signed and mints no shot for any
    ladder.
  • Rollout is verifier-first. A reader without shape 3 refuses the WHOLE roster
    log, since the license throws from the admission hook and the verifier
    propagates it, so every roster-log reader ships this release before any writer
    emits an append that needs it.
  • attributeLadderRungsPerVersion (/resourceLog), the seedless ladder-rung
    attribution the new shape reads: which update keys the account log names as
    rungs of which standing ladder, per version. A ladder is anchored at the entry
    that introduces its verification method: by the rung that entry reveals and is
    signed by, or by the single rung-0 hash that entry commits, taken up when a
    later entry authorizes its pre-image. A ladder-branch bind entry is the one
    exception -- it reveals the ACTING credential's rung while introducing ANOTHER
    credential's ladder VM -- so the walk resolves the revealed key's ownership
    first: a key another ladder already holds anchors nothing, the introduced VM
    anchors on its own committed hash instead, and the acting ladder climbs; an
    entry that also publishes an enrolled client anchors no ladder either, since
    the one key it authorizes is that client's. Anchoring also excludes
    co-introduction and foreign inventory: an entry introducing another
    credential's keyAgreement member hands its ladder VM to the signing
    credential's ladder only when it introduces no other credential-class member
    (or introduces this credential's own), and an entry that merely signs a second
    VM, or another credential's keyAgreement member, into the document past a
    ladder's own standing VM contributes neither that VM nor its rung hashes to
    the signing ladder. Once anchored, a ladder then CLIMBS with the log, by
    decisions/0007-ladder-reveal-hash-order.md's last-position rule read
    forward: an entry authorizing exactly one new update key that signed it
    reveals the next rung of the ladder whose rung both authorized and signed the
    entry that committed the key's hash LAST among its additions, while that
    ladder's VM still stands. Without the climb a self-enrollment would freeze the
    attribution at the rung it retires, so no later ladder-signed enrollment or
    disconnect of that credential could satisfy shape 3 -- which a passkey
    account, self-enrolling by construction, hits from its first login. An
    enrollment approval reuses its rung and so authorizes no key of its own, and
    the client update key its add entry authorizes was committed first rather than
    last, so no client key is ever read as a rung; a recovery spend's handover is
    excluded by the struck ladder VM. A snapshot names the rungs the version still
    authorizes plus any that signed its own entry. Every other shape leaves the
    ladder unattributed, and an unattributed ladder never satisfies shape 3.
  • ControllerInventory (/resourceLog) gains three members for it:
    enrolledClientKeys (the version's capabilityInvocation key multibases),
    entrySignerKeys (the update keys that signed the version's own entry), and
    ladderRungKeys (each attributed ladder VM to its rung keys).
    webvhResourceLogController supplies all three; a hand-built controller view
    must too.
  • convergeUserKeyRosterToDocument (/keys) now converges in two directions.
    Beside retiring a current-epoch recipient the document no longer keys, it
    escrows into every epoch an enrolled client the document DOES key that holds
    no wrap -- the mender for a ceremony torn between the entry that published a
    client and the append that was to wrap the user key to it. It takes an
    optional ownerKeyAgreementKey, which the escrow direction needs to unwrap
    each epoch; without one the retire direction runs alone. The result gains
    escrowedRecipientIds. Every missing recipient escrows in ONE append: on a
    ladder-signed store only the first append per version is licensed, so
    escrowing two wraps separately threw ResourceLogLicenseError after the
    caller's pivot, and the fix runs was-client's addRecipient against a
    buffering descriptor store, writing the staged result back once under the
    compare-and-swap token of the read the whole batch was built on. A pure escrow
    mints no epoch, and a healthy roster still writes nothing.
  • enrolledClientRosterRecipients (/keys): the enrolled clients a document
    keys, as roster recipients, rebuilt from the did:key controller marker and
    the key-agreement method the document carries between them. A standing unlock
    credential is not among them -- its roster kid names a signing key no document
    publishes -- so a credential's missing wrap stays the business of the ceremony
    holding the credential.
  • signAccountEntry and AccountLogSigner (/webvh, /clientAnnex): the
    account-log signer seam. AccountLogSigner is the discriminated union
    { kind: 'client', updateKeys } | { kind: 'ladder', ladderSeed }, and one
    build callback describes a ceremony's document delta once for both arms. The
    client arm is the enrolled-client path unchanged (the active-key precondition,
    the entry's own stated parameters, did.jsonl beside its did:web
    projection); the ladder arm is the standing credential's (the rung attributed
    from the log, the acting rung unioned back into updateKeys, its carry-over
    hash before the build's own commit hashes, did.jsonl alone).
    ladderSignedAccountEntry is now that arm under the annex's name and is
    exported from /clientAnnex beside revealLadderRungWebvh.
  • enrollWebvhClient moved to its own module (webvh/enrollClient.ts); its
    export from /webvh is unchanged. It gains optional expectedDid, pinStore
    and logId.
  • publishUnlockKey (/unlock) and publishRecoveryKey (/recovery) take an
    optional part ('all' by default, or 'key' / 'authority'), which splits
    a bind across two entries where a ceremony must land a credential's decryption
    material before its authority. 'key' publishes the keyAgreement member
    alone; 'authority' installs the ladder VM and commits the rung-0 hash.
  • recoveryClientFromCode (/recovery) returns the code's ladderSeed and
    ladderVmKeyMultibase beside its existing members. updateSeed and
    updateKeyMultibase are now rung 0 of that ladder.
  • UnclaimedLadderVmRetirementError (/unlock) carries an optional
    anchorKeyMultibase, the update key the refused walk was anchored on, named
    in the message beside the unclaimed verification methods.
  • disconnectEligibility (/clients) takes an optional
    signerKind: 'client' | 'ladder' (default 'client', so an existing caller
    is unchanged). The ladder branch lifts the self and last-client refusals
    and keeps unattributed-update-key.
  • wasClientLabelsStore (/keys) takes an optional capability, the option
    userKeyRosterDescriptorStore and wasWebvhLogStore already take. The labels
    read and the labels write both ride it, so a session holding only a delegated
    Space-subtree zcap can read and set client labels; absent, both invoke the
    root capability as before.
  • An optional projectionStore on every ceremony body whose entry strikes
    inventory: revokeWebvhClient (/webvh) and revokeAccountClient
    (/clients), removeUnlockKey and retireUnlockCredential (/unlock), and
    removeRecoveryKey (/recovery). It is the narrow id-collection seam the
    forget ceremony's clientLogStore already takes
    (Pick<WebvhIdStore, 'getIdResourceRaw' | 'putIdResource'>). Supplied, the
    post-strike did:web projection is PUT through it immediately BEFORE the
    entry publishes, on both signer arms, which is what keeps a ladder-signed
    removal (it writes did.jsonl alone through the credential's bridge) from
    leaving did.json naming the struck client or credential. A failed PUT is
    warned and the entry still publishes: a projection under-listing a key the log
    still carries is the safe residue, and the next visit's
    ensureDidWebProjection mends it. Omitted, behavior is unchanged -- the
    client arm keeps its own post-entry projection PUT.

Changed

  • Breaking. The sealable roster store's setControllerFloor (/keys) is
    renamed setMinimumControllerVersion. Same contract: a ceremony that just
    extended the account log hands the store the controller view built from its
    post-edit log, and the store's later operations never resolve to anything
    staler (a fresher resolved view still wins).
  • enrollWebvhClient (/webvh) returns the post-add log beside the did --
    the head it published, or the already-enrolled head it found.
  • Breaking. Every ceremony body that publishes an account-log entry takes
    signer: AccountLogSigner in place of updateKeys: ClientWebvhUpdateKeys:
    publishUnlockKey / removeUnlockKey / retireUnlockCredential (/unlock),
    revokeWebvhClient (/webvh) and revokeAccountClient (/clients),
    enrollWebvhClient (/webvh) and approveEnrollment (/enrollment),
    publishRecoveryKey / removeRecoveryKey (/recovery), and
    setDelegatedClientsPointer / setDelegatedClientsPointerOnce /
    swapClientAnnexGeneration (`/cli...
Read more

v0.62.0

Choose a tag to compare

@dmitrizagidulin dmitrizagidulin released this 31 Aug 20:57

Added

  • credentialKeyAgreementMethods (/webvh), the credential-class subset of
    resolvedKeyAgreementMethods: every keyAgreement entry the account DID
    itself controls, matching a passphrase's MultikeyCommitment and a passkey's
    or recovery code's verbatim Multikey alike, since the two publishing entries
    are indistinguishable by construction. Freewallet FW-356.
  • attributeUnlockLadderInventory (/unlock), wrapping the annex attribution
    helper for a standing credential's own inventory: resolves which ladder rungs
    and which ladder VM currently stand in the published log for a given
    credential. Freewallet FW-356.
  • rosterRecipientsToRetire (/keys), naming the roster kids a set of retired
    credentials leave behind, for replaceUserKeyRosterRecipients'
    retireRecipientIds. Freewallet FW-356.
  • strikeLadderVmWebvh (/clientAnnex), the removal half of a ladder VM's life
    cycle, exported alongside the existing installLadderVmWebvh. Freewallet
    FW-356.
  • credentialLadderAnchor, attributeRetiredCredentialRungs and
    retiredCredentialRungsBeforeKey (/clientAnnex): log-only ladder anchoring.
    The entry that first introduced a credential's keyAgreement member names
    rung 0, either as the one key that entry revealed and signed with or as the
    one hash it newly committed, so a caller holding no registry entry and no
    ladder seed can still resolve a credential's standing rungs. A bind entry that
    is ambiguous, or that introduces an enrolled client, is reported rather than
    acted on. WC-154.
  • attributeLadderInventory accepts anchorHash, and an anchor-free mode that
    derives the anchor from credentialVmId. The existing anchorKeyMultibase
    results are unchanged. WC-154.
  • NextKeyHashesEmptyError and assertNextKeyHashesRemain (/clientAnnex):
    the refusal an entry raises rather than publishing an empty nextKeyHashes,
    which would switch prerotation off. WC-154.

Changed

  • The last-client transition's strike-and-reinstall pair mints two
    ceremony-tail license shots, one at each inventory-changing version. The
    strike version's shot is accepted and the license predicate is unchanged. A
    run foreclosed by a sibling ladder's spend at the reinstall version refuses
    its rotation and converges on a re-run. WC-156.
  • Zcap delegation proofs are signed with eddsa-jcs-2022 (EddsaJcs2022 from
    @interop/ed25519-signature/eddsa-jcs-2022) instead of
    Ed25519Signature2020, at all four ZcapClient construction sites: the
    enrolled client's root client (agentsFromKeyAgent), the did:webvh-keyId and
    bare did:key clients (webvhZcapClient / didKeyZcapClient), and the ladder
    VM client (ladderVmZcapClient). Every grant this wallet mints -- App Connect
    app grants, share grants, the generation delegation, the bridge delegations in
    unlock and recovery records -- canonicalizes with JCS rather than URDNA2015,
    so signing runs no JSON-LD canonicalization and needs no document loader. The
    suite is hard-coded at each site rather than threaded as an option. Requires a
    storage server that verifies both suites and ships first. A client that
    re-delegates one of these grants must be on this suite too: an
    Ed25519Signature2020 client cannot re-delegate a JCS-signed parent on its
    default loader. Invocations (HTTP signatures) and the VP/credential suites are
    unchanged. Freewallet FW-395.
  • A standing credential's ladder VM now lives and dies with the credential
    rather than with enrollment: installed in the same document entry as the
    credential's keyAgreement member and rung-0 commitment, struck only at the
    credential's own retirement. selfEnrollWebvhClient's add entry no longer
    strips ladder VMs. publishUnlockKey gains a required
    ladderSeed: Uint8Array | null parameter (null for a recovery code, which
    carries no ladder) and installs the ladder VM under assertionMethod and
    capabilityDelegation in that same entry; it is idempotent on a re-run with
    the same seed. removeUnlockKey strikes the retiring credential's ladder VM
    with or without the seed in hand: attributeLadderInventory gains a third
    return member, ladderVmIds, attributed either by entry signer or by guarded
    co-introduction (the entry that published the credential's keyAgreement
    member also introduced exactly one ladder VM). An attribution ambiguity or a
    seed/log disagreement fails closed with LadderAttributionError; an unclaimed
    VM is left standing. removeUnlockKey's result and
    UnlockCredentialRetirementResult both gain
    ladderVm: LadderVmRemovalReport { struck, unclaimed }, naming the ladder VMs
    the edit struck and, separately, any ladder VM still standing that this
    credential's attribution could not claim. Freewallet FW-356.
  • retireUnlockCredential gains stage 0, an optional fail-closed
    remintDependentRecords({ document, retiringKeyMultibases }) closure run
    BEFORE the document edit, naming the ladder VM the retirement is about to
    strike so sibling credentials' records and bridge delegations it signed are
    re-minted first. The result gains dependentRecords. It also takes optional
    pinStore / logId parameters, threaded to both its stage-0 read and the
    document edit, and passes stage 0's attributed ladder VM ids to
    removeUnlockKey as expectedLadderVmIds: the edit refuses before writing
    when its own attribution resolves a different set
    (LadderInventoryDriftError). Freewallet FW-356.
  • Both recovery continuations (recoverWebvhLadderAnchored, and the remembered
    continuation in /recovery) now retire every pre-recovery standing credential
    in the add-and-retire entry: every credential-class keyAgreement member and
    every ladder VM. Their results gain retiredCredentialVmIds, and
    withoutRemoved no longer filters out a resumed run's own freshly minted
    ladder VM. A resumed run (the add-and-retire entry already published) derives
    retiredCredentialVmIds off the log instead of the document it would
    otherwise edit again, through the newly exported
    retiredCredentialVmIdsFromLog. Freewallet FW-356.
  • forgetLastEnrolledClient strikes its own ladder VM and reinstalls it
    (strikeLadderVmWebvh then installLadderVmWebvh), anchoring the roster
    rotation at the reinstall entry so the ceremony-tail license admits it on an
    account whose VM already stands; the pair is skipped when the rotation is not
    owed. The result member installed is renamed reinstalled. The function now
    requires a clientLogStore parameter -- the enrolled client's root-invoked
    account-log store -- since the credential's own bridge delegation may be
    signed by the very ladder VM the strike removes, and the strike-and-reinstall
    pair publishes through it rather than through the bridge. Freewallet FW-356.
  • ensureCredentialClientAnnexGeneration's pointer-moving arms reveal a merely
    committed rung (revealLadderRungWebvh) inside one conflict retry before the
    pointer entry publishes; the revealed rung then stands in updateKeys, an
    accepted consequence. setDelegatedClientsPointerOnce is now exported, with
    an optional published parameter: a caller that already read and attributed
    the head it is building on (movePointerAsLadder) passes it through, so the
    entry is built on exactly that head and a racing entry landing first surfaces
    as a WebvhLogConflictError for the outer retry to re-attribute from, rather
    than as a plain not-authorized refusal. Freewallet FW-356.
  • ensureCredentialClientAnnexGeneration renews the unlock record's bridge
    delegation, the fifth durable state a transient visit can mend. On a
    client-less account no remembered login runs the login-time bridge refresh, so
    the credential's one write path into the account log would simply lapse; the
    ladder VM stands on exactly those accounts and signs the replacement. The
    renewal runs before every arm and asks the same two-axis staleness predicate
    the delegatedClients sibling asks (expiry via zcapExpiring, signer rot via
    delegationKeyInDocument under capabilityDelegation), now one shared helper
    rather than a copy per delegation. API changes: the ensure takes a required
    delegation (the record's current bridge); idStore is replaced by an
    idStoreFor({ delegation }) factory, so a pointer entry in either minting arm
    rides a bridge the server still accepts; onRebindRecord takes
    { delegation, delegatedClients } and is called whenever either was freshly
    minted; the outcome gains delegation (the usable bridge) and
    bridgeReminted. A re-seal that fails when only the bridge was renewed is
    reported on the outcome's bridgeResealError rather than thrown: the fresh
    bridge already served the visit, so a remote hiccup must not deny a login.
    Freewallet FW-389.

Fixed

  • attributeLadderInventory (/clientAnnex) now attributes a ladder from any
    rung the recorded anchor has climbed to. A seedless walk recovers the rungs
    behind the anchor from the log's own positional rules (decision 0007, read
    backwards), so a ladder VM the transient recovery's add-and-retire entry
    installed is claimed after a self-enrollment advanced the anchor past rung 0.
    A third attribution arm claims a VM an establishStandingUnlock re-run
    reinstalled under a fresh ladder seed. removeUnlockKey strikes both instead
    of reporting them unclaimed. One shape stays out of seedless reach: a VM the
    last-client transition reinstalled, whose acting rung a later self-enrollment
    spends, since the entry committing the next rung's hash authorizes no key for
    the backward walk to read (WC-158). WC-155.
  • Both recovery continuations (recoverWebvhClient and
    recoverWebvhLadderAnchored) now strike each retired pre-recovery
    credential's committed rung hashes, and any rung of its own left revealed in
    updateKeys, in the same add-and-retire entry that strikes its ladder VM and
    ...
Read more