@agentunion/fastaun-browser 0.5.18 → 0.5.20

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 (149) hide show
  1. package/CHANGELOG.md +172 -79
  2. package/_packed_docs/CHANGELOG.md +172 -79
  3. package/_packed_docs/INDEX.md +244 -116
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +98 -69
  5. package/_packed_docs/agent.md/SCHEMA.md +245 -205
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +42 -15
  8. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -4
  9. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +45 -134
  10. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  11. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +5 -20
  12. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +687 -668
  13. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +19 -28
  14. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  15. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  16. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  17. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  18. package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
  19. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  20. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +68 -61
  21. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  22. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +279 -285
  23. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  24. package/_packed_docs/sdk/09-group-rpc-manual.md +271 -235
  25. package/_packed_docs/sdk/09-message-rpc-manual.md +47 -6
  26. package/_packed_docs/sdk/09-storage-rpc-manual.md +70 -68
  27. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +63 -34
  28. package/_packed_docs/sdk/INDEX.md +105 -34
  29. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  30. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  31. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  32. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  33. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
  34. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
  35. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
  36. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  37. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  38. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  39. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
  40. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  43. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  44. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  45. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  46. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  47. package/dist/agent-md-schema.d.ts +17 -3
  48. package/dist/agent-md-schema.d.ts.map +1 -1
  49. package/dist/agent-md-schema.js +94 -26
  50. package/dist/agent-md-schema.js.map +1 -1
  51. package/dist/agent-md.d.ts +54 -3
  52. package/dist/agent-md.d.ts.map +1 -1
  53. package/dist/agent-md.js +711 -65
  54. package/dist/agent-md.js.map +1 -1
  55. package/dist/aid-store.d.ts +58 -46
  56. package/dist/aid-store.d.ts.map +1 -1
  57. package/dist/aid-store.js +254 -248
  58. package/dist/aid-store.js.map +1 -1
  59. package/dist/aid.d.ts +4 -1
  60. package/dist/aid.d.ts.map +1 -1
  61. package/dist/aid.js +27 -33
  62. package/dist/aid.js.map +1 -1
  63. package/dist/auth.d.ts +4 -1
  64. package/dist/auth.d.ts.map +1 -1
  65. package/dist/auth.js +426 -39
  66. package/dist/auth.js.map +1 -1
  67. package/dist/bundle.js +38179 -32054
  68. package/dist/cert-utils.d.ts +50 -2
  69. package/dist/cert-utils.d.ts.map +1 -1
  70. package/dist/cert-utils.js +105 -47
  71. package/dist/cert-utils.js.map +1 -1
  72. package/dist/client/delivery.d.ts +22 -1
  73. package/dist/client/delivery.d.ts.map +1 -1
  74. package/dist/client/delivery.js +389 -78
  75. package/dist/client/delivery.js.map +1 -1
  76. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  77. package/dist/client/rpc-pipeline.js +103 -77
  78. package/dist/client/rpc-pipeline.js.map +1 -1
  79. package/dist/client/v2-e2ee.d.ts +1 -0
  80. package/dist/client/v2-e2ee.d.ts.map +1 -1
  81. package/dist/client/v2-e2ee.js +182 -89
  82. package/dist/client/v2-e2ee.js.map +1 -1
  83. package/dist/client.d.ts +100 -1
  84. package/dist/client.d.ts.map +1 -1
  85. package/dist/client.js +1220 -25
  86. package/dist/client.js.map +1 -1
  87. package/dist/crypto.d.ts.map +1 -1
  88. package/dist/crypto.js +8 -6
  89. package/dist/crypto.js.map +1 -1
  90. package/dist/errors.d.ts.map +1 -1
  91. package/dist/errors.js +3 -2
  92. package/dist/errors.js.map +1 -1
  93. package/dist/events.d.ts +9 -2
  94. package/dist/events.d.ts.map +1 -1
  95. package/dist/events.js +72 -13
  96. package/dist/events.js.map +1 -1
  97. package/dist/facades.d.ts +82 -2
  98. package/dist/facades.d.ts.map +1 -1
  99. package/dist/facades.js +100 -6
  100. package/dist/facades.js.map +1 -1
  101. package/dist/index.d.ts +7 -3
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +3 -2
  104. package/dist/index.js.map +1 -1
  105. package/dist/keystore/index.d.ts +17 -0
  106. package/dist/keystore/index.d.ts.map +1 -1
  107. package/dist/keystore/indexeddb-shared.d.ts +10 -1
  108. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  109. package/dist/keystore/indexeddb-shared.js +111 -0
  110. package/dist/keystore/indexeddb-shared.js.map +1 -1
  111. package/dist/keystore/indexeddb-token-store.d.ts +7 -1
  112. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  113. package/dist/keystore/indexeddb-token-store.js +30 -1
  114. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  115. package/dist/org-protocol.d.ts +98 -0
  116. package/dist/org-protocol.d.ts.map +1 -0
  117. package/dist/org-protocol.js +1802 -0
  118. package/dist/org-protocol.js.map +1 -0
  119. package/dist/ownership-proof.d.ts +93 -5
  120. package/dist/ownership-proof.d.ts.map +1 -1
  121. package/dist/ownership-proof.js +239 -53
  122. package/dist/ownership-proof.js.map +1 -1
  123. package/dist/ownership-v21.d.ts +194 -0
  124. package/dist/ownership-v21.d.ts.map +1 -0
  125. package/dist/ownership-v21.js +678 -0
  126. package/dist/ownership-v21.js.map +1 -0
  127. package/dist/ownership-workflow.d.ts +151 -0
  128. package/dist/ownership-workflow.d.ts.map +1 -0
  129. package/dist/ownership-workflow.js +1229 -0
  130. package/dist/ownership-workflow.js.map +1 -0
  131. package/dist/seq-tracker.d.ts +1 -0
  132. package/dist/seq-tracker.d.ts.map +1 -1
  133. package/dist/seq-tracker.js +10 -2
  134. package/dist/seq-tracker.js.map +1 -1
  135. package/dist/tools/cross-sdk-agent.d.ts +83 -0
  136. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  137. package/dist/tools/cross-sdk-agent.js +415 -7
  138. package/dist/tools/cross-sdk-agent.js.map +1 -1
  139. package/dist/transport.d.ts +7 -0
  140. package/dist/transport.d.ts.map +1 -1
  141. package/dist/transport.js +90 -2
  142. package/dist/transport.js.map +1 -1
  143. package/dist/v2/e2ee/decrypt.d.ts +1 -0
  144. package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
  145. package/dist/v2/e2ee/decrypt.js +9 -1
  146. package/dist/v2/e2ee/decrypt.js.map +1 -1
  147. package/dist/version.d.ts +1 -1
  148. package/dist/version.js +1 -1
  149. package/package.json +1 -1
package/dist/aid-store.js CHANGED
@@ -1,17 +1,37 @@
1
1
  import { AID } from './aid.js';
2
- import { CryptoProvider, pemToArrayBuffer, importPrivateKeyEcdsa, importCertPublicKeyEcdsa, ecdsaSignDer, ecdsaVerifyDer } from './crypto.js';
2
+ import { CryptoProvider, pemToArrayBuffer, importPrivateKeyEcdsa, importCertPublicKeyEcdsa, ecdsaSignDer, ecdsaVerifyDer, certificateSha256Fingerprint } from './crypto.js';
3
3
  import * as codes from './error-codes.js';
4
- import { certMatchesFingerprint, publicKeyDerB64 } from './cert-utils.js';
4
+ import { certMatchesFingerprint, parseAgentMdTailSignatureForRead, publicKeyDerB64, signDocument, verifyDocument, parseCertMetadata } from './cert-utils.js';
5
5
  import { RegisterFlow } from './register-flow.js';
6
6
  import { GatewayDiscovery } from './discovery.js';
7
- import { ClientSignatureError, IdentityConflictError, NotFoundError, ValidationError } from './errors.js';
7
+ import { AUNError, ClientSignatureError, IdentityConflictError, NotFoundError, StateError, ValidationError } from './errors.js';
8
8
  import { IndexedDBIdentityStore } from './keystore/indexeddb-identity-store.js';
9
9
  import { IndexedDBTokenStore } from './keystore/indexeddb-token-store.js';
10
10
  import { getDeviceId, normalizeSlotId } from './config.js';
11
11
  import { resultErr, resultOk } from './result.js';
12
12
  import { AgentMdManager, buildDefaultAgentMd } from './agent-md.js';
13
13
  import { AuthFlow } from './auth.js';
14
- import { OwnershipStatement, signOwnershipStatement } from './ownership-proof.js';
14
+ import { parseOwnershipRegistrationIntent } from './ownership-workflow.js';
15
+ import { validateAIDFormat } from './validators.js';
16
+ import { validateAgentMdDocument } from './agent-md-schema.js';
17
+ async function readSigningInput(input, inputType = 'text') {
18
+ if (inputType === 'text') {
19
+ if (typeof input === 'string' || input instanceof Uint8Array)
20
+ return input;
21
+ throw new Error('text input requires a string or Uint8Array');
22
+ }
23
+ if (typeof input === 'string' || input instanceof Uint8Array)
24
+ throw new Error('browser file input does not support filesystem paths');
25
+ const file = 'getFile' in input ? await input.getFile() : input;
26
+ if (typeof file.arrayBuffer === 'function')
27
+ return new Uint8Array(await file.arrayBuffer());
28
+ return await new Promise((resolve, reject) => {
29
+ const reader = new FileReader();
30
+ reader.onload = () => resolve(new Uint8Array(reader.result));
31
+ reader.onerror = () => reject(reader.error);
32
+ reader.readAsArrayBuffer(file);
33
+ });
34
+ }
15
35
  // ── 证书 DER 解析工具 ────────────────────────────────────────────
16
36
  function _derReadLength(data, offset) {
17
37
  if (offset >= data.length)
@@ -266,6 +286,8 @@ function resultError(result) {
266
286
  return result.ok ? null : result.error;
267
287
  }
268
288
  function agentMdFailure(exc) {
289
+ if (exc instanceof AUNError && exc.stringCode)
290
+ return resultErr(exc.stringCode, exc.message, exc);
269
291
  if (exc instanceof ClientSignatureError) {
270
292
  return resultErr(codes.SIGNATURE_INVALID, exc.message, exc);
271
293
  }
@@ -277,19 +299,6 @@ function agentMdFailure(exc) {
277
299
  }
278
300
  return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
279
301
  }
280
- function ownerAgentMd(content, ownerAid, proof) {
281
- const marker = content.indexOf('<!-- AUN-SIGNATURE');
282
- const payload = marker >= 0 ? content.slice(0, marker) : content;
283
- const lines = payload.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
284
- const end = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
285
- if (end < 0)
286
- throw new ValidationError('agent.md frontmatter is required for ownership operation');
287
- const filtered = lines.filter((line, index) => !(index > 0 && index < end && (/^\s*owner\s*:/.test(line) || /^\s*ownership_proof\s*:/.test(line))));
288
- const closed = filtered.findIndex((line, index) => index > 0 && line.trim() === '---');
289
- filtered.splice(closed, 0, `owner: ${JSON.stringify(ownerAid)}`, `ownership_proof: ${JSON.stringify(proof)}`);
290
- const result = filtered.join('\n');
291
- return result.endsWith('\n') ? result : `${result}\n`;
292
- }
293
302
  export class AIDStore {
294
303
  aunPath;
295
304
  deviceId;
@@ -337,10 +346,15 @@ export class AIDStore {
337
346
  tokenStore: this._tokenStore,
338
347
  aidValidator: (target) => this._registerFlow.validateAidName(target),
339
348
  gatewayResolver: (target) => this._resolveGateway(target),
340
- peerResolver: async (target, certFingerprint) => {
349
+ entityOwnerResolver: (target, type) => this._resolveEntityAgentMdOwner(target, type),
350
+ entityIdentityGetter: (target, type) => this._currentEntityCertificate(target, type ?? 'group'),
351
+ entitySignerResolver: (target, fingerprint) => this._currentEntitySigner(target, fingerprint),
352
+ peerResolver: async (target, certFingerprint, signedAt) => {
341
353
  const fp = String(certFingerprint ?? '').trim().toLowerCase();
342
354
  const cachedCert = await this._keystore.loadCert(target, fp || undefined);
343
355
  if (cachedCert) {
356
+ const gatewayUrl = await this._resolveGateway(target);
357
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, cachedCert, fp, signedAt);
344
358
  return await AID.create({ aid: target, aunPath: this.aunPath, certPem: cachedCert, privateKeyPem: null, certValid: true, privateKeyValid: false });
345
359
  }
346
360
  if (fp) {
@@ -349,7 +363,7 @@ export class AIDStore {
349
363
  if (!response.ok)
350
364
  throw new Error(`certificate not found for aid: ${target}`);
351
365
  const certPem = await response.text();
352
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp);
366
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt);
353
367
  await this._keystore.saveCert(target, certPem, fp, { makeActive: false });
354
368
  return await AID.create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
355
369
  }
@@ -357,7 +371,7 @@ export class AIDStore {
357
371
  const certPem = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
358
372
  if (!certPem)
359
373
  throw new Error(`certificate not found for aid: ${target}`);
360
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem);
374
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt);
361
375
  await this._keystore.saveCert(target, certPem);
362
376
  return await AID.create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
363
377
  },
@@ -371,6 +385,76 @@ export class AIDStore {
371
385
  if (typeof closeTokens === 'function')
372
386
  closeTokens.call(this._tokenStore);
373
387
  }
388
+ async sign(aid, input, options = {}) {
389
+ try {
390
+ if (options.inputType !== undefined && !['text', 'file'].includes(options.inputType) || options.format !== undefined && !['raw', 'document'].includes(options.format))
391
+ throw new Error('unsupported signing inputType or format');
392
+ if (options.append && options.format !== 'document')
393
+ throw new Error('append requires document format');
394
+ if (options.inPlace && (options.inputType !== 'file' || options.format !== 'document' || typeof input !== 'object' || !('createWritable' in input) || typeof input.createWritable !== 'function'))
395
+ throw new Error('inPlace document signing requires a writable file handle');
396
+ const loaded = options.identity ? resultOk({ aid: options.identity }) : await this.load(aid);
397
+ if (!loaded.ok)
398
+ return loaded;
399
+ if (loaded.data.aid.aid !== aid || parseCertMetadata(loaded.data.aid.certPem).subject.toLowerCase() !== aid.toLowerCase())
400
+ throw new Error('signing identity does not match AID');
401
+ const raw = await readSigningInput(input, options.inputType);
402
+ if (typeof raw === 'string' && new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(new TextEncoder().encode(raw)) !== raw)
403
+ throw new Error('signing input must be valid UTF-8');
404
+ if (options.format !== 'document')
405
+ return await loaded.data.aid.sign(raw);
406
+ const content = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
407
+ const result = await signDocument(loaded.data.aid, content, options);
408
+ if (options.inPlace) {
409
+ const writable = await input.createWritable();
410
+ await writable.write(result.signed);
411
+ await writable.close();
412
+ }
413
+ return resultOk(result);
414
+ }
415
+ catch (exc) {
416
+ return resultErr(codes.SIGNATURE_OPERATION_ERROR, String(exc), exc);
417
+ }
418
+ }
419
+ async verify(aid, input, signatureOrOptions = {}, options = {}) {
420
+ try {
421
+ const opts = typeof signatureOrOptions === 'string' ? options : signatureOrOptions;
422
+ if (opts.inputType !== undefined && !['text', 'file'].includes(opts.inputType) || opts.format !== undefined && !['raw', 'document'].includes(opts.format))
423
+ throw new Error('unsupported verification inputType or format');
424
+ if (opts.identity && (opts.identity.aid !== aid || parseCertMetadata(opts.identity.certPem).subject.toLowerCase() !== aid.toLowerCase()))
425
+ throw new Error('verification identity does not match AID');
426
+ const raw = await readSigningInput(input, opts.inputType);
427
+ if (typeof raw === 'string' && new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(new TextEncoder().encode(raw)) !== raw)
428
+ throw new Error('verification input must be valid UTF-8');
429
+ if (opts.format === 'document') {
430
+ const content = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
431
+ const resolver = opts.certificateResolver ?? (async (signer, fingerprint) => {
432
+ if (opts.identity?.aid === signer)
433
+ return opts.identity;
434
+ const resolved = await this.load(signer);
435
+ if (!resolved.ok)
436
+ throw new Error(resolved.error.message);
437
+ return resolved.data.aid;
438
+ });
439
+ return resultOk(await verifyDocument(content, resolver, { expectedAid: aid }));
440
+ }
441
+ if (typeof signatureOrOptions !== 'string')
442
+ throw new Error('raw verification requires a detached signature');
443
+ const supplied = !opts.identity && opts.certificateResolver ? await opts.certificateResolver(aid, '', 0) : undefined;
444
+ if (supplied && typeof supplied !== 'string' && 'aid' in supplied && supplied.aid !== aid)
445
+ throw new Error('verification certificate identity does not match AID');
446
+ const suppliedPem = typeof supplied === 'string' ? supplied : supplied?.certPem;
447
+ const loaded = opts.identity ? resultOk({ aid: opts.identity }) : suppliedPem ? resultOk({ aid: await AID.create({ aid, aunPath: this.aunPath, certPem: suppliedPem, privateKeyPem: null, certValid: true, privateKeyValid: false }) }) : await this.load(aid);
448
+ if (!loaded.ok)
449
+ return loaded;
450
+ if (loaded.data.aid.aid !== aid || parseCertMetadata(loaded.data.aid.certPem).subject.toLowerCase() !== aid.toLowerCase())
451
+ throw new Error('verification identity does not match AID');
452
+ return await loaded.data.aid.verify(raw, signatureOrOptions);
453
+ }
454
+ catch (exc) {
455
+ return resultErr(codes.VERIFICATION_OPERATION_ERROR, String(exc), exc);
456
+ }
457
+ }
374
458
  async load(aid) {
375
459
  const target = String(aid ?? '').trim();
376
460
  const certPem = await this._keystore.loadCert(target);
@@ -386,6 +470,9 @@ export class AIDStore {
386
470
  catch (exc) {
387
471
  return resultErr(codes.CERT_PARSE_ERROR, `certificate parse failed for aid: ${target}`, exc);
388
472
  }
473
+ if (await this._tokenStore.getMetadata?.(target, 'invalidated_entity_certificate') === await certificateSha256Fingerprint(certPem)) {
474
+ return resultErr(codes.CERT_NOT_VALID, `group/org identity has been invalidated: ${target}`);
475
+ }
389
476
  // 有效期检查(对齐 Python cert_time_error)
390
477
  const validity = parseCertValidity(certPem);
391
478
  if (!validity) {
@@ -492,12 +579,26 @@ export class AIDStore {
492
579
  listAsync() {
493
580
  return this.list();
494
581
  }
495
- async registerIdentity(aid, uploadDefault = true) {
582
+ async register(aid, options) {
496
583
  const target = String(aid ?? '').trim();
497
584
  try {
498
585
  if (!target)
499
586
  throw new ValidationError("AIDStore.register requires 'aid'");
500
587
  this._registerFlow.validateAidName(target);
588
+ const type = String(options?.type ?? '').trim(), owner = String(options?.owner ?? '').trim().toLowerCase();
589
+ const entity = ['group', 'org'].includes(type);
590
+ if (!type || type === 'human' && owner || entity && !owner)
591
+ throw new ValidationError('register requires type; human forbids owner; group/org requires owner');
592
+ if (owner)
593
+ validateAIDFormat(owner, 'owner');
594
+ const previousRaw = await this._tokenStore.getMetadata(target, 'registration_intent');
595
+ const previous = previousRaw ? parseOwnershipRegistrationIntent(previousRaw, target) : null;
596
+ const exttype = String(options?.exttype ?? '').trim();
597
+ if (previous && (previous.type !== type || previous.owner !== owner || previous.exttype !== exttype))
598
+ throw new StateError('ownership registration intent changed', { stringCode: 'conflict' });
599
+ const intent = previous ?? (entity ? { schema_version: 1, sub_aid: target, type, owner, exttype, protocol: 'entity', pending: false } : { schema_version: 1, sub_aid: target, type, owner, exttype, operation_id: crypto.randomUUID(), pending: Boolean(owner) });
600
+ if (type !== 'agent' || owner)
601
+ await this._tokenStore.setMetadata(target, 'registration_intent', JSON.stringify(intent));
501
602
  const gatewayUrl = await this._resolveGateway(target);
502
603
  const result = await this._registerFlow.registerAid(gatewayUrl, target);
503
604
  if (result.cert) {
@@ -511,11 +612,16 @@ export class AIDStore {
511
612
  });
512
613
  }
513
614
  await this._persistGatewayUrl(target, gatewayUrl);
514
- if (uploadDefault)
515
- await this.uploadAgentMd(target, buildDefaultAgentMd(target));
615
+ if (!entity) {
616
+ const snapshot = await this._agentMdManager.readOwnershipFresh(target).catch(() => null);
617
+ if (snapshot && !snapshot.exists && snapshot.relation_version === 0 && snapshot.lock === 'unlocked')
618
+ await this.uploadAgentMd(target, buildDefaultAgentMd(target, { type, exttype: intent.exttype }));
619
+ }
516
620
  return resultOk({ registered: true });
517
621
  }
518
622
  catch (exc) {
623
+ if (exc instanceof StateError && exc.stringCode)
624
+ return resultErr(exc.stringCode, exc.message, exc);
519
625
  if (exc instanceof IdentityConflictError) {
520
626
  return resultErr(codes.IDENTITY_CONFLICT, String(exc), exc);
521
627
  }
@@ -529,218 +635,6 @@ export class AIDStore {
529
635
  return resultErr(codes.SERVER_ERROR, message, exc);
530
636
  }
531
637
  }
532
- async register(aidOrParams) {
533
- if (typeof aidOrParams === 'string')
534
- return await this.registerIdentity(aidOrParams);
535
- const params = aidOrParams;
536
- const target = String(params?.aid ?? '').trim();
537
- const aidType = String(params?.type ?? '').trim().toLowerCase();
538
- const owner = String(params?.owner ?? params?.owner_aid ?? '').trim();
539
- if (!target || !aidType)
540
- return resultErr(codes.INVALID_AID_FORMAT, 'register requires aid and type');
541
- try {
542
- const identity = await this.registerIdentity(target, false);
543
- if (!identity.ok)
544
- return identity;
545
- const gatewayUrl = await this._resolveGateway(target);
546
- const prepareParams = { aid: target, type: aidType };
547
- if (owner)
548
- prepareParams.owner = owner;
549
- const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
550
- if (idempotencyKey)
551
- prepareParams.idempotency_key = idempotencyKey;
552
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_prepare', prepareParams);
553
- const operationId = String(prepared.operation_id ?? '').trim();
554
- if (!operationId)
555
- throw new ValidationError('aid_register_prepare response missing operation_id');
556
- let subjectSignature = params.subjectSignature ?? params.subject_signature;
557
- const ownerSignature = params.ownerSignature ?? params.owner_signature;
558
- if (!subjectSignature && owner) {
559
- const loaded = await this.load(target);
560
- if (loaded.ok) {
561
- const statement = OwnershipStatement.create(String(prepared.proof_id ?? ''), target, owner);
562
- const signed = await signOwnershipStatement({ certFingerprint: loaded.data.aid.certFingerprint, sign: (payload) => loaded.data.aid.sign(payload) }, statement);
563
- subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
564
- }
565
- }
566
- const signatures = {};
567
- if (subjectSignature)
568
- signatures.subject_signature = subjectSignature;
569
- if (ownerSignature)
570
- signatures.owner_signature = ownerSignature;
571
- let response = prepared;
572
- if (Object.keys(signatures).length > 0)
573
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_submit', { operation_id: operationId, ...signatures });
574
- const state = String(response.state ?? response.operation_state ?? '').toLowerCase();
575
- const complete = !owner || owner === target || (!!ownerSignature && !!subjectSignature);
576
- if (complete && state === 'agent_md_pending') {
577
- let content = params.agentMd ?? params.agent_md ?? buildDefaultAgentMd(target, { type: aidType });
578
- if (owner) {
579
- const proof = { proof_id: prepared.proof_id, subject_aid: target, owner_aid: owner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: subjectSignature };
580
- if (owner !== target)
581
- proof.owner_signature = ownerSignature;
582
- content = ownerAgentMd(content, owner, proof);
583
- }
584
- const uploaded = await this.uploadAgentMd(target, content, { ownerOperationId: operationId });
585
- if (!uploaded.ok)
586
- return uploaded;
587
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_activate', { operation_id: operationId });
588
- }
589
- return resultOk(response);
590
- }
591
- catch (exc) {
592
- if (exc instanceof ValidationError)
593
- return resultErr(codes.INVALID_AID_FORMAT, exc.message, exc);
594
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
595
- }
596
- }
597
- async create(params) { return await this.register(params); }
598
- async bindAid(params) {
599
- const target = String(params?.aid ?? '').trim();
600
- const aidType = String(params?.type ?? '').trim().toLowerCase();
601
- const owner = String(params?.owner ?? params?.owner_aid ?? '').trim();
602
- if (!target || !aidType)
603
- return resultErr(codes.INVALID_AID_FORMAT, 'bindAid requires aid and type');
604
- try {
605
- this._registerFlow.validateAidName(target);
606
- const gatewayUrl = await this._resolveGateway(target);
607
- const prepareParams = { aid: target, type: aidType };
608
- if (owner)
609
- prepareParams.owner = owner;
610
- const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
611
- if (idempotencyKey)
612
- prepareParams.idempotency_key = idempotencyKey;
613
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_prepare', prepareParams);
614
- const operationId = String(prepared.operation_id ?? '').trim();
615
- if (!operationId)
616
- throw new ValidationError('aid_bind_prepare response missing operation_id');
617
- const signatures = {};
618
- let subjectSignature = params.subjectSignature ?? params.subject_signature;
619
- if (!subjectSignature && owner) {
620
- const loaded = await this.load(target);
621
- if (loaded.ok) {
622
- const statement = OwnershipStatement.create(String(prepared.proof_id ?? ''), target, owner);
623
- const signed = await signOwnershipStatement(loaded.data.aid, statement);
624
- subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
625
- }
626
- }
627
- if (subjectSignature)
628
- signatures.subject_signature = subjectSignature;
629
- const ownerSignature = params.ownerSignature ?? params.owner_signature;
630
- if (ownerSignature)
631
- signatures.owner_signature = ownerSignature;
632
- let response = prepared;
633
- if (Object.keys(signatures).length > 0) {
634
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_submit', { operation_id: operationId, ...signatures });
635
- }
636
- const agentMd = params.agentMd ?? params.agent_md;
637
- if (agentMd !== undefined) {
638
- const uploaded = await this.uploadAgentMd(target, agentMd, {
639
- ownerOperationId: operationId,
640
- });
641
- if (!uploaded.ok)
642
- return uploaded;
643
- }
644
- const state = String(response.state ?? response.operation_state ?? '').toLowerCase();
645
- if (state === 'agent_md_pending' || (!Object.keys(signatures).length && !owner && String(prepared.state ?? '') === 'agent_md_pending')) {
646
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_activate', { operation_id: operationId });
647
- }
648
- return resultOk(response);
649
- }
650
- catch (exc) {
651
- if (exc instanceof ValidationError)
652
- return resultErr(codes.INVALID_AID_FORMAT, exc.message, exc);
653
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
654
- }
655
- }
656
- async getAidBindingStatus(aid) {
657
- const target = String(aid ?? '').trim();
658
- if (!target)
659
- return resultErr(codes.INVALID_AID_FORMAT, 'getAidBindingStatus requires aid');
660
- try {
661
- this._registerFlow.validateAidName(target);
662
- const gatewayUrl = await this._resolveGateway(target);
663
- return resultOk(await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_status', { aid: target }));
664
- }
665
- catch (exc) {
666
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
667
- }
668
- }
669
- async resumeAidBinding(operationId) {
670
- const target = String(operationId ?? '').trim();
671
- if (!target)
672
- return resultErr(codes.INVALID_AID_FORMAT, 'resumeAidBinding requires operationId');
673
- try {
674
- const gatewayUrl = await this._resolveGateway(target);
675
- let response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_operation_get', { operation_id: target });
676
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending') {
677
- const method = String(response.kind ?? '').toLowerCase() === 'owner_change' ? 'auth.owner_change_activate' : 'auth.aid_bind_activate';
678
- response = await this._registerFlow.shortRpc(gatewayUrl, method, { operation_id: target });
679
- }
680
- return resultOk(response);
681
- }
682
- catch (exc) {
683
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
684
- }
685
- }
686
- async bind_aid(params) { return this.bindAid(params); }
687
- async get_aid_binding_status(aid) { return this.getAidBindingStatus(aid); }
688
- async resume_aid_binding(operationId) { return this.resumeAidBinding(operationId); }
689
- async changeOwner(params) {
690
- const subject = String(params?.subject_aid ?? params?.aid ?? '').trim();
691
- const newOwner = String(params?.new_owner_aid ?? params?.new_owner ?? '').trim();
692
- if (!subject || !newOwner)
693
- return resultErr(codes.INVALID_AID_FORMAT, 'changeOwner requires subject_aid and new_owner_aid');
694
- try {
695
- this._registerFlow.validateAidName(subject);
696
- const gatewayUrl = await this._resolveGateway(subject);
697
- const prepare = { subject_aid: subject, new_owner_aid: newOwner };
698
- if (params.current_owner_aid ?? params.owner_aid)
699
- prepare.current_owner_aid = params.current_owner_aid ?? params.owner_aid;
700
- if (params.expected_relation_version !== undefined)
701
- prepare.expected_relation_version = params.expected_relation_version;
702
- if (params.idempotencyKey ?? params.idempotency_key)
703
- prepare.idempotency_key = params.idempotencyKey ?? params.idempotency_key;
704
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_prepare', prepare);
705
- const operationId = String(prepared.operation_id ?? '').trim();
706
- if (!operationId)
707
- throw new ValidationError('owner_change_prepare response missing operation_id');
708
- const signatures = {};
709
- for (const key of ['old_owner_signature', 'subject_signature', 'new_owner_signature'])
710
- if (params[key])
711
- signatures[key] = params[key];
712
- let response = prepared;
713
- if (Object.keys(signatures).length > 0)
714
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_submit', { operation_id: operationId, ...signatures });
715
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending' && (params.agentMd ?? params.agent_md) !== undefined) {
716
- const proof = { proof_id: prepared.proof_id, subject_aid: subject, owner_aid: newOwner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: params.subject_signature, owner_signature: params.new_owner_signature };
717
- const content = ownerAgentMd(String(params.agentMd ?? params.agent_md), newOwner, proof);
718
- const uploaded = await this.uploadAgentMd(subject, content, { ownerOperationId: operationId });
719
- if (!uploaded.ok)
720
- return uploaded;
721
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_activate', { operation_id: operationId });
722
- }
723
- return resultOk(response);
724
- }
725
- catch (exc) {
726
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
727
- }
728
- }
729
- async resumeOwnerChange(operationId, aid) {
730
- const id = String(operationId ?? '').trim();
731
- if (!id)
732
- return resultErr(codes.INVALID_AID_FORMAT, 'resumeOwnerChange requires operationId');
733
- try {
734
- const gatewayUrl = await this._resolveGateway(String(aid ?? id));
735
- let response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_operation_get', { operation_id: id });
736
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending')
737
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_activate', { operation_id: id });
738
- return resultOk(response);
739
- }
740
- catch (exc) {
741
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
742
- }
743
- }
744
638
  async exists(aid) {
745
639
  const target = String(aid ?? '').trim();
746
640
  try {
@@ -766,7 +660,12 @@ export class AIDStore {
766
660
  let peer;
767
661
  let certFromCache = false;
768
662
  const cached = await this.load(target);
769
- if (cached.ok && !forceRefresh) {
663
+ const localIdentity = !cached.ok && cached.error.code === codes.CERT_EXPIRED && !forceRefresh && !skipAgentMd ? await this._expiredIdentityForAgentMdRead(target) : null;
664
+ if (localIdentity) {
665
+ peer = localIdentity;
666
+ certFromCache = true;
667
+ }
668
+ else if (cached.ok && !forceRefresh) {
770
669
  peer = cached.data.aid;
771
670
  certFromCache = true;
772
671
  }
@@ -786,6 +685,15 @@ export class AIDStore {
786
685
  const source = { cert_from_cache: certFromCache, agent_md_fetched: false };
787
686
  if (skipAgentMd)
788
687
  return resultOk({ aid: peer, source });
688
+ if (!forceRefresh && (localIdentity || peer.isPrivateKeyValid() && peer.privateKeyPem)) {
689
+ const record = await this._agentMdManager.loadRecord(target, true);
690
+ const draft = String(record?.local_draft_content ?? '');
691
+ const local = draft || await this._agentMdManager.readContent(target);
692
+ if (local) {
693
+ const etag = await AgentMdManager.contentEtag(local);
694
+ return resultOk({ aid: peer, source, agent_md: { aid: target, content: local, verification: { status: 'unverified' }, signature: { status: 'unverified' }, cert_pem: peer.certPem, etag: draft ? '' : etag, last_modified: String(record?.last_modified ?? ''), status: 0, in_sync: draft ? false : etag === record?.remote_etag, saved_to: null, save_error: null } });
695
+ }
696
+ }
789
697
  const agentMd = await this.downloadAgentMd(target, opts?.timeout);
790
698
  if (!agentMd.ok)
791
699
  return agentMd;
@@ -796,6 +704,25 @@ export class AIDStore {
796
704
  return resultErr(codes.NETWORK_ERROR, String(exc), exc);
797
705
  }
798
706
  }
707
+ async _expiredIdentityForAgentMdRead(target) {
708
+ const record = await this._agentMdManager.loadRecord(target, true);
709
+ if (!String(record?.local_draft_content ?? '') && !await this._agentMdManager.readContent(target))
710
+ return null;
711
+ const certPem = await this._keystore.loadCert(target);
712
+ const keyPair = await this._keystore.loadKeyPair(target);
713
+ if (!certPem || !keyPair?.private_key_pem || parseCertCN(certPem) !== target)
714
+ return null;
715
+ try {
716
+ const privateKey = await importPrivateKeyEcdsa(String(keyPair.private_key_pem));
717
+ const probe = new TextEncoder().encode('aun-aidstore-private-key-self-test');
718
+ if (!await ecdsaVerifyDer(await importCertPublicKeyEcdsa(certPem), await ecdsaSignDer(privateKey, probe), probe))
719
+ return null;
720
+ return await AID.create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: false, privateKeyValid: false, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, aidStore: this });
721
+ }
722
+ catch {
723
+ return null;
724
+ }
725
+ }
799
726
  async downloadAgentMd(aid, timeoutMs = 30000) {
800
727
  try {
801
728
  return resultOk(await this._agentMdManager.download(aid, timeoutMs));
@@ -812,12 +739,30 @@ export class AIDStore {
812
739
  return agentMdFailure(exc);
813
740
  }
814
741
  }
815
- async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '') {
742
+ async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '', signedAt) {
816
743
  const expectedFp = String(certFingerprint ?? '').trim().toLowerCase();
817
744
  if (expectedFp && !(await certMatchesFingerprint(certPem, expectedFp))) {
818
745
  throw new ValidationError(`certificate fingerprint mismatch for aid: ${aid}`);
819
746
  }
820
- await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid);
747
+ if (signedAt === undefined)
748
+ await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid);
749
+ else
750
+ await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid, signedAt, false, true);
751
+ }
752
+ async _resolveEntityAgentMdOwner(aid, type) {
753
+ const authority = await this._agentMdManager.readEntityAuthorityFresh(aid);
754
+ const certificate = await this._currentEntityCertificate(aid, type);
755
+ if (authority.entity_cert_fingerprint !== certificate.certFingerprint)
756
+ throw new StateError('entity certificate authority changed', { stringCode: 'conflict' });
757
+ return authority.owner_aid;
758
+ }
759
+ async _currentEntityCertificate(aid, type) {
760
+ const cert = await this._peerAuth.resolveCurrentEntityCertificate(await this._resolveGateway(aid), aid, type);
761
+ return AID.create({ aid, aunPath: this.aunPath, certPem: cert.cert_pem, certValid: true, privateKeyPem: null, privateKeyValid: false });
762
+ }
763
+ async _currentEntitySigner(aid, fingerprint) {
764
+ const cert = await this._peerAuth.resolveOwnershipCertificate(await this._resolveGateway(aid), aid, fingerprint);
765
+ return AID.create({ aid, aunPath: this.aunPath, certPem: cert.cert_pem, certValid: true, privateKeyPem: null, privateKeyValid: false });
821
766
  }
822
767
  _authIdentityFromAid(aid) {
823
768
  return {
@@ -853,10 +798,18 @@ export class AIDStore {
853
798
  return resultErr(loaded.error.code, loaded.error.message);
854
799
  }
855
800
  const current = loaded.data.aid;
856
- if (!current.isPrivateKeyValid() || !current.privateKeyPem) {
801
+ const raw = content ?? (String((await this._agentMdManager.loadRecord(target, true))?.local_draft_content ?? '') || await this._agentMdManager.readContent(target));
802
+ const tail = parseAgentMdTailSignatureForRead(String(raw ?? ''));
803
+ if (tail.parseError)
804
+ throw new ValidationError(tail.parseError);
805
+ const base = validateAgentMdDocument(tail.payload, { expectedAid: target, validateOwnership: false });
806
+ const entity = ['group', 'org'].includes(String(base.frontmatter.type));
807
+ const parsed = entity ? validateAgentMdDocument(String(raw ?? ''), { expectedAid: target }) : base;
808
+ const meta = parsed.frontmatter;
809
+ if ((!current.isPrivateKeyValid() || !current.privateKeyPem) && !(entity && parsed.signature)) {
857
810
  return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires local AID with a valid private key: ${target}`);
858
811
  }
859
- const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? target).trim();
812
+ const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? (entity ? meta.owner : target)).trim();
860
813
  this._registerFlow.validateAidName(uploaderTarget);
861
814
  let uploader = current;
862
815
  if (uploaderTarget.toLowerCase() !== target.toLowerCase()) {
@@ -865,22 +818,35 @@ export class AIDStore {
865
818
  return resultErr(uploaderLoaded.error.code, uploaderLoaded.error.message);
866
819
  }
867
820
  uploader = uploaderLoaded.data.aid;
868
- if (!uploader.isPrivateKeyValid() || !uploader.privateKeyPem) {
869
- return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires uploader AID with a valid private key: ${uploaderTarget}`);
870
- }
821
+ }
822
+ if (!uploader.isPrivateKeyValid() || !uploader.privateKeyPem) {
823
+ return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires uploader AID with a valid private key: ${uploaderTarget}`);
871
824
  }
872
825
  const manager = new AgentMdManager({
873
826
  aunPath: this.aunPath,
874
827
  tokenStore: this._tokenStore,
875
828
  uploadViaGatewayHost: true,
876
- ownerAidGetter: () => target,
877
- currentAidGetter: () => current,
829
+ ownerAidGetter: () => uploaderTarget,
830
+ currentAidGetter: () => uploader,
831
+ entityOwnerResolver: options.entityOwnerResolver ?? ((value, type) => this._resolveEntityAgentMdOwner(value, type)),
832
+ entityIdentityGetter: options.entityIdentityGetter ?? ((value, type) => this._currentEntityCertificate(value, type ?? 'group')),
833
+ entitySignerResolver: options.entitySignerResolver ?? ((value, fingerprint) => this._currentEntitySigner(value, fingerprint)),
878
834
  gatewayResolver: (value) => this._resolveGateway(value),
879
835
  accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
880
836
  aidValidator: (value) => this._registerFlow.validateAidName(value),
881
837
  });
882
- const ownerOperationId = options.ownerOperationId ?? options.owner_operation_id;
883
- return resultOk(await manager.upload(content, ownerOperationId));
838
+ if (entity) {
839
+ let signed = String(raw);
840
+ if (!parsed.signature)
841
+ signed = (await signDocument(current, signed, { signerAid: true })).signed;
842
+ if (options.coSignerAid) {
843
+ if (options.coSignerAid !== meta.owner)
844
+ throw new ValidationError('entity additional signer must be current owner');
845
+ signed = (await signDocument(uploader, signed, { append: true })).signed;
846
+ }
847
+ return resultOk(await manager.uploadAs(target, signed, current));
848
+ }
849
+ return resultOk(await manager.upload(raw));
884
850
  }
885
851
  catch (exc) {
886
852
  return agentMdFailure(exc);
@@ -977,7 +943,45 @@ export class AIDStore {
977
943
  return resultErr(codes.PRIVATE_KEY_REQUIRED, `private key required for aid: ${target}`);
978
944
  }
979
945
  const gatewayUrl = await this._resolveGateway(target);
980
- const newIdentity = await this._registerFlow.generateIdentity();
946
+ const pendingStore = this._keystore;
947
+ let pendingDir = null;
948
+ let newIdentity = null;
949
+ for (const handle of await pendingStore.listPendingIdentityDirs(target)) {
950
+ const candidate = await pendingStore.loadPendingKeyPair(handle, target);
951
+ if (candidate?.rekey_pending === true) {
952
+ pendingDir = handle;
953
+ newIdentity = candidate;
954
+ break;
955
+ }
956
+ }
957
+ if (newIdentity) {
958
+ const serverCert = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
959
+ if (serverCert) {
960
+ const validity = parseCertValidity(serverCert);
961
+ const now = Date.now();
962
+ if (publicKeyDerB64(serverCert) === newIdentity.public_key_der_b64
963
+ && parseCertCN(serverCert) === target
964
+ && validity && now >= validity.notBefore && now <= validity.notAfter) {
965
+ await pendingStore.savePendingCert(pendingDir, serverCert);
966
+ await this._keystore.saveIdentity(target, {
967
+ aid: target, private_key_pem: newIdentity.private_key_pem,
968
+ public_key_der_b64: newIdentity.public_key_der_b64, curve: newIdentity.curve, cert: serverCert,
969
+ });
970
+ await pendingStore.discardPendingIdentity(pendingDir);
971
+ const refreshed = await this.load(target);
972
+ if (!refreshed.ok || !refreshed.data)
973
+ return resultErr(codes.REKEY_FAILED, 'rekeyed identity reload failed');
974
+ return resultOk({ rekeyed: true, new_cert_not_after: refreshed.data.aid.certNotAfter, new_fingerprint: refreshed.data.aid.certFingerprint });
975
+ }
976
+ }
977
+ }
978
+ else {
979
+ newIdentity = await this._registerFlow.generateIdentity();
980
+ pendingDir = await pendingStore.pendingIdentityDir(target);
981
+ await pendingStore.savePendingKeyPair(pendingDir, target, { ...newIdentity, aid: target, rekey_pending: true });
982
+ }
983
+ if (!pendingDir || !newIdentity)
984
+ return resultErr(codes.REKEY_FAILED, 'pending rekey identity is unavailable');
981
985
  const phase1 = await this._beginAidOperation(gatewayUrl, oldAid);
982
986
  const signPayload = `${String(phase1.nonce ?? '')}${newIdentity.public_key_der_b64}`;
983
987
  const signResult = await loaded.data.aid.sign(signPayload);
@@ -993,6 +997,7 @@ export class AIDStore {
993
997
  const certPem = String(response.cert ?? response.cert_pem ?? '').trim();
994
998
  if (!certPem)
995
999
  return resultErr(codes.REKEY_FAILED, 'server response missing certificate');
1000
+ await pendingStore.savePendingCert(pendingDir, certPem);
996
1001
  const nextIdentity = {
997
1002
  aid: target,
998
1003
  private_key_pem: newIdentity.private_key_pem,
@@ -1001,6 +1006,7 @@ export class AIDStore {
1001
1006
  cert: certPem,
1002
1007
  };
1003
1008
  await this._keystore.saveIdentity(target, nextIdentity);
1009
+ await pendingStore.discardPendingIdentity(pendingDir);
1004
1010
  const refreshed = await this.load(target);
1005
1011
  if (!refreshed.ok)
1006
1012
  return resultErr(codes.REKEY_FAILED, 'rekeyed identity reload failed');