@interop/wallet-core 0.49.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +127 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.js +195 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -0
- package/dist/clientAnnex/forget.d.ts +138 -0
- package/dist/clientAnnex/forget.d.ts.map +1 -0
- package/dist/clientAnnex/forget.js +133 -0
- package/dist/clientAnnex/forget.js.map +1 -0
- package/dist/clientAnnex/forgetLast.d.ts +291 -0
- package/dist/clientAnnex/forgetLast.d.ts.map +1 -0
- package/dist/clientAnnex/forgetLast.js +408 -0
- package/dist/clientAnnex/forgetLast.js.map +1 -0
- package/dist/{webvh/companionGc.d.ts → clientAnnex/gc.d.ts} +50 -16
- package/dist/clientAnnex/gc.d.ts.map +1 -0
- package/dist/{webvh/companionGc.js → clientAnnex/gc.js} +103 -71
- package/dist/clientAnnex/gc.js.map +1 -0
- package/dist/clientAnnex/index.d.ts +60 -0
- package/dist/clientAnnex/index.d.ts.map +1 -0
- package/dist/clientAnnex/index.js +60 -0
- package/dist/clientAnnex/index.js.map +1 -0
- package/dist/{unlock → clientAnnex}/ladder.d.ts +89 -15
- package/dist/clientAnnex/ladder.d.ts.map +1 -0
- package/dist/clientAnnex/ladder.js +474 -0
- package/dist/clientAnnex/ladder.js.map +1 -0
- package/dist/clientAnnex/ladderAnchored.d.ts +276 -0
- package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -0
- package/dist/clientAnnex/ladderAnchored.js +631 -0
- package/dist/clientAnnex/ladderAnchored.js.map +1 -0
- package/dist/{webvh/companion.d.ts → clientAnnex/log.d.ts} +333 -119
- package/dist/clientAnnex/log.d.ts.map +1 -0
- package/dist/{webvh/companion.js → clientAnnex/log.js} +583 -199
- package/dist/clientAnnex/log.js.map +1 -0
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts +103 -0
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -0
- package/dist/clientAnnex/recoveryLadderAnchored.js +263 -0
- package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -0
- package/dist/{unlock → clientAnnex}/selfEnroll.d.ts +1 -1
- package/dist/clientAnnex/selfEnroll.d.ts.map +1 -0
- package/dist/{unlock → clientAnnex}/selfEnroll.js +1 -1
- package/dist/clientAnnex/selfEnroll.js.map +1 -0
- package/dist/clientAnnex/zcap.d.ts +49 -0
- package/dist/clientAnnex/zcap.d.ts.map +1 -0
- package/dist/clientAnnex/zcap.js +101 -0
- package/dist/clientAnnex/zcap.js.map +1 -0
- package/dist/clients/index.d.ts +9 -6
- package/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +8 -5
- package/dist/clients/index.js.map +1 -1
- package/dist/clients/listing.d.ts +32 -0
- package/dist/clients/listing.d.ts.map +1 -1
- package/dist/clients/listing.js +47 -1
- package/dist/clients/listing.js.map +1 -1
- package/dist/clients/revocation.d.ts +33 -1
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/revocation.js +16 -4
- package/dist/clients/revocation.js.map +1 -1
- package/dist/enrollment/index.d.ts +6 -4
- package/dist/enrollment/index.d.ts.map +1 -1
- package/dist/enrollment/index.js +6 -4
- package/dist/enrollment/index.js.map +1 -1
- package/dist/enrollment/onboardingInvite.d.ts +5 -88
- package/dist/enrollment/onboardingInvite.d.ts.map +1 -1
- package/dist/enrollment/onboardingInvite.js +5 -168
- package/dist/enrollment/onboardingInvite.js.map +1 -1
- package/dist/genesis/accountGenesis.d.ts +12 -0
- package/dist/genesis/accountGenesis.d.ts.map +1 -1
- package/dist/genesis/accountGenesis.js.map +1 -1
- package/dist/genesis/index.d.ts +5 -0
- package/dist/genesis/index.d.ts.map +1 -1
- package/dist/genesis/index.js +5 -0
- package/dist/genesis/index.js.map +1 -1
- package/dist/keys/index.d.ts +1 -1
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +1 -1
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/spaceEpochs.d.ts +7 -2
- package/dist/keys/spaceEpochs.d.ts.map +1 -1
- package/dist/keys/spaceEpochs.js +8 -2
- package/dist/keys/spaceEpochs.js.map +1 -1
- package/dist/keys/userKeyRoster.d.ts +39 -0
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +43 -1
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/recovery/index.d.ts +5 -3
- package/dist/recovery/index.d.ts.map +1 -1
- package/dist/recovery/index.js +4 -2
- package/dist/recovery/index.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +35 -12
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
- package/dist/recovery/recoveryDelegation.js +84 -86
- package/dist/recovery/recoveryDelegation.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts +32 -1
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +2 -2
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/request/capabilityRequest.d.ts +33 -0
- package/dist/request/capabilityRequest.d.ts.map +1 -0
- package/dist/request/capabilityRequest.js +35 -0
- package/dist/request/capabilityRequest.js.map +1 -0
- package/dist/request/ephemeralExchange.d.ts +118 -0
- package/dist/request/ephemeralExchange.d.ts.map +1 -0
- package/dist/request/ephemeralExchange.js +224 -0
- package/dist/request/ephemeralExchange.js.map +1 -0
- package/dist/request/index.d.ts +6 -0
- package/dist/request/index.d.ts.map +1 -1
- package/dist/request/index.js +6 -0
- package/dist/request/index.js.map +1 -1
- package/dist/space/activity.d.ts +2 -2
- package/dist/space/activity.js +3 -3
- package/dist/space/activity.js.map +1 -1
- package/dist/unlock/index.d.ts +11 -23
- package/dist/unlock/index.d.ts.map +1 -1
- package/dist/unlock/index.js +10 -21
- package/dist/unlock/index.js.map +1 -1
- package/dist/unlock/retire.d.ts +47 -3
- package/dist/unlock/retire.d.ts.map +1 -1
- package/dist/unlock/retire.js +21 -5
- package/dist/unlock/retire.js.map +1 -1
- package/dist/unlock/standingWebvh.d.ts +37 -82
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +64 -247
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/unlock/unlockRecord.d.ts +13 -6
- package/dist/unlock/unlockRecord.d.ts.map +1 -1
- package/dist/unlock/unlockRecord.js +14 -8
- package/dist/unlock/unlockRecord.js.map +1 -1
- package/dist/webvh/delegatedLogStore.d.ts +5 -5
- package/dist/webvh/delegatedLogStore.js +1 -1
- package/dist/webvh/didWebvh.d.ts +19 -9
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +16 -16
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/index.d.ts +9 -25
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +9 -23
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +77 -5
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +154 -61
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/standingZcap.d.ts +11 -1
- package/dist/webvh/standingZcap.d.ts.map +1 -1
- package/dist/webvh/standingZcap.js +13 -14
- package/dist/webvh/standingZcap.js.map +1 -1
- package/dist/webvh/wasIdStore.d.ts +13 -7
- package/dist/webvh/wasIdStore.d.ts.map +1 -1
- package/dist/webvh/wasIdStore.js +8 -4
- package/dist/webvh/wasIdStore.js.map +1 -1
- package/dist/webvh/zcap.d.ts +0 -24
- package/dist/webvh/zcap.d.ts.map +1 -1
- package/dist/webvh/zcap.js +0 -42
- package/dist/webvh/zcap.js.map +1 -1
- package/package.json +41 -35
- package/dist/unlock/ladder.d.ts.map +0 -1
- package/dist/unlock/ladder.js +0 -258
- package/dist/unlock/ladder.js.map +0 -1
- package/dist/unlock/selfEnroll.d.ts.map +0 -1
- package/dist/unlock/selfEnroll.js.map +0 -1
- package/dist/webvh/companion.d.ts.map +0 -1
- package/dist/webvh/companion.js.map +0 -1
- package/dist/webvh/companionGc.d.ts.map +0 -1
- package/dist/webvh/companionGc.js.map +0 -1
package/README.md
CHANGED
|
@@ -57,8 +57,11 @@ The subpaths:
|
|
|
57
57
|
injected), the App Connect app-key credential module (query validation,
|
|
58
58
|
matching, minting, store-time refusal, legacy re-issue), the
|
|
59
59
|
`WalletOnboardingQuery` transport vocabulary (compose and classification), the
|
|
60
|
-
VC-API exchange client,
|
|
61
|
-
|
|
60
|
+
VC-API exchange client, VCALM `interaction:` URL handling, the requester's
|
|
61
|
+
half of a server's ephemeral exchange (`createEphemeralExchange` /
|
|
62
|
+
`pollEphemeralExchange`, the poll bounded by a caller signal or its own
|
|
63
|
+
deadline), and `composeCapabilityRequest`, the zcap-only VPR a requester
|
|
64
|
+
stores on such an exchange. The VPR type vocabulary lives in
|
|
62
65
|
[`@interop/data-integrity-core`](https://npm.im/@interop/data-integrity-core)
|
|
63
66
|
and is re-exported here.
|
|
64
67
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The account-genesis ceremony's CREDENTIAL-ANCHORED variant: such a
|
|
6
|
+
* signup mints no durable client, so the ceremony is anchored on the unlock
|
|
7
|
+
* credential's ladder alone. The stage order mirrors `ensureAccountGenesis`
|
|
8
|
+
* with the durable-client members swapped for their ladder analogs:
|
|
9
|
+
*
|
|
10
|
+
* 1. Space provisioning under the ladder VM's bare did:key -- the bootstrap
|
|
11
|
+
* controller (wallet-core decision 0004, freewallet decision 0002's
|
|
12
|
+
* resolution of the orphan-Space tear): re-derivable from the unlock
|
|
13
|
+
* record's ladder seed, so a tab death here strands nothing a later
|
|
14
|
+
* login cannot finish or unwind.
|
|
15
|
+
* 2. The ladder-anchored did:webvh genesis
|
|
16
|
+
* (`ensureLadderAnchoredDidWebvh`): the entry signed by ladder rung 0, `updateKeys` = [rung 0], `nextKeyHashes`
|
|
17
|
+
* = [hash(rung 0), hash(rung 1)], the ladder VM and the credential's
|
|
18
|
+
* `keyAgreement` posture folded in, `portable` unchanged. There is no KMS
|
|
19
|
+
* stage: the keystore is DEFERRED to the first durable enrollment (a
|
|
20
|
+
* ladder-anchored account has no consumer for the convenience VM, and a
|
|
21
|
+
* keystore under an evaporating identity would orphan).
|
|
22
|
+
* 3. The user-key roster genesis, wrapped to the CREDENTIAL's standing
|
|
23
|
+
* key-agreement key -- the only recipient a ladder-anchored account has
|
|
24
|
+
* -- with the entry proof signed by the ladder VM (the ceremony-tail
|
|
25
|
+
* license's first-entry shape). The account is credential-recoverable
|
|
26
|
+
* from the moment this lands.
|
|
27
|
+
* 4. Epoch[0] on every encrypted roster collection -- gated twice, unlike
|
|
28
|
+
* the durable flow. The roster stage must have landed, AND the roster's
|
|
29
|
+
* current epoch must BE the `userKey` this run was handed: the user key
|
|
30
|
+
* here exists only in this tab's memory, so installing collection epochs
|
|
31
|
+
* under a key the roster does not deliver would strand the collections
|
|
32
|
+
* on a key that dies with the tab. The second gate is what a re-run over
|
|
33
|
+
* an earlier run's roster hits: `read()` adopts a roster keyed to that
|
|
34
|
+
* run's user key, and this run's candidate key is a throwaway nobody
|
|
35
|
+
* holds -- installing epoch[0] under it on a collection the earlier run
|
|
36
|
+
* never reached would key that collection to nothing, permanently, since
|
|
37
|
+
* the install is create-if-absent and every later ensure adopts it. So
|
|
38
|
+
* the stage is skipped whole and reported on `epochsSkipped`; the caller
|
|
39
|
+
* that recovers the roster's real key is the one installer. With both
|
|
40
|
+
* gates the tear heal is always clean: no roster means no epochs, and a
|
|
41
|
+
* fresh user key re-runs both.
|
|
42
|
+
* 5. Space-controller promotion, last -- every earlier stage ran under the
|
|
43
|
+
* bootstrap did:key the Space's stored controller authorizes.
|
|
44
|
+
*
|
|
45
|
+
* Idempotent end to end on the durable flow's convention; the did:webvh
|
|
46
|
+
* stage adopts a published log iff the ladder attributes it (a naive re-run
|
|
47
|
+
* would mint a different SCID, so adoption IS the torn-signup convergence).
|
|
48
|
+
*/
|
|
49
|
+
import { type RecipientPublicKey } from '@interop/was-client/edv';
|
|
50
|
+
import type { WasClient } from '@interop/was-client';
|
|
51
|
+
import type { EncryptionDescriptorStore } from '@interop/was-client/edv';
|
|
52
|
+
import type { WebvhIdStore } from '../webvh/didWebvh.js';
|
|
53
|
+
import type { UnlockKeyAgreementPublication } from '../unlock/standingWebvh.js';
|
|
54
|
+
import { type UserKey } from '../keys/index.js';
|
|
55
|
+
import type { ResourceLogPinStore } from '../resourceLog/index.js';
|
|
56
|
+
import { type AccountGenesisResult } from '../genesis/accountGenesis.js';
|
|
57
|
+
/**
|
|
58
|
+
* The key set a credential-anchored signup mints locally before anything
|
|
59
|
+
* touches the network: the data Space id and the account's user key -- nothing else.
|
|
60
|
+
* No client seed and no client update-key seeds exist (that is the point);
|
|
61
|
+
* the ladder seed is minted by the unlock layer beside the record that
|
|
62
|
+
* carries it, and every other key derives from it.
|
|
63
|
+
*
|
|
64
|
+
* @returns {Promise<{ spaceId: string, userKey: Required<UserKey> }>}
|
|
65
|
+
*/
|
|
66
|
+
export declare function mintCredentialAnchoredAccountKeySet(): Promise<{
|
|
67
|
+
spaceId: string;
|
|
68
|
+
userKey: Required<UserKey>;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* Runs the credential-anchored account-genesis ceremony (see the module doc
|
|
72
|
+
* for the stage order and its whys). The caller has already durably written the
|
|
73
|
+
* unlock record carrying the ladder seed -- the transposed
|
|
74
|
+
* persist-before-publish rule: rung 0 must never publish before the seed
|
|
75
|
+
* that derives it is recoverable.
|
|
76
|
+
*
|
|
77
|
+
* @param options {object}
|
|
78
|
+
* @param options.was {WasClient} signing as the ladder VM's bare did:key
|
|
79
|
+
* (the bootstrap controller; build it over `ladderVmAgent`)
|
|
80
|
+
* @param options.wasServerUrl {string}
|
|
81
|
+
* @param options.spaceId {string}
|
|
82
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed,
|
|
83
|
+
* already sealed into the durably written unlock record
|
|
84
|
+
* @param options.keyAgreement {UnlockKeyAgreementPublication} the
|
|
85
|
+
* credential's key-agreement publication (commitment or verbatim), folded
|
|
86
|
+
* into the genesis document
|
|
87
|
+
* @param options.standingRecipient {RecipientPublicKey} the credential's
|
|
88
|
+
* standing key-agreement key as a roster recipient (`id` its roster kid)
|
|
89
|
+
* @param options.userKey {UserKey} the account's user key, epoch[0] of the
|
|
90
|
+
* roster and recipient zero of every encrypted collection
|
|
91
|
+
* @param options.idStore {WebvhIdStore} the account's `id` collection
|
|
92
|
+
* store, signing as the same bootstrap did:key
|
|
93
|
+
* @param options.rosterStoreFor {Function} `({ did }) =>
|
|
94
|
+
* EncryptionDescriptorStore` -- the user-key roster's store once the DID is
|
|
95
|
+
* known, with a LADDER-signed `ResourceLogSigner` (the first-entry shape)
|
|
96
|
+
* @param [options.expectedDid] {string} the account DID, when the caller
|
|
97
|
+
* holds a pointer that already names one (a heal re-run); a fresh signup
|
|
98
|
+
* and a fresh-terminal heal legitimately hold none
|
|
99
|
+
* @param [options.accountLogPinStore] {ResourceLogPinStore} the chain-head
|
|
100
|
+
* pins the genesis read and create ride (a transient signup passes the
|
|
101
|
+
* visit's in-memory store)
|
|
102
|
+
* @param [options.onDidPublished] {Function} `({ did }) => Promise<void>`
|
|
103
|
+
* @param [options.promoteController] {boolean} default `true`; an app whose
|
|
104
|
+
* account pointer must durably name the DID first (freewallet's record
|
|
105
|
+
* re-bind) passes `false` and promotes after that write
|
|
106
|
+
* @returns {Promise<AccountGenesisResult>}
|
|
107
|
+
*/
|
|
108
|
+
export declare function ensureCredentialAnchoredAccountGenesis({ was, wasServerUrl, spaceId, ladderSeed, keyAgreement, standingRecipient, userKey, idStore, rosterStoreFor, expectedDid, accountLogPinStore, onDidPublished, promoteController }: {
|
|
109
|
+
was: WasClient;
|
|
110
|
+
wasServerUrl: string;
|
|
111
|
+
spaceId: string;
|
|
112
|
+
ladderSeed: Uint8Array;
|
|
113
|
+
keyAgreement: UnlockKeyAgreementPublication;
|
|
114
|
+
standingRecipient: RecipientPublicKey;
|
|
115
|
+
userKey: UserKey;
|
|
116
|
+
idStore: WebvhIdStore;
|
|
117
|
+
rosterStoreFor: (options: {
|
|
118
|
+
did: string;
|
|
119
|
+
}) => EncryptionDescriptorStore;
|
|
120
|
+
expectedDid?: string;
|
|
121
|
+
accountLogPinStore?: ResourceLogPinStore;
|
|
122
|
+
onDidPublished?: (published: {
|
|
123
|
+
did: string;
|
|
124
|
+
}) => Promise<void>;
|
|
125
|
+
promoteController?: boolean;
|
|
126
|
+
}): Promise<AccountGenesisResult>;
|
|
127
|
+
//# sourceMappingURL=credentialAnchoredGenesis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentialAnchoredGenesis.d.ts","sourceRoot":"","sources":["../../src/clientAnnex/credentialAnchoredGenesis.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAwB,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAIxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAA;AAC/E,OAAO,EAGL,KAAK,OAAO,EAEb,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,8BAA8B,CAAA;AAErC;;;;;;;;GAQG;AACH,wBAAsB,mCAAmC,IAAI,OAAO,CAAC;IACnE,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAC3B,CAAC,CAED;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,sCAAsC,CAAC,EAC3D,GAAG,EACH,YAAY,EACZ,OAAO,EACP,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,OAAO,EACP,OAAO,EACP,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAwB,EACzB,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,UAAU,CAAA;IACtB,YAAY,EAAE,6BAA6B,CAAA;IAC3C,iBAAiB,EAAE,kBAAkB,CAAA;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,YAAY,CAAA;IACrB,cAAc,EAAE,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,yBAAyB,CAAA;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,mBAAmB,CAAA;IACxC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0FhC"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The account-genesis ceremony's CREDENTIAL-ANCHORED variant: such a
|
|
6
|
+
* signup mints no durable client, so the ceremony is anchored on the unlock
|
|
7
|
+
* credential's ladder alone. The stage order mirrors `ensureAccountGenesis`
|
|
8
|
+
* with the durable-client members swapped for their ladder analogs:
|
|
9
|
+
*
|
|
10
|
+
* 1. Space provisioning under the ladder VM's bare did:key -- the bootstrap
|
|
11
|
+
* controller (wallet-core decision 0004, freewallet decision 0002's
|
|
12
|
+
* resolution of the orphan-Space tear): re-derivable from the unlock
|
|
13
|
+
* record's ladder seed, so a tab death here strands nothing a later
|
|
14
|
+
* login cannot finish or unwind.
|
|
15
|
+
* 2. The ladder-anchored did:webvh genesis
|
|
16
|
+
* (`ensureLadderAnchoredDidWebvh`): the entry signed by ladder rung 0, `updateKeys` = [rung 0], `nextKeyHashes`
|
|
17
|
+
* = [hash(rung 0), hash(rung 1)], the ladder VM and the credential's
|
|
18
|
+
* `keyAgreement` posture folded in, `portable` unchanged. There is no KMS
|
|
19
|
+
* stage: the keystore is DEFERRED to the first durable enrollment (a
|
|
20
|
+
* ladder-anchored account has no consumer for the convenience VM, and a
|
|
21
|
+
* keystore under an evaporating identity would orphan).
|
|
22
|
+
* 3. The user-key roster genesis, wrapped to the CREDENTIAL's standing
|
|
23
|
+
* key-agreement key -- the only recipient a ladder-anchored account has
|
|
24
|
+
* -- with the entry proof signed by the ladder VM (the ceremony-tail
|
|
25
|
+
* license's first-entry shape). The account is credential-recoverable
|
|
26
|
+
* from the moment this lands.
|
|
27
|
+
* 4. Epoch[0] on every encrypted roster collection -- gated twice, unlike
|
|
28
|
+
* the durable flow. The roster stage must have landed, AND the roster's
|
|
29
|
+
* current epoch must BE the `userKey` this run was handed: the user key
|
|
30
|
+
* here exists only in this tab's memory, so installing collection epochs
|
|
31
|
+
* under a key the roster does not deliver would strand the collections
|
|
32
|
+
* on a key that dies with the tab. The second gate is what a re-run over
|
|
33
|
+
* an earlier run's roster hits: `read()` adopts a roster keyed to that
|
|
34
|
+
* run's user key, and this run's candidate key is a throwaway nobody
|
|
35
|
+
* holds -- installing epoch[0] under it on a collection the earlier run
|
|
36
|
+
* never reached would key that collection to nothing, permanently, since
|
|
37
|
+
* the install is create-if-absent and every later ensure adopts it. So
|
|
38
|
+
* the stage is skipped whole and reported on `epochsSkipped`; the caller
|
|
39
|
+
* that recovers the roster's real key is the one installer. With both
|
|
40
|
+
* gates the tear heal is always clean: no roster means no epochs, and a
|
|
41
|
+
* fresh user key re-runs both.
|
|
42
|
+
* 5. Space-controller promotion, last -- every earlier stage ran under the
|
|
43
|
+
* bootstrap did:key the Space's stored controller authorizes.
|
|
44
|
+
*
|
|
45
|
+
* Idempotent end to end on the durable flow's convention; the did:webvh
|
|
46
|
+
* stage adopts a published log iff the ladder attributes it (a naive re-run
|
|
47
|
+
* would mint a different SCID, so adoption IS the torn-signup convergence).
|
|
48
|
+
*/
|
|
49
|
+
import { initRecipients } from '@interop/was-client/edv';
|
|
50
|
+
import { provisionWalletSpace } from '../space/index.js';
|
|
51
|
+
import { ladderVmAgent } from './zcap.js';
|
|
52
|
+
import { ensureLadderAnchoredDidWebvh } from './ladderAnchored.js';
|
|
53
|
+
import { ensureWalletSpaceEpochs, mintUserKey } from '../keys/index.js';
|
|
54
|
+
import { AccountGenesisSpaceError, ensurePromotedSpaceController, mintSpaceId } from '../genesis/accountGenesis.js';
|
|
55
|
+
/**
|
|
56
|
+
* The key set a credential-anchored signup mints locally before anything
|
|
57
|
+
* touches the network: the data Space id and the account's user key -- nothing else.
|
|
58
|
+
* No client seed and no client update-key seeds exist (that is the point);
|
|
59
|
+
* the ladder seed is minted by the unlock layer beside the record that
|
|
60
|
+
* carries it, and every other key derives from it.
|
|
61
|
+
*
|
|
62
|
+
* @returns {Promise<{ spaceId: string, userKey: Required<UserKey> }>}
|
|
63
|
+
*/
|
|
64
|
+
export async function mintCredentialAnchoredAccountKeySet() {
|
|
65
|
+
return { spaceId: mintSpaceId(), userKey: await mintUserKey() };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Runs the credential-anchored account-genesis ceremony (see the module doc
|
|
69
|
+
* for the stage order and its whys). The caller has already durably written the
|
|
70
|
+
* unlock record carrying the ladder seed -- the transposed
|
|
71
|
+
* persist-before-publish rule: rung 0 must never publish before the seed
|
|
72
|
+
* that derives it is recoverable.
|
|
73
|
+
*
|
|
74
|
+
* @param options {object}
|
|
75
|
+
* @param options.was {WasClient} signing as the ladder VM's bare did:key
|
|
76
|
+
* (the bootstrap controller; build it over `ladderVmAgent`)
|
|
77
|
+
* @param options.wasServerUrl {string}
|
|
78
|
+
* @param options.spaceId {string}
|
|
79
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed,
|
|
80
|
+
* already sealed into the durably written unlock record
|
|
81
|
+
* @param options.keyAgreement {UnlockKeyAgreementPublication} the
|
|
82
|
+
* credential's key-agreement publication (commitment or verbatim), folded
|
|
83
|
+
* into the genesis document
|
|
84
|
+
* @param options.standingRecipient {RecipientPublicKey} the credential's
|
|
85
|
+
* standing key-agreement key as a roster recipient (`id` its roster kid)
|
|
86
|
+
* @param options.userKey {UserKey} the account's user key, epoch[0] of the
|
|
87
|
+
* roster and recipient zero of every encrypted collection
|
|
88
|
+
* @param options.idStore {WebvhIdStore} the account's `id` collection
|
|
89
|
+
* store, signing as the same bootstrap did:key
|
|
90
|
+
* @param options.rosterStoreFor {Function} `({ did }) =>
|
|
91
|
+
* EncryptionDescriptorStore` -- the user-key roster's store once the DID is
|
|
92
|
+
* known, with a LADDER-signed `ResourceLogSigner` (the first-entry shape)
|
|
93
|
+
* @param [options.expectedDid] {string} the account DID, when the caller
|
|
94
|
+
* holds a pointer that already names one (a heal re-run); a fresh signup
|
|
95
|
+
* and a fresh-terminal heal legitimately hold none
|
|
96
|
+
* @param [options.accountLogPinStore] {ResourceLogPinStore} the chain-head
|
|
97
|
+
* pins the genesis read and create ride (a transient signup passes the
|
|
98
|
+
* visit's in-memory store)
|
|
99
|
+
* @param [options.onDidPublished] {Function} `({ did }) => Promise<void>`
|
|
100
|
+
* @param [options.promoteController] {boolean} default `true`; an app whose
|
|
101
|
+
* account pointer must durably name the DID first (freewallet's record
|
|
102
|
+
* re-bind) passes `false` and promotes after that write
|
|
103
|
+
* @returns {Promise<AccountGenesisResult>}
|
|
104
|
+
*/
|
|
105
|
+
export async function ensureCredentialAnchoredAccountGenesis({ was, wasServerUrl, spaceId, ladderSeed, keyAgreement, standingRecipient, userKey, idStore, rosterStoreFor, expectedDid, accountLogPinStore, onDidPublished, promoteController = true }) {
|
|
106
|
+
const failed = [];
|
|
107
|
+
const bootstrap = await ladderVmAgent({ ladderSeed });
|
|
108
|
+
// 1. The Space and its collection roster, create-if-absent under the
|
|
109
|
+
// ladder VM's bare did:key. The typed refusal, exactly as on the durable
|
|
110
|
+
// flow: nothing downstream can proceed without a Space.
|
|
111
|
+
try {
|
|
112
|
+
await provisionWalletSpace({ was, spaceId, controllerDid: bootstrap.id });
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
throw new AccountGenesisSpaceError({ spaceId, cause: err });
|
|
116
|
+
}
|
|
117
|
+
// 2. The ladder-anchored did:webvh genesis -- probe, adopt
|
|
118
|
+
// (ladder-attributed), or create-and-publish. Fatal on failure, like the durable stage.
|
|
119
|
+
const { did } = await ensureLadderAnchoredDidWebvh({
|
|
120
|
+
idStore,
|
|
121
|
+
wasServerUrl,
|
|
122
|
+
spaceId,
|
|
123
|
+
ladderSeed,
|
|
124
|
+
keyAgreement,
|
|
125
|
+
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
126
|
+
...(accountLogPinStore ? { pinStore: accountLogPinStore } : {})
|
|
127
|
+
});
|
|
128
|
+
await onDidPublished?.({ did });
|
|
129
|
+
// 3. The roster genesis: epoch[0] IS the user key, wrapped once, to the
|
|
130
|
+
// credential's standing key-agreement key. The store's ladder-signed
|
|
131
|
+
// genesis append is the ceremony-tail license's first-entry shape.
|
|
132
|
+
let rosterDescriptor;
|
|
133
|
+
try {
|
|
134
|
+
const store = rosterStoreFor({ did });
|
|
135
|
+
const current = await store.read();
|
|
136
|
+
rosterDescriptor =
|
|
137
|
+
current !== null
|
|
138
|
+
? current.descriptor
|
|
139
|
+
: await initRecipients({
|
|
140
|
+
store,
|
|
141
|
+
recipients: [standingRecipient],
|
|
142
|
+
epoch: { epochId: userKey.id, secret: userKey.secret }
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
failed.push({ stage: 'roster', error: err });
|
|
147
|
+
}
|
|
148
|
+
// 4. Epoch[0] on every encrypted roster collection, only behind a landed
|
|
149
|
+
// roster whose current epoch IS this run's user key (see the module doc:
|
|
150
|
+
// the user key is memory-only here, and an adopted roster keyed to another
|
|
151
|
+
// run's key would have the collections installed under a throwaway).
|
|
152
|
+
let epochs;
|
|
153
|
+
let epochsSkipped;
|
|
154
|
+
if (rosterDescriptor) {
|
|
155
|
+
if (rosterDescriptor.currentEpoch === userKey.id) {
|
|
156
|
+
try {
|
|
157
|
+
epochs = await ensureWalletSpaceEpochs({ was, spaceId, userKey });
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
failed.push({ stage: 'epochs', error: err });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
epochsSkipped =
|
|
165
|
+
rosterDescriptor.currentEpoch !== undefined
|
|
166
|
+
? { rosterEpochId: rosterDescriptor.currentEpoch }
|
|
167
|
+
: {};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// 5. The controller promotion, last. The bootstrap client IS the bare
|
|
171
|
+
// did:key client, so it serves the heal branch too.
|
|
172
|
+
let promotion;
|
|
173
|
+
if (promoteController) {
|
|
174
|
+
try {
|
|
175
|
+
promotion = await ensurePromotedSpaceController({
|
|
176
|
+
was,
|
|
177
|
+
wasAsClient: was,
|
|
178
|
+
spaceId,
|
|
179
|
+
did
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
failed.push({ stage: 'promotion', error: err });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
did,
|
|
188
|
+
...(rosterDescriptor ? { rosterDescriptor } : {}),
|
|
189
|
+
...(epochs ? { epochs } : {}),
|
|
190
|
+
...(epochsSkipped ? { epochsSkipped } : {}),
|
|
191
|
+
...(promotion ? { promotion } : {}),
|
|
192
|
+
failed
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=credentialAnchoredGenesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentialAnchoredGenesis.js","sourceRoot":"","sources":["../../src/clientAnnex/credentialAnchoredGenesis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The forget ceremony: a remembered browser's durable client removes ITSELF
|
|
6
|
+
* from the account, through the standing unlock credential's bridge, before
|
|
7
|
+
* the app wipes its local state. Self-enrollment in reverse, run with what a
|
|
8
|
+
* live durable session already holds -- the credential's ladder seed and its
|
|
9
|
+
* delegated `did.jsonl` bridge -- and refused for the account's last enrolled
|
|
10
|
+
* durable client ({@link LastDurableClientForgetError}), whose forget is the
|
|
11
|
+
* ladder-anchored transition, a separate ceremony.
|
|
12
|
+
*
|
|
13
|
+
* The stage order INVERTS the revocation cascade's document-edit-first rule,
|
|
14
|
+
* and the inversion is forced twice over: once the removal entry lands, the
|
|
15
|
+
* forgetting client's key is out of `assertionMethod` at the post-edit head,
|
|
16
|
+
* so it can sign no roster append (the entry-proof rule), and its WAS
|
|
17
|
+
* invocations stop verifying (the current-key-set rule), so the collection
|
|
18
|
+
* fan-out cannot run either -- while a ladder-signed append is licensed only
|
|
19
|
+
* at a posture-changing document version, which a not-last-client removal is
|
|
20
|
+
* not. So:
|
|
21
|
+
*
|
|
22
|
+
* 1. **The roster rotation**, PRE-edit, signed by the still-enrolled
|
|
23
|
+
* forgetting client: the user key rotates off this client's wrap, retired
|
|
24
|
+
* by its roster kid explicitly (document convergence cannot drop a client
|
|
25
|
+
* the document still lists). The fresh key is read back through the
|
|
26
|
+
* CREDENTIAL's standing wrap -- the forgetting client just retired its
|
|
27
|
+
* own -- which the rotation fan-out keeps escrowed into every epoch.
|
|
28
|
+
* 2. **The collection fan-out**, still under this client's invocation
|
|
29
|
+
* authority: every encrypted collection re-epochs onto the fresh key, so
|
|
30
|
+
* writes stop landing under epochs a later holder of this browser's
|
|
31
|
+
* residue could open.
|
|
32
|
+
* 3. **The removal entry** ({@link forgetWebvhClient}): one atomic
|
|
33
|
+
* ladder-signed entry through the bridge takes the client's whole document
|
|
34
|
+
* footprint out. The app's local wipe runs after this ceremony returns, so
|
|
35
|
+
* a tear anywhere before the entry reads as "not forgotten" and a re-click
|
|
36
|
+
* resumes.
|
|
37
|
+
*
|
|
38
|
+
* Torn-state map: a run torn after stage 1 leaves the account rotated off a
|
|
39
|
+
* client the document still lists -- writes already land under the fresh key,
|
|
40
|
+
* the client-key record still stands, and the re-run's rotation no-ops (the
|
|
41
|
+
* wrap is already gone) while its later stages finish. Torn after stage 3
|
|
42
|
+
* (the removal published, the app's wipe not run) is the
|
|
43
|
+
* finish-the-wipe state the app's next login maps. What no re-run needs:
|
|
44
|
+
* the roster log's head stays anchored BEFORE the removal entry -- no signer
|
|
45
|
+
* this ceremony holds may append after it -- which is exactly the unsealed
|
|
46
|
+
* state any other enrolled client's login sweep detects and seals.
|
|
47
|
+
*
|
|
48
|
+
* There is deliberately no recovery-delegation or generation-delegation
|
|
49
|
+
* re-mint stage: the forgetting client cannot re-mint with a key that dies at
|
|
50
|
+
* stage 3 (the replacement would rot moments later), so a delegation this
|
|
51
|
+
* client had signed is left to the standing self-heals (a durable login's
|
|
52
|
+
* bridge refresh and `ensureGenerationDelegationCurrent` on the
|
|
53
|
+
* account-document axis).
|
|
54
|
+
*
|
|
55
|
+
* The honest limitation is the cascade's, as everywhere: ciphertext this
|
|
56
|
+
* browser already fetched stays forensically recoverable from its storage,
|
|
57
|
+
* and old epochs stay open to keys it already held. The rotation stops future
|
|
58
|
+
* reads; the wipe removes the local copies.
|
|
59
|
+
*/
|
|
60
|
+
import type { IKeyAgreementKey } from '@interop/data-integrity-core';
|
|
61
|
+
import type { CollectionEncryption } from '@interop/was-client';
|
|
62
|
+
import type { EncryptionDescriptorStore } from '@interop/was-client/edv';
|
|
63
|
+
import type { RevokedClientKeys } from '../webvh/revokeClient.js';
|
|
64
|
+
import { type CascadeCollections, type UserKey, type UserKeyCascadeResult } from '../keys/index.js';
|
|
65
|
+
import type { UnlockLogStore } from '../unlock/standingWebvh.js';
|
|
66
|
+
/**
|
|
67
|
+
* What a completed forget reports: whether the roster's wrap for the
|
|
68
|
+
* forgotten client was actually retired on this run (a re-run of a
|
|
69
|
+
* torn-after-rotation ceremony reports `false`), the per-collection fan-out
|
|
70
|
+
* result, the document as the removal entry left it, and -- when the account
|
|
71
|
+
* has a roster -- the rotated key with the roster descriptor it was read
|
|
72
|
+
* from.
|
|
73
|
+
*/
|
|
74
|
+
export interface DurableClientForgetResult {
|
|
75
|
+
rotated: boolean;
|
|
76
|
+
collections: UserKeyCascadeResult;
|
|
77
|
+
did: string;
|
|
78
|
+
document: object;
|
|
79
|
+
userKey?: UserKey;
|
|
80
|
+
rosterDescriptor?: CollectionEncryption;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Forgets one enrolled durable client -- this browser's own -- from its
|
|
84
|
+
* account. See the module doc for the stage order (rotation and fan-out
|
|
85
|
+
* BEFORE the removal entry, the self-forget inversion) and the torn-state
|
|
86
|
+
* map. The caller runs the local wipe only after this resolves.
|
|
87
|
+
*
|
|
88
|
+
* @param options {object}
|
|
89
|
+
* @param options.logStore {UnlockLogStore} the credential's delegated
|
|
90
|
+
* `did.jsonl` bridge store; also serves the ceremony's public pre-edit
|
|
91
|
+
* read. The caller is expected to have verified the account log under its
|
|
92
|
+
* own chain-head pins in the same session (the ceremony re-reads through
|
|
93
|
+
* the seam and checks `expectedDid`, but carries no pin store itself)
|
|
94
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
95
|
+
* @param options.forgottenClient {RevokedClientKeys} this client's public
|
|
96
|
+
* halves; an `updateKeyMultibase` the log does not authorize (stale, or the
|
|
97
|
+
* staged key) is re-derived from the log
|
|
98
|
+
* @param options.forgottenKeyAgreementKeyMultibase {string} this client's
|
|
99
|
+
* identity key-agreement key (the X25519 twin), naming its roster wrap
|
|
100
|
+
* @param [options.knownLatentHashes] {string[]} standing latent commitments
|
|
101
|
+
* the caller vouches for (the recovery registry's update-key hashes),
|
|
102
|
+
* excluded from the staged-hash attribution
|
|
103
|
+
* @param options.expectedDid {string} the account DID from the caller's
|
|
104
|
+
* stored account pointer
|
|
105
|
+
* @param options.rosterStore {EncryptionDescriptorStore} the
|
|
106
|
+
* `key-map/user-key.jsonl` roster store, signing as the forgetting client
|
|
107
|
+
* @param options.credentialKeyAgreementKey {IKeyAgreementKey} the standing
|
|
108
|
+
* credential's key-agreement key -- the recipient whose wrap survives the
|
|
109
|
+
* rotation, reading the fresh key back and unwrapping the generations for
|
|
110
|
+
* the fan-out
|
|
111
|
+
* @param [options.userKey] {UserKey} this client's cached user key
|
|
112
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
113
|
+
* roster epoch
|
|
114
|
+
* @param [options.onUserKeyAdopted] {Function} persists a rotated key:
|
|
115
|
+
* called with `{ userKey, latestEpochId, descriptor }` after the roster
|
|
116
|
+
* read and BEFORE the fan-out
|
|
117
|
+
* @param options.collections {CascadeCollections} the fan-out's work
|
|
118
|
+
* @returns {Promise<DurableClientForgetResult>}
|
|
119
|
+
*/
|
|
120
|
+
export declare function forgetDurableClient({ logStore, ladderSeed, forgottenClient, forgottenKeyAgreementKeyMultibase, knownLatentHashes, expectedDid, rosterStore, credentialKeyAgreementKey, userKey, pinnedEpochId, onUserKeyAdopted, collections }: {
|
|
121
|
+
logStore: UnlockLogStore;
|
|
122
|
+
ladderSeed: Uint8Array;
|
|
123
|
+
forgottenClient: RevokedClientKeys;
|
|
124
|
+
forgottenKeyAgreementKeyMultibase: string;
|
|
125
|
+
knownLatentHashes?: string[];
|
|
126
|
+
expectedDid: string;
|
|
127
|
+
rosterStore: EncryptionDescriptorStore;
|
|
128
|
+
credentialKeyAgreementKey: IKeyAgreementKey;
|
|
129
|
+
userKey?: UserKey;
|
|
130
|
+
pinnedEpochId?: string | null;
|
|
131
|
+
onUserKeyAdopted?: (adopted: {
|
|
132
|
+
userKey: UserKey;
|
|
133
|
+
latestEpochId: string;
|
|
134
|
+
descriptor: CollectionEncryption;
|
|
135
|
+
}) => Promise<void>;
|
|
136
|
+
collections: CascadeCollections;
|
|
137
|
+
}): Promise<DurableClientForgetResult>;
|
|
138
|
+
//# sourceMappingURL=forget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/clientAnnex/forget.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAGxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAKL,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAA;AAKzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,oBAAoB,CAAA;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,QAAQ,EACR,UAAU,EACV,eAAe,EACf,iCAAiC,EACjC,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,yBAAyB,EACzB,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,WAAW,EACZ,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,iBAAiB,CAAA;IAClC,iCAAiC,EAAE,MAAM,CAAA;IACzC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,yBAAyB,CAAA;IACtC,yBAAyB,EAAE,gBAAgB,CAAA;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC3B,OAAO,EAAE,OAAO,CAAA;QAChB,aAAa,EAAE,MAAM,CAAA;QACrB,UAAU,EAAE,oBAAoB,CAAA;KACjC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnB,WAAW,EAAE,kBAAkB,CAAA;CAChC,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAoGrC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { readPublishedLog, relationIds } from '../webvh/didWebvh.js';
|
|
2
|
+
import { cascadeCollectionsToUserKey, readUserKeyRoster, rosterRecipientKid, rotateUserKeyRoster } from '../keys/index.js';
|
|
3
|
+
import { forgetWebvhClient, LastDurableClientForgetError } from './ladderAnchored.js';
|
|
4
|
+
/**
|
|
5
|
+
* Forgets one enrolled durable client -- this browser's own -- from its
|
|
6
|
+
* account. See the module doc for the stage order (rotation and fan-out
|
|
7
|
+
* BEFORE the removal entry, the self-forget inversion) and the torn-state
|
|
8
|
+
* map. The caller runs the local wipe only after this resolves.
|
|
9
|
+
*
|
|
10
|
+
* @param options {object}
|
|
11
|
+
* @param options.logStore {UnlockLogStore} the credential's delegated
|
|
12
|
+
* `did.jsonl` bridge store; also serves the ceremony's public pre-edit
|
|
13
|
+
* read. The caller is expected to have verified the account log under its
|
|
14
|
+
* own chain-head pins in the same session (the ceremony re-reads through
|
|
15
|
+
* the seam and checks `expectedDid`, but carries no pin store itself)
|
|
16
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
17
|
+
* @param options.forgottenClient {RevokedClientKeys} this client's public
|
|
18
|
+
* halves; an `updateKeyMultibase` the log does not authorize (stale, or the
|
|
19
|
+
* staged key) is re-derived from the log
|
|
20
|
+
* @param options.forgottenKeyAgreementKeyMultibase {string} this client's
|
|
21
|
+
* identity key-agreement key (the X25519 twin), naming its roster wrap
|
|
22
|
+
* @param [options.knownLatentHashes] {string[]} standing latent commitments
|
|
23
|
+
* the caller vouches for (the recovery registry's update-key hashes),
|
|
24
|
+
* excluded from the staged-hash attribution
|
|
25
|
+
* @param options.expectedDid {string} the account DID from the caller's
|
|
26
|
+
* stored account pointer
|
|
27
|
+
* @param options.rosterStore {EncryptionDescriptorStore} the
|
|
28
|
+
* `key-map/user-key.jsonl` roster store, signing as the forgetting client
|
|
29
|
+
* @param options.credentialKeyAgreementKey {IKeyAgreementKey} the standing
|
|
30
|
+
* credential's key-agreement key -- the recipient whose wrap survives the
|
|
31
|
+
* rotation, reading the fresh key back and unwrapping the generations for
|
|
32
|
+
* the fan-out
|
|
33
|
+
* @param [options.userKey] {UserKey} this client's cached user key
|
|
34
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
35
|
+
* roster epoch
|
|
36
|
+
* @param [options.onUserKeyAdopted] {Function} persists a rotated key:
|
|
37
|
+
* called with `{ userKey, latestEpochId, descriptor }` after the roster
|
|
38
|
+
* read and BEFORE the fan-out
|
|
39
|
+
* @param options.collections {CascadeCollections} the fan-out's work
|
|
40
|
+
* @returns {Promise<DurableClientForgetResult>}
|
|
41
|
+
*/
|
|
42
|
+
export async function forgetDurableClient({ logStore, ladderSeed, forgottenClient, forgottenKeyAgreementKeyMultibase, knownLatentHashes, expectedDid, rosterStore, credentialKeyAgreementKey, userKey, pinnedEpochId, onUserKeyAdopted, collections }) {
|
|
43
|
+
// The pre-edit read, doing double duty: the last-client refusal must fire
|
|
44
|
+
// BEFORE the rotation (or a refused forget would already have retired this
|
|
45
|
+
// client's wrap), and the rotation's recipient resolver needs the pre-edit
|
|
46
|
+
// document (which still keys every other recipient).
|
|
47
|
+
const published = await readPublishedLog({
|
|
48
|
+
// readPublishedLog only calls getIdResourceRaw, so the narrow seam is
|
|
49
|
+
// safe.
|
|
50
|
+
idStore: logStore,
|
|
51
|
+
expectedDid
|
|
52
|
+
});
|
|
53
|
+
if (!published) {
|
|
54
|
+
throw new Error('did:webvh: did.jsonl is missing; nothing to forget from.');
|
|
55
|
+
}
|
|
56
|
+
const signingVmId = `${published.did}#${forgottenClient.signingKeyMultibase}`;
|
|
57
|
+
const invocationIds = relationIds(published.doc.capabilityInvocation);
|
|
58
|
+
if (invocationIds.includes(signingVmId) &&
|
|
59
|
+
invocationIds.every(id => id === signingVmId)) {
|
|
60
|
+
throw new LastDurableClientForgetError();
|
|
61
|
+
}
|
|
62
|
+
// Stages 1 and 2: the roster rotation off this client's wrap and the
|
|
63
|
+
// collection fan-out, both under this client's still-standing authority.
|
|
64
|
+
let rotated = false;
|
|
65
|
+
let read;
|
|
66
|
+
let cascade = { outcomes: {}, failed: [] };
|
|
67
|
+
const current = await rosterStore.read();
|
|
68
|
+
if (current !== null) {
|
|
69
|
+
const forgottenKid = rosterRecipientKid({
|
|
70
|
+
signingKeyMultibase: forgottenClient.signingKeyMultibase,
|
|
71
|
+
keyAgreementKeyMultibase: forgottenKeyAgreementKeyMultibase
|
|
72
|
+
});
|
|
73
|
+
let descriptor = current.descriptor;
|
|
74
|
+
const currentEpoch = (descriptor.epochs ?? []).find(epoch => epoch.id === descriptor.currentEpoch);
|
|
75
|
+
const wrapped = currentEpoch?.recipients.some(entry => entry.header.kid === forgottenKid);
|
|
76
|
+
if (wrapped) {
|
|
77
|
+
descriptor = await rotateUserKeyRoster({
|
|
78
|
+
store: rosterStore,
|
|
79
|
+
document: published.doc,
|
|
80
|
+
retireRecipientId: forgottenKid
|
|
81
|
+
});
|
|
82
|
+
rotated = true;
|
|
83
|
+
}
|
|
84
|
+
// The fresh key comes back through the credential's standing wrap -- this
|
|
85
|
+
// client's own is gone from the current epoch -- with the continuity and
|
|
86
|
+
// possession checks still running on the threaded descriptor.
|
|
87
|
+
const adopted = await readUserKeyRoster({
|
|
88
|
+
store: rosterStore,
|
|
89
|
+
descriptor,
|
|
90
|
+
...(userKey ? { userKey } : {}),
|
|
91
|
+
clientKeyAgreementKey: credentialKeyAgreementKey,
|
|
92
|
+
pinnedEpochId
|
|
93
|
+
});
|
|
94
|
+
if (adopted.rotated) {
|
|
95
|
+
await onUserKeyAdopted?.({
|
|
96
|
+
userKey: adopted.userKey,
|
|
97
|
+
latestEpochId: adopted.latestEpochId,
|
|
98
|
+
descriptor: adopted.descriptor
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
read = { userKey: adopted.userKey, descriptor: adopted.descriptor };
|
|
102
|
+
cascade = await cascadeCollectionsToUserKey({
|
|
103
|
+
collectionIds: typeof collections.collectionIds === 'function'
|
|
104
|
+
? await collections.collectionIds()
|
|
105
|
+
: collections.collectionIds,
|
|
106
|
+
storeFor: collections.storeFor,
|
|
107
|
+
...(collections.isEncrypted
|
|
108
|
+
? { isEncrypted: collections.isEncrypted }
|
|
109
|
+
: {}),
|
|
110
|
+
rosterDescriptor: adopted.descriptor,
|
|
111
|
+
clientKeyAgreementKey: credentialKeyAgreementKey,
|
|
112
|
+
userKey: adopted.userKey
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Stage 3: the atomic ladder-signed removal entry, through the bridge.
|
|
116
|
+
const removed = await forgetWebvhClient({
|
|
117
|
+
store: logStore,
|
|
118
|
+
ladderSeed,
|
|
119
|
+
forgottenClient,
|
|
120
|
+
...(knownLatentHashes ? { knownLatentHashes } : {}),
|
|
121
|
+
expectedDid
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
rotated,
|
|
125
|
+
collections: cascade,
|
|
126
|
+
did: removed.did,
|
|
127
|
+
document: removed.doc,
|
|
128
|
+
...(read
|
|
129
|
+
? { userKey: read.userKey, rosterDescriptor: read.descriptor }
|
|
130
|
+
: {})
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=forget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forget.js","sourceRoot":"","sources":["../../src/clientAnnex/forget.ts"],"names":[],"mappings":"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"}
|