@dnsid-ai/sdk 0.29.0 → 0.31.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 CHANGED
@@ -68,6 +68,40 @@ If `ManagedKeyRotationActivationError` is raised, registry acceptance succeeded
68
68
  but local key-state reconciliation is incomplete. Resume with the error's exact
69
69
  rotation state; already-completed activation or supersession is not repeated.
70
70
 
71
+ Rotation requires `persistRotation`, `setApplicationSigningPaused`,
72
+ `setApplicationSigner`, and `verifyPublicationEvidence`. The verifier must return
73
+ fresh protocol-verified signed TXT/ku evidence; evict old publication evidence
74
+ before verification. Acceptance is checked against the saved bytes/hash, new
75
+ key, and indexed log reference before publication and continuity checks.
76
+
77
+ Supply stable `previousProviderReference` and `newProviderReference` values;
78
+ `targetKeyProvider` optionally selects another provider. Recovery uses
79
+ `resolveKeyProvider(reference)` to restore the saved providers, not current
80
+ deployment settings. It restores the saved new signer before unpausing.
81
+ Old rotation records without the new bindings must not be resumed automatically.
82
+ Recovery rejects unrelated active source keys. A removed previous key is accepted
83
+ only with saved acceptance and the exact saved target key already active; for
84
+ cross-provider recovery, the source must have no active key. Provider lookup
85
+ errors, including a listed but unavailable previous key, fail recovery.
86
+ Resolvers must reopen the saved provider state, not create replacement state.
87
+
88
+ Set `targetAlgorithm` (`EdDSA` or `ES256`) when changing algorithms; omission
89
+ preserves the previous algorithm. The target's `validateKeyGeneration(algorithm)`
90
+ checks settings/support before generation, and rotation checks the generated key
91
+ before registry preparation. Providers without this hook must already have an
92
+ active key using the selected algorithm. Local generation accepts either
93
+ algorithm; AWS generation must match the configured KMS algorithm.
94
+
95
+ Custom providers that can retire their active key may implement
96
+ `activeSigningKey()` to return `null` when none exists. Without this hook,
97
+ recovery treats an empty `listKeyIds()` as no active key and otherwise calls
98
+ `signingKey()`; lookup errors are never treated as completed supersession.
99
+
100
+ Cross-provider transfer requires a previous provider that supports superseding
101
+ its active key after transfer. Built-in local and AWS providers currently reject
102
+ this operation. AWS and CLI/in-memory provider state also needs application-owned
103
+ durable recovery. These limits are declared in `SDK_CONFORMANCE.knownDeviations`.
104
+
71
105
  ## Node.js convenience usage
72
106
 
73
107
  ```ts
package/dist/index.cjs CHANGED
@@ -128,18 +128,35 @@ var ManagedKeyRotationActivationError = class extends import_protocol.Verificati
128
128
  }
129
129
  };
130
130
  async function rotateManagedOperationalKey(options) {
131
+ assertCoordination(options);
132
+ const domain = (0, import_protocol.normalizeFQDN)(options.domain, true);
133
+ assertOperationReferences(options.previousProviderReference, options.newProviderReference, options.idempotencyKey);
134
+ const target = options.targetKeyProvider ?? options.keyProvider;
135
+ if (target === options.keyProvider !== (options.previousProviderReference === options.newProviderReference)) {
136
+ throw new import_protocol.ArgumentError("provider references must identify the selected previous and target providers");
137
+ }
131
138
  const previousKey = await options.keyProvider.signingKey();
132
139
  new import_protocol.JWKS([previousKey]).validateOperational();
133
140
  const previousThumbprint = await (0, import_protocol.jwkThumbprint)(previousKey);
134
- const newKid = await options.keyProvider.generateKey();
135
- const newKey = await options.keyProvider.jwk(newKid);
141
+ const algorithm = options.targetAlgorithm ?? previousKey.alg;
142
+ if (algorithm !== "EdDSA" && algorithm !== "ES256") throw new import_protocol.ArgumentError("unsupported target algorithm");
143
+ if (target.validateKeyGeneration) {
144
+ await target.validateKeyGeneration(algorithm);
145
+ } else {
146
+ const active = await target.signingKey();
147
+ new import_protocol.JWKS([active]).validateOperational();
148
+ if (active.alg !== algorithm) throw new import_protocol.ArgumentError("target provider must validate support for the selected algorithm");
149
+ }
150
+ const newKid = await target.generateKey(algorithm);
151
+ const newKey = await target.jwk(newKid);
136
152
  new import_protocol.JWKS([newKey]).validateOperational();
153
+ if (newKey.alg !== algorithm) throw permanentRotationError("generated key does not use the selected algorithm");
137
154
  if (await (0, import_protocol.jwkThumbprint)(newKey) === previousThumbprint) {
138
155
  throw permanentRotationError("managed KEY_ROTATION requires a distinct pending operational key");
139
156
  }
140
157
  let raw;
141
158
  try {
142
- raw = await options.registryClient.prepareKeyRotation(options.domain, {
159
+ raw = await options.registryClient.prepareKeyRotation(domain, {
143
160
  previousKeyId: previousThumbprint,
144
161
  publicKey: newKey
145
162
  }, options.idempotencyKey);
@@ -153,12 +170,12 @@ async function rotateManagedOperationalKey(options) {
153
170
  if (raw.logReference !== options.logReference) {
154
171
  throw permanentRotationError(`registry preparation returned unexpected log reference ${raw.logReference}`);
155
172
  }
156
- const context = { expectedFqdn: options.domain, previousOperationalKey: previousKey };
173
+ const context = { expectedFqdn: domain, previousOperationalKey: previousKey };
157
174
  let prepared = await (0, import_writer.parsePreparedC2spTlogEvent)(raw.entryBytes, raw.logReference, context);
158
175
  if (prepared.envelope.type !== "KEY_ROTATION") {
159
176
  throw permanentRotationError(`registry preparation returned ${String(prepared.envelope.type)} instead of KEY_ROTATION`);
160
177
  }
161
- if (prepared.envelope.fqdn !== options.domain) {
178
+ if (prepared.envelope.fqdn !== domain) {
162
179
  throw permanentRotationError("registry KEY_ROTATION fqdn does not match the expected identity");
163
180
  }
164
181
  if (prepared.envelope.prev_thumb !== previousThumbprint) {
@@ -166,40 +183,61 @@ async function rotateManagedOperationalKey(options) {
166
183
  }
167
184
  await assertPreparedRotationKey(prepared.envelope.new_ku, prepared.envelope.new_thumb, newKey);
168
185
  prepared = await (0, import_writer.signPreparedC2spTlogEvent)(prepared, "PreviousOperational", options.keyProvider, context);
169
- prepared = await (0, import_writer.signPreparedC2spTlogEvent)(prepared, "NewOperational", options.keyProvider, context);
186
+ prepared = await (0, import_writer.signPreparedC2spTlogEvent)(prepared, "NewOperational", target, context);
170
187
  const entryBytes = await (0, import_writer.c2spTlogEntryBytes)(prepared, context);
171
188
  const rotation = {
172
- domain: options.domain,
189
+ domain,
173
190
  logReference: options.logReference,
174
191
  previousKid: previousKey.kid,
175
192
  newKid,
193
+ previousProviderReference: options.previousProviderReference,
194
+ newProviderReference: options.newProviderReference,
195
+ previousKey,
196
+ previousThumbprint,
197
+ newThumbprint: await (0, import_protocol.jwkThumbprint)(newKey),
198
+ entryHash: await sha256Hex(entryBytes),
176
199
  entryBytes: entryBytes.slice(),
177
200
  idempotencyKey: options.idempotencyKey,
178
201
  activated: false,
179
202
  applicationSigningPaused: true
180
203
  };
181
- await options.persistRotation(rotation);
182
- await options.setApplicationSigningPaused(true);
183
- return submitManagedRotation(options.keyProvider, options.registryClient, rotation, options.persistRotation, options.setApplicationSigningPaused);
204
+ try {
205
+ await options.persistRotation(rotation);
206
+ await options.setApplicationSigningPaused(true);
207
+ } catch (cause) {
208
+ throw new ManagedKeyRotationSubmissionError("managed rotation persistence or pause failed; resume this state", rotation, true, cause);
209
+ }
210
+ return submitManagedRotation(options, options.keyProvider, target, rotation);
184
211
  }
185
212
  async function resumeManagedOperationalKeyRotation(options) {
186
- if (options.rotation.activated) {
187
- if (!options.rotation.applicationSigningPaused) return options.rotation;
188
- await options.setApplicationSigningPaused(false);
189
- const completed = { ...options.rotation, applicationSigningPaused: false };
190
- await options.persistRotation(completed);
191
- return completed;
192
- }
193
- if (options.rotation.submission?.state === "rejected") {
194
- throw permanentRotationError("cannot resume a rejected managed key rotation");
213
+ assertCoordination(options);
214
+ if (typeof options.resolveKeyProvider !== "function") throw new import_protocol.ArgumentError("managed rotation recovery requires resolveKeyProvider");
215
+ const rotation = options.rotation;
216
+ try {
217
+ assertOperationReferences(rotation.previousProviderReference, rotation.newProviderReference, rotation.idempotencyKey);
218
+ const previous = await options.resolveKeyProvider(rotation.previousProviderReference);
219
+ const target = rotation.newProviderReference === rotation.previousProviderReference ? previous : await options.resolveKeyProvider(rotation.newProviderReference);
220
+ await validateSavedRotation(rotation, previous, target);
221
+ if (rotation.submission?.state === "rejected") {
222
+ throw new ManagedKeyRotationSubmissionError("cannot resume a rejected managed key rotation", rotation, false);
223
+ }
224
+ await options.setApplicationSigningPaused(true);
225
+ if (rotation.submission?.state === "accepted") {
226
+ await assertSubmissionMatchesRotation(rotation, rotation.submission);
227
+ return activateRotation(options, previous, target, rotation);
228
+ }
229
+ if (rotation.activated) throw permanentRotationError("activated rotation has no accepted submission");
230
+ await options.persistRotation(rotation);
231
+ return submitManagedRotation(options, previous, target, rotation);
232
+ } catch (cause) {
233
+ if (cause instanceof ManagedKeyRotationSubmissionError || cause instanceof ManagedKeyRotationActivationError) throw cause;
234
+ throw new ManagedKeyRotationActivationError("managed rotation recovery failed; signing must remain paused", rotation, cause);
195
235
  }
196
- await options.setApplicationSigningPaused(true);
197
- return submitManagedRotation(options.keyProvider, options.registryClient, options.rotation, options.persistRotation, options.setApplicationSigningPaused);
198
236
  }
199
- async function submitManagedRotation(keyProvider, registryClient, rotation, persistRotation, setApplicationSigningPaused) {
237
+ async function submitManagedRotation(options, previous, target, rotation) {
200
238
  let submission;
201
239
  try {
202
- submission = await registryClient.submitPreparedEvent(rotation.domain, rotation.entryBytes, rotation.idempotencyKey);
240
+ submission = await options.registryClient.submitPreparedEvent(rotation.domain, rotation.entryBytes, rotation.idempotencyKey);
203
241
  } catch (cause) {
204
242
  const retryWithSameBytes2 = structuredRetryWithSameBytes(cause);
205
243
  throw new ManagedKeyRotationSubmissionError(
@@ -209,9 +247,13 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
209
247
  cause
210
248
  );
211
249
  }
212
- await assertSubmissionMatchesRotation(rotation, submission);
213
250
  const next = { ...rotation, submission };
214
- await persistRotation(next);
251
+ try {
252
+ await assertSubmissionMatchesRotation(rotation, submission);
253
+ await options.persistRotation(next);
254
+ } catch (cause) {
255
+ throw new ManagedKeyRotationSubmissionError("managed rotation acceptance binding or persistence failed; resume this state", next, true, cause);
256
+ }
215
257
  if (submission.state === "rejected") {
216
258
  throw new ManagedKeyRotationSubmissionError(
217
259
  `registry rejected the prepared KEY_ROTATION: ${submission.errorCode ?? "unknown error"}`,
@@ -220,15 +262,24 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
220
262
  );
221
263
  }
222
264
  if (submission.state !== "accepted") return next;
223
- let recoverable = next;
265
+ return activateRotation(options, previous, target, next);
266
+ }
267
+ async function activateRotation(options, previous, target, rotation) {
268
+ let recoverable = rotation;
224
269
  try {
225
- await reconcileAcceptedRotation(keyProvider, rotation);
226
- const activated = { ...next, activated: true };
227
- await persistRotation(activated);
270
+ await verifyAcceptedPublication(options, rotation);
271
+ if (!rotation.activated) await reconcileAcceptedRotation(previous, target, rotation);
272
+ if (previous !== target && await activeKey(previous, await previous.listKeyIds())) {
273
+ throw permanentRotationError("superseded source provider still has an active signing key");
274
+ }
275
+ const activated = { ...rotation, activated: true, applicationSigningPaused: true };
276
+ await options.persistRotation(activated);
228
277
  recoverable = activated;
229
- await setApplicationSigningPaused(false);
278
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
279
+ await options.setApplicationSigner(target, rotation.newKid);
280
+ await options.setApplicationSigningPaused(false);
230
281
  const completed = { ...activated, applicationSigningPaused: false };
231
- await persistRotation(completed);
282
+ await options.persistRotation(completed);
232
283
  return completed;
233
284
  } catch (cause) {
234
285
  throw new ManagedKeyRotationActivationError(
@@ -238,15 +289,84 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
238
289
  );
239
290
  }
240
291
  }
241
- async function reconcileAcceptedRotation(keyProvider, rotation) {
242
- const activeKid = (await keyProvider.signingKey()).kid;
243
- if (activeKid === rotation.previousKid) {
244
- await keyProvider.activate(rotation.newKid);
245
- } else if (activeKid !== rotation.newKid) {
246
- throw permanentRotationError(`unexpected active key ${activeKid} while reconciling managed key rotation`);
292
+ async function reconcileAcceptedRotation(previous, target, rotation) {
293
+ const active = await target.signingKey();
294
+ if (active.kid !== rotation.newKid) {
295
+ if (previous === target) await assertKeyBinding(active, rotation.previousKid, rotation.previousThumbprint);
296
+ await target.activate(rotation.newKid);
297
+ }
298
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
299
+ if ((await previous.listKeyIds()).includes(rotation.previousKid)) await previous.supersede(rotation.previousKid);
300
+ }
301
+ function assertCoordination(options) {
302
+ for (const name of ["persistRotation", "setApplicationSigningPaused", "setApplicationSigner", "verifyPublicationEvidence"]) {
303
+ if (typeof options[name] !== "function") throw new import_protocol.ArgumentError(`managed rotation requires ${name}`);
304
+ }
305
+ }
306
+ function assertOperationReferences(previous, next, idempotencyKey) {
307
+ if (!previous?.trim() || !next?.trim() || !idempotencyKey || idempotencyKey.trim() !== idempotencyKey || idempotencyKey.length > 200) {
308
+ throw permanentRotationError("managed rotation requires provider references and a valid idempotency key");
247
309
  }
248
- if ((await keyProvider.listKeyIds()).includes(rotation.previousKid)) {
249
- await keyProvider.supersede(rotation.previousKid);
310
+ }
311
+ async function assertKeyBinding(key, kid, thumbprint) {
312
+ new import_protocol.JWKS([key]).validateOperational();
313
+ if (key.kid !== kid || await (0, import_protocol.jwkThumbprint)(key) !== thumbprint) {
314
+ throw permanentRotationError("saved managed rotation key binding mismatch");
315
+ }
316
+ }
317
+ async function activeKey(provider, listedIds) {
318
+ if (provider.activeSigningKey) return provider.activeSigningKey();
319
+ return listedIds.length ? provider.signingKey() : null;
320
+ }
321
+ async function validateSavedRotation(rotation, previous, target) {
322
+ assertOperationReferences(rotation.previousProviderReference, rotation.newProviderReference, rotation.idempotencyKey);
323
+ if (typeof rotation.activated !== "boolean" || typeof rotation.applicationSigningPaused !== "boolean" || (0, import_protocol.normalizeFQDN)(rotation.domain, true) !== rotation.domain || await sha256Hex(rotation.entryBytes) !== rotation.entryHash) {
324
+ throw permanentRotationError("saved managed rotation domain or entry hash mismatch");
325
+ }
326
+ await assertKeyBinding(rotation.previousKey, rotation.previousKid, rotation.previousThumbprint);
327
+ const previousIds = await previous.listKeyIds();
328
+ const sourceActive = await activeKey(previous, previousIds);
329
+ if (previousIds.includes(rotation.previousKid)) {
330
+ await assertKeyBinding(await previous.jwk(rotation.previousKid), rotation.previousKid, rotation.previousThumbprint);
331
+ if (sourceActive) {
332
+ const targetIsActive = previous === target && rotation.submission?.state === "accepted" && sourceActive.kid === rotation.newKid;
333
+ await assertKeyBinding(
334
+ sourceActive,
335
+ targetIsActive ? rotation.newKid : rotation.previousKid,
336
+ targetIsActive ? rotation.newThumbprint : rotation.previousThumbprint
337
+ );
338
+ }
339
+ } else {
340
+ if (rotation.submission?.state !== "accepted") throw permanentRotationError("saved previous key is unavailable without saved acceptance");
341
+ await assertSubmissionMatchesRotation(rotation, rotation.submission);
342
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
343
+ if (previous !== target && sourceActive) throw permanentRotationError("superseded source provider still has an active signing key");
344
+ }
345
+ const next = await target.jwk(rotation.newKid);
346
+ await assertKeyBinding(next, rotation.newKid, rotation.newThumbprint);
347
+ const context = { expectedFqdn: rotation.domain, previousOperationalKey: rotation.previousKey };
348
+ const prepared = await (0, import_writer.parsePreparedC2spTlogEvent)(rotation.entryBytes, rotation.logReference, context);
349
+ if (prepared.envelope.type !== "KEY_ROTATION" || prepared.envelope.fqdn !== rotation.domain || prepared.envelope.prev_thumb !== rotation.previousThumbprint) {
350
+ throw permanentRotationError("saved managed rotation event binding mismatch");
351
+ }
352
+ await assertPreparedRotationKey(prepared.envelope.new_ku, prepared.envelope.new_thumb, next);
353
+ await (0, import_writer.c2spTlogEntryBytes)(prepared, context);
354
+ if (rotation.submission) await assertSubmissionMatchesRotation(rotation, rotation.submission);
355
+ }
356
+ async function verifyAcceptedPublication(options, rotation) {
357
+ const verified = await options.verifyPublicationEvidence(rotation.domain);
358
+ if (verified.domain !== rotation.domain || verified.record.lr !== rotation.logReference) {
359
+ throw permanentRotationError("current signed publication does not match managed rotation");
360
+ }
361
+ await assertKeyBinding(verified.jwks.currentOperationalSigningKey(), rotation.newKid, rotation.newThumbprint);
362
+ const prepared = await (0, import_writer.parsePreparedC2spTlogEvent)(rotation.entryBytes, rotation.logReference, {
363
+ expectedFqdn: rotation.domain,
364
+ previousOperationalKey: rotation.previousKey
365
+ });
366
+ const signatures = prepared.envelope.sigs;
367
+ const event = await verified.logReader.readEvent(rotation.submission.logRef);
368
+ if (event.type !== "KEY_ROTATION" || event.domain !== rotation.domain || event.previousOperationalThumbprint !== rotation.previousThumbprint || event.newOperationalKid !== rotation.newKid || event.newOperationalThumbprint !== rotation.newThumbprint || event.signingKid !== signatures.prev_op.kid || event.sig !== signatures.prev_op.sig || event.newOperationalProof !== signatures.new_op.sig || event.timestamp.getTime() !== prepared.envelope.ts * 1e3) {
369
+ throw permanentRotationError("accepted managed rotation continuity mismatch");
250
370
  }
251
371
  }
252
372
  async function assertPreparedRotationKey(value, thumbprint, expected) {
@@ -263,12 +383,18 @@ async function assertSubmissionMatchesRotation(rotation, submission) {
263
383
  if (rotation.submission?.entryHash && submission.entryHash && rotation.submission.entryHash !== submission.entryHash) {
264
384
  throw permanentRotationError("registry reconciled a different entry hash for this managed key rotation");
265
385
  }
386
+ if (submission.entryHash !== void 0 && submission.entryHash !== rotation.entryHash) {
387
+ throw permanentRotationError("submission entry hash does not match the exact managed key rotation bytes");
388
+ }
266
389
  if (submission.state !== "accepted") return;
267
390
  const expectedHash = await sha256Hex(rotation.entryBytes);
268
391
  if (submission.entryHash !== expectedHash) {
269
392
  throw permanentRotationError("accepted submission entry hash does not match the exact managed key rotation bytes");
270
393
  }
271
- if (submission.keyId !== void 0 && submission.keyId !== rotation.newKid) {
394
+ if (submission.logRef !== `${rotation.logReference}@${submission.index}` || !Number.isSafeInteger(submission.index) || submission.index < 0) {
395
+ throw permanentRotationError("accepted submission log reference does not match managed rotation");
396
+ }
397
+ if (submission.keyId !== rotation.newKid) {
272
398
  throw permanentRotationError("accepted submission key_id does not match the generated pending key");
273
399
  }
274
400
  }
@@ -579,7 +705,11 @@ var SDK_CONFORMANCE = Object.freeze({
579
705
  logBindings: Object.freeze({
580
706
  "c2sp-tlog": c2spTlogConformance
581
707
  }),
582
- knownDeviations: Object.freeze([])
708
+ knownDeviations: Object.freeze([
709
+ "Managed cross-provider rotation requires providers that can supersede the previous active key after transfer; built-in LocalKeyProvider and AwsKmsKeyProvider reject active-key supersession.",
710
+ "AWS KMS and CLI/in-memory key providers do not durably persist rotation key state; restart recovery requires application-managed provider state or a file-backed local key store.",
711
+ "Account onboarding validates bindings and reports proof readiness, but does not implement named managed creation or establish permanent registration replay claims."
712
+ ])
583
713
  });
584
714
 
585
715
  // src/index.ts
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { VerificationError, KeyProvider, DnsIdJWK, IdentityManagerDependencies, DnsidConfig, IdentityManager } from '@dnsid-ai/protocol';
1
+ import { VerificationError, DnsIdJWK, KeyProvider, VerifiedDomain, IdentityManagerDependencies, DnsidConfig, IdentityManager } from '@dnsid-ai/protocol';
2
2
  export * from '@dnsid-ai/protocol';
3
3
  export { ArgumentError, DNSID_VERSION, DNSSECMode, DNSSECState, DnsIdTxtRecord, DomainLog, DomainSnapshot, IdentityManager, InMemoryIdentityCache, JWKS, JWKS_MAX_RESPONSE_BYTES, LogRegistry, NoopLogReader, ParseError, SIGNING_ALGS, STATUS_MAX_RESPONSE_BYTES, ValidationError, VerificationCode, VerificationError, VerifiedDomain, activeStatusDocument, fromBase64Url, isDomainName, isTransientVerificationError, jwkSignatureAlg, jwkThumbprint, normalizeFQDN, parseKaDuration, parseKeyId, retryTransientVerification, toArrayBuffer, toBase64Url, validateAgentStatus, verifyWithKey } from '@dnsid-ai/protocol';
4
4
  import * as jose from '@dnsid-ai/jose';
@@ -19,24 +19,42 @@ interface ManagedKeyRotationRegistry {
19
19
  prepareKeyRotation(domain: string, request: KeyRotationPreparationRequest, idempotencyKey: string): Promise<PreparedRegistryEvent>;
20
20
  submitPreparedEvent(domain: string, entryBytes: Uint8Array, idempotencyKey: string): Promise<SubmissionResult>;
21
21
  }
22
- interface RotateManagedOperationalKeyOptions {
22
+ interface ManagedKeyRotationCoordination {
23
+ /** Durably persists exact bytes and every subsequent reconciliation state. */
24
+ persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
25
+ /** Blocks new application signing and drains in-flight request/directory signing before resolving;
26
+ * resumes signing only after durable activation. */
27
+ setApplicationSigningPaused: (paused: boolean) => Promise<void>;
28
+ /** Restores the application signer before signing is resumed. */
29
+ setApplicationSigner: (provider: KeyProvider, kid: string) => Promise<void>;
30
+ /** Must return fresh, protocol-verified signed TXT/ku evidence, not cached pre-rotation evidence. */
31
+ verifyPublicationEvidence: (domain: string) => Promise<VerifiedDomain>;
32
+ }
33
+ interface RotateManagedOperationalKeyOptions extends ManagedKeyRotationCoordination {
23
34
  domain: string;
24
35
  /** Exact unindexed c2sp-tlog reference configured for this identity. */
25
36
  logReference: string;
26
37
  keyProvider: KeyProvider;
38
+ previousProviderReference: string;
39
+ /** Omit to rotate within the previous provider. */
40
+ targetKeyProvider?: KeyProvider;
41
+ /** Defaults to the previous key's algorithm. Required when changing algorithms. */
42
+ targetAlgorithm?: 'EdDSA' | 'ES256';
43
+ newProviderReference: string;
27
44
  registryClient: ManagedKeyRotationRegistry;
28
45
  idempotencyKey: string;
29
- /** Durably persists exact bytes and every subsequent reconciliation state. */
30
- persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
31
- /** Blocks new application signing and drains in-flight request/directory signing before resolving;
32
- * resumes signing only after durable activation. */
33
- setApplicationSigningPaused: (paused: boolean) => Promise<void>;
34
46
  }
35
47
  interface ManagedKeyRotationResult {
36
48
  readonly domain: string;
37
49
  readonly logReference: string;
38
50
  readonly previousKid: string;
39
51
  readonly newKid: string;
52
+ readonly previousProviderReference: string;
53
+ readonly newProviderReference: string;
54
+ readonly previousKey: DnsIdJWK;
55
+ readonly previousThumbprint: string;
56
+ readonly newThumbprint: string;
57
+ readonly entryHash: string;
40
58
  /**
41
59
  * Persist these exact bytes with idempotencyKey while activated is false.
42
60
  * Callers must pause new application signing until this rotation is accepted.
@@ -47,12 +65,11 @@ interface ManagedKeyRotationResult {
47
65
  readonly activated: boolean;
48
66
  readonly applicationSigningPaused: boolean;
49
67
  }
50
- interface ResumeManagedOperationalKeyRotationOptions {
51
- keyProvider: KeyProvider;
68
+ interface ResumeManagedOperationalKeyRotationOptions extends ManagedKeyRotationCoordination {
69
+ /** Resolves durable references, never the current deployment's selected provider. */
70
+ resolveKeyProvider: (reference: string) => Promise<KeyProvider>;
52
71
  registryClient: Pick<ManagedKeyRotationRegistry, 'submitPreparedEvent'>;
53
72
  rotation: ManagedKeyRotationResult;
54
- persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
55
- setApplicationSigningPaused: (paused: boolean) => Promise<void>;
56
73
  }
57
74
  /** Submission failed after the completed entry bytes were fixed. */
58
75
  declare class ManagedKeyRotationSubmissionError extends VerificationError {
@@ -208,4 +225,4 @@ declare function createIdentityManager(config: DnsidConfig, deps: CreateIdentity
208
225
  */
209
226
  declare function createIdentityVerifier(config: Omit<DnsidConfig, 'identity'>, deps: Omit<CreateIdentityManagerDependencies, 'keyProvider' | 'entityKeyProvider'>): IdentityManager;
210
227
 
211
- export { type CreateIdentityManagerDependencies, type IssueManagedIdentityOptions, ManagedIssuanceActivationError, type ManagedIssuanceCoordination, type ManagedIssuanceRegistry, type ManagedIssuanceState, type ManagedIssuanceSubmission, ManagedIssuanceSubmissionError, ManagedKeyRotationActivationError, type ManagedKeyRotationRegistry, type ManagedKeyRotationResult, ManagedKeyRotationSubmissionError, type ResumeManagedIssuanceOptions, type ResumeManagedOperationalKeyRotationOptions, type RotateManagedOperationalKeyOptions, type SDKConformance, SDK_CONFORMANCE, createIdentityManager, createIdentityVerifier, issueManagedIdentity, resumeManagedIssuance, resumeManagedOperationalKeyRotation, rotateManagedOperationalKey };
228
+ export { type CreateIdentityManagerDependencies, type IssueManagedIdentityOptions, ManagedIssuanceActivationError, type ManagedIssuanceCoordination, type ManagedIssuanceRegistry, type ManagedIssuanceState, type ManagedIssuanceSubmission, ManagedIssuanceSubmissionError, ManagedKeyRotationActivationError, type ManagedKeyRotationCoordination, type ManagedKeyRotationRegistry, type ManagedKeyRotationResult, ManagedKeyRotationSubmissionError, type ResumeManagedIssuanceOptions, type ResumeManagedOperationalKeyRotationOptions, type RotateManagedOperationalKeyOptions, type SDKConformance, SDK_CONFORMANCE, createIdentityManager, createIdentityVerifier, issueManagedIdentity, resumeManagedIssuance, resumeManagedOperationalKeyRotation, rotateManagedOperationalKey };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VerificationError, KeyProvider, DnsIdJWK, IdentityManagerDependencies, DnsidConfig, IdentityManager } from '@dnsid-ai/protocol';
1
+ import { VerificationError, DnsIdJWK, KeyProvider, VerifiedDomain, IdentityManagerDependencies, DnsidConfig, IdentityManager } from '@dnsid-ai/protocol';
2
2
  export * from '@dnsid-ai/protocol';
3
3
  export { ArgumentError, DNSID_VERSION, DNSSECMode, DNSSECState, DnsIdTxtRecord, DomainLog, DomainSnapshot, IdentityManager, InMemoryIdentityCache, JWKS, JWKS_MAX_RESPONSE_BYTES, LogRegistry, NoopLogReader, ParseError, SIGNING_ALGS, STATUS_MAX_RESPONSE_BYTES, ValidationError, VerificationCode, VerificationError, VerifiedDomain, activeStatusDocument, fromBase64Url, isDomainName, isTransientVerificationError, jwkSignatureAlg, jwkThumbprint, normalizeFQDN, parseKaDuration, parseKeyId, retryTransientVerification, toArrayBuffer, toBase64Url, validateAgentStatus, verifyWithKey } from '@dnsid-ai/protocol';
4
4
  import * as jose from '@dnsid-ai/jose';
@@ -19,24 +19,42 @@ interface ManagedKeyRotationRegistry {
19
19
  prepareKeyRotation(domain: string, request: KeyRotationPreparationRequest, idempotencyKey: string): Promise<PreparedRegistryEvent>;
20
20
  submitPreparedEvent(domain: string, entryBytes: Uint8Array, idempotencyKey: string): Promise<SubmissionResult>;
21
21
  }
22
- interface RotateManagedOperationalKeyOptions {
22
+ interface ManagedKeyRotationCoordination {
23
+ /** Durably persists exact bytes and every subsequent reconciliation state. */
24
+ persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
25
+ /** Blocks new application signing and drains in-flight request/directory signing before resolving;
26
+ * resumes signing only after durable activation. */
27
+ setApplicationSigningPaused: (paused: boolean) => Promise<void>;
28
+ /** Restores the application signer before signing is resumed. */
29
+ setApplicationSigner: (provider: KeyProvider, kid: string) => Promise<void>;
30
+ /** Must return fresh, protocol-verified signed TXT/ku evidence, not cached pre-rotation evidence. */
31
+ verifyPublicationEvidence: (domain: string) => Promise<VerifiedDomain>;
32
+ }
33
+ interface RotateManagedOperationalKeyOptions extends ManagedKeyRotationCoordination {
23
34
  domain: string;
24
35
  /** Exact unindexed c2sp-tlog reference configured for this identity. */
25
36
  logReference: string;
26
37
  keyProvider: KeyProvider;
38
+ previousProviderReference: string;
39
+ /** Omit to rotate within the previous provider. */
40
+ targetKeyProvider?: KeyProvider;
41
+ /** Defaults to the previous key's algorithm. Required when changing algorithms. */
42
+ targetAlgorithm?: 'EdDSA' | 'ES256';
43
+ newProviderReference: string;
27
44
  registryClient: ManagedKeyRotationRegistry;
28
45
  idempotencyKey: string;
29
- /** Durably persists exact bytes and every subsequent reconciliation state. */
30
- persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
31
- /** Blocks new application signing and drains in-flight request/directory signing before resolving;
32
- * resumes signing only after durable activation. */
33
- setApplicationSigningPaused: (paused: boolean) => Promise<void>;
34
46
  }
35
47
  interface ManagedKeyRotationResult {
36
48
  readonly domain: string;
37
49
  readonly logReference: string;
38
50
  readonly previousKid: string;
39
51
  readonly newKid: string;
52
+ readonly previousProviderReference: string;
53
+ readonly newProviderReference: string;
54
+ readonly previousKey: DnsIdJWK;
55
+ readonly previousThumbprint: string;
56
+ readonly newThumbprint: string;
57
+ readonly entryHash: string;
40
58
  /**
41
59
  * Persist these exact bytes with idempotencyKey while activated is false.
42
60
  * Callers must pause new application signing until this rotation is accepted.
@@ -47,12 +65,11 @@ interface ManagedKeyRotationResult {
47
65
  readonly activated: boolean;
48
66
  readonly applicationSigningPaused: boolean;
49
67
  }
50
- interface ResumeManagedOperationalKeyRotationOptions {
51
- keyProvider: KeyProvider;
68
+ interface ResumeManagedOperationalKeyRotationOptions extends ManagedKeyRotationCoordination {
69
+ /** Resolves durable references, never the current deployment's selected provider. */
70
+ resolveKeyProvider: (reference: string) => Promise<KeyProvider>;
52
71
  registryClient: Pick<ManagedKeyRotationRegistry, 'submitPreparedEvent'>;
53
72
  rotation: ManagedKeyRotationResult;
54
- persistRotation: (rotation: ManagedKeyRotationResult) => Promise<void>;
55
- setApplicationSigningPaused: (paused: boolean) => Promise<void>;
56
73
  }
57
74
  /** Submission failed after the completed entry bytes were fixed. */
58
75
  declare class ManagedKeyRotationSubmissionError extends VerificationError {
@@ -208,4 +225,4 @@ declare function createIdentityManager(config: DnsidConfig, deps: CreateIdentity
208
225
  */
209
226
  declare function createIdentityVerifier(config: Omit<DnsidConfig, 'identity'>, deps: Omit<CreateIdentityManagerDependencies, 'keyProvider' | 'entityKeyProvider'>): IdentityManager;
210
227
 
211
- export { type CreateIdentityManagerDependencies, type IssueManagedIdentityOptions, ManagedIssuanceActivationError, type ManagedIssuanceCoordination, type ManagedIssuanceRegistry, type ManagedIssuanceState, type ManagedIssuanceSubmission, ManagedIssuanceSubmissionError, ManagedKeyRotationActivationError, type ManagedKeyRotationRegistry, type ManagedKeyRotationResult, ManagedKeyRotationSubmissionError, type ResumeManagedIssuanceOptions, type ResumeManagedOperationalKeyRotationOptions, type RotateManagedOperationalKeyOptions, type SDKConformance, SDK_CONFORMANCE, createIdentityManager, createIdentityVerifier, issueManagedIdentity, resumeManagedIssuance, resumeManagedOperationalKeyRotation, rotateManagedOperationalKey };
228
+ export { type CreateIdentityManagerDependencies, type IssueManagedIdentityOptions, ManagedIssuanceActivationError, type ManagedIssuanceCoordination, type ManagedIssuanceRegistry, type ManagedIssuanceState, type ManagedIssuanceSubmission, ManagedIssuanceSubmissionError, ManagedKeyRotationActivationError, type ManagedKeyRotationCoordination, type ManagedKeyRotationRegistry, type ManagedKeyRotationResult, ManagedKeyRotationSubmissionError, type ResumeManagedIssuanceOptions, type ResumeManagedOperationalKeyRotationOptions, type RotateManagedOperationalKeyOptions, type SDKConformance, SDK_CONFORMANCE, createIdentityManager, createIdentityVerifier, issueManagedIdentity, resumeManagedIssuance, resumeManagedOperationalKeyRotation, rotateManagedOperationalKey };
package/dist/index.js CHANGED
@@ -25,8 +25,8 @@ import {
25
25
  ValidationError,
26
26
  VerificationError as VerificationError3,
27
27
  VerificationCode as VerificationCode3,
28
- ArgumentError,
29
- normalizeFQDN as normalizeFQDN2,
28
+ ArgumentError as ArgumentError2,
29
+ normalizeFQDN as normalizeFQDN3,
30
30
  isDomainName,
31
31
  parseKeyId,
32
32
  toBase64Url,
@@ -55,6 +55,8 @@ import {
55
55
 
56
56
  // src/managed-key-rotation.ts
57
57
  import {
58
+ ArgumentError,
59
+ normalizeFQDN,
58
60
  JWKS,
59
61
  VerificationCode,
60
62
  VerificationError,
@@ -84,18 +86,35 @@ var ManagedKeyRotationActivationError = class extends VerificationError {
84
86
  }
85
87
  };
86
88
  async function rotateManagedOperationalKey(options) {
89
+ assertCoordination(options);
90
+ const domain = normalizeFQDN(options.domain, true);
91
+ assertOperationReferences(options.previousProviderReference, options.newProviderReference, options.idempotencyKey);
92
+ const target = options.targetKeyProvider ?? options.keyProvider;
93
+ if (target === options.keyProvider !== (options.previousProviderReference === options.newProviderReference)) {
94
+ throw new ArgumentError("provider references must identify the selected previous and target providers");
95
+ }
87
96
  const previousKey = await options.keyProvider.signingKey();
88
97
  new JWKS([previousKey]).validateOperational();
89
98
  const previousThumbprint = await jwkThumbprint(previousKey);
90
- const newKid = await options.keyProvider.generateKey();
91
- const newKey = await options.keyProvider.jwk(newKid);
99
+ const algorithm = options.targetAlgorithm ?? previousKey.alg;
100
+ if (algorithm !== "EdDSA" && algorithm !== "ES256") throw new ArgumentError("unsupported target algorithm");
101
+ if (target.validateKeyGeneration) {
102
+ await target.validateKeyGeneration(algorithm);
103
+ } else {
104
+ const active = await target.signingKey();
105
+ new JWKS([active]).validateOperational();
106
+ if (active.alg !== algorithm) throw new ArgumentError("target provider must validate support for the selected algorithm");
107
+ }
108
+ const newKid = await target.generateKey(algorithm);
109
+ const newKey = await target.jwk(newKid);
92
110
  new JWKS([newKey]).validateOperational();
111
+ if (newKey.alg !== algorithm) throw permanentRotationError("generated key does not use the selected algorithm");
93
112
  if (await jwkThumbprint(newKey) === previousThumbprint) {
94
113
  throw permanentRotationError("managed KEY_ROTATION requires a distinct pending operational key");
95
114
  }
96
115
  let raw;
97
116
  try {
98
- raw = await options.registryClient.prepareKeyRotation(options.domain, {
117
+ raw = await options.registryClient.prepareKeyRotation(domain, {
99
118
  previousKeyId: previousThumbprint,
100
119
  publicKey: newKey
101
120
  }, options.idempotencyKey);
@@ -109,12 +128,12 @@ async function rotateManagedOperationalKey(options) {
109
128
  if (raw.logReference !== options.logReference) {
110
129
  throw permanentRotationError(`registry preparation returned unexpected log reference ${raw.logReference}`);
111
130
  }
112
- const context = { expectedFqdn: options.domain, previousOperationalKey: previousKey };
131
+ const context = { expectedFqdn: domain, previousOperationalKey: previousKey };
113
132
  let prepared = await parsePreparedC2spTlogEvent(raw.entryBytes, raw.logReference, context);
114
133
  if (prepared.envelope.type !== "KEY_ROTATION") {
115
134
  throw permanentRotationError(`registry preparation returned ${String(prepared.envelope.type)} instead of KEY_ROTATION`);
116
135
  }
117
- if (prepared.envelope.fqdn !== options.domain) {
136
+ if (prepared.envelope.fqdn !== domain) {
118
137
  throw permanentRotationError("registry KEY_ROTATION fqdn does not match the expected identity");
119
138
  }
120
139
  if (prepared.envelope.prev_thumb !== previousThumbprint) {
@@ -122,40 +141,61 @@ async function rotateManagedOperationalKey(options) {
122
141
  }
123
142
  await assertPreparedRotationKey(prepared.envelope.new_ku, prepared.envelope.new_thumb, newKey);
124
143
  prepared = await signPreparedC2spTlogEvent(prepared, "PreviousOperational", options.keyProvider, context);
125
- prepared = await signPreparedC2spTlogEvent(prepared, "NewOperational", options.keyProvider, context);
144
+ prepared = await signPreparedC2spTlogEvent(prepared, "NewOperational", target, context);
126
145
  const entryBytes = await c2spTlogEntryBytes(prepared, context);
127
146
  const rotation = {
128
- domain: options.domain,
147
+ domain,
129
148
  logReference: options.logReference,
130
149
  previousKid: previousKey.kid,
131
150
  newKid,
151
+ previousProviderReference: options.previousProviderReference,
152
+ newProviderReference: options.newProviderReference,
153
+ previousKey,
154
+ previousThumbprint,
155
+ newThumbprint: await jwkThumbprint(newKey),
156
+ entryHash: await sha256Hex(entryBytes),
132
157
  entryBytes: entryBytes.slice(),
133
158
  idempotencyKey: options.idempotencyKey,
134
159
  activated: false,
135
160
  applicationSigningPaused: true
136
161
  };
137
- await options.persistRotation(rotation);
138
- await options.setApplicationSigningPaused(true);
139
- return submitManagedRotation(options.keyProvider, options.registryClient, rotation, options.persistRotation, options.setApplicationSigningPaused);
162
+ try {
163
+ await options.persistRotation(rotation);
164
+ await options.setApplicationSigningPaused(true);
165
+ } catch (cause) {
166
+ throw new ManagedKeyRotationSubmissionError("managed rotation persistence or pause failed; resume this state", rotation, true, cause);
167
+ }
168
+ return submitManagedRotation(options, options.keyProvider, target, rotation);
140
169
  }
141
170
  async function resumeManagedOperationalKeyRotation(options) {
142
- if (options.rotation.activated) {
143
- if (!options.rotation.applicationSigningPaused) return options.rotation;
144
- await options.setApplicationSigningPaused(false);
145
- const completed = { ...options.rotation, applicationSigningPaused: false };
146
- await options.persistRotation(completed);
147
- return completed;
148
- }
149
- if (options.rotation.submission?.state === "rejected") {
150
- throw permanentRotationError("cannot resume a rejected managed key rotation");
171
+ assertCoordination(options);
172
+ if (typeof options.resolveKeyProvider !== "function") throw new ArgumentError("managed rotation recovery requires resolveKeyProvider");
173
+ const rotation = options.rotation;
174
+ try {
175
+ assertOperationReferences(rotation.previousProviderReference, rotation.newProviderReference, rotation.idempotencyKey);
176
+ const previous = await options.resolveKeyProvider(rotation.previousProviderReference);
177
+ const target = rotation.newProviderReference === rotation.previousProviderReference ? previous : await options.resolveKeyProvider(rotation.newProviderReference);
178
+ await validateSavedRotation(rotation, previous, target);
179
+ if (rotation.submission?.state === "rejected") {
180
+ throw new ManagedKeyRotationSubmissionError("cannot resume a rejected managed key rotation", rotation, false);
181
+ }
182
+ await options.setApplicationSigningPaused(true);
183
+ if (rotation.submission?.state === "accepted") {
184
+ await assertSubmissionMatchesRotation(rotation, rotation.submission);
185
+ return activateRotation(options, previous, target, rotation);
186
+ }
187
+ if (rotation.activated) throw permanentRotationError("activated rotation has no accepted submission");
188
+ await options.persistRotation(rotation);
189
+ return submitManagedRotation(options, previous, target, rotation);
190
+ } catch (cause) {
191
+ if (cause instanceof ManagedKeyRotationSubmissionError || cause instanceof ManagedKeyRotationActivationError) throw cause;
192
+ throw new ManagedKeyRotationActivationError("managed rotation recovery failed; signing must remain paused", rotation, cause);
151
193
  }
152
- await options.setApplicationSigningPaused(true);
153
- return submitManagedRotation(options.keyProvider, options.registryClient, options.rotation, options.persistRotation, options.setApplicationSigningPaused);
154
194
  }
155
- async function submitManagedRotation(keyProvider, registryClient, rotation, persistRotation, setApplicationSigningPaused) {
195
+ async function submitManagedRotation(options, previous, target, rotation) {
156
196
  let submission;
157
197
  try {
158
- submission = await registryClient.submitPreparedEvent(rotation.domain, rotation.entryBytes, rotation.idempotencyKey);
198
+ submission = await options.registryClient.submitPreparedEvent(rotation.domain, rotation.entryBytes, rotation.idempotencyKey);
159
199
  } catch (cause) {
160
200
  const retryWithSameBytes2 = structuredRetryWithSameBytes(cause);
161
201
  throw new ManagedKeyRotationSubmissionError(
@@ -165,9 +205,13 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
165
205
  cause
166
206
  );
167
207
  }
168
- await assertSubmissionMatchesRotation(rotation, submission);
169
208
  const next = { ...rotation, submission };
170
- await persistRotation(next);
209
+ try {
210
+ await assertSubmissionMatchesRotation(rotation, submission);
211
+ await options.persistRotation(next);
212
+ } catch (cause) {
213
+ throw new ManagedKeyRotationSubmissionError("managed rotation acceptance binding or persistence failed; resume this state", next, true, cause);
214
+ }
171
215
  if (submission.state === "rejected") {
172
216
  throw new ManagedKeyRotationSubmissionError(
173
217
  `registry rejected the prepared KEY_ROTATION: ${submission.errorCode ?? "unknown error"}`,
@@ -176,15 +220,24 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
176
220
  );
177
221
  }
178
222
  if (submission.state !== "accepted") return next;
179
- let recoverable = next;
223
+ return activateRotation(options, previous, target, next);
224
+ }
225
+ async function activateRotation(options, previous, target, rotation) {
226
+ let recoverable = rotation;
180
227
  try {
181
- await reconcileAcceptedRotation(keyProvider, rotation);
182
- const activated = { ...next, activated: true };
183
- await persistRotation(activated);
228
+ await verifyAcceptedPublication(options, rotation);
229
+ if (!rotation.activated) await reconcileAcceptedRotation(previous, target, rotation);
230
+ if (previous !== target && await activeKey(previous, await previous.listKeyIds())) {
231
+ throw permanentRotationError("superseded source provider still has an active signing key");
232
+ }
233
+ const activated = { ...rotation, activated: true, applicationSigningPaused: true };
234
+ await options.persistRotation(activated);
184
235
  recoverable = activated;
185
- await setApplicationSigningPaused(false);
236
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
237
+ await options.setApplicationSigner(target, rotation.newKid);
238
+ await options.setApplicationSigningPaused(false);
186
239
  const completed = { ...activated, applicationSigningPaused: false };
187
- await persistRotation(completed);
240
+ await options.persistRotation(completed);
188
241
  return completed;
189
242
  } catch (cause) {
190
243
  throw new ManagedKeyRotationActivationError(
@@ -194,15 +247,84 @@ async function submitManagedRotation(keyProvider, registryClient, rotation, pers
194
247
  );
195
248
  }
196
249
  }
197
- async function reconcileAcceptedRotation(keyProvider, rotation) {
198
- const activeKid = (await keyProvider.signingKey()).kid;
199
- if (activeKid === rotation.previousKid) {
200
- await keyProvider.activate(rotation.newKid);
201
- } else if (activeKid !== rotation.newKid) {
202
- throw permanentRotationError(`unexpected active key ${activeKid} while reconciling managed key rotation`);
250
+ async function reconcileAcceptedRotation(previous, target, rotation) {
251
+ const active = await target.signingKey();
252
+ if (active.kid !== rotation.newKid) {
253
+ if (previous === target) await assertKeyBinding(active, rotation.previousKid, rotation.previousThumbprint);
254
+ await target.activate(rotation.newKid);
255
+ }
256
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
257
+ if ((await previous.listKeyIds()).includes(rotation.previousKid)) await previous.supersede(rotation.previousKid);
258
+ }
259
+ function assertCoordination(options) {
260
+ for (const name of ["persistRotation", "setApplicationSigningPaused", "setApplicationSigner", "verifyPublicationEvidence"]) {
261
+ if (typeof options[name] !== "function") throw new ArgumentError(`managed rotation requires ${name}`);
262
+ }
263
+ }
264
+ function assertOperationReferences(previous, next, idempotencyKey) {
265
+ if (!previous?.trim() || !next?.trim() || !idempotencyKey || idempotencyKey.trim() !== idempotencyKey || idempotencyKey.length > 200) {
266
+ throw permanentRotationError("managed rotation requires provider references and a valid idempotency key");
267
+ }
268
+ }
269
+ async function assertKeyBinding(key, kid, thumbprint) {
270
+ new JWKS([key]).validateOperational();
271
+ if (key.kid !== kid || await jwkThumbprint(key) !== thumbprint) {
272
+ throw permanentRotationError("saved managed rotation key binding mismatch");
203
273
  }
204
- if ((await keyProvider.listKeyIds()).includes(rotation.previousKid)) {
205
- await keyProvider.supersede(rotation.previousKid);
274
+ }
275
+ async function activeKey(provider, listedIds) {
276
+ if (provider.activeSigningKey) return provider.activeSigningKey();
277
+ return listedIds.length ? provider.signingKey() : null;
278
+ }
279
+ async function validateSavedRotation(rotation, previous, target) {
280
+ assertOperationReferences(rotation.previousProviderReference, rotation.newProviderReference, rotation.idempotencyKey);
281
+ if (typeof rotation.activated !== "boolean" || typeof rotation.applicationSigningPaused !== "boolean" || normalizeFQDN(rotation.domain, true) !== rotation.domain || await sha256Hex(rotation.entryBytes) !== rotation.entryHash) {
282
+ throw permanentRotationError("saved managed rotation domain or entry hash mismatch");
283
+ }
284
+ await assertKeyBinding(rotation.previousKey, rotation.previousKid, rotation.previousThumbprint);
285
+ const previousIds = await previous.listKeyIds();
286
+ const sourceActive = await activeKey(previous, previousIds);
287
+ if (previousIds.includes(rotation.previousKid)) {
288
+ await assertKeyBinding(await previous.jwk(rotation.previousKid), rotation.previousKid, rotation.previousThumbprint);
289
+ if (sourceActive) {
290
+ const targetIsActive = previous === target && rotation.submission?.state === "accepted" && sourceActive.kid === rotation.newKid;
291
+ await assertKeyBinding(
292
+ sourceActive,
293
+ targetIsActive ? rotation.newKid : rotation.previousKid,
294
+ targetIsActive ? rotation.newThumbprint : rotation.previousThumbprint
295
+ );
296
+ }
297
+ } else {
298
+ if (rotation.submission?.state !== "accepted") throw permanentRotationError("saved previous key is unavailable without saved acceptance");
299
+ await assertSubmissionMatchesRotation(rotation, rotation.submission);
300
+ await assertKeyBinding(await target.signingKey(), rotation.newKid, rotation.newThumbprint);
301
+ if (previous !== target && sourceActive) throw permanentRotationError("superseded source provider still has an active signing key");
302
+ }
303
+ const next = await target.jwk(rotation.newKid);
304
+ await assertKeyBinding(next, rotation.newKid, rotation.newThumbprint);
305
+ const context = { expectedFqdn: rotation.domain, previousOperationalKey: rotation.previousKey };
306
+ const prepared = await parsePreparedC2spTlogEvent(rotation.entryBytes, rotation.logReference, context);
307
+ if (prepared.envelope.type !== "KEY_ROTATION" || prepared.envelope.fqdn !== rotation.domain || prepared.envelope.prev_thumb !== rotation.previousThumbprint) {
308
+ throw permanentRotationError("saved managed rotation event binding mismatch");
309
+ }
310
+ await assertPreparedRotationKey(prepared.envelope.new_ku, prepared.envelope.new_thumb, next);
311
+ await c2spTlogEntryBytes(prepared, context);
312
+ if (rotation.submission) await assertSubmissionMatchesRotation(rotation, rotation.submission);
313
+ }
314
+ async function verifyAcceptedPublication(options, rotation) {
315
+ const verified = await options.verifyPublicationEvidence(rotation.domain);
316
+ if (verified.domain !== rotation.domain || verified.record.lr !== rotation.logReference) {
317
+ throw permanentRotationError("current signed publication does not match managed rotation");
318
+ }
319
+ await assertKeyBinding(verified.jwks.currentOperationalSigningKey(), rotation.newKid, rotation.newThumbprint);
320
+ const prepared = await parsePreparedC2spTlogEvent(rotation.entryBytes, rotation.logReference, {
321
+ expectedFqdn: rotation.domain,
322
+ previousOperationalKey: rotation.previousKey
323
+ });
324
+ const signatures = prepared.envelope.sigs;
325
+ const event = await verified.logReader.readEvent(rotation.submission.logRef);
326
+ if (event.type !== "KEY_ROTATION" || event.domain !== rotation.domain || event.previousOperationalThumbprint !== rotation.previousThumbprint || event.newOperationalKid !== rotation.newKid || event.newOperationalThumbprint !== rotation.newThumbprint || event.signingKid !== signatures.prev_op.kid || event.sig !== signatures.prev_op.sig || event.newOperationalProof !== signatures.new_op.sig || event.timestamp.getTime() !== prepared.envelope.ts * 1e3) {
327
+ throw permanentRotationError("accepted managed rotation continuity mismatch");
206
328
  }
207
329
  }
208
330
  async function assertPreparedRotationKey(value, thumbprint, expected) {
@@ -219,12 +341,18 @@ async function assertSubmissionMatchesRotation(rotation, submission) {
219
341
  if (rotation.submission?.entryHash && submission.entryHash && rotation.submission.entryHash !== submission.entryHash) {
220
342
  throw permanentRotationError("registry reconciled a different entry hash for this managed key rotation");
221
343
  }
344
+ if (submission.entryHash !== void 0 && submission.entryHash !== rotation.entryHash) {
345
+ throw permanentRotationError("submission entry hash does not match the exact managed key rotation bytes");
346
+ }
222
347
  if (submission.state !== "accepted") return;
223
348
  const expectedHash = await sha256Hex(rotation.entryBytes);
224
349
  if (submission.entryHash !== expectedHash) {
225
350
  throw permanentRotationError("accepted submission entry hash does not match the exact managed key rotation bytes");
226
351
  }
227
- if (submission.keyId !== void 0 && submission.keyId !== rotation.newKid) {
352
+ if (submission.logRef !== `${rotation.logReference}@${submission.index}` || !Number.isSafeInteger(submission.index) || submission.index < 0) {
353
+ throw permanentRotationError("accepted submission log reference does not match managed rotation");
354
+ }
355
+ if (submission.keyId !== rotation.newKid) {
228
356
  throw permanentRotationError("accepted submission key_id does not match the generated pending key");
229
357
  }
230
358
  }
@@ -249,7 +377,7 @@ import {
249
377
  VerificationCode as VerificationCode2,
250
378
  VerificationError as VerificationError2,
251
379
  jwkThumbprint as jwkThumbprint2,
252
- normalizeFQDN
380
+ normalizeFQDN as normalizeFQDN2
253
381
  } from "@dnsid-ai/protocol";
254
382
  import {
255
383
  c2spTlogEntryBytes as c2spTlogEntryBytes2,
@@ -279,8 +407,8 @@ var ManagedIssuanceActivationError = class extends VerificationError2 {
279
407
  }
280
408
  };
281
409
  async function issueManagedIdentity(options) {
282
- const domain = normalizeFQDN(options.domain, true);
283
- const governanceId = normalizeFQDN(options.governanceId, true);
410
+ const domain = normalizeFQDN2(options.domain, true);
411
+ const governanceId = normalizeFQDN2(options.governanceId, true);
284
412
  if (!options.idempotencyKey || options.idempotencyKey.trim() !== options.idempotencyKey || options.idempotencyKey.length > 200) {
285
413
  throw permanentLogError("managed ISSUANCE idempotencyKey must contain 1 to 200 characters without surrounding whitespace");
286
414
  }
@@ -549,7 +677,11 @@ var SDK_CONFORMANCE = Object.freeze({
549
677
  logBindings: Object.freeze({
550
678
  "c2sp-tlog": c2spTlogConformance
551
679
  }),
552
- knownDeviations: Object.freeze([])
680
+ knownDeviations: Object.freeze([
681
+ "Managed cross-provider rotation requires providers that can supersede the previous active key after transfer; built-in LocalKeyProvider and AwsKmsKeyProvider reject active-key supersession.",
682
+ "AWS KMS and CLI/in-memory key providers do not durably persist rotation key state; restart recovery requires application-managed provider state or a file-backed local key store.",
683
+ "Account onboarding validates bindings and reports proof readiness, but does not implement named managed creation or establish permanent registration replay claims."
684
+ ])
553
685
  });
554
686
 
555
687
  // src/index.ts
@@ -560,7 +692,7 @@ function createIdentityVerifier(config, deps) {
560
692
  return new IdentityManager(config, deps);
561
693
  }
562
694
  export {
563
- ArgumentError,
695
+ ArgumentError2 as ArgumentError,
564
696
  DEFAULT_REGISTRY_URL,
565
697
  DNSID_VERSION2 as DNSID_VERSION,
566
698
  DNSSECMode,
@@ -607,7 +739,7 @@ export {
607
739
  jose,
608
740
  jwkSignatureAlg,
609
741
  jwkThumbprint3 as jwkThumbprint,
610
- normalizeFQDN2 as normalizeFQDN,
742
+ normalizeFQDN3 as normalizeFQDN,
611
743
  parseKaDuration,
612
744
  parseKeyId,
613
745
  publishClientControlledRecord,
package/dist/node.cjs CHANGED
@@ -162,9 +162,14 @@ var LocalKeyProvider = class _LocalKeyProvider {
162
162
  const sig = await crypto.subtle.sign(signAlgorithm(key), cryptoKey, toArrayBuffer(payload));
163
163
  return new Uint8Array(sig);
164
164
  }
165
- async generateKey() {
165
+ async validateKeyGeneration(algorithm) {
166
+ if (algorithm !== "EdDSA" && algorithm !== "ES256") throw new import_protocol.ArgumentError(`unsupported local key algorithm: ${algorithm}`);
167
+ }
168
+ async generateKey(algorithm) {
166
169
  return this.mutate(async (next) => {
167
- const key = await generateStoredKey(localKeyAlgorithm(next.active));
170
+ const selected = algorithm ?? localKeyAlgorithm(next.active);
171
+ await this.validateKeyGeneration(selected);
172
+ const key = await generateStoredKey(selected);
168
173
  next.pending.push(key);
169
174
  return key.kid;
170
175
  });
@@ -321,6 +326,13 @@ var import_protocol2 = require("@dnsid-ai/protocol");
321
326
  async function createNodeIdentityManager(config, deps = {}) {
322
327
  const { transport, ...core } = (0, import_protocol2.validateDnsidConfig)(config);
323
328
  const { dnsServer, caBundlePath, privateAddressHosts } = transport;
329
+ validateInjectedTransport(transport, deps);
330
+ const transportModule = deps.fetchJson && deps.dnsResolver ? null : await loadNodeTransport();
331
+ const fetchJson = deps.fetchJson ?? ((url, fetchOptions) => transportModule.fetchJson(url, { ...fetchOptions, dnsServer, caBundlePath, privateAddressHosts }));
332
+ const dnsResolver = deps.dnsResolver ?? transportModule.createDefaultDnsResolver({ dnsServer });
333
+ return new import_protocol2.IdentityManager(core, { ...deps, dnsResolver, fetchJson });
334
+ }
335
+ function validateInjectedTransport({ dnsServer, caBundlePath, privateAddressHosts }, deps) {
324
336
  if (dnsServer !== void 0 && deps.dnsResolver && deps.fetchJson) {
325
337
  throw new import_protocol2.ArgumentError("config.transport.dnsServer has no SDK-managed consumer when both dnsResolver and fetchJson are injected");
326
338
  }
@@ -330,10 +342,6 @@ async function createNodeIdentityManager(config, deps = {}) {
330
342
  if (privateAddressHosts !== void 0 && deps.fetchJson) {
331
343
  throw new import_protocol2.ArgumentError("config.transport.privateAddressHosts has no SDK-managed consumer when fetchJson is injected");
332
344
  }
333
- const transportModule = deps.fetchJson && deps.dnsResolver ? null : await loadNodeTransport();
334
- const fetchJson = deps.fetchJson ?? ((url, fetchOptions) => transportModule.fetchJson(url, { ...fetchOptions, dnsServer, caBundlePath, privateAddressHosts }));
335
- const dnsResolver = deps.dnsResolver ?? transportModule.createDefaultDnsResolver({ dnsServer });
336
- return new import_protocol2.IdentityManager(core, { ...deps, dnsResolver, fetchJson });
337
345
  }
338
346
  async function createNodeIdentityVerifier(config = {}, deps = {}) {
339
347
  return createNodeIdentityManager(config, deps);
@@ -559,7 +567,23 @@ function mergeSection(base, overlay) {
559
567
  }
560
568
  var MANAGED_DEFAULTS_MS = 10 * 60 * 1e3;
561
569
  async function constructIdentityManager(loaded, deps = {}) {
562
- (0, import_protocol3.validateDnsidConfig)(loaded.dnsid ?? {});
570
+ const config = (0, import_protocol3.validateDnsidConfig)(loaded.dnsid ?? {});
571
+ validateInjectedTransport(config.transport ?? {}, deps);
572
+ if (!config.identity && (deps.keyProvider || deps.entityKeyProvider)) {
573
+ throw new import_protocol3.ArgumentError("key providers have no consumer without config.identity");
574
+ }
575
+ if (config.identity && !deps.keyProvider) {
576
+ const source = loaded.keySource;
577
+ if (source) await validateKeySource(source);
578
+ if (source?.generation) throw new import_protocol3.ArgumentError("identity-manager construction opens existing keys only; use managed setup for generation");
579
+ if (!source?.keyRef && !source?.cliDirectory && !source?.keyStorePath) {
580
+ throw new import_protocol3.ArgumentError("deps.keyProvider or a configured operational key source is required when config.identity is set");
581
+ }
582
+ }
583
+ const entityKeyPath = loaded.dnsid?.identity && !deps.entityKeyProvider ? loaded.keySource?.entityKeyPath : void 0;
584
+ if (entityKeyPath !== void 0 && (typeof entityKeyPath !== "string" || !entityKeyPath.trim())) {
585
+ throw new import_protocol3.ArgumentError("keySource.entityKeyPath must be nonempty");
586
+ }
563
587
  const filled = { ...deps };
564
588
  if (!filled.logRegistry && loaded.logTrust) {
565
589
  filled.logRegistry = await logRegistryFromTrust(loaded.logTrust, loaded.dnsid?.transport);
@@ -621,9 +645,10 @@ async function operationalKeyProvider(source, domain) {
621
645
  async function validateKeySource(source = {}) {
622
646
  const provider = source.provider ?? "file";
623
647
  if (!["file", "aws-kms", "google-kms", "azure-key-vault"].includes(provider)) throw new import_protocol3.ArgumentError(`unknown key provider: ${provider}`);
624
- for (const field of ["keyRef", "keyStorePath", "cliDirectory", "entityKeyPath"]) {
648
+ for (const field of ["keyRef", "keyStorePath", "cliDirectory"]) {
625
649
  if (source[field] !== void 0 && (typeof source[field] !== "string" || !source[field].trim())) throw new import_protocol3.ArgumentError(`keySource.${field} must be nonempty`);
626
650
  }
651
+ if (source.settings !== void 0) object(source.settings, "keySource.settings");
627
652
  if (source.keyRef && source.generation) throw new import_protocol3.ArgumentError("keyRef and generation are mutually exclusive");
628
653
  if ((source.cliDirectory || source.keyStorePath) && (provider !== "file" || source.keyRef || source.generation)) throw new import_protocol3.ArgumentError("CLI/keyStorePath cannot combine with cloud, keyRef, or generation");
629
654
  if (source.generation && (typeof source.generation.locator !== "string" || !source.generation.locator.trim() || !["EdDSA", "ES256"].includes(source.generation.algorithm))) throw new import_protocol3.ArgumentError("generation requires a stable locator and supported EdDSA or ES256 algorithm");
package/dist/node.d.cts CHANGED
@@ -36,7 +36,8 @@ declare class LocalKeyProvider implements KeyProvider {
36
36
  sign(payload: Uint8Array): Promise<Uint8Array>;
37
37
  signKey(kid: string, payload: Uint8Array): Promise<Uint8Array>;
38
38
  private signWithKey;
39
- generateKey(): Promise<string>;
39
+ validateKeyGeneration(algorithm: string): Promise<void>;
40
+ generateKey(algorithm?: string): Promise<string>;
40
41
  activate(kid: string): Promise<void>;
41
42
  supersede(kid: string): Promise<void>;
42
43
  /** @deprecated Use supersede(). */
package/dist/node.d.ts CHANGED
@@ -36,7 +36,8 @@ declare class LocalKeyProvider implements KeyProvider {
36
36
  sign(payload: Uint8Array): Promise<Uint8Array>;
37
37
  signKey(kid: string, payload: Uint8Array): Promise<Uint8Array>;
38
38
  private signWithKey;
39
- generateKey(): Promise<string>;
39
+ validateKeyGeneration(algorithm: string): Promise<void>;
40
+ generateKey(algorithm?: string): Promise<string>;
40
41
  activate(kid: string): Promise<void>;
41
42
  supersede(kid: string): Promise<void>;
42
43
  /** @deprecated Use supersede(). */
package/dist/node.js CHANGED
@@ -115,9 +115,14 @@ var LocalKeyProvider = class _LocalKeyProvider {
115
115
  const sig = await crypto.subtle.sign(signAlgorithm(key), cryptoKey, toArrayBuffer(payload));
116
116
  return new Uint8Array(sig);
117
117
  }
118
- async generateKey() {
118
+ async validateKeyGeneration(algorithm) {
119
+ if (algorithm !== "EdDSA" && algorithm !== "ES256") throw new ArgumentError(`unsupported local key algorithm: ${algorithm}`);
120
+ }
121
+ async generateKey(algorithm) {
119
122
  return this.mutate(async (next) => {
120
- const key = await generateStoredKey(localKeyAlgorithm(next.active));
123
+ const selected = algorithm ?? localKeyAlgorithm(next.active);
124
+ await this.validateKeyGeneration(selected);
125
+ const key = await generateStoredKey(selected);
121
126
  next.pending.push(key);
122
127
  return key.kid;
123
128
  });
@@ -274,6 +279,13 @@ import { IdentityManager, ArgumentError as ArgumentError2, validateDnsidConfig }
274
279
  async function createNodeIdentityManager(config, deps = {}) {
275
280
  const { transport, ...core } = validateDnsidConfig(config);
276
281
  const { dnsServer, caBundlePath, privateAddressHosts } = transport;
282
+ validateInjectedTransport(transport, deps);
283
+ const transportModule = deps.fetchJson && deps.dnsResolver ? null : await loadNodeTransport();
284
+ const fetchJson = deps.fetchJson ?? ((url, fetchOptions) => transportModule.fetchJson(url, { ...fetchOptions, dnsServer, caBundlePath, privateAddressHosts }));
285
+ const dnsResolver = deps.dnsResolver ?? transportModule.createDefaultDnsResolver({ dnsServer });
286
+ return new IdentityManager(core, { ...deps, dnsResolver, fetchJson });
287
+ }
288
+ function validateInjectedTransport({ dnsServer, caBundlePath, privateAddressHosts }, deps) {
277
289
  if (dnsServer !== void 0 && deps.dnsResolver && deps.fetchJson) {
278
290
  throw new ArgumentError2("config.transport.dnsServer has no SDK-managed consumer when both dnsResolver and fetchJson are injected");
279
291
  }
@@ -283,10 +295,6 @@ async function createNodeIdentityManager(config, deps = {}) {
283
295
  if (privateAddressHosts !== void 0 && deps.fetchJson) {
284
296
  throw new ArgumentError2("config.transport.privateAddressHosts has no SDK-managed consumer when fetchJson is injected");
285
297
  }
286
- const transportModule = deps.fetchJson && deps.dnsResolver ? null : await loadNodeTransport();
287
- const fetchJson = deps.fetchJson ?? ((url, fetchOptions) => transportModule.fetchJson(url, { ...fetchOptions, dnsServer, caBundlePath, privateAddressHosts }));
288
- const dnsResolver = deps.dnsResolver ?? transportModule.createDefaultDnsResolver({ dnsServer });
289
- return new IdentityManager(core, { ...deps, dnsResolver, fetchJson });
290
298
  }
291
299
  async function createNodeIdentityVerifier(config = {}, deps = {}) {
292
300
  return createNodeIdentityManager(config, deps);
@@ -523,7 +531,23 @@ function mergeSection(base, overlay) {
523
531
  }
524
532
  var MANAGED_DEFAULTS_MS = 10 * 60 * 1e3;
525
533
  async function constructIdentityManager(loaded, deps = {}) {
526
- validateDnsidConfig2(loaded.dnsid ?? {});
534
+ const config = validateDnsidConfig2(loaded.dnsid ?? {});
535
+ validateInjectedTransport(config.transport ?? {}, deps);
536
+ if (!config.identity && (deps.keyProvider || deps.entityKeyProvider)) {
537
+ throw new ArgumentError3("key providers have no consumer without config.identity");
538
+ }
539
+ if (config.identity && !deps.keyProvider) {
540
+ const source = loaded.keySource;
541
+ if (source) await validateKeySource(source);
542
+ if (source?.generation) throw new ArgumentError3("identity-manager construction opens existing keys only; use managed setup for generation");
543
+ if (!source?.keyRef && !source?.cliDirectory && !source?.keyStorePath) {
544
+ throw new ArgumentError3("deps.keyProvider or a configured operational key source is required when config.identity is set");
545
+ }
546
+ }
547
+ const entityKeyPath = loaded.dnsid?.identity && !deps.entityKeyProvider ? loaded.keySource?.entityKeyPath : void 0;
548
+ if (entityKeyPath !== void 0 && (typeof entityKeyPath !== "string" || !entityKeyPath.trim())) {
549
+ throw new ArgumentError3("keySource.entityKeyPath must be nonempty");
550
+ }
527
551
  const filled = { ...deps };
528
552
  if (!filled.logRegistry && loaded.logTrust) {
529
553
  filled.logRegistry = await logRegistryFromTrust(loaded.logTrust, loaded.dnsid?.transport);
@@ -585,9 +609,10 @@ async function operationalKeyProvider(source, domain) {
585
609
  async function validateKeySource(source = {}) {
586
610
  const provider = source.provider ?? "file";
587
611
  if (!["file", "aws-kms", "google-kms", "azure-key-vault"].includes(provider)) throw new ArgumentError3(`unknown key provider: ${provider}`);
588
- for (const field of ["keyRef", "keyStorePath", "cliDirectory", "entityKeyPath"]) {
612
+ for (const field of ["keyRef", "keyStorePath", "cliDirectory"]) {
589
613
  if (source[field] !== void 0 && (typeof source[field] !== "string" || !source[field].trim())) throw new ArgumentError3(`keySource.${field} must be nonempty`);
590
614
  }
615
+ if (source.settings !== void 0) object(source.settings, "keySource.settings");
591
616
  if (source.keyRef && source.generation) throw new ArgumentError3("keyRef and generation are mutually exclusive");
592
617
  if ((source.cliDirectory || source.keyStorePath) && (provider !== "file" || source.keyRef || source.generation)) throw new ArgumentError3("CLI/keyStorePath cannot combine with cloud, keyRef, or generation");
593
618
  if (source.generation && (typeof source.generation.locator !== "string" || !source.generation.locator.trim() || !["EdDSA", "ES256"].includes(source.generation.algorithm))) throw new ArgumentError3("generation requires a stable locator and supported EdDSA or ES256 algorithm");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dnsid-ai/sdk",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "Runtime-neutral DNSid SDK entrypoint with optional Node.js convenience helpers.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -39,16 +39,16 @@
39
39
  "typecheck": "tsc --noEmit"
40
40
  },
41
41
  "dependencies": {
42
- "@dnsid-ai/protocol": "^0.29.0",
43
- "@dnsid-ai/http-signatures": "^0.29.0",
44
- "@dnsid-ai/jose": "^0.29.0",
45
- "@dnsid-ai/log-c2sp-tlog": "^0.29.0",
46
- "@dnsid-ai/registry": "^0.29.0",
47
- "@dnsid-ai/web-bot-auth": "^0.29.0"
42
+ "@dnsid-ai/protocol": "^0.31.0",
43
+ "@dnsid-ai/http-signatures": "^0.31.0",
44
+ "@dnsid-ai/jose": "^0.31.0",
45
+ "@dnsid-ai/log-c2sp-tlog": "^0.31.0",
46
+ "@dnsid-ai/registry": "^0.31.0",
47
+ "@dnsid-ai/web-bot-auth": "^0.31.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@dnsid-ai/key-aws": "^0.29.0",
51
- "@dnsid-ai/transport": "^0.29.0"
50
+ "@dnsid-ai/key-aws": "^0.31.0",
51
+ "@dnsid-ai/transport": "^0.31.0"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@dnsid-ai/key-aws": {