@agentunion/fastaun 0.5.19 → 0.5.21

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 (129) hide show
  1. package/CHANGELOG.md +176 -78
  2. package/_packed_docs/CHANGELOG.md +176 -78
  3. package/_packed_docs/INDEX.md +288 -269
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +101 -95
  5. package/_packed_docs/agent.md/SCHEMA.md +7 -7
  6. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
  7. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
  8. package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
  9. package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
  10. 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
  11. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +372 -331
  12. package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
  13. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
  14. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
  15. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +445 -0
  16. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  17. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +666 -0
  18. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  19. package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
  20. package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
  21. package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
  22. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
  23. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
  24. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
  25. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +70 -63
  26. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +43 -40
  27. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +179 -19
  28. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +50 -49
  29. package/_packed_docs/sdk/09-group-rpc-manual.md +261 -254
  30. package/_packed_docs/sdk/09-message-rpc-manual.md +253 -160
  31. package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
  32. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +71 -0
  33. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +88 -0
  34. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +105 -92
  35. package/_packed_docs/sdk/INDEX.md +197 -167
  36. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  37. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  38. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +930 -0
  39. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  40. 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 +363 -0
  41. 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 +553 -0
  42. 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
  43. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +480 -0
  44. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  45. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. package/dist/agent-md-schema.d.ts +13 -1
  55. package/dist/agent-md-schema.js +63 -33
  56. package/dist/agent-md-schema.js.map +1 -1
  57. package/dist/agent-md.d.ts +30 -3
  58. package/dist/agent-md.js +293 -152
  59. package/dist/agent-md.js.map +1 -1
  60. package/dist/aid-store.d.ts +42 -0
  61. package/dist/aid-store.js +224 -44
  62. package/dist/aid-store.js.map +1 -1
  63. package/dist/aid.js +18 -13
  64. package/dist/aid.js.map +1 -1
  65. package/dist/auth.js +78 -2
  66. package/dist/auth.js.map +1 -1
  67. package/dist/cert-utils.d.ts +44 -1
  68. package/dist/cert-utils.js +157 -56
  69. package/dist/cert-utils.js.map +1 -1
  70. package/dist/client/delivery.d.ts +68 -1
  71. package/dist/client/delivery.js +1196 -135
  72. package/dist/client/delivery.js.map +1 -1
  73. package/dist/client/rpc-pipeline.d.ts +5 -1
  74. package/dist/client/rpc-pipeline.js +240 -111
  75. package/dist/client/rpc-pipeline.js.map +1 -1
  76. package/dist/client/v2-e2ee.d.ts +12 -2
  77. package/dist/client/v2-e2ee.js +842 -379
  78. package/dist/client/v2-e2ee.js.map +1 -1
  79. package/dist/client.d.ts +26 -2
  80. package/dist/client.js +420 -75
  81. package/dist/client.js.map +1 -1
  82. package/dist/config.d.ts +4 -0
  83. package/dist/config.js +1 -0
  84. package/dist/config.js.map +1 -1
  85. package/dist/events.d.ts +23 -4
  86. package/dist/events.js +171 -15
  87. package/dist/events.js.map +1 -1
  88. package/dist/facades.d.ts +43 -1
  89. package/dist/facades.js +368 -12
  90. package/dist/facades.js.map +1 -1
  91. package/dist/index.d.ts +7 -4
  92. package/dist/index.js +1 -1
  93. package/dist/index.js.map +1 -1
  94. package/dist/keystore/aid-db.d.ts +7 -2
  95. package/dist/keystore/aid-db.js +194 -43
  96. package/dist/keystore/aid-db.js.map +1 -1
  97. package/dist/keystore/index.d.ts +15 -1
  98. package/dist/keystore/local-token-store.d.ts +10 -2
  99. package/dist/keystore/local-token-store.js +81 -16
  100. package/dist/keystore/local-token-store.js.map +1 -1
  101. package/dist/org-protocol.d.ts +45 -0
  102. package/dist/org-protocol.js +829 -22
  103. package/dist/org-protocol.js.map +1 -1
  104. package/dist/ownership-v21.d.ts +40 -9
  105. package/dist/ownership-v21.js +174 -79
  106. package/dist/ownership-v21.js.map +1 -1
  107. package/dist/ownership-workflow.d.ts +22 -0
  108. package/dist/ownership-workflow.js +861 -66
  109. package/dist/ownership-workflow.js.map +1 -1
  110. package/dist/secret-store/file-store.js +84 -0
  111. package/dist/secret-store/file-store.js.map +1 -1
  112. package/dist/seq-tracker.d.ts +1 -0
  113. package/dist/seq-tracker.js +2 -2
  114. package/dist/seq-tracker.js.map +1 -1
  115. package/dist/tools/cross-sdk-agent.d.ts +67 -3
  116. package/dist/tools/cross-sdk-agent.js +575 -42
  117. package/dist/tools/cross-sdk-agent.js.map +1 -1
  118. package/dist/transport.d.ts +4 -0
  119. package/dist/transport.js +108 -44
  120. package/dist/transport.js.map +1 -1
  121. package/dist/types.d.ts +1 -0
  122. package/dist/v2/e2ee/encrypt-group.js +1 -0
  123. package/dist/v2/e2ee/encrypt-group.js.map +1 -1
  124. package/dist/v2/e2ee/encrypt-p2p.js +1 -0
  125. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
  126. package/dist/v2/e2ee/types.d.ts +1 -0
  127. package/dist/version.d.ts +1 -1
  128. package/dist/version.js +1 -1
  129. package/package.json +1 -1
package/dist/aid-store.js CHANGED
@@ -7,9 +7,10 @@ import * as https from 'node:https';
7
7
  import { X509Certificate, createPrivateKey, createPublicKey, randomUUID } from 'node:crypto';
8
8
  import { join } from 'node:path';
9
9
  import { homedir } from 'node:os';
10
+ import { readFileSync, writeFileSync } from 'node:fs';
10
11
  import { AID } from './aid.js';
11
12
  import * as codes from './error-codes.js';
12
- import { certCommonName, certMatchesFingerprint, certTimeError, signBytes, verifySignatureWithCert } from './cert-utils.js';
13
+ import { certCommonName, certMatchesFingerprint, certTimeError, parseAgentMdTailSignature, publicKeyMatchesFingerprint, signBytes, signDocument, verifyDocument, verifySignatureWithCert } from './cert-utils.js';
13
14
  import { certFingerprint as _certFingerprint } from './cert-utils.js';
14
15
  import { resultErr, resultOk } from './result.js';
15
16
  import { LocalIdentityStore } from './keystore/local-identity-store.js';
@@ -21,10 +22,10 @@ import { AUNLogger } from './logger.js';
21
22
  import { getDeviceId, normalizeSlotId } from './config.js';
22
23
  import { AUNError, ClientSignatureError, IdentityConflictError, NotFoundError, ValidationError } from './errors.js';
23
24
  import { AgentMdManager, buildDefaultAgentMd } from './agent-md.js';
24
- import { validateAgentMdDocument } from './agent-md-schema.js';
25
+ import { parseFileFrontmatter, validateAgentMdCertificate, validateAgentMdDocument, validateAgentMdReadDocument } from './agent-md-schema.js';
25
26
  import { LocalTokenStore } from './keystore/local-token-store.js';
26
27
  import { AuthFlow } from './auth.js';
27
- import { OWNERSHIP_UUID_V4_RE, ownershipFailure, signOwnershipFile } from './ownership-v21.js';
28
+ import { OWNERSHIP_UUID_V4_RE, ownershipFailure } from './ownership-v21.js';
28
29
  import { validateAIDFormat } from './validators.js';
29
30
  function resultError(r) {
30
31
  if (r.ok)
@@ -96,6 +97,114 @@ export class AIDStore {
96
97
  _gatewayCache = new Map();
97
98
  _agentMdManager;
98
99
  _peerAuth;
100
+ async sign(aid, input, options = {}) {
101
+ try {
102
+ if (!['text', 'file'].includes(options.inputType ?? 'text') || !['raw', 'document'].includes(options.format ?? 'raw'))
103
+ throw new Error('invalid signature inputType or format');
104
+ if (options.append && options.format !== 'document')
105
+ throw new Error('append requires document format');
106
+ if (options.inPlace && (options.inputType !== 'file' || options.format !== 'document'))
107
+ throw new Error('inPlace requires file input and document format');
108
+ const raw = options.inputType === 'file' ? readFileSync(input) : input;
109
+ if (typeof raw === 'string' && options.format !== 'document' && new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(new TextEncoder().encode(raw)) !== raw)
110
+ throw new Error('signature text must be valid UTF-8');
111
+ const loaded = options.identity ? resultOk({ aid: options.identity }) : this.load(aid);
112
+ if (!loaded.ok)
113
+ return loaded;
114
+ const identity = loaded.data.aid;
115
+ if (identity.aid !== aid || identity.certSubject.toLowerCase() !== aid.toLowerCase() || !identity.isPrivateKeyValid() || !identity.isCertValid())
116
+ return resultErr(codes.PRIVATE_KEY_NOT_VALID, 'sign requires a valid local signing identity');
117
+ if (options.format !== 'document')
118
+ return identity.sign(raw);
119
+ const content = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
120
+ const signed = signDocument(identity, content, { append: options.append, tag: options.tag, timestamp: options.timestamp, signerAid: options.signerAid });
121
+ if (options.inPlace)
122
+ writeFileSync(input, signed.signed, 'utf8');
123
+ return resultOk(signed);
124
+ }
125
+ catch (error) {
126
+ return resultErr(codes.SIGNATURE_OPERATION_ERROR, error instanceof Error ? error.message : String(error), error);
127
+ }
128
+ }
129
+ async verify(aid, input, signatureOrOptions, options = {}) {
130
+ const signature = typeof signatureOrOptions === 'string' ? signatureOrOptions : undefined;
131
+ if (signatureOrOptions && typeof signatureOrOptions === 'object')
132
+ options = signatureOrOptions;
133
+ try {
134
+ if (!['text', 'file'].includes(options.inputType ?? 'text') || !['raw', 'document'].includes(options.format ?? 'raw'))
135
+ throw new Error('invalid signature inputType or format');
136
+ if (options.inPlace)
137
+ throw new Error('verify does not modify files');
138
+ if (options.identity && (options.identity.aid !== aid || options.identity.certSubject.toLowerCase() !== aid.toLowerCase()))
139
+ throw new Error('verification identity does not match AID');
140
+ const raw = options.inputType === 'file' ? readFileSync(input) : input;
141
+ if (typeof raw === 'string' && options.format !== 'document' && new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(new TextEncoder().encode(raw)) !== raw)
142
+ throw new Error('signature text must be valid UTF-8');
143
+ if (options.format === 'document') {
144
+ const text = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
145
+ const parsed = parseAgentMdTailSignature(text, options.readCompatibility);
146
+ if (parsed.parseError || !parsed.fields)
147
+ return resultOk(verifyDocument(text, { expectedAid: aid, readCompatibility: options.readCompatibility }));
148
+ }
149
+ let loaded = options.identity ? resultOk({ aid: options.identity }) : this.load(aid);
150
+ if (!loaded.ok && options.format === 'document' && options.certificateResolver) {
151
+ const text = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
152
+ const primary = parseAgentMdTailSignature(text, options.readCompatibility).fields;
153
+ if (primary) {
154
+ const certificate = await options.certificateResolver(aid, primary.cert_fingerprint, Number(primary.timestamp));
155
+ loaded = resultOk({ aid: typeof certificate === 'string' ? AID._create({ aid, aunPath: this.aunPath, certPem: certificate, certValid: true, privateKeyPem: null, privateKeyValid: false }) : certificate });
156
+ }
157
+ }
158
+ if (!loaded.ok)
159
+ return loaded;
160
+ const identity = loaded.data.aid;
161
+ if (identity.aid !== aid || identity.certSubject.toLowerCase() !== aid.toLowerCase())
162
+ throw new Error('verification identity does not match AID');
163
+ if (options.format !== 'document')
164
+ return identity.verify(raw, signature ?? '');
165
+ const content = typeof raw === 'string' ? raw : new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw);
166
+ const certificates = await this._documentCertificates(content, options, identity);
167
+ const primary = parseAgentMdTailSignature(content, options.readCompatibility).fields;
168
+ const primaryCertPem = primary ? certificates.get(`${aid};${primary.cert_fingerprint}`) ?? identity.certPem : identity.certPem;
169
+ return resultOk(verifyDocument(content, { expectedAid: aid, primaryCertPem, readCompatibility: options.readCompatibility, requireCurrentValidity: options.requireCurrentValidity, certificateResolver: (signer, fingerprint) => certificates.get(`${signer};${fingerprint}`) }));
170
+ }
171
+ catch (error) {
172
+ return resultErr(codes.VERIFICATION_OPERATION_ERROR, error instanceof Error ? error.message : String(error), error);
173
+ }
174
+ }
175
+ async _documentCertificates(content, options, primary) {
176
+ const certificates = new Map();
177
+ let inferredAid = primary?.aid;
178
+ if (!inferredAid) {
179
+ try {
180
+ const meta = parseFileFrontmatter(parseAgentMdTailSignature(content).payload);
181
+ inferredAid = String(meta.aid ?? meta.owner_aid ?? '') || undefined;
182
+ }
183
+ catch { }
184
+ }
185
+ for (const item of parseAgentMdTailSignature(content, options.readCompatibility).signatures) {
186
+ const fields = item.fields, signer = fields.signer_aid ?? inferredAid;
187
+ if (!signer)
188
+ continue;
189
+ let cert;
190
+ if (options.certificateResolver)
191
+ cert = await options.certificateResolver(signer, fields.cert_fingerprint, Number(fields.timestamp));
192
+ else if (primary?.aid === signer && certMatchesFingerprint(primary.certPem, fields.cert_fingerprint))
193
+ cert = primary;
194
+ else {
195
+ const loaded = this.load(signer);
196
+ if (loaded.ok && certMatchesFingerprint(loaded.data.aid.certPem, fields.cert_fingerprint))
197
+ cert = loaded.data.aid;
198
+ else
199
+ throw new Error('signing certificate is unavailable locally; an explicit certificateResolver is required');
200
+ }
201
+ if (cert && typeof cert !== 'string' && cert.aid !== signer)
202
+ throw new Error('resolved certificate identity does not match signer AID');
203
+ if (cert)
204
+ certificates.set(`${signer};${fields.cert_fingerprint}`, typeof cert === 'string' ? cert : cert.certPem);
205
+ }
206
+ return certificates;
207
+ }
99
208
  constructor(opts) {
100
209
  this.aunPath = String(opts.aunPath ?? join(homedir(), '.aun'));
101
210
  this._encryptionSeed = String(opts.encryptionSeed ?? '');
@@ -148,28 +257,7 @@ export class AIDStore {
148
257
  const material = await this._peerAuth.resolveCurrentEntityCertificate(await this._resolveGateway(target), target, type ?? 'org');
149
258
  return AID._create({ aid: target, aunPath: this.aunPath, certPem: material.cert_pem, privateKeyPem: null, certValid: true, privateKeyValid: false });
150
259
  },
151
- peerResolver: async (target, certFingerprint, signedAt) => {
152
- const fp = String(certFingerprint ?? '').trim().toLowerCase();
153
- const cachedCert = this._keystore.loadCert(target, fp || undefined);
154
- if (cachedCert) {
155
- return AID._create({ aid: target, aunPath: this.aunPath, certPem: cachedCert, privateKeyPem: null, certValid: true, privateKeyValid: false, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug });
156
- }
157
- if (fp) {
158
- const gatewayUrl = await this._resolveGateway(target);
159
- const certPem = await this._net.httpGetText(pkiCertUrl(gatewayUrl, target, fp), 10_000);
160
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt);
161
- this._keystore.saveCert(target, certPem, fp, { makeActive: false });
162
- return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
163
- }
164
- const gatewayUrl = await this._resolveGateway(target);
165
- const certPem = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
166
- if (!certPem) {
167
- throw new Error(`certificate not found for aid: ${target}`);
168
- }
169
- await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt);
170
- this._keystore.saveCert(target, certPem);
171
- return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
172
- },
260
+ peerResolver: (target, certFingerprint, signedAt) => this._resolveAgentMdPeerCertificate(target, certFingerprint, signedAt),
173
261
  });
174
262
  }
175
263
  close() {
@@ -179,6 +267,29 @@ export class AIDStore {
179
267
  if (typeof net.close === 'function')
180
268
  net.close();
181
269
  }
270
+ async _resolveAgentMdPeerCertificate(target, certFingerprint, signedAt) {
271
+ const fp = String(certFingerprint ?? '').trim().toLowerCase();
272
+ const cachedCert = this._keystore.loadCert(target, fp || undefined);
273
+ if (cachedCert && (!fp || certMatchesFingerprint(cachedCert, fp) || publicKeyMatchesFingerprint(cachedCert, fp))) {
274
+ if (signedAt !== undefined)
275
+ await this._verifyDownloadedPeerCertificate(await this._resolveGateway(target), target, cachedCert, fp, signedAt);
276
+ return AID._create({ aid: target, aunPath: this.aunPath, certPem: cachedCert, privateKeyPem: null, certValid: true, privateKeyValid: false, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug });
277
+ }
278
+ if (fp) {
279
+ const gatewayUrl = await this._resolveGateway(target);
280
+ const certPem = await this._net.httpGetText(pkiCertUrl(gatewayUrl, target, fp), 10_000);
281
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt);
282
+ this._keystore.saveCert(target, certPem, _certFingerprint(certPem), { makeActive: false });
283
+ return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
284
+ }
285
+ const gatewayUrl = await this._resolveGateway(target);
286
+ const certPem = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
287
+ if (!certPem)
288
+ throw new Error(`certificate not found for aid: ${target}`);
289
+ await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt);
290
+ this._keystore.saveCert(target, certPem);
291
+ return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
292
+ }
182
293
  // ── 离线方法 ──────────────────────────────────────────────────
183
294
  load(aid) {
184
295
  const target = String(aid ?? '').trim();
@@ -287,6 +398,7 @@ export class AIDStore {
287
398
  return resultErr(codes.PRIVATE_KEY_PARSE_ERROR, 'newSeed must differ from oldSeed');
288
399
  try {
289
400
  const result = this._keystore.changeSeed(oldSeed, newSeed);
401
+ this._tokenStore['_setEncryptionSeed'](newSeed);
290
402
  this._encryptionSeed = newSeed;
291
403
  return resultOk({ changed: true, count: result.migrated ?? 0 });
292
404
  }
@@ -307,8 +419,8 @@ export class AIDStore {
307
419
  const entity = ['group', 'org'].includes(type);
308
420
  if (entity && !owner)
309
421
  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');
422
+ if (!type || type === 'human' && owner)
423
+ throw new ValidationError('register requires type; human forbids owner');
312
424
  if (owner)
313
425
  validateAIDFormat(owner, 'owner');
314
426
  const previous = this._tokenStore.loadMetadata(target)?.registration_intent;
@@ -318,7 +430,8 @@ export class AIDStore {
318
430
  if (previous && (previous.type !== type || previous.owner !== owner || previous.exttype !== exttype))
319
431
  throw new IdentityConflictError('registration ownership intent already exists with a different context');
320
432
  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 });
433
+ if (type !== 'agent' || owner)
434
+ this._tokenStore.saveMetadata(target, { registration_intent: intent });
322
435
  const gatewayUrl = await this._resolveGateway(target);
323
436
  const result = await this._registerFlow.registerAid(gatewayUrl, target);
324
437
  if (result.cert) {
@@ -360,15 +473,10 @@ export class AIDStore {
360
473
  accessTokenResolver: async () => this._uploadAgentMdToken(current, await this._resolveGateway(target)),
361
474
  aidValidator: (value) => this._registerFlow.validateAidName(value),
362
475
  });
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);
476
+ const fresh = await manager.readAgentMdFresh(target);
366
477
  if (fresh.exists)
367
478
  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: '*' });
479
+ await manager.upload(buildDefaultAgentMd(target, { type, exttype }), { if_none_match: '*' });
372
480
  return resultOk({ initialized: true });
373
481
  }
374
482
  catch (error) {
@@ -401,6 +509,19 @@ export class AIDStore {
401
509
  let peer;
402
510
  let certFromCache = false;
403
511
  const loaded = this.load(target);
512
+ const local = !forceRefresh && !skipAgentMd && ((loaded.ok && loaded.data?.aid.isPrivateKeyValid()) || this._keystore.loadKeyPair(target)?.private_key_pem) ? (this._agentMdManager._loadRecord?.(target, true) ?? this._agentMdManager.loadRecord(target)) : null;
513
+ const draft = String(local?.local_draft_content ?? '');
514
+ const content = draft || String(local?.content ?? '');
515
+ if (content) {
516
+ peer = loaded.ok && loaded.data ? loaded.data.aid : AID._create({ aid: target, aunPath: this.aunPath, certPem: this._keystore.loadCert(target) ?? '', privateKeyPem: null, certValid: false, privateKeyValid: false, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug, aidStore: this });
517
+ const localEtag = AgentMdManager.contentEtag(content), remoteEtag = String(local?.remote_etag ?? '');
518
+ return await this._resolveOwnership({ aid: peer, source: { cert_from_cache: true, agent_md_fetched: false }, agent_md: {
519
+ aid: target, content, frontmatter: validateAgentMdReadDocument(content).frontmatter, verification: { status: 'unverified' }, signature: {}, cert_pem: '',
520
+ etag: localEtag, last_modified: String(local?.last_modified ?? ''), status: 200,
521
+ in_sync: !draft && !!remoteEtag && localEtag === remoteEtag,
522
+ saved_to: draft ? this._agentMdManager.metaPath(target) : this._agentMdManager.filePath(target), save_error: null,
523
+ } }, opts);
524
+ }
404
525
  if (loaded.ok && loaded.data && !forceRefresh) {
405
526
  peer = loaded.data.aid;
406
527
  certFromCache = true;
@@ -419,17 +540,42 @@ export class AIDStore {
419
540
  }
420
541
  const source = { cert_from_cache: certFromCache, agent_md_fetched: false };
421
542
  if (skipAgentMd)
422
- return resultOk({ aid: peer, source });
543
+ return await this._resolveOwnership({ aid: peer, source }, opts);
423
544
  const agentMd = await this.downloadAgentMd(target, opts?.timeout ?? 10000);
424
545
  if (!agentMd.ok)
425
546
  return agentMd;
426
547
  source.agent_md_fetched = true;
427
- return resultOk({ aid: peer, agent_md: agentMd.data, source });
548
+ return await this._resolveOwnership({ aid: peer, agent_md: agentMd.data, source }, opts);
428
549
  }
429
550
  catch (exc) {
551
+ if (exc instanceof AUNError && exc.stringCode)
552
+ return resultErr(exc.stringCode, exc.message, exc);
430
553
  return resultErr(codes.NETWORK_ERROR, String(exc), exc);
431
554
  }
432
555
  }
556
+ async _resolveOwnership(data, opts) {
557
+ if (!opts?.ownership)
558
+ return resultOk(data);
559
+ const target = data.aid.aid;
560
+ try {
561
+ const cached = this._agentMdManager.loadRecord(target)?.ownership_snapshot;
562
+ const now = Date.now(), ttl = opts.ownershipTtlMs ?? 60_000;
563
+ const hash = data.agent_md ? AgentMdManager.contentEtag(data.agent_md.content).slice(1, -1) : '';
564
+ if (!opts.forceRefresh && cached?.sub_aid === target && cached.status !== 'unavailable' && now >= cached.checked_at && now - cached.checked_at < ttl && (!hash || cached.agent_md_sha256 === hash)) {
565
+ data.ownership = { ...JSON.parse(JSON.stringify(cached)), cached: true };
566
+ }
567
+ else {
568
+ const checked = await opts.ownership.checkOwnershipConsensus(target);
569
+ data.ownership = { ...JSON.parse(JSON.stringify(checked)), checked_at: Date.now(), cached: false };
570
+ this._agentMdManager.saveRecord(target, { ownership_snapshot: JSON.parse(JSON.stringify(data.ownership)) });
571
+ }
572
+ }
573
+ catch (error) {
574
+ data.ownership = { status: 'unavailable', sub_aid: target, owner_aid: null, relation_version: 0, lock: 'unlocked', agent_md_etag: '', agent_md_sha256: '', owner_md_sha256: '', code: 'unavailable', reason: error instanceof Error ? error.message : String(error), retryable: true, checked_at: Date.now(), cached: false };
575
+ this._agentMdManager.saveRecord(target, { ownership_snapshot: data.ownership });
576
+ }
577
+ return resultOk(data);
578
+ }
433
579
  async downloadAgentMd(aid, timeoutMs = 30000) {
434
580
  try {
435
581
  return resultOk(await this._agentMdManager.download(aid, timeoutMs));
@@ -448,13 +594,15 @@ export class AIDStore {
448
594
  }
449
595
  async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '', signedAt) {
450
596
  const expectedFp = String(certFingerprint ?? '').trim().toLowerCase();
451
- if (expectedFp && !certMatchesFingerprint(certPem, expectedFp)) {
597
+ if (expectedFp && !certMatchesFingerprint(certPem, expectedFp) && !(signedAt !== undefined && publicKeyMatchesFingerprint(certPem, expectedFp))) {
452
598
  throw new ValidationError(`certificate fingerprint mismatch for aid: ${aid}`);
453
599
  }
454
600
  if (signedAt === undefined)
455
601
  await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid);
456
- else
457
- await this._peerAuth.verifyPeerCertificate(gatewayUrl, certPem, aid, signedAt);
602
+ else {
603
+ validateAgentMdCertificate(certPem, aid, signedAt, { requireCurrentValidity: false, readCompatibility: true });
604
+ await this._peerAuth._verifyAuthCertChain(gatewayUrl, new X509Certificate(certPem), aid, signedAt);
605
+ }
458
606
  }
459
607
  _authIdentityFromAid(aid) {
460
608
  return {
@@ -493,8 +641,9 @@ export class AIDStore {
493
641
  return resultErr(err?.code ?? codes.CERT_NOT_FOUND, err?.message ?? `certificate not found for aid: ${target}`);
494
642
  }
495
643
  const current = loaded.data.aid;
496
- const raw = content ?? this._agentMdManager.readContent(target);
497
- const parsed = validateAgentMdDocument(raw, { expectedAid: target });
644
+ const pending = content === undefined || content === null ? this._agentMdManager.pendingUpload(target) : null;
645
+ const raw = pending?.content ?? content ?? (String((this._agentMdManager._loadRecord?.(target, true) ?? this._agentMdManager.loadRecord(target))?.local_draft_content ?? '') || this._agentMdManager.readContent(target));
646
+ const parsed = validateAgentMdDocument(raw, { expectedAid: target, validateOwnership: false });
498
647
  const meta = parsed.frontmatter;
499
648
  const entity = ['group', 'org'].includes(String(meta.type));
500
649
  const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? (entity ? meta.owner : target)).trim();
@@ -511,7 +660,7 @@ export class AIDStore {
511
660
  return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires uploader AID with a valid private key: ${uploaderTarget}`);
512
661
  }
513
662
  }
514
- if ((!current.isPrivateKeyValid() || !current.privateKeyPem) && !(entity && parsed.signature && uploaderTarget.toLowerCase() !== target.toLowerCase())) {
663
+ if (!entity && (!current.isPrivateKeyValid() || !current.privateKeyPem)) {
515
664
  return resultErr(codes.PRIVATE_KEY_NOT_VALID, `uploadAgentMd requires local AID with a valid private key: ${target}`);
516
665
  }
517
666
  const manager = new AgentMdManager({
@@ -532,11 +681,32 @@ export class AIDStore {
532
681
  return AID._create({ aid: value, aunPath: this.aunPath, certPem: material.cert_pem, certValid: true, privateKeyPem: null, privateKeyValid: false });
533
682
  },
534
683
  entityOwnerResolver: options.entityOwnerResolver,
684
+ peerResolver: (value, fingerprint, signedAt) => this._resolveAgentMdPeerCertificate(value, fingerprint, signedAt),
535
685
  gatewayResolver: (value) => this._resolveGateway(value),
536
686
  accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
537
687
  aidValidator: (value) => this._registerFlow.validateAidName(value),
538
688
  });
539
- return resultOk(entity ? await manager.uploadAs(target, raw, current) : await manager.upload(raw));
689
+ if (pending) {
690
+ await manager.resumePendingUpload(target, current);
691
+ const record = manager.loadRecord(target);
692
+ return resultOk({ aid: target, etag: String(record?.remote_etag ?? ''), last_modified: String(record?.last_modified ?? '') });
693
+ }
694
+ let prepared = raw;
695
+ if (entity && !parsed.signature) {
696
+ const signed = await this.sign(target, raw, { format: 'document', identity: current, signerAid: target });
697
+ if (!signed.ok)
698
+ return signed;
699
+ prepared = signed.data.signed;
700
+ }
701
+ if (entity && options.coSignerAid) {
702
+ if (options.coSignerAid !== uploaderTarget)
703
+ throw new ValidationError('entity additional signer must be its uploading owner');
704
+ const signed = await this.sign(options.coSignerAid, prepared, { format: 'document', append: true, identity: uploader });
705
+ if (!signed.ok)
706
+ return signed;
707
+ prepared = signed.data.signed;
708
+ }
709
+ return resultOk(entity ? await manager.uploadAs(target, prepared, current) : await manager.upload(raw));
540
710
  }
541
711
  catch (exc) {
542
712
  return agentMdFailure(exc);
@@ -587,6 +757,12 @@ export class AIDStore {
587
757
  });
588
758
  }
589
759
  async renewCert(aid) {
760
+ return this._renewCert(aid);
761
+ }
762
+ async _renewCert(aid, guard) {
763
+ const check = () => { if (guard && (!guard.current() || guard.signal?.aborted))
764
+ throw new Error('credential renewal was superseded'); };
765
+ check();
590
766
  const target = String(aid ?? '').trim();
591
767
  const loaded = this.load(target);
592
768
  if (!loaded.ok || !loaded.data || !loaded.data.aid.isPrivateKeyValid()) {
@@ -595,12 +771,15 @@ export class AIDStore {
595
771
  try {
596
772
  this._registerFlow.validateAidName(target);
597
773
  const gatewayUrl = await this._resolveGateway(target);
774
+ check();
598
775
  const aidObj = loaded.data.aid;
599
776
  const clientNonce = this._registerFlow.newClientNonce();
600
777
  const phase1 = await this._registerFlow.shortRpc(gatewayUrl, 'auth.aid_login1', {
601
778
  aid: target, cert: aidObj.certPem, client_nonce: clientNonce,
602
779
  });
780
+ check();
603
781
  await this._registerFlow.verifyPhase1Response(gatewayUrl, phase1, clientNonce);
782
+ check();
604
783
  const signResult = aidObj.sign(String(phase1.nonce));
605
784
  if (!signResult.ok || !signResult.data) {
606
785
  return resultErr(codes.CERT_RENEWAL_FAILED, resultError(signResult)?.message ?? 'sign failed');
@@ -611,6 +790,7 @@ export class AIDStore {
611
790
  const certPem = String(response.cert ?? response.cert_pem ?? '').trim();
612
791
  if (!certPem)
613
792
  return resultErr(codes.CERT_RENEWAL_FAILED, 'server response missing certificate');
793
+ check();
614
794
  this._keystore.saveCert(target, certPem);
615
795
  const refreshed = this.load(target);
616
796
  if (!refreshed.ok || !refreshed.data)