@agentunion/fastaun 0.5.18 → 0.5.19

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 (94) hide show
  1. package/CHANGELOG.md +44 -2
  2. package/_packed_docs/CHANGELOG.md +44 -2
  3. package/_packed_docs/INDEX.md +136 -14
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +42 -16
  5. package/_packed_docs/agent.md/SCHEMA.md +245 -205
  6. 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
  7. 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
  8. 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
  9. 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
  10. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +28 -9
  11. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +22 -27
  12. package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
  13. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +5 -2
  14. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +250 -283
  15. package/_packed_docs/sdk/09-group-rpc-manual.md +46 -14
  16. package/_packed_docs/sdk/09-message-rpc-manual.md +40 -2
  17. package/_packed_docs/sdk/09-storage-rpc-manual.md +74 -64
  18. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +25 -5
  19. package/_packed_docs/sdk/INDEX.md +76 -17
  20. package/dist/agent-md-schema.d.ts +2 -0
  21. package/dist/agent-md-schema.js +45 -10
  22. package/dist/agent-md-schema.js.map +1 -1
  23. package/dist/agent-md.d.ts +47 -2
  24. package/dist/agent-md.js +493 -33
  25. package/dist/agent-md.js.map +1 -1
  26. package/dist/aid-store.d.ts +9 -59
  27. package/dist/aid-store.js +135 -245
  28. package/dist/aid-store.js.map +1 -1
  29. package/dist/aid.js +19 -5
  30. package/dist/aid.js.map +1 -1
  31. package/dist/auth.d.ts +6 -1
  32. package/dist/auth.js +428 -15
  33. package/dist/auth.js.map +1 -1
  34. package/dist/cert-utils.d.ts +1 -1
  35. package/dist/cert-utils.js +19 -7
  36. package/dist/cert-utils.js.map +1 -1
  37. package/dist/client/delivery.d.ts +2 -1
  38. package/dist/client/delivery.js +143 -52
  39. package/dist/client/delivery.js.map +1 -1
  40. package/dist/client/rpc-pipeline.js +22 -5
  41. package/dist/client/rpc-pipeline.js.map +1 -1
  42. package/dist/client/v2-e2ee.d.ts +1 -0
  43. package/dist/client/v2-e2ee.js +93 -29
  44. package/dist/client/v2-e2ee.js.map +1 -1
  45. package/dist/client.d.ts +84 -1
  46. package/dist/client.js +749 -28
  47. package/dist/client.js.map +1 -1
  48. package/dist/errors.js +4 -3
  49. package/dist/errors.js.map +1 -1
  50. package/dist/events.d.ts +5 -1
  51. package/dist/events.js +47 -6
  52. package/dist/events.js.map +1 -1
  53. package/dist/facades.d.ts +83 -3
  54. package/dist/facades.js +98 -4
  55. package/dist/facades.js.map +1 -1
  56. package/dist/index.d.ts +8 -4
  57. package/dist/index.js +3 -2
  58. package/dist/index.js.map +1 -1
  59. package/dist/keystore/aid-db.d.ts +5 -0
  60. package/dist/keystore/aid-db.js +48 -0
  61. package/dist/keystore/aid-db.js.map +1 -1
  62. package/dist/keystore/index.d.ts +8 -0
  63. package/dist/keystore/local-identity-store.js +10 -3
  64. package/dist/keystore/local-identity-store.js.map +1 -1
  65. package/dist/keystore/local-token-store.d.ts +6 -1
  66. package/dist/keystore/local-token-store.js +24 -1
  67. package/dist/keystore/local-token-store.js.map +1 -1
  68. package/dist/org-protocol.d.ts +98 -0
  69. package/dist/org-protocol.js +1689 -0
  70. package/dist/org-protocol.js.map +1 -0
  71. package/dist/ownership-proof.d.ts +82 -5
  72. package/dist/ownership-proof.js +327 -31
  73. package/dist/ownership-proof.js.map +1 -1
  74. package/dist/ownership-v21.d.ts +178 -0
  75. package/dist/ownership-v21.js +635 -0
  76. package/dist/ownership-v21.js.map +1 -0
  77. package/dist/ownership-workflow.d.ts +82 -0
  78. package/dist/ownership-workflow.js +872 -0
  79. package/dist/ownership-workflow.js.map +1 -0
  80. package/dist/seq-tracker.d.ts +1 -0
  81. package/dist/seq-tracker.js +10 -1
  82. package/dist/seq-tracker.js.map +1 -1
  83. package/dist/tools/cross-sdk-agent.d.ts +89 -1
  84. package/dist/tools/cross-sdk-agent.js +338 -7
  85. package/dist/tools/cross-sdk-agent.js.map +1 -1
  86. package/dist/transport.d.ts +7 -0
  87. package/dist/transport.js +108 -4
  88. package/dist/transport.js.map +1 -1
  89. package/dist/v2/e2ee/decrypt.d.ts +1 -0
  90. package/dist/v2/e2ee/decrypt.js +9 -1
  91. package/dist/v2/e2ee/decrypt.js.map +1 -1
  92. package/dist/version.d.ts +1 -1
  93. package/dist/version.js +1 -1
  94. package/package.json +1 -1
package/dist/aid-store.js CHANGED
@@ -4,12 +4,13 @@
4
4
  */
5
5
  import * as http from 'node:http';
6
6
  import * as https from 'node:https';
7
- import { X509Certificate, createPrivateKey, createPublicKey } from 'node:crypto';
7
+ import { X509Certificate, createPrivateKey, createPublicKey, randomUUID } from 'node:crypto';
8
8
  import { join } from 'node:path';
9
9
  import { homedir } from 'node:os';
10
10
  import { AID } from './aid.js';
11
11
  import * as codes from './error-codes.js';
12
12
  import { certCommonName, certMatchesFingerprint, certTimeError, signBytes, verifySignatureWithCert } from './cert-utils.js';
13
+ import { certFingerprint as _certFingerprint } from './cert-utils.js';
13
14
  import { resultErr, resultOk } from './result.js';
14
15
  import { LocalIdentityStore } from './keystore/local-identity-store.js';
15
16
  import { CryptoProvider } from './crypto.js';
@@ -18,17 +19,21 @@ import { GatewayDiscovery } from './discovery.js';
18
19
  import { DnsResilientNet } from './net.js';
19
20
  import { AUNLogger } from './logger.js';
20
21
  import { getDeviceId, normalizeSlotId } from './config.js';
21
- import { ClientSignatureError, IdentityConflictError, NotFoundError, ValidationError } from './errors.js';
22
+ import { AUNError, ClientSignatureError, IdentityConflictError, NotFoundError, ValidationError } from './errors.js';
22
23
  import { AgentMdManager, buildDefaultAgentMd } from './agent-md.js';
24
+ import { validateAgentMdDocument } from './agent-md-schema.js';
23
25
  import { LocalTokenStore } from './keystore/local-token-store.js';
24
26
  import { AuthFlow } from './auth.js';
25
- import { OwnershipStatement, signOwnershipStatement } from './ownership-proof.js';
27
+ import { OWNERSHIP_UUID_V4_RE, ownershipFailure, signOwnershipFile } from './ownership-v21.js';
28
+ import { validateAIDFormat } from './validators.js';
26
29
  function resultError(r) {
27
30
  if (r.ok)
28
31
  return null;
29
32
  return r.error;
30
33
  }
31
34
  function agentMdFailure(exc) {
35
+ if (exc instanceof AUNError && exc.stringCode)
36
+ return resultErr(exc.stringCode, exc.message, exc);
32
37
  if (exc instanceof ClientSignatureError) {
33
38
  return resultErr(codes.SIGNATURE_INVALID, exc.message, exc);
34
39
  }
@@ -40,19 +45,6 @@ function agentMdFailure(exc) {
40
45
  }
41
46
  return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
42
47
  }
43
- function ownerAgentMd(content, ownerAid, proof) {
44
- const marker = content.indexOf('<!-- AUN-SIGNATURE');
45
- const payload = marker >= 0 ? content.slice(0, marker) : content;
46
- const lines = payload.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
47
- const end = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
48
- if (end < 0)
49
- throw new ValidationError('agent.md frontmatter is required for ownership operation');
50
- const filtered = lines.filter((line, index) => !(index > 0 && index < end && (/^\s*owner\s*:/.test(line) || /^\s*ownership_proof\s*:/.test(line))));
51
- const closed = filtered.findIndex((line, index) => index > 0 && line.trim() === '---');
52
- filtered.splice(closed, 0, `owner: ${JSON.stringify(ownerAid)}`, `ownership_proof: ${JSON.stringify(proof)}`);
53
- const result = filtered.join('\n');
54
- return result.endsWith('\n') ? result : `${result}\n`;
55
- }
56
48
  /** 发起 HTTP HEAD 请求,返回 [status, headers] */
57
49
  function httpHead(url, verifySsl, timeoutMs = 5000) {
58
50
  return new Promise((resolve, reject) => {
@@ -148,7 +140,15 @@ export class AIDStore {
148
140
  logger: this._log.for('aun_core.agent_md'),
149
141
  aidValidator: (target) => this._registerFlow.validateAidName(target),
150
142
  gatewayResolver: (target) => this._resolveGateway(target),
151
- peerResolver: async (target, certFingerprint) => {
143
+ entitySignerResolver: async (target, fingerprint) => {
144
+ const material = await this._peerAuth.resolveOwnershipCertificate(await this._resolveGateway(target), target, fingerprint, 'signing');
145
+ return AID._create({ aid: target, aunPath: this.aunPath, certPem: material.cert_pem, privateKeyPem: null, certValid: true, privateKeyValid: false });
146
+ },
147
+ entityIdentityGetter: async (target, type) => {
148
+ const material = await this._peerAuth.resolveCurrentEntityCertificate(await this._resolveGateway(target), target, type ?? 'org');
149
+ return AID._create({ aid: target, aunPath: this.aunPath, certPem: material.cert_pem, privateKeyPem: null, certValid: true, privateKeyValid: false });
150
+ },
151
+ peerResolver: async (target, certFingerprint, signedAt) => {
152
152
  const fp = String(certFingerprint ?? '').trim().toLowerCase();
153
153
  const cachedCert = this._keystore.loadCert(target, fp || undefined);
154
154
  if (cachedCert) {
@@ -157,7 +157,7 @@ export class AIDStore {
157
157
  if (fp) {
158
158
  const gatewayUrl = await this._resolveGateway(target);
159
159
  const certPem = await this._net.httpGetText(pkiCertUrl(gatewayUrl, target, fp), 10_000);
160
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp);
160
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt);
161
161
  this._keystore.saveCert(target, certPem, fp, { makeActive: false });
162
162
  return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
163
163
  }
@@ -166,7 +166,7 @@ export class AIDStore {
166
166
  if (!certPem) {
167
167
  throw new Error(`certificate not found for aid: ${target}`);
168
168
  }
169
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem);
169
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt);
170
170
  this._keystore.saveCert(target, certPem);
171
171
  return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
172
172
  },
@@ -193,6 +193,9 @@ export class AIDStore {
193
193
  catch (exc) {
194
194
  return resultErr(codes.CERT_PARSE_ERROR, `certificate parse failed for aid: ${target}`, exc);
195
195
  }
196
+ if (this._tokenStore.loadMetadata?.(target)?.invalidated_entity_certificate === _certFingerprint(certPem)) {
197
+ return resultErr(codes.CERT_NOT_VALID, `group/org identity has been invalidated: ${target}`);
198
+ }
196
199
  const timeErr = certTimeError(certPem);
197
200
  if (timeErr === 'expired')
198
201
  return resultErr(codes.CERT_EXPIRED, `certificate expired for aid: ${target}`);
@@ -295,11 +298,27 @@ export class AIDStore {
295
298
  return Promise.resolve(this.changeSeed(oldSeed, newSeed));
296
299
  }
297
300
  // ── 联网方法 ──────────────────────────────────────────────────
298
- async registerIdentity(aid, uploadDefault = true) {
301
+ async register(aid, options) {
299
302
  const target = String(aid ?? '').trim();
300
303
  this._log.for('aun_core.aid_store').debug(`register enter: aid=${target || '-'}`);
301
304
  try {
302
305
  this._registerFlow.validateAidName(target);
306
+ const type = String(options?.type ?? '').trim(), owner = String(options?.owner ?? '').trim().toLowerCase();
307
+ const entity = ['group', 'org'].includes(type);
308
+ if (entity && !owner)
309
+ throw new ValidationError('group/org registration requires owner');
310
+ if (!type || type === 'human' && owner || type === 'agent' && !owner)
311
+ throw new ValidationError('register requires type; human forbids owner; agent requires owner');
312
+ if (owner)
313
+ validateAIDFormat(owner, 'owner');
314
+ const previous = this._tokenStore.loadMetadata(target)?.registration_intent;
315
+ const exttype = String(options?.exttype ?? '').trim();
316
+ if (previous && (entity ? previous.protocol !== 'entity' || previous.pending !== false : previous.protocol !== '2.1' || !OWNERSHIP_UUID_V4_RE.test(String(previous.operation_id)) || typeof previous.pending !== 'boolean'))
317
+ throw ownershipFailure('persistence_unavailable', 'registration ownership intent is corrupt');
318
+ if (previous && (previous.type !== type || previous.owner !== owner || previous.exttype !== exttype))
319
+ throw new IdentityConflictError('registration ownership intent already exists with a different context');
320
+ const intent = previous ?? (entity ? { protocol: 'entity', type, owner, exttype, pending: false } : { protocol: '2.1', type, owner, exttype, operation_id: randomUUID(), pending: Boolean(owner) });
321
+ this._tokenStore.saveMetadata(target, { registration_intent: intent });
303
322
  const gatewayUrl = await this._resolveGateway(target);
304
323
  const result = await this._registerFlow.registerAid(gatewayUrl, target);
305
324
  if (result.cert) {
@@ -310,8 +329,8 @@ export class AIDStore {
310
329
  public_key_der_b64: result.public_key_der_b64,
311
330
  curve: result.curve,
312
331
  });
313
- if (uploadDefault)
314
- await this.uploadAgentMd(target, buildDefaultAgentMd(target));
332
+ if (!entity)
333
+ await this._createInitialRegistrationAgentMd(target, type, exttype);
315
334
  return resultOk({ registered: true });
316
335
  }
317
336
  catch (exc) {
@@ -328,219 +347,32 @@ export class AIDStore {
328
347
  return resultErr(codes.SERVER_ERROR, msg, exc);
329
348
  }
330
349
  }
331
- async register(aidOrParams) {
332
- if (typeof aidOrParams === 'string')
333
- return await this.registerIdentity(aidOrParams);
334
- const params = aidOrParams;
335
- const target = String(params?.aid ?? '').trim();
336
- const aidType = String(params?.type ?? '').trim().toLowerCase();
337
- const owner = String(params?.owner ?? params?.owner_aid ?? '').trim();
338
- if (!target || !aidType)
339
- return resultErr(codes.INVALID_AID_FORMAT, 'register requires aid and type');
340
- try {
341
- const identity = await this.registerIdentity(target, false);
342
- if (!identity.ok)
343
- return identity;
344
- const gatewayUrl = await this._resolveGateway(target);
345
- const prepareParams = { aid: target, type: aidType };
346
- if (owner)
347
- prepareParams.owner = owner;
348
- const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
349
- if (idempotencyKey)
350
- prepareParams.idempotency_key = idempotencyKey;
351
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_prepare', prepareParams);
352
- const operationId = String(prepared.operation_id ?? '').trim();
353
- if (!operationId)
354
- throw new ValidationError('aid_register_prepare response missing operation_id');
355
- let subjectSignature = params.subjectSignature ?? params.subject_signature;
356
- const ownerSignature = params.ownerSignature ?? params.owner_signature;
357
- if (!subjectSignature && owner) {
358
- const loaded = this.load(target);
359
- if (loaded.ok) {
360
- const statement = OwnershipStatement.create(String(prepared.proof_id ?? ''), target, owner);
361
- const signed = signOwnershipStatement({ certFingerprint: loaded.data.aid.certFingerprint, sign: (payload) => loaded.data.aid.sign(Buffer.from(payload)) }, statement);
362
- subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
363
- }
364
- }
365
- const signatures = {};
366
- if (subjectSignature)
367
- signatures.subject_signature = subjectSignature;
368
- if (ownerSignature)
369
- signatures.owner_signature = ownerSignature;
370
- let response = prepared;
371
- if (Object.keys(signatures).length > 0)
372
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_submit', { operation_id: operationId, ...signatures });
373
- const state = String(response.state ?? response.operation_state ?? '').toLowerCase();
374
- const complete = !owner || owner === target || (!!ownerSignature && !!subjectSignature);
375
- if (complete && state === 'agent_md_pending') {
376
- let content = params.agentMd ?? params.agent_md ?? buildDefaultAgentMd(target, { type: aidType });
377
- if (owner) {
378
- 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 };
379
- if (owner !== target)
380
- proof.owner_signature = ownerSignature;
381
- content = ownerAgentMd(content, owner, proof);
382
- }
383
- const uploaded = await this.uploadAgentMd(target, content, { ownerOperationId: operationId });
384
- if (!uploaded.ok)
385
- return uploaded;
386
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_register_activate', { operation_id: operationId });
387
- }
388
- return resultOk(response);
389
- }
390
- catch (exc) {
391
- if (exc instanceof ValidationError)
392
- return resultErr(codes.INVALID_AID_FORMAT, exc.message, exc);
393
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
394
- }
395
- }
396
- async create(params) { return await this.register(params); }
397
- async bindAid(params) {
398
- const target = String(params?.aid ?? '').trim();
399
- const aidType = String(params?.type ?? '').trim().toLowerCase();
400
- const owner = String(params?.owner ?? params?.owner_aid ?? '').trim();
401
- if (!target || !aidType)
402
- return resultErr(codes.INVALID_AID_FORMAT, 'bindAid requires aid and type');
403
- try {
404
- this._registerFlow.validateAidName(target);
405
- const gatewayUrl = await this._resolveGateway(target);
406
- const prepareParams = { aid: target, type: aidType };
407
- if (owner)
408
- prepareParams.owner = owner;
409
- const idempotencyKey = params.idempotencyKey ?? params.idempotency_key;
410
- if (idempotencyKey)
411
- prepareParams.idempotency_key = idempotencyKey;
412
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_prepare', prepareParams);
413
- const operationId = String(prepared.operation_id ?? '').trim();
414
- if (!operationId)
415
- throw new ValidationError('aid_bind_prepare response missing operation_id');
416
- const signatures = {};
417
- let subjectSignature = params.subjectSignature ?? params.subject_signature;
418
- if (!subjectSignature && owner) {
419
- const loaded = this.load(target);
420
- if (loaded.ok) {
421
- const statement = OwnershipStatement.create(String(prepared.proof_id ?? ''), target, owner);
422
- const signed = signOwnershipStatement({
423
- certFingerprint: loaded.data.aid.certFingerprint,
424
- sign: (payload) => loaded.data.aid.sign(Buffer.from(payload)),
425
- }, statement);
426
- subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
427
- }
428
- }
429
- if (subjectSignature)
430
- signatures.subject_signature = subjectSignature;
431
- const ownerSignature = params.ownerSignature ?? params.owner_signature;
432
- if (ownerSignature)
433
- signatures.owner_signature = ownerSignature;
434
- let response = prepared;
435
- if (Object.keys(signatures).length > 0) {
436
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_submit', { operation_id: operationId, ...signatures });
437
- }
438
- const agentMd = params.agentMd ?? params.agent_md;
439
- if (agentMd !== undefined) {
440
- const uploaded = await this.uploadAgentMd(target, agentMd, {
441
- ownerOperationId: operationId,
442
- });
443
- if (!uploaded.ok)
444
- return uploaded;
445
- }
446
- const state = String(response.state ?? response.operation_state ?? '').toLowerCase();
447
- if (state === 'agent_md_pending' || (!Object.keys(signatures).length && !owner && String(prepared.state ?? '') === 'agent_md_pending')) {
448
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_activate', { operation_id: operationId });
449
- }
450
- return resultOk(response);
451
- }
452
- catch (exc) {
453
- if (exc instanceof ValidationError)
454
- return resultErr(codes.INVALID_AID_FORMAT, exc.message, exc);
455
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
456
- }
457
- }
458
- async getAidBindingStatus(aid) {
459
- const target = String(aid ?? '').trim();
460
- if (!target)
461
- return resultErr(codes.INVALID_AID_FORMAT, 'getAidBindingStatus requires aid');
462
- try {
463
- this._registerFlow.validateAidName(target);
464
- const gatewayUrl = await this._resolveGateway(target);
465
- return resultOk(await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_bind_status', { aid: target }));
466
- }
467
- catch (exc) {
468
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
469
- }
470
- }
471
- async resumeAidBinding(operationId) {
472
- const target = String(operationId ?? '').trim();
473
- if (!target)
474
- return resultErr(codes.INVALID_AID_FORMAT, 'resumeAidBinding requires operationId');
350
+ async _createInitialRegistrationAgentMd(target, type, exttype) {
475
351
  try {
476
- const gatewayUrl = await this._resolveGateway(target);
477
- let response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_operation_get', { operation_id: target });
478
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending') {
479
- const method = String(response.kind ?? '').toLowerCase() === 'owner_change' ? 'auth.owner_change_activate' : 'auth.aid_bind_activate';
480
- response = await this._registerFlow.shortRpc(gatewayUrl, method, { operation_id: target });
481
- }
482
- return resultOk(response);
483
- }
484
- catch (exc) {
485
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
486
- }
487
- }
488
- async bind_aid(params) { return this.bindAid(params); }
489
- async get_aid_binding_status(aid) { return this.getAidBindingStatus(aid); }
490
- async resume_aid_binding(operationId) { return this.resumeAidBinding(operationId); }
491
- async changeOwner(params) {
492
- const subject = String(params?.subject_aid ?? params?.aid ?? '').trim();
493
- const newOwner = String(params?.new_owner_aid ?? params?.new_owner ?? '').trim();
494
- if (!subject || !newOwner)
495
- return resultErr(codes.INVALID_AID_FORMAT, 'changeOwner requires subject_aid and new_owner_aid');
496
- try {
497
- this._registerFlow.validateAidName(subject);
498
- const gatewayUrl = await this._resolveGateway(subject);
499
- const prepare = { subject_aid: subject, new_owner_aid: newOwner };
500
- if (params.current_owner_aid ?? params.owner_aid)
501
- prepare.current_owner_aid = params.current_owner_aid ?? params.owner_aid;
502
- if (params.expected_relation_version !== undefined)
503
- prepare.expected_relation_version = params.expected_relation_version;
504
- if (params.idempotencyKey ?? params.idempotency_key)
505
- prepare.idempotency_key = params.idempotencyKey ?? params.idempotency_key;
506
- const prepared = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_prepare', prepare);
507
- const operationId = String(prepared.operation_id ?? '').trim();
508
- if (!operationId)
509
- throw new ValidationError('owner_change_prepare response missing operation_id');
510
- const signatures = {};
511
- for (const key of ['old_owner_signature', 'subject_signature', 'new_owner_signature'])
512
- if (params[key])
513
- signatures[key] = params[key];
514
- let response = prepared;
515
- if (Object.keys(signatures).length > 0)
516
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_submit', { operation_id: operationId, ...signatures });
517
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending' && (params.agentMd ?? params.agent_md) !== undefined) {
518
- 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 };
519
- const content = ownerAgentMd(String(params.agentMd ?? params.agent_md), newOwner, proof);
520
- const uploaded = await this.uploadAgentMd(subject, content, { ownerOperationId: operationId });
521
- if (!uploaded.ok)
522
- return uploaded;
523
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_activate', { operation_id: operationId });
524
- }
525
- return resultOk(response);
526
- }
527
- catch (exc) {
528
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
529
- }
530
- }
531
- async resumeOwnerChange(operationId, aid) {
532
- const id = String(operationId ?? '').trim();
533
- if (!id)
534
- return resultErr(codes.INVALID_AID_FORMAT, 'resumeOwnerChange requires operationId');
535
- try {
536
- const gatewayUrl = await this._resolveGateway(String(aid ?? id));
537
- let response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_operation_get', { operation_id: id });
538
- if (String(response.state ?? response.operation_state ?? '').toLowerCase() === 'agent_md_pending')
539
- response = await this._registerFlow.shortRpc(gatewayUrl, 'auth.owner_change_activate', { operation_id: id });
540
- return resultOk(response);
352
+ const loaded = this.load(target);
353
+ if (!loaded.ok)
354
+ return loaded;
355
+ const current = loaded.data.aid;
356
+ const manager = new AgentMdManager({
357
+ aunPath: this.aunPath, verifySsl: this._verifySsl, logger: this._log.for('aun_core.agent_md'), uploadViaGatewayHost: true,
358
+ ownerAidGetter: () => target, currentAidGetter: () => current,
359
+ gatewayResolver: (value) => this._resolveGateway(value),
360
+ accessTokenResolver: async () => this._uploadAgentMdToken(current, await this._resolveGateway(target)),
361
+ aidValidator: (value) => this._registerFlow.validateAidName(value),
362
+ });
363
+ const fresh = await manager.readOwnershipFresh(target), now = Math.trunc(Date.now() / 1000);
364
+ if (fresh.protocol !== '2.1' || fresh.sub_aid !== target || !Number.isSafeInteger(fresh.relation_version) || fresh.relation_version < 0 || !['locked', 'unlocked'].includes(fresh.lock) || !Number.isSafeInteger(fresh.observed_at) || fresh.observed_at < now - 30 || fresh.observed_at > now + 5)
365
+ throw ownershipFailure('unavailable', 'initial NS authority is unavailable', true);
366
+ if (fresh.exists)
367
+ return resultOk({ initialized: false });
368
+ if (fresh.relation_version !== 0 || fresh.lock !== 'unlocked')
369
+ throw ownershipFailure('conflict', 'initial NS relation is not unbound');
370
+ const raw = signOwnershipFile(current, buildDefaultAgentMd(target, { type, exttype }), 'agent', { now });
371
+ await manager.submitOwnershipRaw(target, raw, { if_none_match: '*' });
372
+ return resultOk({ initialized: true });
541
373
  }
542
- catch (exc) {
543
- return resultErr(codes.NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
374
+ catch (error) {
375
+ return agentMdFailure(error);
544
376
  }
545
377
  }
546
378
  async exists(aid) {
@@ -614,12 +446,15 @@ export class AIDStore {
614
446
  return agentMdFailure(exc);
615
447
  }
616
448
  }
617
- async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '') {
449
+ async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '', signedAt) {
618
450
  const expectedFp = String(certFingerprint ?? '').trim().toLowerCase();
619
451
  if (expectedFp && !certMatchesFingerprint(certPem, expectedFp)) {
620
452
  throw new ValidationError(`certificate fingerprint mismatch for aid: ${aid}`);
621
453
  }
622
- await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid);
454
+ if (signedAt === undefined)
455
+ await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid);
456
+ else
457
+ await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid, signedAt);
623
458
  }
624
459
  _authIdentityFromAid(aid) {
625
460
  return {
@@ -658,10 +493,11 @@ export class AIDStore {
658
493
  return resultErr(err?.code ?? codes.CERT_NOT_FOUND, err?.message ?? `certificate not found for aid: ${target}`);
659
494
  }
660
495
  const current = loaded.data.aid;
661
- if (!current.isPrivateKeyValid() || !current.privateKeyPem) {
662
- return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires local AID with a valid private key: ${target}`);
663
- }
664
- const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? target).trim();
496
+ const raw = content ?? this._agentMdManager.readContent(target);
497
+ const parsed = validateAgentMdDocument(raw, { expectedAid: target });
498
+ const meta = parsed.frontmatter;
499
+ const entity = ['group', 'org'].includes(String(meta.type));
500
+ const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? (entity ? meta.owner : target)).trim();
665
501
  this._registerFlow.validateAidName(uploaderTarget);
666
502
  let uploader = current;
667
503
  if (uploaderTarget.toLowerCase() !== target.toLowerCase()) {
@@ -675,19 +511,32 @@ export class AIDStore {
675
511
  return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires uploader AID with a valid private key: ${uploaderTarget}`);
676
512
  }
677
513
  }
514
+ if ((!current.isPrivateKeyValid() || !current.privateKeyPem) && !(entity && parsed.signature && uploaderTarget.toLowerCase() !== target.toLowerCase())) {
515
+ return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires local AID with a valid private key: ${target}`);
516
+ }
678
517
  const manager = new AgentMdManager({
679
518
  aunPath: this.aunPath,
680
519
  verifySsl: this._verifySsl,
681
520
  logger: this._log.for('aun_core.agent_md'),
682
521
  uploadViaGatewayHost: true,
683
- ownerAidGetter: () => target,
684
- currentAidGetter: () => current,
522
+ ownerAidGetter: () => entity ? uploaderTarget : target,
523
+ currentAidGetter: () => entity ? uploader : current,
524
+ entityIdentityGetter: options.entityIdentityGetter ?? (async (value, type) => {
525
+ const gateway = await this._resolveGateway(value);
526
+ const material = await this._peerAuth.resolveCurrentEntityCertificate(gateway, value, type ?? 'org');
527
+ const certPem = material.cert_pem;
528
+ return AID._create({ aid: value, aunPath: this.aunPath, certPem, certValid: true, privateKeyPem: null, privateKeyValid: false });
529
+ }),
530
+ entitySignerResolver: async (value, fingerprint) => {
531
+ const material = await this._peerAuth.resolveOwnershipCertificate(await this._resolveGateway(value), value, fingerprint, 'signing');
532
+ return AID._create({ aid: value, aunPath: this.aunPath, certPem: material.cert_pem, certValid: true, privateKeyPem: null, privateKeyValid: false });
533
+ },
534
+ entityOwnerResolver: options.entityOwnerResolver,
685
535
  gatewayResolver: (value) => this._resolveGateway(value),
686
536
  accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
687
537
  aidValidator: (value) => this._registerFlow.validateAidName(value),
688
538
  });
689
- const ownerOperationId = options.ownerOperationId ?? options.owner_operation_id;
690
- return resultOk(await manager.upload(content, ownerOperationId));
539
+ return resultOk(entity ? await manager.uploadAs(target, raw, current) : await manager.upload(raw));
691
540
  }
692
541
  catch (exc) {
693
542
  return agentMdFailure(exc);
@@ -782,8 +631,44 @@ export class AIDStore {
782
631
  try {
783
632
  this._registerFlow.validateAidName(target);
784
633
  const oldAid = loaded.data.aid;
785
- const newIdentity = this._registerFlow.generateIdentity();
786
634
  const gatewayUrl = await this._resolveGateway(target);
635
+ const pendingStore = this._keystore;
636
+ let pendingDir = null;
637
+ let newIdentity = null;
638
+ for (const handle of pendingStore.listPendingIdentityDirs(target)) {
639
+ const candidate = pendingStore.loadPendingKeyPair(handle, target);
640
+ if (candidate?.rekey_pending === true) {
641
+ pendingDir = handle;
642
+ newIdentity = candidate;
643
+ break;
644
+ }
645
+ }
646
+ if (newIdentity) {
647
+ const serverCert = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
648
+ if (serverCert) {
649
+ const cert = new X509Certificate(serverCert);
650
+ const serverPublicKey = cert.publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
651
+ if (serverPublicKey === newIdentity.public_key_der_b64 && certCommonName(serverCert) === target && !certTimeError(serverCert)) {
652
+ pendingStore.savePendingCert(pendingDir, serverCert);
653
+ this._keystore.saveIdentity(target, {
654
+ aid: target, private_key_pem: newIdentity.private_key_pem,
655
+ public_key_der_b64: newIdentity.public_key_der_b64, curve: newIdentity.curve, cert: serverCert,
656
+ });
657
+ pendingStore.discardPendingIdentity(pendingDir);
658
+ const refreshed = this.load(target);
659
+ if (!refreshed.ok || !refreshed.data)
660
+ return resultErr(codes.REKEY_FAILED, 'rekeyed identity reload failed');
661
+ return resultOk({ rekeyed: true, new_cert_not_after: refreshed.data.aid.certNotAfter, new_fingerprint: refreshed.data.aid.certFingerprint });
662
+ }
663
+ }
664
+ }
665
+ else {
666
+ newIdentity = this._registerFlow.generateIdentity();
667
+ pendingDir = pendingStore.pendingIdentityDir(target);
668
+ pendingStore.savePendingKeyPair(pendingDir, target, { ...newIdentity, aid: target, rekey_pending: true });
669
+ }
670
+ if (!pendingDir || !newIdentity)
671
+ return resultErr(codes.REKEY_FAILED, 'pending rekey identity is unavailable');
787
672
  const clientNonce = this._registerFlow.newClientNonce();
788
673
  const phase1 = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_login1', {
789
674
  aid: target, cert: oldAid.certPem, client_nonce: clientNonce,
@@ -801,7 +686,12 @@ export class AIDStore {
801
686
  const certPem = String(response.cert ?? response.cert_pem ?? '').trim();
802
687
  if (!certPem)
803
688
  return resultErr(codes.REKEY_FAILED, 'server response missing certificate');
804
- this._keystore.saveIdentity(target, { aid: target, ...newIdentity, cert: certPem });
689
+ pendingStore.savePendingCert(pendingDir, certPem);
690
+ this._keystore.saveIdentity(target, {
691
+ aid: target, private_key_pem: newIdentity.private_key_pem,
692
+ public_key_der_b64: newIdentity.public_key_der_b64, curve: newIdentity.curve, cert: certPem,
693
+ });
694
+ pendingStore.discardPendingIdentity(pendingDir);
805
695
  const refreshed = this.load(target);
806
696
  if (!refreshed.ok || !refreshed.data)
807
697
  return resultErr(codes.REKEY_FAILED, 'rekeyed identity reload failed');