@interop/wallet-core 0.61.0 → 0.65.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 +41 -16
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +46 -26
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -1
- package/dist/clientAnnex/credentialAnchoredGenesis.js +91 -39
- package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -1
- package/dist/clientAnnex/establish.d.ts +55 -34
- package/dist/clientAnnex/establish.d.ts.map +1 -1
- package/dist/clientAnnex/establish.js +102 -56
- package/dist/clientAnnex/establish.js.map +1 -1
- package/dist/clientAnnex/forget.d.ts +16 -2
- package/dist/clientAnnex/forget.d.ts.map +1 -1
- package/dist/clientAnnex/forget.js +11 -9
- package/dist/clientAnnex/forget.js.map +1 -1
- package/dist/clientAnnex/forgetLast.d.ts +126 -35
- package/dist/clientAnnex/forgetLast.d.ts.map +1 -1
- package/dist/clientAnnex/forgetLast.js +208 -60
- package/dist/clientAnnex/forgetLast.js.map +1 -1
- package/dist/clientAnnex/gc.d.ts +3 -2
- package/dist/clientAnnex/gc.d.ts.map +1 -1
- package/dist/clientAnnex/gc.js +7 -7
- package/dist/clientAnnex/gc.js.map +1 -1
- package/dist/clientAnnex/heal.d.ts +101 -33
- package/dist/clientAnnex/heal.d.ts.map +1 -1
- package/dist/clientAnnex/heal.js +559 -212
- package/dist/clientAnnex/heal.js.map +1 -1
- package/dist/clientAnnex/index.d.ts +21 -5
- package/dist/clientAnnex/index.d.ts.map +1 -1
- package/dist/clientAnnex/index.js +24 -5
- package/dist/clientAnnex/index.js.map +1 -1
- package/dist/clientAnnex/ladder.d.ts +304 -18
- package/dist/clientAnnex/ladder.d.ts.map +1 -1
- package/dist/clientAnnex/ladder.js +942 -64
- package/dist/clientAnnex/ladder.js.map +1 -1
- package/dist/clientAnnex/ladderAnchored.d.ts +261 -28
- package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/ladderAnchored.js +521 -295
- package/dist/clientAnnex/ladderAnchored.js.map +1 -1
- package/dist/clientAnnex/log.d.ts +193 -38
- package/dist/clientAnnex/log.d.ts.map +1 -1
- package/dist/clientAnnex/log.js +445 -190
- package/dist/clientAnnex/log.js.map +1 -1
- package/dist/clientAnnex/mend.d.ts +12 -6
- package/dist/clientAnnex/mend.d.ts.map +1 -1
- package/dist/clientAnnex/mend.js +26 -12
- package/dist/clientAnnex/mend.js.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts +38 -11
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.js +177 -55
- package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -1
- package/dist/clientAnnex/spaceCapability.d.ts +123 -0
- package/dist/clientAnnex/spaceCapability.d.ts.map +1 -0
- package/dist/clientAnnex/spaceCapability.js +152 -0
- package/dist/clientAnnex/spaceCapability.js.map +1 -0
- package/dist/clientAnnex/stages.d.ts +63 -0
- package/dist/clientAnnex/stages.d.ts.map +1 -0
- package/dist/clientAnnex/stages.js +64 -0
- package/dist/clientAnnex/stages.js.map +1 -0
- package/dist/clientAnnex/zcap.d.ts +1 -1
- package/dist/clientAnnex/zcap.d.ts.map +1 -1
- package/dist/clientAnnex/zcap.js +42 -35
- package/dist/clientAnnex/zcap.js.map +1 -1
- package/dist/clients/policy.d.ts +15 -1
- package/dist/clients/policy.d.ts.map +1 -1
- package/dist/clients/policy.js +12 -6
- package/dist/clients/policy.js.map +1 -1
- package/dist/clients/revocation.d.ts +18 -10
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/revocation.js +13 -5
- package/dist/clients/revocation.js.map +1 -1
- package/dist/clients/rosterPolicy.d.ts +10 -2
- package/dist/clients/rosterPolicy.d.ts.map +1 -1
- package/dist/clients/rosterPolicy.js +41 -24
- package/dist/clients/rosterPolicy.js.map +1 -1
- package/dist/descriptors/acquire.d.ts.map +1 -1
- package/dist/descriptors/acquire.js +6 -23
- package/dist/descriptors/acquire.js.map +1 -1
- package/dist/descriptors/cipher.d.ts.map +1 -1
- package/dist/descriptors/cipher.js +5 -0
- package/dist/descriptors/cipher.js.map +1 -1
- package/dist/descriptors/errors.d.ts +43 -0
- package/dist/descriptors/errors.d.ts.map +1 -0
- package/dist/descriptors/errors.js +45 -0
- package/dist/descriptors/errors.js.map +1 -0
- package/dist/descriptors/index.d.ts +5 -0
- package/dist/descriptors/index.d.ts.map +1 -1
- package/dist/descriptors/index.js +5 -0
- package/dist/descriptors/index.js.map +1 -1
- package/dist/enrollment/enrollment.d.ts +37 -10
- package/dist/enrollment/enrollment.d.ts.map +1 -1
- package/dist/enrollment/enrollment.js +52 -15
- package/dist/enrollment/enrollment.js.map +1 -1
- package/dist/genesis/accountGenesis.d.ts +42 -11
- package/dist/genesis/accountGenesis.d.ts.map +1 -1
- package/dist/genesis/accountGenesis.js +80 -22
- package/dist/genesis/accountGenesis.js.map +1 -1
- package/dist/genesis/index.d.ts +3 -1
- package/dist/genesis/index.d.ts.map +1 -1
- package/dist/genesis/index.js +3 -1
- package/dist/genesis/index.js.map +1 -1
- package/dist/identity/agents.d.ts +17 -1
- package/dist/identity/agents.d.ts.map +1 -1
- package/dist/identity/agents.js +24 -8
- package/dist/identity/agents.js.map +1 -1
- package/dist/identity/index.d.ts +3 -1
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/identity/index.js +3 -1
- package/dist/identity/index.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/keyring/index.d.ts +2 -1
- package/dist/keyring/index.d.ts.map +1 -1
- package/dist/keyring/index.js +2 -1
- package/dist/keyring/index.js.map +1 -1
- package/dist/keyring/record.d.ts.map +1 -1
- package/dist/keyring/record.js +3 -6
- package/dist/keyring/record.js.map +1 -1
- package/dist/keyring/unlockSpace.d.ts +14 -5
- package/dist/keyring/unlockSpace.d.ts.map +1 -1
- package/dist/keyring/unlockSpace.js +31 -36
- package/dist/keyring/unlockSpace.js.map +1 -1
- package/dist/keys/index.d.ts +10 -6
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +9 -5
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/rosterLogStore.d.ts +12 -11
- package/dist/keys/rosterLogStore.d.ts.map +1 -1
- package/dist/keys/rosterLogStore.js +15 -13
- package/dist/keys/rosterLogStore.js.map +1 -1
- package/dist/keys/rosterStore.d.ts +3 -6
- package/dist/keys/rosterStore.d.ts.map +1 -1
- package/dist/keys/rosterStore.js +10 -9
- package/dist/keys/rosterStore.js.map +1 -1
- package/dist/keys/spaceEpochs.d.ts.map +1 -1
- package/dist/keys/spaceEpochs.js +2 -3
- package/dist/keys/spaceEpochs.js.map +1 -1
- package/dist/keys/userKeyRoster.d.ts +89 -9
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +262 -19
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/keys/userKeyRosterCascade.d.ts +7 -6
- package/dist/keys/userKeyRosterCascade.d.ts.map +1 -1
- package/dist/keys/userKeyRosterCascade.js +7 -6
- package/dist/keys/userKeyRosterCascade.js.map +1 -1
- package/dist/keys/wasLabelsStore.d.ts +9 -2
- package/dist/keys/wasLabelsStore.d.ts.map +1 -1
- package/dist/keys/wasLabelsStore.js +14 -6
- package/dist/keys/wasLabelsStore.js.map +1 -1
- package/dist/log.d.ts +7 -2
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +6 -1
- package/dist/log.js.map +1 -1
- package/dist/recovery/index.d.ts +12 -8
- package/dist/recovery/index.d.ts.map +1 -1
- package/dist/recovery/index.js +11 -7
- package/dist/recovery/index.js.map +1 -1
- package/dist/recovery/recoveryCode.d.ts +27 -7
- package/dist/recovery/recoveryCode.d.ts.map +1 -1
- package/dist/recovery/recoveryCode.js +18 -6
- package/dist/recovery/recoveryCode.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +4 -1
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
- package/dist/recovery/recoveryDelegation.js +25 -36
- package/dist/recovery/recoveryDelegation.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts +117 -65
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +279 -126
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/request/classify.d.ts +32 -8
- package/dist/request/classify.d.ts.map +1 -1
- package/dist/request/classify.js +39 -14
- package/dist/request/classify.js.map +1 -1
- package/dist/request/ephemeralExchange.d.ts +1 -6
- package/dist/request/ephemeralExchange.d.ts.map +1 -1
- package/dist/request/ephemeralExchange.js.map +1 -1
- package/dist/request/onboarding.d.ts.map +1 -1
- package/dist/request/onboarding.js +2 -2
- package/dist/request/onboarding.js.map +1 -1
- package/dist/request/parse.d.ts.map +1 -1
- package/dist/request/parse.js +2 -3
- package/dist/request/parse.js.map +1 -1
- package/dist/resourceLog/controller.d.ts +34 -12
- package/dist/resourceLog/controller.d.ts.map +1 -1
- package/dist/resourceLog/controller.js +79 -86
- package/dist/resourceLog/controller.js.map +1 -1
- package/dist/resourceLog/document.d.ts +182 -0
- package/dist/resourceLog/document.d.ts.map +1 -0
- package/dist/resourceLog/document.js +159 -0
- package/dist/resourceLog/document.js.map +1 -0
- package/dist/resourceLog/errors.d.ts +47 -8
- package/dist/resourceLog/errors.d.ts.map +1 -1
- package/dist/resourceLog/errors.js +54 -8
- package/dist/resourceLog/errors.js.map +1 -1
- package/dist/resourceLog/index.d.ts +7 -3
- package/dist/resourceLog/index.d.ts.map +1 -1
- package/dist/resourceLog/index.js +7 -3
- package/dist/resourceLog/index.js.map +1 -1
- package/dist/resourceLog/ladderRungs.d.ts +35 -0
- package/dist/resourceLog/ladderRungs.d.ts.map +1 -0
- package/dist/resourceLog/ladderRungs.js +352 -0
- package/dist/resourceLog/ladderRungs.js.map +1 -0
- package/dist/resourceLog/license.d.ts +42 -17
- package/dist/resourceLog/license.d.ts.map +1 -1
- package/dist/resourceLog/license.js +38 -24
- package/dist/resourceLog/license.js.map +1 -1
- package/dist/space/activity.d.ts +15 -15
- package/dist/space/activity.d.ts.map +1 -1
- package/dist/space/activity.js +15 -15
- package/dist/space/activity.js.map +1 -1
- package/dist/space/collections.d.ts +11 -0
- package/dist/space/collections.d.ts.map +1 -1
- package/dist/space/collections.js +13 -0
- package/dist/space/collections.js.map +1 -1
- package/dist/space/deleteSpace.d.ts +28 -0
- package/dist/space/deleteSpace.d.ts.map +1 -0
- package/dist/space/deleteSpace.js +44 -0
- package/dist/space/deleteSpace.js.map +1 -0
- package/dist/space/errors.d.ts.map +1 -1
- package/dist/space/errors.js +0 -1
- package/dist/space/errors.js.map +1 -1
- package/dist/space/index.d.ts +6 -0
- package/dist/space/index.d.ts.map +1 -1
- package/dist/space/index.js +6 -0
- package/dist/space/index.js.map +1 -1
- package/dist/space/plaintextCollection.d.ts +43 -0
- package/dist/space/plaintextCollection.d.ts.map +1 -0
- package/dist/space/plaintextCollection.js +17 -0
- package/dist/space/plaintextCollection.js.map +1 -0
- package/dist/stages.d.ts +23 -0
- package/dist/stages.d.ts.map +1 -0
- package/dist/stages.js +23 -0
- package/dist/stages.js.map +1 -0
- package/dist/sync/index.d.ts +7 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +7 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/push.js +4 -4
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/remint.js +2 -2
- package/dist/sync/remint.js.map +1 -1
- package/dist/sync/types.d.ts +41 -1
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +47 -1
- package/dist/sync/types.js.map +1 -1
- package/dist/unlock/index.d.ts +6 -2
- package/dist/unlock/index.d.ts.map +1 -1
- package/dist/unlock/index.js +5 -1
- package/dist/unlock/index.js.map +1 -1
- package/dist/unlock/retire.d.ts +95 -14
- package/dist/unlock/retire.d.ts.map +1 -1
- package/dist/unlock/retire.js +102 -10
- package/dist/unlock/retire.js.map +1 -1
- package/dist/unlock/standingClient.d.ts.map +1 -1
- package/dist/unlock/standingClient.js +5 -1
- package/dist/unlock/standingClient.js.map +1 -1
- package/dist/unlock/standingWebvh.d.ts +341 -49
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +608 -164
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/webvh/accountEntry.d.ts +146 -0
- package/dist/webvh/accountEntry.d.ts.map +1 -0
- package/dist/webvh/accountEntry.js +239 -0
- package/dist/webvh/accountEntry.js.map +1 -0
- package/dist/webvh/didWeb.d.ts +12 -7
- package/dist/webvh/didWeb.d.ts.map +1 -1
- package/dist/webvh/didWeb.js +2 -2
- package/dist/webvh/didWeb.js.map +1 -1
- package/dist/webvh/didWebProjection.d.ts +164 -0
- package/dist/webvh/didWebProjection.d.ts.map +1 -0
- package/dist/webvh/didWebProjection.js +230 -0
- package/dist/webvh/didWebProjection.js.map +1 -0
- package/dist/webvh/didWebvh.d.ts +278 -138
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +314 -345
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/enrollClient.d.ts +65 -0
- package/dist/webvh/enrollClient.d.ts.map +1 -0
- package/dist/webvh/enrollClient.js +172 -0
- package/dist/webvh/enrollClient.js.map +1 -0
- package/dist/webvh/index.d.ts +37 -12
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +33 -10
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/listClients.d.ts +1 -33
- package/dist/webvh/listClients.d.ts.map +1 -1
- package/dist/webvh/listClients.js +2 -28
- package/dist/webvh/listClients.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +89 -11
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +182 -47
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/standingZcap.d.ts +61 -14
- package/dist/webvh/standingZcap.d.ts.map +1 -1
- package/dist/webvh/standingZcap.js +91 -0
- package/dist/webvh/standingZcap.js.map +1 -1
- package/dist/webvh/verifyLog.d.ts +59 -5
- package/dist/webvh/verifyLog.d.ts.map +1 -1
- package/dist/webvh/verifyLog.js +76 -11
- package/dist/webvh/verifyLog.js.map +1 -1
- package/dist/webvh/wasIdStore.d.ts +8 -8
- package/dist/webvh/wasIdStore.d.ts.map +1 -1
- package/dist/webvh/wasIdStore.js +39 -14
- package/dist/webvh/wasIdStore.js.map +1 -1
- package/dist/webvh/zcap.d.ts +14 -1
- package/dist/webvh/zcap.d.ts.map +1 -1
- package/dist/webvh/zcap.js +3 -27
- package/dist/webvh/zcap.js.map +1 -1
- package/package.json +5 -5
- package/dist/webvh/keyAgreement.d.ts +0 -72
- package/dist/webvh/keyAgreement.d.ts.map +0 -1
- package/dist/webvh/keyAgreement.js +0 -47
- package/dist/webvh/keyAgreement.js.map +0 -1
package/dist/clientAnnex/heal.js
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import { WasClient } from '@interop/was-client';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { spacePath } from '@interop/was-client/paths';
|
|
3
|
+
import { currentLogParameters, readPublishedLog, readPublishedLogOrThrow, WebvhLogConflictError, withLogConflictRetry } from '../webvh/didWebvh.js';
|
|
4
|
+
import { ladderVmIds } from '../resourceLog/document.js';
|
|
5
|
+
import { standingZcapStale } from '../webvh/standingZcap.js';
|
|
6
|
+
import { delegateLogWrite } from '../recovery/recoveryDelegation.js';
|
|
5
7
|
import { accountLogPinId } from '../webvh/verifyLog.js';
|
|
6
8
|
import { mintSpaceId } from '../genesis/accountGenesis.js';
|
|
7
9
|
import { attributeLadderRung, ladderRung, ladderVmKeyMultibase } from './ladder.js';
|
|
10
|
+
import { revealLadderRungWebvh } from './ladderAnchored.js';
|
|
8
11
|
import { ladderVmAgent, ladderVmZcapClient } from './zcap.js';
|
|
9
|
-
import {
|
|
12
|
+
import { mintSpaceRootVerbCapability } from './spaceCapability.js';
|
|
13
|
+
import { clientAnnexDidParts, clientAnnexLogPinId, clientAnnexLogStore, delegatedClientsDelegationSpaceId, delegatedClientsPointer, ensureClientAnnexSpace, ensureGenerationDelegationCurrent, mintCredentialClientAnnexGeneration, mintDelegatedClientsDelegation, mintGenerationDelegation, setDelegatedClientsPointerOnce } from './log.js';
|
|
14
|
+
/**
|
|
15
|
+
* The HTTP status a raw signed request's rejection carries, when it carries
|
|
16
|
+
* one. `WasClient.request` applies no error mapping, so the status is all a
|
|
17
|
+
* caller has to dispatch on, and different transports hang it in different
|
|
18
|
+
* places (`status`, or `response.status`).
|
|
19
|
+
*
|
|
20
|
+
* @param err {unknown}
|
|
21
|
+
* @returns {number | undefined}
|
|
22
|
+
*/
|
|
23
|
+
function rawRequestStatus(err) {
|
|
24
|
+
const raw = err;
|
|
25
|
+
const status = raw?.status ?? raw?.response?.status;
|
|
26
|
+
return typeof status === 'number' ? status : undefined;
|
|
27
|
+
}
|
|
10
28
|
/**
|
|
11
29
|
* The typed refusal of {@link ensureCredentialClientAnnexGeneration}: the
|
|
12
30
|
* account is not in a shape this visit can mend, and nothing was written.
|
|
@@ -81,13 +99,18 @@ export function ladderSignedGenerationDelegationMinter({ accountDid, ladderSeed,
|
|
|
81
99
|
* -- the storage client for the fresh-Space arm, signing as the ladder
|
|
82
100
|
* VM's bare did:key (the caller wires the transport; the agent is derived
|
|
83
101
|
* here from the ladder seed)
|
|
84
|
-
* @param options.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
102
|
+
* @param options.delegation {IZcap} the record's bridge delegation (PUT on
|
|
103
|
+
* the account's `did.jsonl`), renewed here when it is stale
|
|
104
|
+
* @param options.idStoreFor {Function}
|
|
105
|
+
* `({ delegation }) => WebvhIdStore` -- builds the ACCOUNT log's store over
|
|
106
|
+
* the usable bridge (a bridge-delegated store suffices: pointer entries
|
|
107
|
+
* publish with `logOnly: true`). Called once, after the bridge renewal, so
|
|
108
|
+
* a pointer entry never rides a lapsed delegation
|
|
87
109
|
* @param options.onRebindRecord {Function}
|
|
88
|
-
* `({ delegatedClients }) => Promise<void>` -- REQUIRED:
|
|
89
|
-
* unlock record with the
|
|
90
|
-
*
|
|
110
|
+
* `({ delegation, delegatedClients }) => Promise<void>` -- REQUIRED:
|
|
111
|
+
* re-seals the unlock record with the usable bridge and sibling
|
|
112
|
+
* delegations; called whenever either was freshly minted, after the
|
|
113
|
+
* generation and pointer are durable
|
|
91
114
|
* @param [options.delegatedClients] {IZcap} the record's sibling
|
|
92
115
|
* delegation, when the record carries one
|
|
93
116
|
* @param [options.pinStore] {ResourceLogPinStore} chain-head pins (a
|
|
@@ -101,7 +124,8 @@ export function ensureCredentialClientAnnexGeneration(options) {
|
|
|
101
124
|
// into the record would strand the very credential this ensure serves.
|
|
102
125
|
if (typeof options.onRebindRecord !== 'function') {
|
|
103
126
|
throw new TypeError('ensureCredentialClientAnnexGeneration requires onRebindRecord: a ' +
|
|
104
|
-
'fresh sibling delegation must be re-sealed into the
|
|
127
|
+
'fresh bridge or sibling delegation must be re-sealed into the ' +
|
|
128
|
+
'unlock record.');
|
|
105
129
|
}
|
|
106
130
|
return ensureCredentialClientAnnexGenerationChecked(options);
|
|
107
131
|
}
|
|
@@ -111,14 +135,17 @@ export function ensureCredentialClientAnnexGeneration(options) {
|
|
|
111
135
|
* @param options {object} see {@link ensureCredentialClientAnnexGeneration}
|
|
112
136
|
* @returns {Promise<ClientAnnexGenerationEnsureOutcome>}
|
|
113
137
|
*/
|
|
114
|
-
async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spaceId, account, ladderSeed, standingClient, bootstrapWasFor,
|
|
138
|
+
async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spaceId, account, ladderSeed, standingClient, bootstrapWasFor, delegation, idStoreFor, onRebindRecord, delegatedClients, pinStore, now }) {
|
|
115
139
|
// The gate: everything below signs as the ladder (the delegations as the
|
|
116
140
|
// ladder VM, the annex entries as its per-generation rung), so unless THIS
|
|
117
141
|
// ladder's VM stands in the document under the ladder-VM relation
|
|
118
142
|
// asymmetry (`capabilityDelegation` without `capabilityInvocation` -- the
|
|
119
143
|
// authority the mends actually exercise, which mere key presence says
|
|
120
144
|
// nothing about), every mend is unverifiable. The honest refusal, before
|
|
121
|
-
// anything is written.
|
|
145
|
+
// anything is written. A standing credential's VM stands whether or not the
|
|
146
|
+
// account has enrolled clients, so this fires only for a document that
|
|
147
|
+
// never carried it -- a torn establishment, or another credential's
|
|
148
|
+
// account.
|
|
122
149
|
const vmKey = await ladderVmKeyMultibase({ ladderSeed });
|
|
123
150
|
if (!ladderVmIds({ doc: account.doc }).includes(`${account.did}#${vmKey}`)) {
|
|
124
151
|
throw new ClientAnnexGenerationUnavailableError({
|
|
@@ -144,231 +171,420 @@ async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spac
|
|
|
144
171
|
spaceId,
|
|
145
172
|
...(now !== undefined ? { now } : {})
|
|
146
173
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
174
|
+
// THE BRIDGE RENEWAL, before any arm. The bridge is the credential's one
|
|
175
|
+
// write path into the account log, and both minting arms end in a pointer
|
|
176
|
+
// entry that rides it. A stale one is replaced with a ladder-VM-signed
|
|
177
|
+
// delegation (the gate above proved the VM a document verification
|
|
178
|
+
// method), and the caller's account-log store is built over whichever
|
|
179
|
+
// bridge is usable.
|
|
180
|
+
let bridgeReminted = false;
|
|
181
|
+
let usableBridge = delegation;
|
|
182
|
+
if (standingZcapStale({
|
|
183
|
+
zcap: delegation,
|
|
184
|
+
doc: account.doc,
|
|
185
|
+
...(now !== undefined ? { now } : {})
|
|
186
|
+
})) {
|
|
187
|
+
usableBridge = await delegateLogWrite({
|
|
188
|
+
zcapClient: ladderClient,
|
|
189
|
+
pointer: { did: account.did, spaceId, host: wasServerUrl },
|
|
190
|
+
recoveryClientDid: standingClient.did,
|
|
191
|
+
...(now !== undefined ? { now } : {})
|
|
154
192
|
});
|
|
193
|
+
bridgeReminted = true;
|
|
194
|
+
}
|
|
195
|
+
const idStore = idStoreFor({ delegation: usableBridge });
|
|
196
|
+
/**
|
|
197
|
+
* The record re-seal, through the required `onRebindRecord` seam: the one
|
|
198
|
+
* call site for every arm, run once the generation and pointer are
|
|
199
|
+
* durable. It fires whenever either recorded delegation was freshly
|
|
200
|
+
* minted, and grades a failure by which one that was. A fresh sibling
|
|
201
|
+
* nothing re-seals would strand the credential, so that throw propagates.
|
|
202
|
+
* A bridge-only renewal needs nothing from the re-seal: the fresh bridge
|
|
203
|
+
* is minted offline and already served this visit, so a lost re-seal
|
|
204
|
+
* leaves no wrong state behind and the next visit re-mints. It is reported
|
|
205
|
+
* on the outcome instead.
|
|
206
|
+
*
|
|
207
|
+
* @param options {object}
|
|
208
|
+
* @param options.delegatedClients {IZcap} the usable sibling delegation
|
|
209
|
+
* @param options.siblingReminted {boolean} whether that sibling is fresh
|
|
210
|
+
* @returns {Promise<object>} the outcome's `bridgeResealError` member,
|
|
211
|
+
* present only when a bridge-only re-seal failed
|
|
212
|
+
*/
|
|
213
|
+
async function resealRecord({ delegatedClients: usableSibling, siblingReminted }) {
|
|
214
|
+
if (!siblingReminted && !bridgeReminted) {
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
await onRebindRecord({
|
|
219
|
+
delegation: usableBridge,
|
|
220
|
+
delegatedClients: usableSibling
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
if (siblingReminted) {
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
return { bridgeResealError: err };
|
|
228
|
+
}
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The fresh-Space stage, controller-first past the create. The create
|
|
233
|
+
* itself must name the ladder VM's bare did:key: a storage server
|
|
234
|
+
* authorizes a Space create against the controller the request body names,
|
|
235
|
+
* and a root invocation must be signed by that very DID, so a create
|
|
236
|
+
* naming the account did:webvh is refused. The controller is flipped to
|
|
237
|
+
* the account DID in the very next request, before anything publishes into
|
|
238
|
+
* the Space.
|
|
239
|
+
*
|
|
240
|
+
* So the stranding window is one request wide. Inside it the stranded
|
|
241
|
+
* Space is did:key-controlled, which no server orphan sweep can reap,
|
|
242
|
+
* since a did:key resolves from its own bytes forever. Past the flip it is
|
|
243
|
+
* account-controlled and reapable. Narrowing the window to that one
|
|
244
|
+
* request is what this ordering buys; it does not close it.
|
|
245
|
+
*
|
|
246
|
+
* The flip preceding the generation mint is load-bearing rather than
|
|
247
|
+
* cosmetic. The mint and the delegation embed ride the ladder-signed
|
|
248
|
+
* sibling delegation, and the server admits that chain only once the
|
|
249
|
+
* Space's controller is the account DID whose document lists the ladder
|
|
250
|
+
* VM. Minting first and flipping afterwards would need the bootstrap key's
|
|
251
|
+
* own root invocations, which is the ordering this stage replaced.
|
|
252
|
+
*
|
|
253
|
+
* @returns {Promise<string>} the fresh auxiliary Space's id
|
|
254
|
+
*/
|
|
255
|
+
async function mintFreshAnnexSpace() {
|
|
155
256
|
const freshSpaceId = mintSpaceId();
|
|
156
257
|
const keyAgent = await ladderVmAgent({ ladderSeed });
|
|
157
258
|
const bootstrapWas = bootstrapWasFor({ keyAgent });
|
|
158
|
-
const
|
|
259
|
+
const created = await ensureClientAnnexSpace({
|
|
159
260
|
was: bootstrapWas,
|
|
160
|
-
wasServerUrl,
|
|
161
261
|
spaceId: freshSpaceId,
|
|
162
|
-
controller: keyAgent.id
|
|
163
|
-
|
|
262
|
+
controller: keyAgent.id
|
|
263
|
+
});
|
|
264
|
+
// The create's own answer is the current Description, so the flip carries
|
|
265
|
+
// it as `current` rather than re-reading what this call just wrote.
|
|
266
|
+
await bootstrapWas
|
|
267
|
+
.space(freshSpaceId)
|
|
268
|
+
.configure({ current: created, controller: account.did, force: true });
|
|
269
|
+
return freshSpaceId;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* One Space Description read, judged by its HTTP STATUS alone. A 404 is
|
|
273
|
+
* `'not-found'`; a 2xx is `'present'`, whatever its body says, since a
|
|
274
|
+
* Space served with an unreadable body is a present Space and reading it
|
|
275
|
+
* as absence is exactly what would re-point a live account. Every other
|
|
276
|
+
* answer -- a transport failure, a 5xx, a 4xx that is not 404 -- throws,
|
|
277
|
+
* so nothing but a real 404 can ever reach the absence decision.
|
|
278
|
+
*
|
|
279
|
+
* The read goes through the raw signed request rather than the
|
|
280
|
+
* `describe()` handle, whose null-on-404 translation also swallows 401 and
|
|
281
|
+
* 403 and an unparseable body.
|
|
282
|
+
*
|
|
283
|
+
* @param options {object}
|
|
284
|
+
* @param options.was {WasClient} the client whose signer invokes
|
|
285
|
+
* @param options.annexSpaceId {string}
|
|
286
|
+
* @param [options.capability] {IZcap} the attached capability; absent
|
|
287
|
+
* means a root invocation
|
|
288
|
+
* @returns {Promise<'present' | 'not-found'>}
|
|
289
|
+
*/
|
|
290
|
+
async function readSpaceDescription({ was, annexSpaceId, capability }) {
|
|
291
|
+
let status;
|
|
292
|
+
try {
|
|
293
|
+
const response = await was.request({
|
|
294
|
+
path: spacePath(annexSpaceId),
|
|
295
|
+
method: 'GET',
|
|
296
|
+
...(capability !== undefined ? { capability } : {})
|
|
297
|
+
});
|
|
298
|
+
status = response.status;
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
if (rawRequestStatus(err) === 404) {
|
|
302
|
+
return 'not-found';
|
|
303
|
+
}
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
if (status >= 200 && status < 300) {
|
|
307
|
+
return 'present';
|
|
308
|
+
}
|
|
309
|
+
if (status === 404) {
|
|
310
|
+
return 'not-found';
|
|
311
|
+
}
|
|
312
|
+
throw new Error(`client annex: the Space Description read for "${annexSpaceId}" ` +
|
|
313
|
+
`answered ${status}; the visit cannot tell whether the Space is gone.`);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Whether the auxiliary Space itself is gone from the server, asked only
|
|
317
|
+
* when the pointed generation's log did not read back. A storage server
|
|
318
|
+
* masks an unauthorized read as the same 404 an absent Space answers, so
|
|
319
|
+
* ONE 404 is not absence: it is "absent, or this reader has no authority
|
|
320
|
+
* here". Absence is therefore corroborated by two independent readers, and
|
|
321
|
+
* the Space is gone only when both answer a real 404.
|
|
322
|
+
*
|
|
323
|
+
* The first reader is a ladder-signed GET-only child of the Space's own
|
|
324
|
+
* root, which the account DID's document backs. The sibling delegation
|
|
325
|
+
* cannot carry the question: it targets the items subtree beneath the
|
|
326
|
+
* Space and says nothing about the Space Description.
|
|
327
|
+
*
|
|
328
|
+
* The second is a root invocation as the ladder VM's BARE did:key, the
|
|
329
|
+
* controller a torn establishment leaves behind when its flip never
|
|
330
|
+
* landed. That reader answers 2xx on exactly the Space the first reader
|
|
331
|
+
* has no authority over, so the pair covers both controllers a live annex
|
|
332
|
+
* Space of this credential's can have.
|
|
333
|
+
*
|
|
334
|
+
* The residual bound, stated because no read can close it: a server that
|
|
335
|
+
* does not admit the ladder delegation clause's single-verb predicate
|
|
336
|
+
* refuses the first probe with the same masked 404, and on an
|
|
337
|
+
* account-controlled Space the second probe is refused too. Both readers
|
|
338
|
+
* then say `'not-found'` about a live Space. That is a server older than
|
|
339
|
+
* the one this arm was built against, and the arm's cost there is a
|
|
340
|
+
* re-point of a live generation.
|
|
341
|
+
*
|
|
342
|
+
* @param options {object}
|
|
343
|
+
* @param options.annexSpaceId {string}
|
|
344
|
+
* @param options.was {WasClient} the standing client's storage client
|
|
345
|
+
* @returns {Promise<boolean>}
|
|
346
|
+
*/
|
|
347
|
+
async function annexSpaceAbsent({ annexSpaceId, was }) {
|
|
348
|
+
const probe = await mintSpaceRootVerbCapability({
|
|
349
|
+
zcapClient: ladderClient,
|
|
350
|
+
storageServerUrl: wasServerUrl,
|
|
351
|
+
spaceId: annexSpaceId,
|
|
352
|
+
verb: 'GET',
|
|
353
|
+
controller: standingClient.did,
|
|
354
|
+
...(now !== undefined ? { now } : {})
|
|
355
|
+
});
|
|
356
|
+
const delegated = await readSpaceDescription({
|
|
357
|
+
was,
|
|
358
|
+
annexSpaceId,
|
|
359
|
+
capability: probe
|
|
360
|
+
});
|
|
361
|
+
if (delegated === 'present') {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
const bootstrapWas = bootstrapWasFor({
|
|
365
|
+
keyAgent: await ladderVmAgent({ ladderSeed })
|
|
366
|
+
});
|
|
367
|
+
const asBootstrap = await readSpaceDescription({
|
|
368
|
+
was: bootstrapWas,
|
|
369
|
+
annexSpaceId
|
|
370
|
+
});
|
|
371
|
+
return asBootstrap === 'not-found';
|
|
372
|
+
}
|
|
373
|
+
let spaceMinted = false;
|
|
374
|
+
let pointedSpaceMissing = false;
|
|
375
|
+
/**
|
|
376
|
+
* The arms that run inside one auxiliary Space: the sibling delegation,
|
|
377
|
+
* the pointed generation's renewal, and the fresh-generation mint with its
|
|
378
|
+
* pointer move. Called once on the resolved Space, and once more on a
|
|
379
|
+
* fresh Space when the pointed one turns out to be gone -- the replacement
|
|
380
|
+
* is decided before any write, so nothing is stranded by the re-run.
|
|
381
|
+
*
|
|
382
|
+
* @param options {object}
|
|
383
|
+
* @param options.annexSpaceId {string} the Space this run works in
|
|
384
|
+
* @param options.allowSpaceReplacement {boolean} whether an absent
|
|
385
|
+
* pointed generation may be checked against the Space itself, and a
|
|
386
|
+
* fresh Space minted when the Space is gone. False on the run that
|
|
387
|
+
* already works in a Space this visit created
|
|
388
|
+
* @param [options.pointer] {string} the pointed annex DID, when the
|
|
389
|
+
* document names one and this run works in its Space
|
|
390
|
+
* @returns {Promise<ClientAnnexGenerationEnsureOutcome>}
|
|
391
|
+
*/
|
|
392
|
+
async function runInAnnexSpace({ annexSpaceId, allowSpaceReplacement, pointer }) {
|
|
393
|
+
// A usable sibling first: absent, targeting a different Space than this
|
|
394
|
+
// run works in, or stale on either of the standing-zcap axes -- expiry
|
|
395
|
+
// (past, or inside the renewal window) and signer death (its proof key
|
|
396
|
+
// no longer under `capabilityDelegation` in the verified account
|
|
397
|
+
// document, the current-key-set rule) -- a fresh sibling is minted
|
|
398
|
+
// (local ladder-VM signing, which verifies because the gate above proved
|
|
399
|
+
// the VM a document verification method) so every annex request below
|
|
400
|
+
// can ride it.
|
|
401
|
+
let sibling = delegatedClients;
|
|
402
|
+
let siblingReminted = false;
|
|
403
|
+
const siblingStale = sibling !== undefined &&
|
|
404
|
+
standingZcapStale({
|
|
405
|
+
zcap: sibling,
|
|
406
|
+
doc: account.doc,
|
|
407
|
+
...(now !== undefined ? { now } : {})
|
|
408
|
+
});
|
|
409
|
+
if (sibling === undefined ||
|
|
410
|
+
siblingSpaceId !== annexSpaceId ||
|
|
411
|
+
siblingStale) {
|
|
412
|
+
sibling = await mintDelegatedClientsDelegation({
|
|
413
|
+
zcapClient: ladderClient,
|
|
414
|
+
wasServerUrl,
|
|
415
|
+
clientAnnexSpaceId: annexSpaceId,
|
|
416
|
+
controller: standingClient.did,
|
|
417
|
+
...(now !== undefined ? { now } : {})
|
|
418
|
+
});
|
|
419
|
+
siblingReminted = true;
|
|
420
|
+
}
|
|
421
|
+
const usableSibling = sibling;
|
|
422
|
+
const standingWas = new WasClient({
|
|
423
|
+
serverUrl: wasServerUrl,
|
|
424
|
+
zcapClient: standingClient.zcapClient
|
|
425
|
+
});
|
|
426
|
+
const storeFor = (generationId) => clientAnnexLogStore({
|
|
427
|
+
was: standingWas,
|
|
428
|
+
spaceId: annexSpaceId,
|
|
429
|
+
generationId,
|
|
430
|
+
capability: usableSibling
|
|
431
|
+
});
|
|
432
|
+
const annexPin = (generationId) => pinStore !== undefined
|
|
433
|
+
? {
|
|
434
|
+
pinStore,
|
|
435
|
+
logId: clientAnnexLogPinId({ spaceId: annexSpaceId, generationId })
|
|
436
|
+
}
|
|
437
|
+
: {};
|
|
438
|
+
// RENEW PRECEDES MINT: a live, verifiable pointed generation is renewed
|
|
439
|
+
// in place; only a rung this generation never committed falls through to
|
|
440
|
+
// the fresh mint (the GC swap's no-committed-survivor escape).
|
|
441
|
+
if (pointer !== undefined) {
|
|
442
|
+
const parts = clientAnnexDidParts({ did: pointer });
|
|
443
|
+
const pointedLog = await readPublishedLog({
|
|
444
|
+
idStore: storeFor(parts.generationId),
|
|
445
|
+
expectedDid: pointer,
|
|
446
|
+
...annexPin(parts.generationId)
|
|
447
|
+
});
|
|
448
|
+
if (pointedLog !== undefined) {
|
|
449
|
+
try {
|
|
450
|
+
const ensured = await ensureGenerationDelegationCurrent({
|
|
451
|
+
store: storeFor(parts.generationId),
|
|
452
|
+
ladderSeed,
|
|
453
|
+
generationId: parts.generationId,
|
|
454
|
+
mintGenerationDelegation: mintDelegation,
|
|
455
|
+
expectedDid: pointer,
|
|
456
|
+
accountDoc: account.doc,
|
|
457
|
+
// The head just read: on a healthy account this stage spends no
|
|
458
|
+
// second round trip on the same log, and hands its head back for
|
|
459
|
+
// the enrollment to build on.
|
|
460
|
+
published: pointedLog,
|
|
461
|
+
...annexPin(parts.generationId),
|
|
462
|
+
...(now !== undefined ? { now } : {})
|
|
463
|
+
});
|
|
464
|
+
const resealed = await resealRecord({
|
|
465
|
+
delegatedClients: usableSibling,
|
|
466
|
+
siblingReminted
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
clientAnnexDid: pointer,
|
|
470
|
+
generationDelegation: ensured.delegation,
|
|
471
|
+
delegation: usableBridge,
|
|
472
|
+
delegatedClients: usableSibling,
|
|
473
|
+
generationMinted: false,
|
|
474
|
+
spaceMinted,
|
|
475
|
+
pointedSpaceMissing,
|
|
476
|
+
delegationRenewed: ensured.renewed,
|
|
477
|
+
siblingReminted,
|
|
478
|
+
bridgeReminted,
|
|
479
|
+
...(ensured.published !== undefined
|
|
480
|
+
? { generationLog: ensured.published }
|
|
481
|
+
: {}),
|
|
482
|
+
...resealed
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
if (err.name !==
|
|
487
|
+
'ClientAnnexRungUncommittedError') {
|
|
488
|
+
throw err;
|
|
489
|
+
}
|
|
490
|
+
// This credential's rung was never committed into the pointed
|
|
491
|
+
// generation (bound mid-generation): fall through to the fresh
|
|
492
|
+
// mint, which commits it with the fresh genesis.
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else if (allowSpaceReplacement &&
|
|
496
|
+
(await annexSpaceAbsent({ annexSpaceId, was: standingWas }))) {
|
|
497
|
+
// THE POINTED SPACE IS GONE. A missing generation log inside a live
|
|
498
|
+
// Space is the fresh-generation arm's case; a missing SPACE is not,
|
|
499
|
+
// since every write below would land in a Space that does not exist
|
|
500
|
+
// and the visit would fail on something other than the typed
|
|
501
|
+
// refusal. The Space is re-minted and the run starts over in it,
|
|
502
|
+
// before anything has been written.
|
|
503
|
+
await assertPointerEntryAttributable({ ladderSeed, log: account.log });
|
|
504
|
+
const freshSpaceId = await mintFreshAnnexSpace();
|
|
505
|
+
spaceMinted = true;
|
|
506
|
+
pointedSpaceMissing = true;
|
|
507
|
+
return runInAnnexSpace({
|
|
508
|
+
annexSpaceId: freshSpaceId,
|
|
509
|
+
allowSpaceReplacement: false
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// THE FRESH-GENERATION ARM: pre-flight attribution first (never mint a
|
|
514
|
+
// generation the pointer entry cannot then name), then the GC swap's
|
|
515
|
+
// stage order minus its revoke -- mint, install the delegation,
|
|
516
|
+
// re-point. The fresh genesis commits only the acting credential's annex
|
|
517
|
+
// rung; other standing credentials' per-generation rungs are re-committed
|
|
518
|
+
// only by their own later ceremonies (a property of every generation
|
|
519
|
+
// swap).
|
|
520
|
+
await assertPointerEntryAttributable({ ladderSeed, log: account.log });
|
|
521
|
+
const minted = await mintCredentialClientAnnexGeneration({
|
|
522
|
+
was: standingWas,
|
|
523
|
+
wasServerUrl,
|
|
524
|
+
spaceId: annexSpaceId,
|
|
525
|
+
controller: account.did,
|
|
526
|
+
ladderSeed,
|
|
527
|
+
capability: usableSibling
|
|
164
528
|
});
|
|
165
|
-
// The delegation embeds while the Space still answers to the bootstrap
|
|
166
|
-
// did:key; the controller flip follows, then the pointer entry.
|
|
167
529
|
const ensured = await ensureGenerationDelegationCurrent({
|
|
168
|
-
store:
|
|
169
|
-
was: bootstrapWas,
|
|
170
|
-
spaceId: freshSpaceId,
|
|
171
|
-
generationId: minted.generationId
|
|
172
|
-
}),
|
|
530
|
+
store: storeFor(minted.generationId),
|
|
173
531
|
ladderSeed,
|
|
174
532
|
generationId: minted.generationId,
|
|
175
533
|
mintGenerationDelegation: mintDelegation,
|
|
176
534
|
expectedDid: minted.did,
|
|
177
|
-
...(
|
|
178
|
-
? {
|
|
179
|
-
pinStore,
|
|
180
|
-
logId: clientAnnexLogPinId({
|
|
181
|
-
spaceId: freshSpaceId,
|
|
182
|
-
generationId: minted.generationId
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
: {}),
|
|
535
|
+
...annexPin(minted.generationId),
|
|
186
536
|
...(now !== undefined ? { now } : {})
|
|
187
537
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
538
|
+
// No revocation of the superseded generation's delegation is attempted:
|
|
539
|
+
// a transient visit has no reach that could invoke it (the standing
|
|
540
|
+
// client is neither the Space controller nor in that delegation's
|
|
541
|
+
// chain). The pointer move itself retires it on a conforming server --
|
|
542
|
+
// the inspector clause compares the delegation's controller against the
|
|
543
|
+
// document's pointer -- and it otherwise rots on its TTL.
|
|
544
|
+
await movePointerAsLadder({
|
|
192
545
|
idStore,
|
|
193
|
-
|
|
546
|
+
ladderSeed,
|
|
194
547
|
clientAnnexDid: minted.did,
|
|
195
|
-
|
|
196
|
-
logOnly: true,
|
|
548
|
+
accountDid: account.did,
|
|
197
549
|
...(pinStore !== undefined
|
|
198
550
|
? { pinStore, logId: accountLogPinId({ spaceId }) }
|
|
199
551
|
: {})
|
|
200
552
|
});
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
clientAnnexSpaceId: freshSpaceId,
|
|
205
|
-
controller: standingClient.did,
|
|
206
|
-
...(now !== undefined ? { now } : {})
|
|
553
|
+
const resealed = await resealRecord({
|
|
554
|
+
delegatedClients: usableSibling,
|
|
555
|
+
siblingReminted
|
|
207
556
|
});
|
|
208
|
-
await onRebindRecord({ delegatedClients: sibling });
|
|
209
557
|
return {
|
|
210
558
|
clientAnnexDid: minted.did,
|
|
211
559
|
generationDelegation: ensured.delegation,
|
|
212
|
-
|
|
560
|
+
delegation: usableBridge,
|
|
561
|
+
delegatedClients: usableSibling,
|
|
213
562
|
generationMinted: true,
|
|
214
|
-
spaceMinted
|
|
563
|
+
spaceMinted,
|
|
564
|
+
pointedSpaceMissing,
|
|
215
565
|
delegationRenewed: false,
|
|
216
|
-
siblingReminted
|
|
566
|
+
siblingReminted,
|
|
567
|
+
bridgeReminted,
|
|
568
|
+
...resealed
|
|
217
569
|
};
|
|
218
570
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const siblingStale = sibling !== undefined &&
|
|
230
|
-
(zcapExpiring({
|
|
231
|
-
...(sibling.expires !== undefined
|
|
232
|
-
? { expires: sibling.expires }
|
|
233
|
-
: {}),
|
|
234
|
-
...(now !== undefined ? { now } : {})
|
|
235
|
-
}) ||
|
|
236
|
-
!delegationKeyInDocument({
|
|
237
|
-
doc: account.doc,
|
|
238
|
-
...(delegationProofKeyId(sibling) !== undefined
|
|
239
|
-
? { delegationKeyId: delegationProofKeyId(sibling) }
|
|
240
|
-
: {})
|
|
241
|
-
}));
|
|
242
|
-
if (sibling === undefined ||
|
|
243
|
-
siblingSpaceId !== annexSpaceId ||
|
|
244
|
-
siblingStale) {
|
|
245
|
-
sibling = await mintDelegatedClientsDelegation({
|
|
246
|
-
zcapClient: ladderClient,
|
|
247
|
-
wasServerUrl,
|
|
248
|
-
clientAnnexSpaceId: annexSpaceId,
|
|
249
|
-
controller: standingClient.did,
|
|
250
|
-
...(now !== undefined ? { now } : {})
|
|
251
|
-
});
|
|
252
|
-
siblingReminted = true;
|
|
253
|
-
}
|
|
254
|
-
const usableSibling = sibling;
|
|
255
|
-
const standingWas = new WasClient({
|
|
256
|
-
serverUrl: wasServerUrl,
|
|
257
|
-
zcapClient: standingClient.zcapClient
|
|
258
|
-
});
|
|
259
|
-
const storeFor = (generationId) => clientAnnexLogStore({
|
|
260
|
-
was: standingWas,
|
|
261
|
-
spaceId: annexSpaceId,
|
|
262
|
-
generationId,
|
|
263
|
-
capability: usableSibling
|
|
264
|
-
});
|
|
265
|
-
const annexPin = (generationId) => pinStore !== undefined
|
|
266
|
-
? {
|
|
267
|
-
pinStore,
|
|
268
|
-
logId: clientAnnexLogPinId({ spaceId: annexSpaceId, generationId })
|
|
269
|
-
}
|
|
270
|
-
: {};
|
|
271
|
-
// RENEW PRECEDES MINT: a live, verifiable pointed generation is renewed in
|
|
272
|
-
// place; only a rung this generation never committed falls through to the
|
|
273
|
-
// fresh mint (the GC swap's no-committed-survivor escape).
|
|
274
|
-
if (pointer !== undefined) {
|
|
275
|
-
const parts = clientAnnexDidParts({ did: pointer });
|
|
276
|
-
const pointedLog = await readPublishedLog({
|
|
277
|
-
idStore: storeFor(parts.generationId),
|
|
278
|
-
expectedDid: pointer,
|
|
279
|
-
...annexPin(parts.generationId)
|
|
571
|
+
if (annexSpaceId === undefined) {
|
|
572
|
+
// NEITHER THE POINTER NOR A SIBLING NAMES A SPACE. The pre-flight
|
|
573
|
+
// attribution runs before any Space or generation is minted (a bridge
|
|
574
|
+
// delegation mint writes nothing durable).
|
|
575
|
+
await assertPointerEntryAttributable({ ladderSeed, log: account.log });
|
|
576
|
+
const freshSpaceId = await mintFreshAnnexSpace();
|
|
577
|
+
spaceMinted = true;
|
|
578
|
+
return runInAnnexSpace({
|
|
579
|
+
annexSpaceId: freshSpaceId,
|
|
580
|
+
allowSpaceReplacement: false
|
|
280
581
|
});
|
|
281
|
-
if (pointedLog !== undefined) {
|
|
282
|
-
try {
|
|
283
|
-
const ensured = await ensureGenerationDelegationCurrent({
|
|
284
|
-
store: storeFor(parts.generationId),
|
|
285
|
-
ladderSeed,
|
|
286
|
-
generationId: parts.generationId,
|
|
287
|
-
mintGenerationDelegation: mintDelegation,
|
|
288
|
-
expectedDid: pointer,
|
|
289
|
-
accountDoc: account.doc,
|
|
290
|
-
...annexPin(parts.generationId),
|
|
291
|
-
...(now !== undefined ? { now } : {})
|
|
292
|
-
});
|
|
293
|
-
if (siblingReminted) {
|
|
294
|
-
await onRebindRecord({ delegatedClients: usableSibling });
|
|
295
|
-
}
|
|
296
|
-
return {
|
|
297
|
-
clientAnnexDid: pointer,
|
|
298
|
-
generationDelegation: ensured.delegation,
|
|
299
|
-
delegatedClients: usableSibling,
|
|
300
|
-
generationMinted: false,
|
|
301
|
-
spaceMinted: false,
|
|
302
|
-
delegationRenewed: ensured.renewed,
|
|
303
|
-
siblingReminted
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
catch (err) {
|
|
307
|
-
if (err.name !== 'ClientAnnexRungUncommittedError') {
|
|
308
|
-
throw err;
|
|
309
|
-
}
|
|
310
|
-
// This credential's rung was never committed into the pointed
|
|
311
|
-
// generation (bound mid-generation): fall through to the fresh mint,
|
|
312
|
-
// which commits it with the fresh genesis.
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
582
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
// rung; other standing credentials' per-generation rungs are re-committed
|
|
321
|
-
// only by their own later ceremonies (a property of every generation
|
|
322
|
-
// swap).
|
|
323
|
-
const updateKeys = await pointerEntryUpdateKeys({
|
|
324
|
-
ladderSeed,
|
|
325
|
-
log: account.log
|
|
326
|
-
});
|
|
327
|
-
const minted = await mintCredentialClientAnnexGeneration({
|
|
328
|
-
was: standingWas,
|
|
329
|
-
wasServerUrl,
|
|
330
|
-
spaceId: annexSpaceId,
|
|
331
|
-
controller: account.did,
|
|
332
|
-
ladderSeed,
|
|
333
|
-
capability: usableSibling
|
|
334
|
-
});
|
|
335
|
-
const ensured = await ensureGenerationDelegationCurrent({
|
|
336
|
-
store: storeFor(minted.generationId),
|
|
337
|
-
ladderSeed,
|
|
338
|
-
generationId: minted.generationId,
|
|
339
|
-
mintGenerationDelegation: mintDelegation,
|
|
340
|
-
expectedDid: minted.did,
|
|
341
|
-
...annexPin(minted.generationId),
|
|
342
|
-
...(now !== undefined ? { now } : {})
|
|
343
|
-
});
|
|
344
|
-
// No revocation of the superseded generation's delegation is attempted:
|
|
345
|
-
// a transient visit has no reach that could invoke it (the standing client
|
|
346
|
-
// is neither the Space controller nor in that delegation's chain). The
|
|
347
|
-
// pointer move itself retires it on a conforming server -- the inspector
|
|
348
|
-
// clause compares the delegation's controller against the document's
|
|
349
|
-
// pointer -- and it otherwise rots on its TTL.
|
|
350
|
-
await setDelegatedClientsPointer({
|
|
351
|
-
idStore,
|
|
352
|
-
updateKeys,
|
|
353
|
-
clientAnnexDid: minted.did,
|
|
354
|
-
expectedDid: account.did,
|
|
355
|
-
logOnly: true,
|
|
356
|
-
...(pinStore !== undefined
|
|
357
|
-
? { pinStore, logId: accountLogPinId({ spaceId }) }
|
|
358
|
-
: {})
|
|
583
|
+
return runInAnnexSpace({
|
|
584
|
+
annexSpaceId,
|
|
585
|
+
allowSpaceReplacement: true,
|
|
586
|
+
...(pointer !== undefined ? { pointer } : {})
|
|
359
587
|
});
|
|
360
|
-
if (siblingReminted) {
|
|
361
|
-
await onRebindRecord({ delegatedClients: usableSibling });
|
|
362
|
-
}
|
|
363
|
-
return {
|
|
364
|
-
clientAnnexDid: minted.did,
|
|
365
|
-
generationDelegation: ensured.delegation,
|
|
366
|
-
delegatedClients: usableSibling,
|
|
367
|
-
generationMinted: true,
|
|
368
|
-
spaceMinted: false,
|
|
369
|
-
delegationRenewed: false,
|
|
370
|
-
siblingReminted
|
|
371
|
-
};
|
|
372
588
|
}
|
|
373
589
|
/**
|
|
374
590
|
* The annex Space, in the settled resolution order: the account document's
|
|
@@ -417,11 +633,7 @@ export function resolveClientAnnexSpaceId({ doc, delegatedClients }) {
|
|
|
417
633
|
* @returns {Promise<ClientWebvhUpdateKeys>}
|
|
418
634
|
*/
|
|
419
635
|
export async function pointerEntryUpdateKeys({ ladderSeed, log }) {
|
|
420
|
-
const
|
|
421
|
-
const current = params[params.length - 1] ?? {
|
|
422
|
-
updateKeys: [],
|
|
423
|
-
nextKeyHashes: []
|
|
424
|
-
};
|
|
636
|
+
const current = currentLogParameters({ log });
|
|
425
637
|
const unattributable = () => new ClientAnnexGenerationUnavailableError({
|
|
426
638
|
reason: 'update-key-not-attributable',
|
|
427
639
|
message: "No current account-log update key is a rung of this credential's " +
|
|
@@ -446,4 +658,139 @@ export async function pointerEntryUpdateKeys({ ladderSeed, log }) {
|
|
|
446
658
|
});
|
|
447
659
|
return { updateSeed: attributed.rung.seed, stagedSeed: staged.seed };
|
|
448
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* The pre-flight the pointer-moving arms run before minting anything: this
|
|
663
|
+
* ladder has a rung the pointer entry will be able to sign with, either
|
|
664
|
+
* standing in `updateKeys` already or committed in `nextKeyHashes` and
|
|
665
|
+
* revealable by {@link movePointerAsLadder}. A ladder the log carries no rung
|
|
666
|
+
* of at all, and an ambiguous attribution, refuse here with
|
|
667
|
+
* {@link ClientAnnexGenerationUnavailableError} before a generation or a
|
|
668
|
+
* Space is minted.
|
|
669
|
+
*
|
|
670
|
+
* @param options {object}
|
|
671
|
+
* @param options.ladderSeed {Uint8Array}
|
|
672
|
+
* @param options.log {DIDLog} the VERIFIED account log
|
|
673
|
+
* @returns {Promise<void>}
|
|
674
|
+
*/
|
|
675
|
+
async function assertPointerEntryAttributable({ ladderSeed, log }) {
|
|
676
|
+
const current = currentLogParameters({ log });
|
|
677
|
+
try {
|
|
678
|
+
await attributeLadderRung({ ladderSeed, published: current });
|
|
679
|
+
}
|
|
680
|
+
catch (err) {
|
|
681
|
+
if (err.name === 'LadderAttributionError') {
|
|
682
|
+
throw new ClientAnnexGenerationUnavailableError({
|
|
683
|
+
reason: 'update-key-not-attributable',
|
|
684
|
+
message: "The account log carries no rung of this credential's ladder, or " +
|
|
685
|
+
'the attribution is ambiguous; the pointer entry could not be ' +
|
|
686
|
+
'signed, so nothing is minted.'
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
throw err;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* The `#DelegatedClients` pointer move as a credential-only visit makes it:
|
|
694
|
+
* reveal this ladder's rung when only its hash stands committed, re-read, and
|
|
695
|
+
* write the pointer entry signed by the now-revealed rung.
|
|
696
|
+
*
|
|
697
|
+
* A self-enrollment's add entry spends the revealed rung, so on any account
|
|
698
|
+
* that has ever self-enrolled the rung is merely committed and the reveal is
|
|
699
|
+
* what makes the pointer entry signable at all.
|
|
700
|
+
*
|
|
701
|
+
* ACCEPTED CONSEQUENCE (design FW-356, finding R3): the reveal retires
|
|
702
|
+
* nothing, and the pointer entry re-states `updateKeys` verbatim, so the
|
|
703
|
+
* acting rung stands in the account log's `updateKeys` afterwards. The price
|
|
704
|
+
* of a pointer move is therefore a standing account update key in the
|
|
705
|
+
* credential's hand -- direct document-edit authority through the bridge with
|
|
706
|
+
* no further reveal -- retired at that credential's next self-enrollment
|
|
707
|
+
* (whose add entry drops the attributed rung) or at its retirement. This is
|
|
708
|
+
* documented rather than prevented.
|
|
709
|
+
*
|
|
710
|
+
* Both entries run inside ONE conflict retry, and every attempt re-reads the
|
|
711
|
+
* head and re-attributes the rung from it, so a racing ceremony that consumes
|
|
712
|
+
* the rung climbs to the winner's committed rung instead of refusing
|
|
713
|
+
* `update-key-not-attributable` on a rung that is no longer current.
|
|
714
|
+
*
|
|
715
|
+
* Three things make that hold. A rung the winner consumed between this
|
|
716
|
+
* attempt's reveal and its re-read reads back as
|
|
717
|
+
* `update-key-not-attributable`, which inside this loop is staleness rather
|
|
718
|
+
* than a refusal -- the pre-flight already found a rung -- so it is raised as
|
|
719
|
+
* a conflict and the next attempt reveals the winner's committed rung. The pointer entry runs as
|
|
720
|
+
* {@link setDelegatedClientsPointerOnce} rather than through its retrying
|
|
721
|
+
* wrapper: the wrapper's inner loop would re-invoke the attempt with the
|
|
722
|
+
* attribution's stale `updateKeys`, and a rung the racing winner consumed
|
|
723
|
+
* cannot become authorized by re-reading, so the attempt would end on the
|
|
724
|
+
* plain not-authorized refusal, which the outer loop does not retry. And the
|
|
725
|
+
* attempt is handed the very head this attempt attributed against, so a
|
|
726
|
+
* racing entry landing between the attribution and the PUT loses the CAS and
|
|
727
|
+
* surfaces as a `WebvhLogConflictError` -- the refusal the outer loop
|
|
728
|
+
* re-attributes from. The pre-flight guard
|
|
729
|
+
* ({@link assertPointerEntryAttributable}) therefore cannot fire from
|
|
730
|
+
* staleness here: it runs on the caller's snapshot before anything is minted,
|
|
731
|
+
* while every entry this function publishes is built on a head it read
|
|
732
|
+
* itself.
|
|
733
|
+
*
|
|
734
|
+
* @param options {object}
|
|
735
|
+
* @param options.idStore {WebvhIdStore} the account log's store, from the
|
|
736
|
+
* record's bridge delegation
|
|
737
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
738
|
+
* @param options.clientAnnexDid {string} the generation to point at
|
|
739
|
+
* @param options.accountDid {string} the account DID the log must resolve to
|
|
740
|
+
* @param [options.pinStore] {ResourceLogPinStore} the visit's chain-head pins
|
|
741
|
+
* @param [options.logId] {string} the account log's pin slot; required
|
|
742
|
+
* whenever a `pinStore` is supplied
|
|
743
|
+
* @returns {Promise<void>}
|
|
744
|
+
*/
|
|
745
|
+
async function movePointerAsLadder({ idStore, ladderSeed, clientAnnexDid, accountDid, pinStore, logId }) {
|
|
746
|
+
const pin = pinStore !== undefined && logId !== undefined ? { pinStore, logId } : {};
|
|
747
|
+
await withLogConflictRetry(async () => {
|
|
748
|
+
await revealLadderRungWebvh({
|
|
749
|
+
store: idStore,
|
|
750
|
+
ladderSeed,
|
|
751
|
+
expectedDid: accountDid,
|
|
752
|
+
...pin
|
|
753
|
+
});
|
|
754
|
+
// The head the reveal entry just published (or the unchanged head, when
|
|
755
|
+
// the rung was revealed already), under the same pin.
|
|
756
|
+
const published = await readPublishedLogOrThrow({
|
|
757
|
+
idStore,
|
|
758
|
+
expectedDid: accountDid,
|
|
759
|
+
...pin,
|
|
760
|
+
missingMessage: 'did:webvh: did.jsonl is missing; nothing to point at a client annex.'
|
|
761
|
+
});
|
|
762
|
+
// A racing ceremony that consumed the rung between this reveal and this
|
|
763
|
+
// read leaves the ladder committed-only again, which the attribution
|
|
764
|
+
// reports as `update-key-not-attributable`. Inside this loop that is a
|
|
765
|
+
// staleness signal rather than a refusal -- the pre-flight already found
|
|
766
|
+
// a rung, and a fresh reveal is exactly what the next attempt does -- so
|
|
767
|
+
// it becomes a conflict for the retry to re-run.
|
|
768
|
+
let updateKeys;
|
|
769
|
+
try {
|
|
770
|
+
updateKeys = await pointerEntryUpdateKeys({
|
|
771
|
+
ladderSeed,
|
|
772
|
+
log: published.log
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
if (err.name ===
|
|
777
|
+
'ClientAnnexGenerationUnavailableError' &&
|
|
778
|
+
err.reason ===
|
|
779
|
+
'update-key-not-attributable') {
|
|
780
|
+
throw new WebvhLogConflictError('did:webvh: a concurrent ceremony consumed the rung this pointer ' +
|
|
781
|
+
'move just revealed; the move re-runs from a fresh reveal.', { cause: err });
|
|
782
|
+
}
|
|
783
|
+
throw err;
|
|
784
|
+
}
|
|
785
|
+
await setDelegatedClientsPointerOnce({
|
|
786
|
+
idStore,
|
|
787
|
+
signer: { kind: 'client', updateKeys },
|
|
788
|
+
clientAnnexDid,
|
|
789
|
+
expectedDid: accountDid,
|
|
790
|
+
logOnly: true,
|
|
791
|
+
published,
|
|
792
|
+
...pin
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
}
|
|
449
796
|
//# sourceMappingURL=heal.js.map
|