@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.
- package/CHANGELOG.md +176 -78
- package/_packed_docs/CHANGELOG.md +176 -78
- package/_packed_docs/INDEX.md +288 -269
- package/_packed_docs/KITE_DOCS_GUIDE.md +101 -95
- package/_packed_docs/agent.md/SCHEMA.md +7 -7
- 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
- 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
- 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
- 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
- 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
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +372 -331
- package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +445 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +666 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
- package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
- 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
- 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
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +70 -63
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +43 -40
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +179 -19
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +50 -49
- package/_packed_docs/sdk/09-group-rpc-manual.md +261 -254
- package/_packed_docs/sdk/09-message-rpc-manual.md +253 -160
- package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
- 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
- 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
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +105 -92
- package/_packed_docs/sdk/INDEX.md +197 -167
- package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +930 -0
- package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
- 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
- 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
- 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
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +480 -0
- package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
- package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/dist/agent-md-schema.d.ts +13 -1
- package/dist/agent-md-schema.js +63 -33
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +30 -3
- package/dist/agent-md.js +293 -152
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +42 -0
- package/dist/aid-store.js +224 -44
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.js +18 -13
- package/dist/aid.js.map +1 -1
- package/dist/auth.js +78 -2
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +44 -1
- package/dist/cert-utils.js +157 -56
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +68 -1
- package/dist/client/delivery.js +1196 -135
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +5 -1
- package/dist/client/rpc-pipeline.js +240 -111
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +12 -2
- package/dist/client/v2-e2ee.js +842 -379
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +26 -2
- package/dist/client.js +420 -75
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +23 -4
- package/dist/events.js +171 -15
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +43 -1
- package/dist/facades.js +368 -12
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +7 -2
- package/dist/keystore/aid-db.js +194 -43
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +15 -1
- package/dist/keystore/local-token-store.d.ts +10 -2
- package/dist/keystore/local-token-store.js +81 -16
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +45 -0
- package/dist/org-protocol.js +829 -22
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +40 -9
- package/dist/ownership-v21.js +174 -79
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +22 -0
- package/dist/ownership-workflow.js +861 -66
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/secret-store/file-store.js +84 -0
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +2 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +67 -3
- package/dist/tools/cross-sdk-agent.js +575 -42
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +4 -0
- package/dist/transport.js +108 -44
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2/e2ee/encrypt-group.js +1 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/e2ee/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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
|
|
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:
|
|
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
|
|
311
|
-
throw new ValidationError('register requires type; human forbids 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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
497
|
-
const
|
|
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)
|
|
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
|
-
|
|
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)
|