@interop/wallet-core 0.41.0 → 0.42.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.
Files changed (121) hide show
  1. package/README.md +17 -4
  2. package/dist/clients/rosterPolicy.d.ts +22 -15
  3. package/dist/clients/rosterPolicy.d.ts.map +1 -1
  4. package/dist/clients/rosterPolicy.js +32 -15
  5. package/dist/clients/rosterPolicy.js.map +1 -1
  6. package/dist/descriptors/index.d.ts +1 -1
  7. package/dist/descriptors/index.d.ts.map +1 -1
  8. package/dist/descriptors/index.js +1 -1
  9. package/dist/descriptors/index.js.map +1 -1
  10. package/dist/descriptors/logSource.d.ts +58 -6
  11. package/dist/descriptors/logSource.d.ts.map +1 -1
  12. package/dist/descriptors/logSource.js +57 -15
  13. package/dist/descriptors/logSource.js.map +1 -1
  14. package/dist/enrollment/enrollment.d.ts.map +1 -1
  15. package/dist/enrollment/enrollment.js +14 -26
  16. package/dist/enrollment/enrollment.js.map +1 -1
  17. package/dist/keyring/fetch.d.ts +12 -0
  18. package/dist/keyring/fetch.d.ts.map +1 -1
  19. package/dist/keyring/fetch.js +0 -2
  20. package/dist/keyring/fetch.js.map +1 -1
  21. package/dist/keyring/index.d.ts +1 -1
  22. package/dist/keyring/index.d.ts.map +1 -1
  23. package/dist/keyring/index.js +1 -1
  24. package/dist/keyring/index.js.map +1 -1
  25. package/dist/keyring/kdf.d.ts +22 -6
  26. package/dist/keyring/kdf.d.ts.map +1 -1
  27. package/dist/keyring/kdf.js +9 -14
  28. package/dist/keyring/kdf.js.map +1 -1
  29. package/dist/keys/index.d.ts +1 -1
  30. package/dist/keys/index.d.ts.map +1 -1
  31. package/dist/keys/index.js +1 -1
  32. package/dist/keys/index.js.map +1 -1
  33. package/dist/keys/rosterLogStore.d.ts +4 -1
  34. package/dist/keys/rosterLogStore.d.ts.map +1 -1
  35. package/dist/keys/rosterLogStore.js +13 -16
  36. package/dist/keys/rosterLogStore.js.map +1 -1
  37. package/dist/keys/rosterStore.d.ts +13 -1
  38. package/dist/keys/rosterStore.d.ts.map +1 -1
  39. package/dist/keys/rosterStore.js +18 -0
  40. package/dist/keys/rosterStore.js.map +1 -1
  41. package/dist/keys/userKeyRoster.d.ts +17 -5
  42. package/dist/keys/userKeyRoster.d.ts.map +1 -1
  43. package/dist/keys/userKeyRoster.js +38 -13
  44. package/dist/keys/userKeyRoster.js.map +1 -1
  45. package/dist/recovery/index.d.ts +14 -12
  46. package/dist/recovery/index.d.ts.map +1 -1
  47. package/dist/recovery/index.js +13 -11
  48. package/dist/recovery/index.js.map +1 -1
  49. package/dist/recovery/recoveryCode.d.ts +5 -17
  50. package/dist/recovery/recoveryCode.d.ts.map +1 -1
  51. package/dist/recovery/recoveryCode.js +4 -14
  52. package/dist/recovery/recoveryCode.js.map +1 -1
  53. package/dist/recovery/recoveryDelegation.d.ts +50 -19
  54. package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
  55. package/dist/recovery/recoveryDelegation.js +86 -41
  56. package/dist/recovery/recoveryDelegation.js.map +1 -1
  57. package/dist/recovery/recoveryWebvh.d.ts +13 -22
  58. package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
  59. package/dist/recovery/recoveryWebvh.js +37 -129
  60. package/dist/recovery/recoveryWebvh.js.map +1 -1
  61. package/dist/resourceLog/append.d.ts +12 -3
  62. package/dist/resourceLog/append.d.ts.map +1 -1
  63. package/dist/resourceLog/append.js +17 -10
  64. package/dist/resourceLog/append.js.map +1 -1
  65. package/dist/resourceLog/index.d.ts +1 -1
  66. package/dist/resourceLog/index.d.ts.map +1 -1
  67. package/dist/resourceLog/index.js +1 -1
  68. package/dist/resourceLog/index.js.map +1 -1
  69. package/dist/resourceLog/pin.d.ts +46 -8
  70. package/dist/resourceLog/pin.d.ts.map +1 -1
  71. package/dist/resourceLog/pin.js +32 -7
  72. package/dist/resourceLog/pin.js.map +1 -1
  73. package/dist/resourceLog/seal.d.ts +4 -1
  74. package/dist/resourceLog/seal.d.ts.map +1 -1
  75. package/dist/resourceLog/seal.js +6 -2
  76. package/dist/resourceLog/seal.js.map +1 -1
  77. package/dist/unlock/index.d.ts +40 -0
  78. package/dist/unlock/index.d.ts.map +1 -0
  79. package/dist/unlock/index.js +36 -0
  80. package/dist/unlock/index.js.map +1 -0
  81. package/dist/unlock/ladder.d.ts +100 -0
  82. package/dist/unlock/ladder.d.ts.map +1 -0
  83. package/dist/unlock/ladder.js +144 -0
  84. package/dist/unlock/ladder.js.map +1 -0
  85. package/dist/unlock/selfEnroll.d.ts +83 -0
  86. package/dist/unlock/selfEnroll.d.ts.map +1 -0
  87. package/dist/unlock/selfEnroll.js +126 -0
  88. package/dist/unlock/selfEnroll.js.map +1 -0
  89. package/dist/unlock/standingClient.d.ts +68 -0
  90. package/dist/unlock/standingClient.d.ts.map +1 -0
  91. package/dist/unlock/standingClient.js +81 -0
  92. package/dist/unlock/standingClient.js.map +1 -0
  93. package/dist/unlock/standingWebvh.d.ts +153 -0
  94. package/dist/unlock/standingWebvh.d.ts.map +1 -0
  95. package/dist/unlock/standingWebvh.js +370 -0
  96. package/dist/unlock/standingWebvh.js.map +1 -0
  97. package/dist/unlock/unlockRecord.d.ts +207 -0
  98. package/dist/unlock/unlockRecord.d.ts.map +1 -0
  99. package/dist/unlock/unlockRecord.js +435 -0
  100. package/dist/unlock/unlockRecord.js.map +1 -0
  101. package/dist/webvh/didWebvh.d.ts +94 -6
  102. package/dist/webvh/didWebvh.d.ts.map +1 -1
  103. package/dist/webvh/didWebvh.js +175 -19
  104. package/dist/webvh/didWebvh.js.map +1 -1
  105. package/dist/webvh/index.d.ts +7 -2
  106. package/dist/webvh/index.d.ts.map +1 -1
  107. package/dist/webvh/index.js +7 -2
  108. package/dist/webvh/index.js.map +1 -1
  109. package/dist/webvh/keyAgreement.d.ts +22 -6
  110. package/dist/webvh/keyAgreement.d.ts.map +1 -1
  111. package/dist/webvh/keyAgreement.js +1 -1
  112. package/dist/webvh/keyAgreement.js.map +1 -1
  113. package/dist/webvh/verifyLog.d.ts +15 -2
  114. package/dist/webvh/verifyLog.d.ts.map +1 -1
  115. package/dist/webvh/verifyLog.js +22 -3
  116. package/dist/webvh/verifyLog.js.map +1 -1
  117. package/package.json +11 -5
  118. package/dist/recovery/recoveryRecord.d.ts +0 -172
  119. package/dist/recovery/recoveryRecord.d.ts.map +0 -1
  120. package/dist/recovery/recoveryRecord.js +0 -288
  121. package/dist/recovery/recoveryRecord.js.map +0 -1
@@ -0,0 +1,126 @@
1
+ import { agentsFromSeed } from '../identity/agents.js';
2
+ import { addUserKeyRosterRecipient, readUserKeyRoster, rosterRecipientKid, userKeyRosterLogSigner } from '../keys/userKeyRoster.js';
3
+ import { userKeyRosterDescriptorStore } from '../keys/rosterStore.js';
4
+ import { memoryResourceLogPinStore, webvhResourceLogController } from '../resourceLog/index.js';
5
+ import { mintClientWebvhUpdateKeys, updateKeyMultibase } from '../webvh/didWebvh.js';
6
+ import { verifyAccountLog } from '../webvh/verifyLog.js';
7
+ import { clientSigningKeyMultibase, isWebvhDid, webvhZcapClient } from '../webvh/zcap.js';
8
+ import { selfEnrollWebvhClient } from './standingWebvh.js';
9
+ /**
10
+ * Runs the whole self-enrollment described in the module doc. The caller has
11
+ * already unwrapped the credential's unlock record (so it holds the
12
+ * credential-authenticated pointer, the bridge delegation behind `logStore`,
13
+ * and the ladder seed) and derived the credential's client identity (so it
14
+ * holds the key-agreement key with its secret half).
15
+ *
16
+ * @param options {object}
17
+ * @param options.pointer {AccountPointer} the credential-authenticated
18
+ * account pointer; must name a did:webvh
19
+ * @param options.ladderSeed {Uint8Array} the credential's update-key ladder
20
+ * seed, from its unlock record
21
+ * @param options.credentialKeyAgreementKey {IKeyAgreementKey} the
22
+ * credential's own key-agreement key (secret half included) -- its roster
23
+ * entry, which the user key is unwrapped from and every epoch re-wrapped
24
+ * with
25
+ * @param options.logStore {UnlockLogStore} the public log read plus the
26
+ * delegated `did.jsonl` PUT, built by the app around the record's bridge
27
+ * delegation
28
+ * @param [options.accountLogPinStore] {ResourceLogPinStore} this client's
29
+ * chain-head pin for the account log. A fresh browser normally has none
30
+ * (this is its first contact), which is exactly the pin's
31
+ * trust-on-first-use establishment
32
+ * @returns {Promise<object>} the new client's key set (for the caller to
33
+ * persist under its unlock layer), its did:key, the account DID, the user
34
+ * key, and the roster epoch to pin
35
+ */
36
+ export async function selfEnrollClientCore({ pointer, ladderSeed, credentialKeyAgreementKey, logStore, accountLogPinStore }) {
37
+ const expectedDid = pointer.did;
38
+ if (!expectedDid || !isWebvhDid(expectedDid)) {
39
+ throw new Error('The account pointer names no did:webvh; only a promoted account can ' +
40
+ 'self-enroll a client.');
41
+ }
42
+ // The new client's whole key set, minted locally; nothing travels but the
43
+ // public halves the log entries publish.
44
+ const clientSeed = crypto.getRandomValues(new Uint8Array(32));
45
+ const { keyAgent, keyAgreementKey } = await agentsFromSeed({
46
+ seed: clientSeed
47
+ });
48
+ const { publicKeyMultibase: keyAgreementKeyMultibase } = keyAgreementKey;
49
+ if (!keyAgreementKeyMultibase) {
50
+ throw new Error('The minted key-agreement key has no public multibase.');
51
+ }
52
+ const signingKeyMultibase = clientSigningKeyMultibase({ keyAgent });
53
+ const webvhUpdateKeys = await mintClientWebvhUpdateKeys();
54
+ const newClientKeys = {
55
+ signingKeyMultibase,
56
+ keyAgreementKeyMultibase,
57
+ updateKeyMultibase: await updateKeyMultibase({
58
+ seed: webvhUpdateKeys.updateSeed
59
+ }),
60
+ stagedUpdateKeyMultibase: await updateKeyMultibase({
61
+ seed: webvhUpdateKeys.stagedSeed
62
+ })
63
+ };
64
+ // The loud half: two log entries through the delegated bridge.
65
+ await selfEnrollWebvhClient({
66
+ store: logStore,
67
+ ladderSeed,
68
+ newClientKeys,
69
+ newClientUpdateSeeds: webvhUpdateKeys,
70
+ expectedDid
71
+ });
72
+ // Verify the continuation from the world-readable log -- the same
73
+ // first-contact read an enrollee's completion runs, and the controller the
74
+ // roster log's entry proofs are checked against.
75
+ const verified = await verifyAccountLog({
76
+ did: expectedDid,
77
+ spaceId: pointer.spaceId,
78
+ host: pointer.host,
79
+ ...(accountLogPinStore ? { pinStore: accountLogPinStore } : {})
80
+ });
81
+ // The first roster read: signed with the `<did:webvh>#<multibase>` keyId
82
+ // the add entry just published, unwrapping the user key from the
83
+ // CREDENTIAL's standing wrap. First contact: the chain-head pin this read
84
+ // establishes is session-local here; the app's durable pin is established
85
+ // by its own first login read.
86
+ const zcapClient = webvhZcapClient({ keyAgent, did: expectedDid });
87
+ const store = userKeyRosterDescriptorStore({
88
+ storageServerUrl: pointer.host,
89
+ zcapClient,
90
+ spaceId: pointer.spaceId,
91
+ resolveController: async () => webvhResourceLogController({ did: expectedDid, log: verified.log }),
92
+ pinStore: memoryResourceLogPinStore(),
93
+ signer: userKeyRosterLogSigner({ keyAgent })
94
+ });
95
+ const read = await readUserKeyRoster({
96
+ store,
97
+ clientKeyAgreementKey: credentialKeyAgreementKey
98
+ });
99
+ if (!read) {
100
+ throw new Error('The account has no user key roster; it must finish provisioning ' +
101
+ 'before a client can self-enroll.');
102
+ }
103
+ // Escrow the new client into the roster as its own recipient, so later
104
+ // logins on this browser read the roster the ordinary enrolled way.
105
+ // Idempotent: a wrap already standing is returned as-is.
106
+ await addUserKeyRosterRecipient({
107
+ store,
108
+ recipient: {
109
+ id: rosterRecipientKid({
110
+ signingKeyMultibase,
111
+ keyAgreementKeyMultibase
112
+ }),
113
+ publicKeyMultibase: keyAgreementKeyMultibase
114
+ },
115
+ ownerKeyAgreementKey: credentialKeyAgreementKey
116
+ });
117
+ return {
118
+ clientSeed,
119
+ webvhUpdateKeys,
120
+ clientDid: keyAgent.id,
121
+ did: expectedDid,
122
+ userKey: read.userKey,
123
+ latestEpochId: read.latestEpochId
124
+ };
125
+ }
126
+ //# sourceMappingURL=selfEnroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selfEnroll.js","sourceRoot":"","sources":["../../src/unlock/selfEnroll.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { ProfileAgents } from '../identity/agents.js';
2
+ /**
3
+ * The HKDF salt for a standing credential's client-side expansions, and the
4
+ * per-key info labels. All permanent. The salt differs from the recovery
5
+ * code's client salt (`freewallet/recovery/client-keys/v1`), so a code and a
6
+ * standing method that somehow shared input material could still never derive
7
+ * the same client identity.
8
+ */
9
+ export declare const STANDING_CLIENT_SALT = "freewallet/unlock/standing-client/v1";
10
+ /**
11
+ * A credential-derived client identity, assembled from its 32-byte client
12
+ * seed: the derived agents, the client did:key, the public multibases the
13
+ * document and roster carry, and the roster recipient kid. The shared shape
14
+ * of a standing credential's identity and a recovery code's (which extends it
15
+ * with the code's single update key).
16
+ */
17
+ export interface UnlockClientIdentity {
18
+ clientSeed: Uint8Array;
19
+ agents: ProfileAgents;
20
+ clientDid: string;
21
+ signingKeyMultibase: string;
22
+ keyAgreementKeyMultibase: string;
23
+ /**
24
+ * The kid of the credential's user-key-roster entry -- its key-agreement
25
+ * key's id exactly as `agentsFromSeed` derives it (`did:key:<ed>#<x>`), so
26
+ * the wrap minted at bind time is the one a fresh browser's roster read
27
+ * looks for.
28
+ */
29
+ recipientKid: string;
30
+ }
31
+ /**
32
+ * Assembles a client identity from its 32-byte client seed: the one place the
33
+ * agents, multibases, and roster kid are derived, shared by the standing
34
+ * credential derivation here and the recovery code's
35
+ * (`recoveryClientFromCode`), so the two postures can never disagree on how a
36
+ * seed becomes an identity.
37
+ *
38
+ * @param options {object}
39
+ * @param options.clientSeed {Uint8Array} the 32-byte client seed
40
+ * @returns {Promise<UnlockClientIdentity>}
41
+ */
42
+ export declare function unlockClientIdentityFromSeed({ clientSeed }: {
43
+ clientSeed: Uint8Array;
44
+ }): Promise<UnlockClientIdentity>;
45
+ /**
46
+ * A standing unlock credential's full client-side key set: the client
47
+ * identity plus the binding MAC key that authenticates the unlock record's
48
+ * account core (computed at bind time, verified before the pointer is
49
+ * trusted -- the storage host never holds it).
50
+ */
51
+ export interface StandingUnlockClient extends UnlockClientIdentity {
52
+ bindingMacKey: Uint8Array;
53
+ }
54
+ /**
55
+ * Derives a standing credential's client key set from the method's 32-byte
56
+ * unlock seed (the output of `deriveUnlockSeed` under the method's own KDF).
57
+ * Deterministic: the same secret under the same KDF always yields the same
58
+ * key set, which is what makes a fresh browser's self-enrollment possible
59
+ * with nothing but the credential in hand.
60
+ *
61
+ * @param options {object}
62
+ * @param options.unlockSeed {Uint8Array} the method's 32-byte unlock seed
63
+ * @returns {Promise<StandingUnlockClient>}
64
+ */
65
+ export declare function standingClientFromUnlockSeed({ unlockSeed }: {
66
+ unlockSeed: Uint8Array;
67
+ }): Promise<StandingUnlockClient>;
68
+ //# sourceMappingURL=standingClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standingClient.d.ts","sourceRoot":"","sources":["../../src/unlock/standingClient.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,yCAAyC,CAAA;AAI1E;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,aAAa,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,wBAAwB,EAAE,MAAM,CAAA;IAChC;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,4BAA4B,CAAC,EACjD,UAAU,EACX,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgBhC;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE,aAAa,EAAE,UAAU,CAAA;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,4BAA4B,CAAC,EACjD,UAAU,EACX,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkBhC"}
@@ -0,0 +1,81 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The standing unlock credential's client identity: the deterministic key set
6
+ * every unlock method (a passphrase, a passkey PRF output) derives from its
7
+ * own unlock seed under the standing-credential posture -- the recovery-code
8
+ * posture minus spend-on-use. The credential's key-agreement key holds a
9
+ * standing wrap in the user key roster (escrowed into every epoch, kept alive
10
+ * by rotation fan-out), and its binding MAC key authenticates the unlock
11
+ * record's account core, so a fresh browser holding nothing but the
12
+ * credential can locate the account, prove the record genuine, and decrypt.
13
+ * Update authority stays latent and rides the record instead: the ladder seed
14
+ * (`./ladder`) never derives from the secret.
15
+ *
16
+ * The derivations are wire-level: two wallet apps must produce byte-identical
17
+ * output for the same secret and KDF, so every salt and info label below is
18
+ * permanent. The HKDF input is the method's own 32-byte unlock seed
19
+ * (`deriveUnlockSeed`), so the expensive passphrase stretch runs once per
20
+ * typed secret and each unlock method's distinct KDF salt keeps two methods'
21
+ * client identities from ever colliding.
22
+ */
23
+ import { hkdf } from '@noble/hashes/hkdf.js';
24
+ import { sha256 } from '@noble/hashes/sha2.js';
25
+ import { agentsFromSeed } from '../identity/agents.js';
26
+ /**
27
+ * The HKDF salt for a standing credential's client-side expansions, and the
28
+ * per-key info labels. All permanent. The salt differs from the recovery
29
+ * code's client salt (`freewallet/recovery/client-keys/v1`), so a code and a
30
+ * standing method that somehow shared input material could still never derive
31
+ * the same client identity.
32
+ */
33
+ export const STANDING_CLIENT_SALT = 'freewallet/unlock/standing-client/v1';
34
+ const STANDING_CLIENT_SEED_INFO = 'client-seed';
35
+ const STANDING_BINDING_MAC_INFO = 'binding-mac';
36
+ /**
37
+ * Assembles a client identity from its 32-byte client seed: the one place the
38
+ * agents, multibases, and roster kid are derived, shared by the standing
39
+ * credential derivation here and the recovery code's
40
+ * (`recoveryClientFromCode`), so the two postures can never disagree on how a
41
+ * seed becomes an identity.
42
+ *
43
+ * @param options {object}
44
+ * @param options.clientSeed {Uint8Array} the 32-byte client seed
45
+ * @returns {Promise<UnlockClientIdentity>}
46
+ */
47
+ export async function unlockClientIdentityFromSeed({ clientSeed }) {
48
+ const agents = await agentsFromSeed({ seed: clientSeed });
49
+ const { publicKeyMultibase: keyAgreementKeyMultibase } = agents.keyAgreementKey;
50
+ if (!keyAgreementKeyMultibase) {
51
+ throw new Error('The derived key-agreement key has no public multibase.');
52
+ }
53
+ const [, , signingKeyMultibase] = agents.keyAgent.id.split(':');
54
+ return {
55
+ clientSeed,
56
+ agents,
57
+ clientDid: agents.keyAgent.id,
58
+ signingKeyMultibase: signingKeyMultibase,
59
+ keyAgreementKeyMultibase,
60
+ recipientKid: `${agents.keyAgent.id}#${keyAgreementKeyMultibase}`
61
+ };
62
+ }
63
+ /**
64
+ * Derives a standing credential's client key set from the method's 32-byte
65
+ * unlock seed (the output of `deriveUnlockSeed` under the method's own KDF).
66
+ * Deterministic: the same secret under the same KDF always yields the same
67
+ * key set, which is what makes a fresh browser's self-enrollment possible
68
+ * with nothing but the credential in hand.
69
+ *
70
+ * @param options {object}
71
+ * @param options.unlockSeed {Uint8Array} the method's 32-byte unlock seed
72
+ * @returns {Promise<StandingUnlockClient>}
73
+ */
74
+ export async function standingClientFromUnlockSeed({ unlockSeed }) {
75
+ const salt = new TextEncoder().encode(STANDING_CLIENT_SALT);
76
+ const clientSeed = hkdf(sha256, unlockSeed, salt, new TextEncoder().encode(STANDING_CLIENT_SEED_INFO), 32);
77
+ const bindingMacKey = hkdf(sha256, unlockSeed, salt, new TextEncoder().encode(STANDING_BINDING_MAC_INFO), 32);
78
+ const identity = await unlockClientIdentityFromSeed({ clientSeed });
79
+ return { ...identity, bindingMacKey };
80
+ }
81
+ //# sourceMappingURL=standingClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standingClient.js","sourceRoot":"","sources":["../../src/unlock/standingClient.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAGtD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,sCAAsC,CAAA;AAC1E,MAAM,yBAAyB,GAAG,aAAa,CAAA;AAC/C,MAAM,yBAAyB,GAAG,aAAa,CAAA;AAwB/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,EACjD,UAAU,EAGX;IACC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;IACzD,MAAM,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,GACpD,MAAM,CAAC,eAA6D,CAAA;IACtE,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IACD,MAAM,CAAC,EAAE,AAAD,EAAG,mBAAmB,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC/D,OAAO;QACL,UAAU;QACV,MAAM;QACN,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE;QAC7B,mBAAmB,EAAE,mBAAoB;QACzC,wBAAwB;QACxB,YAAY,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,wBAAwB,EAAE;KAClE,CAAA;AACH,CAAC;AAYD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,EACjD,UAAU,EAGX;IACC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAC3D,MAAM,UAAU,GAAG,IAAI,CACrB,MAAM,EACN,UAAU,EACV,IAAI,EACJ,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,EACnD,EAAE,CACH,CAAA;IACD,MAAM,aAAa,GAAG,IAAI,CACxB,MAAM,EACN,UAAU,EACV,IAAI,EACJ,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,EACnD,EAAE,CACH,CAAA;IACD,MAAM,QAAQ,GAAG,MAAM,4BAA4B,CAAC,EAAE,UAAU,EAAE,CAAC,CAAA;IACnE,OAAO,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,CAAA;AACvC,CAAC"}
@@ -0,0 +1,153 @@
1
+ import type { VerificationMethod } from '@interop/did-method-webvh';
2
+ import type { ClientWebvhUpdateKeys, WebvhEnrollmentKeys, WebvhIdStore } from '../webvh/didWebvh.js';
3
+ /**
4
+ * The narrow store seam the self-enrollment continuation writes through: a
5
+ * public read of the log and the delegated `did.jsonl` PUT. A subset of
6
+ * {@link WebvhIdStore}, so an app's remote-store class satisfies it too.
7
+ */
8
+ export type UnlockLogStore = Pick<WebvhIdStore, 'getIdResourceRaw' | 'putIdResource'>;
9
+ /**
10
+ * How a credential's key-agreement key is published in the document: the key
11
+ * verbatim (a high-entropy credential -- passkey PRF, recovery code), or its
12
+ * hash commitment (`keyAgreementCommitment`) for a low-entropy-derived key.
13
+ * A commitment withholds the key material and gives the roster's recipient
14
+ * resolver a document-anchored check to verify a roster-carried key against.
15
+ */
16
+ export type UnlockKeyAgreementPublication = {
17
+ publicKeyMultibase: string;
18
+ } | {
19
+ commitment: string;
20
+ };
21
+ /**
22
+ * A standing credential's public posture as the document and log carry it:
23
+ * its key-agreement publication and the update key whose hash stands in
24
+ * `nextKeyHashes` (ladder rung 0 at bind time; a code's single derived key).
25
+ */
26
+ export interface StandingUnlockKeys {
27
+ keyAgreement: UnlockKeyAgreementPublication;
28
+ updateKeyMultibase: string;
29
+ }
30
+ /**
31
+ * The verification-method id a credential's key-agreement entry publishes
32
+ * under: `<did>#<multibase>` for a verbatim key (indistinguishable by id from
33
+ * any other keyAgreement entry), `<did>#<commitment>` for a commitment entry
34
+ * (the commitment string is deterministic, so the id is too).
35
+ *
36
+ * @param options {object}
37
+ * @param options.did {string} the account's did:webvh
38
+ * @param options.keyAgreement {UnlockKeyAgreementPublication}
39
+ * @returns {string}
40
+ */
41
+ export declare function unlockKeyVmId({ did, keyAgreement }: {
42
+ did: string;
43
+ keyAgreement: UnlockKeyAgreementPublication;
44
+ }): string;
45
+ /**
46
+ * The credential's `keyAgreement` verification method: an ordinary unmarked
47
+ * entry carrying either the key verbatim (a `Multikey` with
48
+ * `publicKeyMultibase`) or its hash commitment (a `MultikeyCommitment` with
49
+ * `publicKeyCommitment` -- the document convention for a low-entropy-derived
50
+ * key, which withholds the key material and gives the roster resolver a
51
+ * document-anchored check). Controlled by the account and deliberately
52
+ * unmarked: a credential is not a listed client, so its entry must never
53
+ * carry the controller marker a client listing or a revocation removal
54
+ * matches on.
55
+ *
56
+ * @param options {object}
57
+ * @param options.did {string} the account's did:webvh
58
+ * @param options.keyAgreement {UnlockKeyAgreementPublication}
59
+ * @returns {VerificationMethod}
60
+ */
61
+ export declare function unlockKeyVerificationMethod({ did, keyAgreement }: {
62
+ did: string;
63
+ keyAgreement: UnlockKeyAgreementPublication;
64
+ }): VerificationMethod;
65
+ /**
66
+ * BIND (run by an enrolled client, root authority): publishes a standing
67
+ * credential's split posture into the document -- one entry adding the
68
+ * credential's `keyAgreement` entry (verbatim or commitment) and committing
69
+ * its current update key's hash in `nextKeyHashes`. The update key joins
70
+ * `updateKeys` nowhere. Idempotent: a posture already published is a no-op,
71
+ * so re-running a torn bind converges. The entry publishes conditionally on
72
+ * the log this call read; a race lost to a concurrent ceremony re-runs and
73
+ * rebases on the new head.
74
+ *
75
+ * @param options {object}
76
+ * @param options.idStore {WebvhIdStore}
77
+ * @param options.updateKeys {ClientWebvhUpdateKeys} the BINDING client's own
78
+ * did:webvh update-key seeds
79
+ * @param options.unlockKeys {StandingUnlockKeys} the credential's public
80
+ * posture
81
+ * @param [options.expectedDid] {string} the account DID the log must resolve
82
+ * to, from the caller's stored account pointer
83
+ * @param [options.verb] {string} what the caller is doing, for the
84
+ * pending-rotation refusal message (e.g. `'issuing a recovery code'`)
85
+ * @returns {Promise<{ did: string }>}
86
+ */
87
+ export declare function publishUnlockKey(options: {
88
+ idStore: WebvhIdStore;
89
+ updateKeys: ClientWebvhUpdateKeys;
90
+ unlockKeys: StandingUnlockKeys;
91
+ expectedDid?: string;
92
+ verb?: string;
93
+ }): Promise<{
94
+ did: string;
95
+ }>;
96
+ /**
97
+ * REMOVAL (run by an enrolled client, root authority): removes a standing
98
+ * credential's posture from the document -- its `keyAgreement` entry and its
99
+ * committed update-key hash -- in one entry. Idempotent. The roster-side half
100
+ * (rotating the user key epoch off the credential's wrap) is the caller's,
101
+ * and runs after this so the resolver's document no longer backs the removed
102
+ * entry.
103
+ *
104
+ * @param options {object} see {@link publishUnlockKey}
105
+ * @returns {Promise<{ did: string }>}
106
+ */
107
+ export declare function removeUnlockKey(options: {
108
+ idStore: WebvhIdStore;
109
+ updateKeys: ClientWebvhUpdateKeys;
110
+ unlockKeys: StandingUnlockKeys;
111
+ expectedDid?: string;
112
+ verb?: string;
113
+ }): Promise<{
114
+ did: string;
115
+ }>;
116
+ /**
117
+ * SELF-ENROLLMENT (run by the credential-derived client through the delegated
118
+ * `did.jsonl` PUT): writes the standing continuation described in the module
119
+ * doc -- the reveal-and-commit entry signed by the attributed ladder rung,
120
+ * then the add entry signed by the new ordinary client's update key, which
121
+ * also retires the spent rung. Resumable from durable state alone: a
122
+ * completed continuation is detected by the new client's update key already
123
+ * being authorized (no-op), a torn one by the attribution finding the rung
124
+ * already revealed. Both entries publish conditionally on the read they were
125
+ * built on, and a race lost to a concurrent ceremony re-runs from the top --
126
+ * re-attributing, which is exactly the retry-up-the-ladder resolution.
127
+ *
128
+ * @param options {object}
129
+ * @param options.store {UnlockLogStore} public log read + delegated PUT
130
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed, from
131
+ * its unlock record
132
+ * @param options.newClientKeys {WebvhEnrollmentKeys} the new ordinary
133
+ * client's public halves
134
+ * @param options.newClientUpdateSeeds {ClientWebvhUpdateKeys} the new
135
+ * client's update-key seeds (minted by the self-enrolling flow, which
136
+ * therefore holds them and can sign the add entry)
137
+ * @param [options.expectedDid] {string} the account DID the log must resolve
138
+ * to, from the credential-authenticated pointer
139
+ * @returns {Promise<{ did: string, webDoc?: object }>} the account DID and,
140
+ * when the add entry ran here, the final `did.json` projection for the
141
+ * enrolled session to republish
142
+ */
143
+ export declare function selfEnrollWebvhClient(options: {
144
+ store: UnlockLogStore;
145
+ ladderSeed: Uint8Array;
146
+ newClientKeys: WebvhEnrollmentKeys;
147
+ newClientUpdateSeeds: ClientWebvhUpdateKeys;
148
+ expectedDid?: string;
149
+ }): Promise<{
150
+ did: string;
151
+ webDoc?: object;
152
+ }>;
153
+ //# sourceMappingURL=standingWebvh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standingWebvh.d.ts","sourceRoot":"","sources":["../../src/unlock/standingWebvh.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAU,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAe3E,OAAO,KAAK,EACV,qBAAqB,EAErB,mBAAmB,EACnB,YAAY,EACb,MAAM,sBAAsB,CAAA;AAG7B;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,YAAY,EACZ,kBAAkB,GAAG,eAAe,CACrC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,6BAA6B,GACvC;IAAE,kBAAkB,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,6BAA6B,CAAA;IAC3C,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,YAAY,EACb,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,6BAA6B,CAAA;CAC5C,GAAG,MAAM,CAMT;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,2BAA2B,CAAC,EAC1C,GAAG,EACH,YAAY,EACb,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,6BAA6B,CAAA;CAC5C,GAAG,kBAAkB,CAgBrB;AAsDD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,EAAE,qBAAqB,CAAA;IACjC,UAAU,EAAE,kBAAkB,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAI3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,EAAE,qBAAqB,CAAA;IACjC,UAAU,EAAE,kBAAkB,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAI3B;AAkGD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,KAAK,EAAE,cAAc,CAAA;IACrB,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,mBAAmB,CAAA;IAClC,oBAAoB,EAAE,qBAAqB,CAAA;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAE5C"}