@naylence/advanced-security 0.3.11 → 0.3.15
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/dist/browser/index.cjs +1961 -1952
- package/dist/browser/index.mjs +1962 -1953
- package/dist/cjs/browser.js +11 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/node.js +27 -0
- package/dist/cjs/node.js.map +1 -1
- package/dist/cjs/version.js +2 -2
- package/dist/esm/browser.js +11 -0
- package/dist/esm/browser.js.map +1 -1
- package/dist/esm/node.js +27 -0
- package/dist/esm/node.js.map +1 -1
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.mjs +2 -2
- package/dist/node/node.cjs +2009 -1983
- package/dist/node/node.mjs +2011 -1985
- package/dist/types/browser.d.ts.map +1 -1
- package/dist/types/node.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -2
package/dist/browser/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var factory = require('@naylence/factory');
|
|
3
4
|
var runtime = require('@naylence/runtime');
|
|
4
5
|
var asn1Schema = require('@peculiar/asn1-schema');
|
|
5
6
|
var asn1Csr = require('@peculiar/asn1-csr');
|
|
@@ -12,603 +13,1127 @@ var ed25519_js = require('@noble/curves/ed25519.js');
|
|
|
12
13
|
var hkdf_js = require('@noble/hashes/hkdf.js');
|
|
13
14
|
var utils_js = require('@noble/hashes/utils.js');
|
|
14
15
|
var jose = require('jose');
|
|
15
|
-
var factory = require('@naylence/factory');
|
|
16
16
|
var sha256_js = require('@noble/hashes/sha256.js');
|
|
17
17
|
var x509 = require('@peculiar/x509');
|
|
18
18
|
|
|
19
|
-
// This file is auto-generated during build - do not edit manually
|
|
20
|
-
// Generated from package.json version: 0.3.11
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* AUTO-GENERATED FILE. DO NOT EDIT DIRECTLY.
|
|
21
|
+
* Generated by scripts/generate-factory-manifest.mjs
|
|
22
|
+
*
|
|
23
|
+
* Provides the list of advanced security factory modules for registration.
|
|
24
24
|
*/
|
|
25
|
-
const
|
|
25
|
+
const MODULES = [
|
|
26
|
+
"./security/cert/default-ca-service-factory.js",
|
|
27
|
+
"./security/cert/default-certificate-manager-factory.js",
|
|
28
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js",
|
|
29
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
30
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js",
|
|
31
|
+
"./security/encryption/composite-encryption-manager-factory.js",
|
|
32
|
+
"./security/encryption/default-secure-channel-manager-factory.js",
|
|
33
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
34
|
+
"./security/keys/x5c-key-manager-factory.js",
|
|
35
|
+
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
36
|
+
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
37
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
38
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
39
|
+
"./welcome/advanced-welcome-service-factory.js"
|
|
40
|
+
];
|
|
41
|
+
const MODULE_LOADERS = {
|
|
42
|
+
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
43
|
+
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
44
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
45
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
46
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
47
|
+
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
48
|
+
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
49
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
50
|
+
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
51
|
+
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
52
|
+
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
53
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
54
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
55
|
+
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
56
|
+
};
|
|
26
57
|
|
|
27
|
-
const logger$h = runtime.getLogger("naylence.fame.security.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
const logger$h = runtime.getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
59
|
+
class EncryptionManagerFactoryRegistry {
|
|
60
|
+
constructor(autoDiscover = true) {
|
|
61
|
+
this.factories = [];
|
|
62
|
+
this.algorithmToFactory = new Map();
|
|
63
|
+
this.typeToFactories = new Map();
|
|
64
|
+
this.factorySet = new Set();
|
|
65
|
+
this.autoDiscoveredFactories = new Set();
|
|
66
|
+
this.autoDiscovered = false;
|
|
67
|
+
if (autoDiscover) {
|
|
68
|
+
this.autoDiscoverFactories();
|
|
69
|
+
}
|
|
35
70
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
autoDiscoverFactories() {
|
|
72
|
+
if (this.autoDiscovered) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const extensionInfos = factory.ExtensionManager.getExtensionsByType(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
77
|
+
let registeredCount = 0;
|
|
78
|
+
for (const [factoryName, info] of extensionInfos) {
|
|
79
|
+
if (factoryName === "CompositeEncryptionManager") {
|
|
80
|
+
logger$h.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
81
|
+
factory_name: factoryName,
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const factoryInstance = (info.instance ??
|
|
87
|
+
factory.ExtensionManager.getGlobalFactory(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
88
|
+
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
89
|
+
registeredCount += 1;
|
|
90
|
+
logger$h.debug("auto_discovered_factory", {
|
|
91
|
+
factory_name: factoryName,
|
|
92
|
+
factory_class: factoryInstance.constructor.name,
|
|
93
|
+
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
94
|
+
encryption_type: factoryInstance.getEncryptionType(),
|
|
95
|
+
priority: factoryInstance.getPriority(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
logger$h.warning("failed_to_auto_register_factory", {
|
|
100
|
+
factory_name: factoryName,
|
|
101
|
+
error: error instanceof Error ? error.message : String(error),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.autoDiscovered = true;
|
|
106
|
+
logger$h.debug("completed_auto_discovery", {
|
|
107
|
+
registered_factories: registeredCount,
|
|
108
|
+
total_discovered: extensionInfos.size,
|
|
109
|
+
skipped_composite: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
logger$h.warning("failed_auto_discovery_of_factories", {
|
|
114
|
+
error: error instanceof Error ? error.message : String(error),
|
|
56
115
|
});
|
|
57
|
-
return cached;
|
|
58
116
|
}
|
|
59
|
-
logger$h.debug("certificate_cache_miss", {
|
|
60
|
-
call_id: callId,
|
|
61
|
-
cache_key: cacheKey,
|
|
62
|
-
});
|
|
63
117
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
118
|
+
registerFactory(factory, options = {}) {
|
|
119
|
+
if (this.factorySet.has(factory)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.factorySet.add(factory);
|
|
123
|
+
this.factories.push(factory);
|
|
124
|
+
if (options.autoDiscovered) {
|
|
125
|
+
this.autoDiscoveredFactories.add(factory);
|
|
126
|
+
}
|
|
127
|
+
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
128
|
+
const existing = this.algorithmToFactory.get(algorithm);
|
|
129
|
+
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
130
|
+
this.algorithmToFactory.set(algorithm, factory);
|
|
131
|
+
logger$h.debug("registered_algorithm_mapping", {
|
|
132
|
+
algorithm,
|
|
133
|
+
factory: factory.constructor.name,
|
|
134
|
+
priority: factory.getPriority(),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const encryptionType = factory.getEncryptionType();
|
|
139
|
+
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
140
|
+
typeFactories.push(factory);
|
|
141
|
+
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
142
|
+
this.typeToFactories.set(encryptionType, typeFactories);
|
|
143
|
+
logger$h.debug("registered_encryption_manager_factory", {
|
|
144
|
+
factory: factory.constructor.name,
|
|
145
|
+
encryption_type: encryptionType,
|
|
146
|
+
algorithms: factory.getSupportedAlgorithms(),
|
|
147
|
+
priority: factory.getPriority(),
|
|
148
|
+
auto_discovered: options.autoDiscovered ?? false,
|
|
149
|
+
});
|
|
67
150
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
certificate: validation.certificate,
|
|
72
|
-
};
|
|
151
|
+
getFactoryForAlgorithm(algorithm) {
|
|
152
|
+
this.ensureAutoDiscovery();
|
|
153
|
+
return this.algorithmToFactory.get(algorithm);
|
|
73
154
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
155
|
+
getFactoryForOptions(opts) {
|
|
156
|
+
this.ensureAutoDiscovery();
|
|
157
|
+
for (const factory of this.factories) {
|
|
158
|
+
if (factory.supportsOptions(opts ?? undefined)) {
|
|
159
|
+
logger$h.debug("found_factory_for_options", {
|
|
160
|
+
factory: factory.constructor.name,
|
|
161
|
+
encryption_type: factory.getEncryptionType(),
|
|
162
|
+
});
|
|
163
|
+
return factory;
|
|
164
|
+
}
|
|
82
165
|
}
|
|
83
|
-
|
|
166
|
+
logger$h.debug("no_factory_found_for_options", { opts });
|
|
167
|
+
return undefined;
|
|
84
168
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
169
|
+
getFactoriesByType(encryptionType) {
|
|
170
|
+
this.ensureAutoDiscovery();
|
|
171
|
+
return this.typeToFactories.get(encryptionType) ?? [];
|
|
88
172
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const error = "Invalid x5c field in JWK";
|
|
93
|
-
if (strict) {
|
|
94
|
-
throw new Error(error);
|
|
95
|
-
}
|
|
96
|
-
return { isValid: false, error };
|
|
173
|
+
getAllSupportedAlgorithms() {
|
|
174
|
+
this.ensureAutoDiscovery();
|
|
175
|
+
return Array.from(this.algorithmToFactory.keys());
|
|
97
176
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
177
|
+
getRegistryInfo() {
|
|
178
|
+
return {
|
|
179
|
+
totalFactories: this.factories.length,
|
|
180
|
+
autoDiscovered: this.autoDiscovered,
|
|
181
|
+
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
182
|
+
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
183
|
+
encType,
|
|
184
|
+
factories.map((factory) => factory.constructor.name),
|
|
185
|
+
])),
|
|
186
|
+
};
|
|
104
187
|
}
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
188
|
+
forceRediscovery() {
|
|
189
|
+
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
190
|
+
this.autoDiscovered = false;
|
|
191
|
+
this.algorithmToFactory.clear();
|
|
192
|
+
this.typeToFactories.clear();
|
|
193
|
+
this.factories.length = 0;
|
|
194
|
+
this.factorySet.clear();
|
|
195
|
+
this.autoDiscoveredFactories.clear();
|
|
196
|
+
for (const factory of manualFactories) {
|
|
197
|
+
this.registerFactory(factory);
|
|
110
198
|
}
|
|
111
|
-
|
|
199
|
+
this.autoDiscoverFactories();
|
|
112
200
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const leaf = parsed[0];
|
|
116
|
-
const nowMs = Date.now();
|
|
117
|
-
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
118
|
-
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
119
|
-
const notBeforeMs = notBefore.getTime();
|
|
120
|
-
const notAfterMs = notAfter.getTime();
|
|
121
|
-
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
122
|
-
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
201
|
+
isAutoDiscovered() {
|
|
202
|
+
return this.autoDiscovered;
|
|
123
203
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
127
|
-
validateNameConstraints(issuers, leafUris);
|
|
204
|
+
ensureInitialized() {
|
|
205
|
+
this.ensureAutoDiscovery();
|
|
128
206
|
}
|
|
129
|
-
|
|
130
|
-
|
|
207
|
+
ensureAutoDiscovery() {
|
|
208
|
+
if (!this.autoDiscovered) {
|
|
209
|
+
this.autoDiscoverFactories();
|
|
210
|
+
}
|
|
131
211
|
}
|
|
132
|
-
validateChainContinuity(parsed);
|
|
133
|
-
const publicKey = leaf.subjectPublicKey.slice();
|
|
134
|
-
return {
|
|
135
|
-
publicKey,
|
|
136
|
-
certificate: leaf.certificate,
|
|
137
|
-
notAfter,
|
|
138
|
-
};
|
|
139
212
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
213
|
+
const globalRegistry = new EncryptionManagerFactoryRegistry(true);
|
|
214
|
+
function getEncryptionManagerFactoryRegistry() {
|
|
215
|
+
globalRegistry.ensureInitialized();
|
|
216
|
+
return globalRegistry;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const SECURITY_PREFIX = "./security/";
|
|
220
|
+
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
221
|
+
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
222
|
+
const NODE_ONLY_MODULES = new Set([
|
|
223
|
+
"./security/cert/default-ca-service-factory.js",
|
|
224
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
225
|
+
]);
|
|
226
|
+
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
227
|
+
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
228
|
+
function detectModuleUrl() {
|
|
229
|
+
if (typeof __filename === "string") {
|
|
157
230
|
try {
|
|
158
|
-
|
|
231
|
+
return __filename.startsWith("file://")
|
|
232
|
+
? __filename
|
|
233
|
+
: `file://${__filename}`;
|
|
159
234
|
}
|
|
160
|
-
catch
|
|
161
|
-
|
|
162
|
-
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
235
|
+
catch {
|
|
236
|
+
// fall through to stack parsing
|
|
163
237
|
}
|
|
164
|
-
parsed.push(createParsedCertificate(certificate, der));
|
|
165
|
-
derChunks.push(der);
|
|
166
238
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
function createParsedCertificate(certificate, raw) {
|
|
170
|
-
return {
|
|
171
|
-
raw,
|
|
172
|
-
certificate,
|
|
173
|
-
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
174
|
-
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
175
|
-
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
176
|
-
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function extractUrisFromCert(cert) {
|
|
180
|
-
const extension = findExtension(cert, asn1X509.id_ce_subjectAltName);
|
|
181
|
-
if (!extension) {
|
|
182
|
-
return [];
|
|
239
|
+
try {
|
|
240
|
+
throw new Error();
|
|
183
241
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
242
|
+
catch (error) {
|
|
243
|
+
const stack = typeof error === "object" && error && "stack" in error
|
|
244
|
+
? String(error.stack ?? "")
|
|
245
|
+
: "";
|
|
246
|
+
const lines = stack.split("\n");
|
|
247
|
+
for (const line of lines) {
|
|
248
|
+
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
249
|
+
const candidate = match?.[1];
|
|
250
|
+
if (!candidate) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
254
|
+
return candidate;
|
|
255
|
+
}
|
|
256
|
+
if (candidate.startsWith("file://")) {
|
|
257
|
+
return candidate;
|
|
258
|
+
}
|
|
259
|
+
return `file://${candidate}`;
|
|
189
260
|
}
|
|
190
261
|
}
|
|
191
|
-
return
|
|
262
|
+
return null;
|
|
192
263
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
264
|
+
function computeBrowserFactoryBase(rawUrl) {
|
|
265
|
+
if (!rawUrl) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const sanitized = rawUrl.split("?")[0]?.split("#")[0] ?? rawUrl;
|
|
269
|
+
const esmMarker = "/dist/esm/naylence/fame/";
|
|
270
|
+
const distMarker = "/dist/";
|
|
271
|
+
if (sanitized.includes(esmMarker)) {
|
|
272
|
+
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
273
|
+
}
|
|
274
|
+
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
275
|
+
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
276
|
+
}
|
|
277
|
+
if (sanitized.includes(BROWSER_DIST_SEGMENT)) {
|
|
278
|
+
const base = sanitized.slice(0, sanitized.indexOf(BROWSER_DIST_SEGMENT) + BROWSER_DIST_SEGMENT.length);
|
|
279
|
+
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
280
|
+
}
|
|
281
|
+
if (sanitized.includes(distMarker)) {
|
|
282
|
+
const index = sanitized.indexOf(distMarker);
|
|
283
|
+
const base = sanitized.slice(0, index + distMarker.length);
|
|
284
|
+
return `${base}esm/naylence/fame/`;
|
|
285
|
+
}
|
|
286
|
+
const srcMarker = "/src/naylence/fame/";
|
|
287
|
+
if (sanitized.includes(srcMarker)) {
|
|
288
|
+
const index = sanitized.indexOf(srcMarker);
|
|
289
|
+
const projectRoot = sanitized.slice(0, index);
|
|
290
|
+
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
291
|
+
}
|
|
292
|
+
if (sanitized.startsWith("http://") || sanitized.startsWith("https://")) {
|
|
293
|
+
try {
|
|
294
|
+
const parsed = new URL(rawUrl);
|
|
295
|
+
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
296
|
+
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
297
|
+
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
298
|
+
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
211
299
|
}
|
|
212
300
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
function collectPermittedUris(subtrees) {
|
|
216
|
-
const uris = [];
|
|
217
|
-
for (const subtree of subtrees) {
|
|
218
|
-
if (subtree.base.uniformResourceIdentifier &&
|
|
219
|
-
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
220
|
-
uris.push(subtree.base.uniformResourceIdentifier);
|
|
301
|
+
catch {
|
|
302
|
+
// ignore
|
|
221
303
|
}
|
|
222
304
|
}
|
|
223
|
-
return
|
|
305
|
+
return null;
|
|
224
306
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
307
|
+
const moduleUrl = detectModuleUrl();
|
|
308
|
+
const browserFactoryBase = computeBrowserFactoryBase(moduleUrl);
|
|
309
|
+
const prefersSource = typeof moduleUrl === "string" && moduleUrl.includes("/src/");
|
|
310
|
+
function resolveFactoryModuleSpecifier$1(specifier) {
|
|
311
|
+
if (specifier.startsWith("../")) {
|
|
312
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("../".length)}`;
|
|
229
313
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
trust_store_cert_count: trustedCerts.length,
|
|
233
|
-
});
|
|
234
|
-
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
235
|
-
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
236
|
-
logger$h.debug("certificate_chain_validation", {
|
|
237
|
-
chain_certificates: chainInfo,
|
|
238
|
-
trust_store_certificates: trustedInfo,
|
|
239
|
-
});
|
|
240
|
-
// Strategy 1: direct trust (exact certificate match)
|
|
241
|
-
for (let i = 0; i < chain.length; i += 1) {
|
|
242
|
-
const cert = chain[i];
|
|
243
|
-
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
244
|
-
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
245
|
-
if (match) {
|
|
246
|
-
logger$h.debug("certificate_chain_trust_validation_passed", {
|
|
247
|
-
matching_serial: match.serialNumber,
|
|
248
|
-
validation_strategy: `direct_trust_cert_${i}`,
|
|
249
|
-
});
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
314
|
+
if (specifier.startsWith("./")) {
|
|
315
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("./".length)}`;
|
|
252
316
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
matching_serial: trusted.serialNumber,
|
|
261
|
-
validation_strategy: "leaf_issuer_trust",
|
|
262
|
-
});
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
function resolveModuleCandidates(spec) {
|
|
320
|
+
const candidates = [];
|
|
321
|
+
const seen = new Set();
|
|
322
|
+
const addCandidate = (candidate) => {
|
|
323
|
+
if (!candidate) {
|
|
263
324
|
return;
|
|
264
325
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
276
|
-
});
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
326
|
+
if (!seen.has(candidate)) {
|
|
327
|
+
seen.add(candidate);
|
|
328
|
+
candidates.push(candidate);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
if (prefersSource && spec.startsWith("./")) {
|
|
332
|
+
const sourceCandidate = `../${spec.slice(2)}`;
|
|
333
|
+
addCandidate(sourceCandidate);
|
|
334
|
+
if (sourceCandidate.endsWith(".js")) {
|
|
335
|
+
addCandidate(sourceCandidate.replace(/\.js$/u, ".ts"));
|
|
279
336
|
}
|
|
280
337
|
}
|
|
281
|
-
|
|
282
|
-
leaf_subject: leaf.subjectName,
|
|
283
|
-
leaf_issuer: leaf.issuerName,
|
|
284
|
-
leaf_serial: leaf.serialNumber,
|
|
285
|
-
trusted_certificates: trustedInfo,
|
|
286
|
-
chain_certificates: chainInfo,
|
|
287
|
-
reason: "no_matching_trust_anchor",
|
|
288
|
-
});
|
|
289
|
-
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
290
|
-
}
|
|
291
|
-
function parseTrustStore(trustStorePem) {
|
|
292
|
-
const normalized = normalizePem$2(trustStorePem);
|
|
293
|
-
const blocks = extractPemBlocks$1(normalized);
|
|
294
|
-
const parsed = [];
|
|
295
|
-
for (const block of blocks) {
|
|
338
|
+
if (browserFactoryBase && spec.startsWith("./")) {
|
|
296
339
|
try {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
340
|
+
const browserCandidate = new URL(spec.slice("./".length), browserFactoryBase).href;
|
|
341
|
+
addCandidate(browserCandidate);
|
|
342
|
+
if (browserCandidate.endsWith(".js")) {
|
|
343
|
+
addCandidate(browserCandidate.replace(/\.js$/u, ".ts"));
|
|
344
|
+
}
|
|
300
345
|
}
|
|
301
|
-
catch
|
|
302
|
-
|
|
303
|
-
logger$h.debug("trust_store_certificate_parse_failed", { reason });
|
|
346
|
+
catch {
|
|
347
|
+
// ignore resolution failures for browser base
|
|
304
348
|
}
|
|
305
349
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const regex = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/gu;
|
|
311
|
-
let match;
|
|
312
|
-
// eslint-disable-next-line no-cond-assign
|
|
313
|
-
while ((match = regex.exec(pem)) !== null) {
|
|
314
|
-
const body = match[1] ?? "";
|
|
315
|
-
blocks.push(body.replace(/\s+/gu, ""));
|
|
350
|
+
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
351
|
+
addCandidate(packageCandidate);
|
|
352
|
+
if (packageCandidate?.endsWith(".js")) {
|
|
353
|
+
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
316
354
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return;
|
|
355
|
+
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
356
|
+
addCandidate(fallback);
|
|
357
|
+
if (fallback.endsWith(".js")) {
|
|
358
|
+
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
322
359
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const issuer = chain[index + 1];
|
|
327
|
-
if (!namesEqual(cert.certificate.tbsCertificate.issuer, issuer.certificate.tbsCertificate.subject)) {
|
|
328
|
-
logger$h.warning("certificate_chain_continuity_failed", {
|
|
329
|
-
cert_index: index,
|
|
330
|
-
cert_subject: cert.subjectName,
|
|
331
|
-
cert_issuer: cert.issuerName,
|
|
332
|
-
expected_issuer_subject: issuer.subjectName,
|
|
333
|
-
reason: "issuer_name_mismatch",
|
|
334
|
-
});
|
|
335
|
-
throw new Error(`Certificate chain continuity broken: certificate at index ${index} issuer does not match next certificate subject`);
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
verifyCertificateSignature(cert.certificate, issuer.certificate);
|
|
339
|
-
logger$h.debug("chain_continuity_verification_success", {
|
|
340
|
-
cert_index: index,
|
|
341
|
-
cert_serial: cert.serialNumber,
|
|
342
|
-
issuer_serial: issuer.serialNumber,
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
347
|
-
logger$h.warning("certificate_chain_continuity_failed", {
|
|
348
|
-
cert_index: index,
|
|
349
|
-
cert_subject: cert.subjectName,
|
|
350
|
-
issuer_subject: issuer.subjectName,
|
|
351
|
-
cert_serial: cert.serialNumber,
|
|
352
|
-
issuer_serial: issuer.serialNumber,
|
|
353
|
-
error: reason,
|
|
354
|
-
reason: "signature_verification_failed",
|
|
355
|
-
});
|
|
356
|
-
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
357
|
-
}
|
|
360
|
+
addCandidate(spec);
|
|
361
|
+
if (spec.endsWith(".js")) {
|
|
362
|
+
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
358
363
|
}
|
|
359
|
-
|
|
360
|
-
chain_length: chain.length,
|
|
361
|
-
});
|
|
364
|
+
return candidates;
|
|
362
365
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const signatureBytes = new Uint8Array(certificate.signatureValue);
|
|
371
|
-
const tbsBytes = new Uint8Array(asn1Schema.AsnConvert.serialize(certificate.tbsCertificate));
|
|
372
|
-
const issuerKey = new Uint8Array(issuer.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
|
373
|
-
if (issuerKey.length !== 32) {
|
|
374
|
-
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
375
|
-
}
|
|
376
|
-
const valid = ed25519.verify(signatureBytes, tbsBytes, issuerKey);
|
|
377
|
-
if (!valid) {
|
|
378
|
-
throw new Error("Certificate signature verification failed");
|
|
379
|
-
}
|
|
366
|
+
const registeredModules = new Set();
|
|
367
|
+
const inflightModules = new Map();
|
|
368
|
+
const browserSkippedModules = new Set();
|
|
369
|
+
function isNodeEnvironment$5() {
|
|
370
|
+
return (typeof process !== "undefined" &&
|
|
371
|
+
typeof process.release !== "undefined" &&
|
|
372
|
+
process.release?.name === "node");
|
|
380
373
|
}
|
|
381
|
-
function
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
etcPatch.sha512 = (message) => sha2_js.sha512(message);
|
|
374
|
+
function shouldSkipModule(spec) {
|
|
375
|
+
if (isNodeEnvironment$5()) {
|
|
376
|
+
return false;
|
|
385
377
|
}
|
|
386
|
-
if (!
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
378
|
+
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
if (!browserSkippedModules.has(spec)) {
|
|
382
|
+
// console.warn(
|
|
383
|
+
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
384
|
+
// spec,
|
|
385
|
+
// );
|
|
386
|
+
browserSkippedModules.add(spec);
|
|
394
387
|
}
|
|
388
|
+
return true;
|
|
395
389
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
if (!extensions) {
|
|
390
|
+
function getDynamicImporter() {
|
|
391
|
+
if (typeof globalThis === "undefined") {
|
|
399
392
|
return null;
|
|
400
393
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
394
|
+
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
395
|
+
if (typeof candidate === "function") {
|
|
396
|
+
return candidate;
|
|
405
397
|
}
|
|
406
398
|
return null;
|
|
407
399
|
}
|
|
408
|
-
function
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
400
|
+
async function registerModule(spec, registrar) {
|
|
401
|
+
const candidates = resolveModuleCandidates(spec);
|
|
402
|
+
const dynamicImporter = getDynamicImporter();
|
|
403
|
+
const loader = dynamicImporter
|
|
404
|
+
? (specifier) => dynamicImporter(specifier)
|
|
405
|
+
: (specifier) => import(/* @vite-ignore */ specifier);
|
|
406
|
+
const attempts = [];
|
|
407
|
+
const staticLoader = MODULE_LOADERS?.[spec];
|
|
408
|
+
if (staticLoader) {
|
|
409
|
+
attempts.push({ load: () => staticLoader(), candidate: spec });
|
|
413
410
|
}
|
|
414
|
-
for (
|
|
415
|
-
|
|
411
|
+
for (const candidate of candidates) {
|
|
412
|
+
attempts.push({ load: () => loader(candidate), candidate });
|
|
413
|
+
}
|
|
414
|
+
const registerFromModule = (mod) => {
|
|
415
|
+
const meta = mod.FACTORY_META;
|
|
416
|
+
const Ctor = mod.default;
|
|
417
|
+
if (!meta?.base || !meta?.key || typeof Ctor !== "function") {
|
|
418
|
+
console.warn("[debug] invalid factory module", spec, {
|
|
419
|
+
meta,
|
|
420
|
+
hasCtor: typeof Ctor === "function",
|
|
421
|
+
});
|
|
422
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "— missing FACTORY_META or default export ctor");
|
|
416
423
|
return false;
|
|
417
424
|
}
|
|
425
|
+
const { base, key, ...metadata } = meta;
|
|
426
|
+
const extraMetadata = Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
427
|
+
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
428
|
+
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
429
|
+
return true;
|
|
430
|
+
};
|
|
431
|
+
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
432
|
+
try {
|
|
433
|
+
const mod = await load();
|
|
434
|
+
return registerFromModule(mod);
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
438
|
+
const moduleNotFound = message.includes("Cannot find module") ||
|
|
439
|
+
message.includes("ERR_MODULE_NOT_FOUND") ||
|
|
440
|
+
message.includes("Unknown file extension") ||
|
|
441
|
+
message.includes("Failed to fetch dynamically imported module") ||
|
|
442
|
+
message.includes("Failed to resolve module specifier") ||
|
|
443
|
+
message.includes("Importing a module script failed");
|
|
444
|
+
const isLastAttempt = index === attempts.length - 1;
|
|
445
|
+
if (!moduleNotFound || isLastAttempt) {
|
|
446
|
+
console.warn("[debug] failed to import candidate", {
|
|
447
|
+
spec,
|
|
448
|
+
candidate,
|
|
449
|
+
message,
|
|
450
|
+
});
|
|
451
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
418
455
|
}
|
|
419
|
-
return
|
|
420
|
-
}
|
|
421
|
-
function serializeName(name) {
|
|
422
|
-
const rdns = Array.from(name);
|
|
423
|
-
return rdns
|
|
424
|
-
.map((rdn) => Array.from(rdn)
|
|
425
|
-
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
426
|
-
.join("+"))
|
|
427
|
-
.join(",");
|
|
456
|
+
return false;
|
|
428
457
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return "CN";
|
|
433
|
-
case "2.5.4.6":
|
|
434
|
-
return "C";
|
|
435
|
-
case "2.5.4.7":
|
|
436
|
-
return "L";
|
|
437
|
-
case "2.5.4.8":
|
|
438
|
-
return "ST";
|
|
439
|
-
case "2.5.4.10":
|
|
440
|
-
return "O";
|
|
441
|
-
case "2.5.4.11":
|
|
442
|
-
return "OU";
|
|
443
|
-
default:
|
|
444
|
-
return oid;
|
|
458
|
+
async function registerModuleOnce(spec, registrar) {
|
|
459
|
+
if (registeredModules.has(spec)) {
|
|
460
|
+
return false;
|
|
445
461
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
462
|
+
const inflight = inflightModules.get(spec);
|
|
463
|
+
if (inflight) {
|
|
464
|
+
return inflight;
|
|
465
|
+
}
|
|
466
|
+
const registration = (async () => {
|
|
467
|
+
const registered = await registerModule(spec, registrar);
|
|
468
|
+
if (registered) {
|
|
469
|
+
registeredModules.add(spec);
|
|
470
|
+
}
|
|
471
|
+
return registered;
|
|
472
|
+
})();
|
|
473
|
+
inflightModules.set(spec, registration);
|
|
474
|
+
try {
|
|
475
|
+
return await registration;
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
inflightModules.delete(spec);
|
|
454
479
|
}
|
|
455
|
-
return result;
|
|
456
480
|
}
|
|
457
|
-
function
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
481
|
+
async function registerModules(modules, registrar) {
|
|
482
|
+
if (modules.length === 0) {
|
|
483
|
+
return 0;
|
|
461
484
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const bytes = new Uint8Array(binary.length);
|
|
466
|
-
for (let i = 0; i < binary.length; i += 1) {
|
|
467
|
-
bytes[i] = binary.charCodeAt(i);
|
|
468
|
-
}
|
|
469
|
-
return bytes;
|
|
485
|
+
const eligibleModules = modules.filter((spec) => !shouldSkipModule(spec));
|
|
486
|
+
if (eligibleModules.length === 0) {
|
|
487
|
+
return 0;
|
|
470
488
|
}
|
|
471
|
-
|
|
489
|
+
const results = await Promise.all(eligibleModules.map((spec) => registerModuleOnce(spec, registrar)));
|
|
490
|
+
return results.reduce((count, registered) => (registered ? count + 1 : count), 0);
|
|
472
491
|
}
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.
|
|
477
|
-
}
|
|
478
|
-
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
479
|
-
const chainHash = toHex(sha2_js.sha256(chainBytes));
|
|
480
|
-
const trustHash = trustStorePem
|
|
481
|
-
? toHex(sha2_js.sha256(textEncoder.encode(trustStorePem)))
|
|
482
|
-
: "no-trust";
|
|
483
|
-
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
484
|
-
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
485
|
-
}
|
|
486
|
-
function getCachedPublicKey(cacheKey) {
|
|
487
|
-
const entry = trustCache.get(cacheKey);
|
|
488
|
-
if (!entry) {
|
|
489
|
-
return null;
|
|
492
|
+
async function registerAdvancedSecurityFactories(registrar = factory.Registry, options) {
|
|
493
|
+
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
494
|
+
if (newlyRegisteredSecurity > 0) {
|
|
495
|
+
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
490
496
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
logger$h.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
494
|
-
return null;
|
|
497
|
+
{
|
|
498
|
+
await registerModules(EXTRA_MODULES, registrar);
|
|
495
499
|
}
|
|
496
|
-
return entry.value.slice();
|
|
497
500
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
|
|
502
|
+
// This file is auto-generated during build - do not edit manually
|
|
503
|
+
// Generated from package.json version: 0.3.15
|
|
504
|
+
/**
|
|
505
|
+
* The package version, injected at build time.
|
|
506
|
+
* @internal
|
|
507
|
+
*/
|
|
508
|
+
const VERSION = '0.3.15';
|
|
509
|
+
|
|
510
|
+
async function registerAdvancedSecurityPluginFactories(registrar = factory.Registry) {
|
|
511
|
+
await registerAdvancedSecurityFactories(registrar);
|
|
512
|
+
}
|
|
513
|
+
let initialized = false;
|
|
514
|
+
let initializing = null;
|
|
515
|
+
const advancedSecurityPlugin = {
|
|
516
|
+
name: "naylence:advanced-security",
|
|
517
|
+
version: VERSION,
|
|
518
|
+
async register() {
|
|
519
|
+
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
520
|
+
if (initialized) {
|
|
521
|
+
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
522
|
+
return;
|
|
503
523
|
}
|
|
504
|
-
|
|
505
|
-
|
|
524
|
+
if (initializing) {
|
|
525
|
+
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
526
|
+
await initializing;
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
initializing = (async () => {
|
|
530
|
+
try {
|
|
531
|
+
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
532
|
+
await registerAdvancedSecurityPluginFactories();
|
|
533
|
+
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
534
|
+
initialized = true;
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
initializing = null;
|
|
538
|
+
}
|
|
539
|
+
})();
|
|
540
|
+
await initializing;
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
544
|
+
|
|
545
|
+
var plugin = /*#__PURE__*/Object.freeze({
|
|
546
|
+
__proto__: null,
|
|
547
|
+
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
548
|
+
default: advancedSecurityPlugin,
|
|
549
|
+
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
const logger$g = runtime.getLogger("naylence.fame.security.cert.util");
|
|
553
|
+
const CACHE_LIMIT = 512;
|
|
554
|
+
const OID_ED25519 = "1.3.101.112";
|
|
555
|
+
const textEncoder = new TextEncoder();
|
|
556
|
+
const trustCache = new Map();
|
|
557
|
+
function publicKeyFromX5c(x5c, options = {}) {
|
|
558
|
+
if (!Array.isArray(x5c) || x5c.length === 0) {
|
|
559
|
+
throw new Error("Empty certificate chain");
|
|
506
560
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
561
|
+
const callId = generateCallId();
|
|
562
|
+
const enforceNameConstraints = options.enforceNameConstraints ?? true;
|
|
563
|
+
const trustStorePem = normalizeTrustStoreOption(options.trustStorePem ?? null);
|
|
564
|
+
const returnCertificate = options.returnCertificate ?? false;
|
|
565
|
+
const { parsed, chainBytes } = parseCertificateChain(x5c);
|
|
566
|
+
logger$g.debug("public_key_from_x5c_called", {
|
|
567
|
+
call_id: callId,
|
|
568
|
+
x5c_count: parsed.length,
|
|
569
|
+
enforce_name_constraints: enforceNameConstraints,
|
|
570
|
+
has_trust_store: Boolean(trustStorePem),
|
|
571
|
+
return_cert: returnCertificate,
|
|
515
572
|
});
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
573
|
+
let cacheKey = null;
|
|
574
|
+
if (!returnCertificate) {
|
|
575
|
+
cacheKey = buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints);
|
|
576
|
+
const cached = getCachedPublicKey(cacheKey);
|
|
577
|
+
if (cached) {
|
|
578
|
+
logger$g.debug("certificate_cache_hit", {
|
|
579
|
+
call_id: callId,
|
|
580
|
+
cache_key: cacheKey,
|
|
581
|
+
});
|
|
582
|
+
return cached;
|
|
583
|
+
}
|
|
584
|
+
logger$g.debug("certificate_cache_miss", {
|
|
585
|
+
call_id: callId,
|
|
586
|
+
cache_key: cacheKey,
|
|
587
|
+
});
|
|
520
588
|
}
|
|
521
|
-
const
|
|
522
|
-
if (
|
|
523
|
-
|
|
589
|
+
const validation = validateCertificateChain(parsed, enforceNameConstraints, trustStorePem);
|
|
590
|
+
if (cacheKey) {
|
|
591
|
+
setCachedPublicKey(cacheKey, validation.publicKey, validation.notAfter);
|
|
524
592
|
}
|
|
525
|
-
if (
|
|
526
|
-
|
|
593
|
+
if (returnCertificate) {
|
|
594
|
+
return {
|
|
595
|
+
publicKey: validation.publicKey.slice(),
|
|
596
|
+
certificate: validation.certificate,
|
|
597
|
+
};
|
|
527
598
|
}
|
|
528
|
-
return
|
|
529
|
-
}
|
|
530
|
-
function normalizePem$2(pem) {
|
|
531
|
-
return pem.replace(/\r/gu, "").trim();
|
|
532
|
-
}
|
|
533
|
-
function generateCallId() {
|
|
534
|
-
return Math.random().toString(36).slice(2, 10);
|
|
599
|
+
return validation.publicKey.slice();
|
|
535
600
|
}
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if (!instance) {
|
|
545
|
-
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
601
|
+
function validateJwkX5cCertificate(options) {
|
|
602
|
+
const { jwk, trustStorePem = null, enforceNameConstraints = true, strict = true, } = options;
|
|
603
|
+
if (!jwk || typeof jwk !== "object") {
|
|
604
|
+
const error = "Invalid JWK object";
|
|
605
|
+
if (strict) {
|
|
606
|
+
throw new Error(error);
|
|
607
|
+
}
|
|
608
|
+
return { isValid: false, error };
|
|
546
609
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (!commonName || typeof commonName !== "string") {
|
|
551
|
-
throw new Error("commonName must be a non-empty string");
|
|
610
|
+
const x5c = jwk.x5c;
|
|
611
|
+
if (x5c === undefined) {
|
|
612
|
+
return { isValid: true };
|
|
552
613
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
function arrayBufferToBase64(buffer) {
|
|
563
|
-
const bytes = new Uint8Array(buffer);
|
|
564
|
-
if (typeof globalThis.Buffer?.from === "function") {
|
|
565
|
-
return globalThis.Buffer.from(bytes).toString("base64");
|
|
614
|
+
if (!Array.isArray(x5c) ||
|
|
615
|
+
x5c.length === 0 ||
|
|
616
|
+
x5c.some((entry) => typeof entry !== "string")) {
|
|
617
|
+
const error = "Invalid x5c field in JWK";
|
|
618
|
+
if (strict) {
|
|
619
|
+
throw new Error(error);
|
|
620
|
+
}
|
|
621
|
+
return { isValid: false, error };
|
|
566
622
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
623
|
+
try {
|
|
624
|
+
publicKeyFromX5c(x5c, {
|
|
625
|
+
trustStorePem,
|
|
626
|
+
enforceNameConstraints,
|
|
627
|
+
});
|
|
628
|
+
return { isValid: true };
|
|
572
629
|
}
|
|
573
|
-
|
|
574
|
-
|
|
630
|
+
catch (error) {
|
|
631
|
+
const message = error instanceof Error ? error.message : String(error ?? "unknown");
|
|
632
|
+
const normalized = `Certificate validation failed: ${message}`;
|
|
633
|
+
if (strict) {
|
|
634
|
+
throw new Error(normalized);
|
|
635
|
+
}
|
|
636
|
+
return { isValid: false, error: normalized };
|
|
575
637
|
}
|
|
576
|
-
return globalThis.btoa(binary);
|
|
577
638
|
}
|
|
578
|
-
function
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
639
|
+
function validateCertificateChain(parsed, enforceNameConstraints, trustStorePem) {
|
|
640
|
+
const leaf = parsed[0];
|
|
641
|
+
const nowMs = Date.now();
|
|
642
|
+
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
643
|
+
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
644
|
+
const notBeforeMs = notBefore.getTime();
|
|
645
|
+
const notAfterMs = notAfter.getTime();
|
|
646
|
+
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
647
|
+
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
583
648
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const { privateKey, publicKey, commonName } = options;
|
|
589
|
-
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
590
|
-
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
649
|
+
const issuers = parsed.slice(1);
|
|
650
|
+
if (enforceNameConstraints && issuers.length > 0) {
|
|
651
|
+
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
652
|
+
validateNameConstraints(issuers, leafUris);
|
|
591
653
|
}
|
|
592
|
-
if (
|
|
593
|
-
|
|
654
|
+
if (trustStorePem) {
|
|
655
|
+
validateTrustAnchor(parsed, trustStorePem);
|
|
594
656
|
}
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
new
|
|
610
|
-
|
|
611
|
-
|
|
657
|
+
validateChainContinuity(parsed);
|
|
658
|
+
const publicKey = leaf.subjectPublicKey.slice();
|
|
659
|
+
return {
|
|
660
|
+
publicKey,
|
|
661
|
+
certificate: leaf.certificate,
|
|
662
|
+
notAfter,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function parseCertificateChain(x5c) {
|
|
666
|
+
const parsed = [];
|
|
667
|
+
const derChunks = [];
|
|
668
|
+
for (let index = 0; index < x5c.length; index += 1) {
|
|
669
|
+
const entry = x5c[index];
|
|
670
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
671
|
+
throw new Error(`Invalid certificate at index ${index}`);
|
|
672
|
+
}
|
|
673
|
+
let der;
|
|
674
|
+
try {
|
|
675
|
+
der = decodeBase64$2(entry);
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
679
|
+
throw new Error(`Failed to decode certificate at index ${index}: ${reason}`);
|
|
680
|
+
}
|
|
681
|
+
let certificate;
|
|
682
|
+
try {
|
|
683
|
+
certificate = asn1Schema.AsnConvert.parse(der, asn1X509.Certificate);
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
687
|
+
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
688
|
+
}
|
|
689
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
690
|
+
derChunks.push(der);
|
|
691
|
+
}
|
|
692
|
+
return { parsed, chainBytes: concatBytes(derChunks) };
|
|
693
|
+
}
|
|
694
|
+
function createParsedCertificate(certificate, raw) {
|
|
695
|
+
return {
|
|
696
|
+
raw,
|
|
697
|
+
certificate,
|
|
698
|
+
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
699
|
+
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
700
|
+
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
701
|
+
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function extractUrisFromCert(cert) {
|
|
705
|
+
const extension = findExtension(cert, asn1X509.id_ce_subjectAltName);
|
|
706
|
+
if (!extension) {
|
|
707
|
+
return [];
|
|
708
|
+
}
|
|
709
|
+
const subjectAlternativeName = asn1Schema.AsnConvert.parse(extension.extnValue.buffer, asn1X509.SubjectAlternativeName);
|
|
710
|
+
const uris = [];
|
|
711
|
+
for (const generalName of subjectAlternativeName) {
|
|
712
|
+
if (generalName.uniformResourceIdentifier) {
|
|
713
|
+
uris.push(generalName.uniformResourceIdentifier);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return uris;
|
|
717
|
+
}
|
|
718
|
+
function validateNameConstraints(issuers, leafUris) {
|
|
719
|
+
for (const issuer of issuers) {
|
|
720
|
+
const extension = findExtension(issuer.certificate, asn1X509.id_ce_nameConstraints);
|
|
721
|
+
if (!extension) {
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const constraints = asn1Schema.AsnConvert.parse(extension.extnValue.buffer, asn1X509.NameConstraints);
|
|
725
|
+
if (!constraints.permittedSubtrees) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
const permittedUris = collectPermittedUris(Array.from(constraints.permittedSubtrees));
|
|
729
|
+
if (permittedUris.length === 0) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
for (const uri of leafUris) {
|
|
733
|
+
const allowed = permittedUris.some((prefix) => uri.startsWith(prefix));
|
|
734
|
+
if (!allowed) {
|
|
735
|
+
throw new Error(`URI '${uri}' violates name constraints - not in permitted subtrees: ${permittedUris.join(", ")}`);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function collectPermittedUris(subtrees) {
|
|
741
|
+
const uris = [];
|
|
742
|
+
for (const subtree of subtrees) {
|
|
743
|
+
if (subtree.base.uniformResourceIdentifier &&
|
|
744
|
+
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
745
|
+
uris.push(subtree.base.uniformResourceIdentifier);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return uris;
|
|
749
|
+
}
|
|
750
|
+
function validateTrustAnchor(chain, trustStorePem) {
|
|
751
|
+
const trustedCerts = parseTrustStore(trustStorePem);
|
|
752
|
+
if (trustedCerts.length === 0) {
|
|
753
|
+
throw new Error("No valid certificates found in trust store");
|
|
754
|
+
}
|
|
755
|
+
logger$g.debug("trust_anchor_validation_start", {
|
|
756
|
+
chain_length: chain.length,
|
|
757
|
+
trust_store_cert_count: trustedCerts.length,
|
|
758
|
+
});
|
|
759
|
+
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
760
|
+
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
761
|
+
logger$g.debug("certificate_chain_validation", {
|
|
762
|
+
chain_certificates: chainInfo,
|
|
763
|
+
trust_store_certificates: trustedInfo,
|
|
764
|
+
});
|
|
765
|
+
// Strategy 1: direct trust (exact certificate match)
|
|
766
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
767
|
+
const cert = chain[i];
|
|
768
|
+
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
769
|
+
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
770
|
+
if (match) {
|
|
771
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
772
|
+
matching_serial: match.serialNumber,
|
|
773
|
+
validation_strategy: `direct_trust_cert_${i}`,
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const leaf = chain[0];
|
|
779
|
+
// Strategy 2: leaf issuer in trust store
|
|
780
|
+
for (const trusted of trustedCerts) {
|
|
781
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, leaf.certificate.tbsCertificate.issuer) &&
|
|
782
|
+
trusted.serialNumber !== leaf.serialNumber) {
|
|
783
|
+
verifyCertificateSignature(leaf.certificate, trusted.certificate);
|
|
784
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
785
|
+
matching_serial: trusted.serialNumber,
|
|
786
|
+
validation_strategy: "leaf_issuer_trust",
|
|
787
|
+
});
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
// Strategy 3: any intermediate issuer in trust store
|
|
792
|
+
for (let index = 1; index < chain.length; index += 1) {
|
|
793
|
+
const intermediate = chain[index];
|
|
794
|
+
for (const trusted of trustedCerts) {
|
|
795
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, intermediate.certificate.tbsCertificate.issuer) &&
|
|
796
|
+
trusted.serialNumber !== intermediate.serialNumber) {
|
|
797
|
+
verifyCertificateSignature(intermediate.certificate, trusted.certificate);
|
|
798
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
799
|
+
matching_serial: trusted.serialNumber,
|
|
800
|
+
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
801
|
+
});
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
logger$g.warning("certificate_chain_trust_validation_failed", {
|
|
807
|
+
leaf_subject: leaf.subjectName,
|
|
808
|
+
leaf_issuer: leaf.issuerName,
|
|
809
|
+
leaf_serial: leaf.serialNumber,
|
|
810
|
+
trusted_certificates: trustedInfo,
|
|
811
|
+
chain_certificates: chainInfo,
|
|
812
|
+
reason: "no_matching_trust_anchor",
|
|
813
|
+
});
|
|
814
|
+
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
815
|
+
}
|
|
816
|
+
function parseTrustStore(trustStorePem) {
|
|
817
|
+
const normalized = normalizePem$2(trustStorePem);
|
|
818
|
+
const blocks = extractPemBlocks$1(normalized);
|
|
819
|
+
const parsed = [];
|
|
820
|
+
for (const block of blocks) {
|
|
821
|
+
try {
|
|
822
|
+
const der = decodeBase64$2(block);
|
|
823
|
+
const certificate = asn1Schema.AsnConvert.parse(der, asn1X509.Certificate);
|
|
824
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
825
|
+
}
|
|
826
|
+
catch (error) {
|
|
827
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
828
|
+
logger$g.debug("trust_store_certificate_parse_failed", { reason });
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return parsed;
|
|
832
|
+
}
|
|
833
|
+
function extractPemBlocks$1(pem) {
|
|
834
|
+
const blocks = [];
|
|
835
|
+
const regex = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/gu;
|
|
836
|
+
let match;
|
|
837
|
+
// eslint-disable-next-line no-cond-assign
|
|
838
|
+
while ((match = regex.exec(pem)) !== null) {
|
|
839
|
+
const body = match[1] ?? "";
|
|
840
|
+
blocks.push(body.replace(/\s+/gu, ""));
|
|
841
|
+
}
|
|
842
|
+
return blocks;
|
|
843
|
+
}
|
|
844
|
+
function validateChainContinuity(chain) {
|
|
845
|
+
if (chain.length <= 1) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
logger$g.debug("validating_chain_continuity", { chain_length: chain.length });
|
|
849
|
+
for (let index = 0; index < chain.length - 1; index += 1) {
|
|
850
|
+
const cert = chain[index];
|
|
851
|
+
const issuer = chain[index + 1];
|
|
852
|
+
if (!namesEqual(cert.certificate.tbsCertificate.issuer, issuer.certificate.tbsCertificate.subject)) {
|
|
853
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
854
|
+
cert_index: index,
|
|
855
|
+
cert_subject: cert.subjectName,
|
|
856
|
+
cert_issuer: cert.issuerName,
|
|
857
|
+
expected_issuer_subject: issuer.subjectName,
|
|
858
|
+
reason: "issuer_name_mismatch",
|
|
859
|
+
});
|
|
860
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} issuer does not match next certificate subject`);
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
verifyCertificateSignature(cert.certificate, issuer.certificate);
|
|
864
|
+
logger$g.debug("chain_continuity_verification_success", {
|
|
865
|
+
cert_index: index,
|
|
866
|
+
cert_serial: cert.serialNumber,
|
|
867
|
+
issuer_serial: issuer.serialNumber,
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
catch (error) {
|
|
871
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
872
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
873
|
+
cert_index: index,
|
|
874
|
+
cert_subject: cert.subjectName,
|
|
875
|
+
issuer_subject: issuer.subjectName,
|
|
876
|
+
cert_serial: cert.serialNumber,
|
|
877
|
+
issuer_serial: issuer.serialNumber,
|
|
878
|
+
error: reason,
|
|
879
|
+
reason: "signature_verification_failed",
|
|
880
|
+
});
|
|
881
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
logger$g.debug("chain_continuity_validation_passed", {
|
|
885
|
+
chain_length: chain.length,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
function verifyCertificateSignature(certificate, issuer) {
|
|
889
|
+
ensureEd25519Support();
|
|
890
|
+
const signatureAlgorithm = certificate.signatureAlgorithm.algorithm;
|
|
891
|
+
const issuerAlgorithm = issuer.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm;
|
|
892
|
+
if (signatureAlgorithm !== OID_ED25519 || issuerAlgorithm !== OID_ED25519) {
|
|
893
|
+
throw new Error(`Unsupported signature algorithm (certificate: ${signatureAlgorithm}, issuer: ${issuerAlgorithm})`);
|
|
894
|
+
}
|
|
895
|
+
const signatureBytes = new Uint8Array(certificate.signatureValue);
|
|
896
|
+
const tbsBytes = new Uint8Array(asn1Schema.AsnConvert.serialize(certificate.tbsCertificate));
|
|
897
|
+
const issuerKey = new Uint8Array(issuer.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
|
898
|
+
if (issuerKey.length !== 32) {
|
|
899
|
+
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
900
|
+
}
|
|
901
|
+
const valid = ed25519.verify(signatureBytes, tbsBytes, issuerKey);
|
|
902
|
+
if (!valid) {
|
|
903
|
+
throw new Error("Certificate signature verification failed");
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
function ensureEd25519Support() {
|
|
907
|
+
const etcPatch = ed25519.etc;
|
|
908
|
+
if (!etcPatch.sha512) {
|
|
909
|
+
etcPatch.sha512 = (message) => sha2_js.sha512(message);
|
|
910
|
+
}
|
|
911
|
+
if (!etcPatch.sha512Sync) {
|
|
912
|
+
etcPatch.sha512Sync = (...messages) => {
|
|
913
|
+
if (messages.length === 1) {
|
|
914
|
+
return sha2_js.sha512(messages[0]);
|
|
915
|
+
}
|
|
916
|
+
const combined = ed25519.etc.concatBytes(...messages);
|
|
917
|
+
return sha2_js.sha512(combined);
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
function findExtension(certificate, oid) {
|
|
922
|
+
const extensions = certificate.tbsCertificate.extensions;
|
|
923
|
+
if (!extensions) {
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
for (const extension of extensions) {
|
|
927
|
+
if (extension.extnID === oid) {
|
|
928
|
+
return extension;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
function namesEqual(a, b) {
|
|
934
|
+
const left = new Uint8Array(asn1Schema.AsnConvert.serialize(a));
|
|
935
|
+
const right = new Uint8Array(asn1Schema.AsnConvert.serialize(b));
|
|
936
|
+
if (left.length !== right.length) {
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
940
|
+
if (left[i] !== right[i]) {
|
|
941
|
+
return false;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
946
|
+
function serializeName(name) {
|
|
947
|
+
const rdns = Array.from(name);
|
|
948
|
+
return rdns
|
|
949
|
+
.map((rdn) => Array.from(rdn)
|
|
950
|
+
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
951
|
+
.join("+"))
|
|
952
|
+
.join(",");
|
|
953
|
+
}
|
|
954
|
+
function oidToLabel$1(oid) {
|
|
955
|
+
switch (oid) {
|
|
956
|
+
case "2.5.4.3":
|
|
957
|
+
return "CN";
|
|
958
|
+
case "2.5.4.6":
|
|
959
|
+
return "C";
|
|
960
|
+
case "2.5.4.7":
|
|
961
|
+
return "L";
|
|
962
|
+
case "2.5.4.8":
|
|
963
|
+
return "ST";
|
|
964
|
+
case "2.5.4.10":
|
|
965
|
+
return "O";
|
|
966
|
+
case "2.5.4.11":
|
|
967
|
+
return "OU";
|
|
968
|
+
default:
|
|
969
|
+
return oid;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function concatBytes(chunks) {
|
|
973
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
974
|
+
const result = new Uint8Array(totalLength);
|
|
975
|
+
let offset = 0;
|
|
976
|
+
for (const chunk of chunks) {
|
|
977
|
+
result.set(chunk, offset);
|
|
978
|
+
offset += chunk.length;
|
|
979
|
+
}
|
|
980
|
+
return result;
|
|
981
|
+
}
|
|
982
|
+
function decodeBase64$2(input) {
|
|
983
|
+
if (typeof Buffer !== "undefined") {
|
|
984
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
985
|
+
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
986
|
+
}
|
|
987
|
+
if (typeof atob === "function") {
|
|
988
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
989
|
+
const binary = atob(normalized);
|
|
990
|
+
const bytes = new Uint8Array(binary.length);
|
|
991
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
992
|
+
bytes[i] = binary.charCodeAt(i);
|
|
993
|
+
}
|
|
994
|
+
return bytes;
|
|
995
|
+
}
|
|
996
|
+
throw new Error("No base64 decoder available in this environment");
|
|
997
|
+
}
|
|
998
|
+
function toHex(data) {
|
|
999
|
+
return Array.from(data)
|
|
1000
|
+
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
1001
|
+
.join("");
|
|
1002
|
+
}
|
|
1003
|
+
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
1004
|
+
const chainHash = toHex(sha2_js.sha256(chainBytes));
|
|
1005
|
+
const trustHash = trustStorePem
|
|
1006
|
+
? toHex(sha2_js.sha256(textEncoder.encode(trustStorePem)))
|
|
1007
|
+
: "no-trust";
|
|
1008
|
+
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
1009
|
+
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
1010
|
+
}
|
|
1011
|
+
function getCachedPublicKey(cacheKey) {
|
|
1012
|
+
const entry = trustCache.get(cacheKey);
|
|
1013
|
+
if (!entry) {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
if (Date.now() > entry.expiresAt) {
|
|
1017
|
+
trustCache.delete(cacheKey);
|
|
1018
|
+
logger$g.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
return entry.value.slice();
|
|
1022
|
+
}
|
|
1023
|
+
function setCachedPublicKey(cacheKey, value, notAfter) {
|
|
1024
|
+
while (trustCache.size >= CACHE_LIMIT) {
|
|
1025
|
+
const firstKey = trustCache.keys().next().value;
|
|
1026
|
+
if (firstKey === undefined) {
|
|
1027
|
+
break;
|
|
1028
|
+
}
|
|
1029
|
+
trustCache.delete(firstKey);
|
|
1030
|
+
logger$g.debug("certificate_cache_evicted", { cache_key: firstKey });
|
|
1031
|
+
}
|
|
1032
|
+
trustCache.set(cacheKey, {
|
|
1033
|
+
value: value.slice(),
|
|
1034
|
+
expiresAt: notAfter.getTime(),
|
|
1035
|
+
});
|
|
1036
|
+
logger$g.debug("certificate_cache_stored", {
|
|
1037
|
+
cache_key: cacheKey,
|
|
1038
|
+
expires_at: notAfter.toISOString(),
|
|
1039
|
+
cache_size: trustCache.size,
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function normalizeTrustStoreOption(value) {
|
|
1043
|
+
if (!value) {
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
const trimmed = value.trim();
|
|
1047
|
+
if (trimmed.length === 0) {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
if (!trimmed.includes("-----BEGIN CERTIFICATE-----")) {
|
|
1051
|
+
throw new Error("trustStorePem must contain PEM-encoded certificates when provided");
|
|
1052
|
+
}
|
|
1053
|
+
return normalizePem$2(trimmed);
|
|
1054
|
+
}
|
|
1055
|
+
function normalizePem$2(pem) {
|
|
1056
|
+
return pem.replace(/\r/gu, "").trim();
|
|
1057
|
+
}
|
|
1058
|
+
function generateCallId() {
|
|
1059
|
+
return Math.random().toString(36).slice(2, 10);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const GRANT_PURPOSE_CA_SIGN = "ca_sign";
|
|
1063
|
+
|
|
1064
|
+
const ED25519_OID$2 = "1.3.101.112";
|
|
1065
|
+
const OID_COMMON_NAME$2 = "2.5.4.3";
|
|
1066
|
+
const LOGICAL_URI_PREFIX$1 = "naylence://";
|
|
1067
|
+
function ensureSubtleCrypto() {
|
|
1068
|
+
const instance = globalThis.crypto?.subtle;
|
|
1069
|
+
if (!instance) {
|
|
1070
|
+
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
1071
|
+
}
|
|
1072
|
+
return instance;
|
|
1073
|
+
}
|
|
1074
|
+
function buildSubject(commonName) {
|
|
1075
|
+
if (!commonName || typeof commonName !== "string") {
|
|
1076
|
+
throw new Error("commonName must be a non-empty string");
|
|
1077
|
+
}
|
|
1078
|
+
return new asn1X509.Name([
|
|
1079
|
+
new asn1X509.RelativeDistinguishedName([
|
|
1080
|
+
new asn1X509.AttributeTypeAndValue({
|
|
1081
|
+
type: OID_COMMON_NAME$2,
|
|
1082
|
+
value: new asn1X509.AttributeValue({ utf8String: commonName }),
|
|
1083
|
+
}),
|
|
1084
|
+
]),
|
|
1085
|
+
]);
|
|
1086
|
+
}
|
|
1087
|
+
function arrayBufferToBase64(buffer) {
|
|
1088
|
+
const bytes = new Uint8Array(buffer);
|
|
1089
|
+
if (typeof globalThis.Buffer?.from === "function") {
|
|
1090
|
+
return globalThis.Buffer.from(bytes).toString("base64");
|
|
1091
|
+
}
|
|
1092
|
+
let binary = "";
|
|
1093
|
+
const chunkSize = 0x8000;
|
|
1094
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
1095
|
+
const slice = bytes.subarray(offset, offset + chunkSize);
|
|
1096
|
+
binary += String.fromCharCode(...slice);
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof globalThis.btoa !== "function") {
|
|
1099
|
+
throw new Error("Base64 encoding not available in this environment");
|
|
1100
|
+
}
|
|
1101
|
+
return globalThis.btoa(binary);
|
|
1102
|
+
}
|
|
1103
|
+
function derToPem$1(der, label) {
|
|
1104
|
+
const base64 = arrayBufferToBase64(der);
|
|
1105
|
+
const lines = [];
|
|
1106
|
+
for (let index = 0; index < base64.length; index += 64) {
|
|
1107
|
+
lines.push(base64.slice(index, index + 64));
|
|
1108
|
+
}
|
|
1109
|
+
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
|
1110
|
+
}
|
|
1111
|
+
async function createEd25519Csr(options) {
|
|
1112
|
+
const subtle = ensureSubtleCrypto();
|
|
1113
|
+
const { privateKey, publicKey, commonName } = options;
|
|
1114
|
+
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
1115
|
+
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
1116
|
+
}
|
|
1117
|
+
if (!(publicKey instanceof CryptoKey) || publicKey.type !== "public") {
|
|
1118
|
+
throw new Error("publicKey must be a CryptoKey of type 'public'");
|
|
1119
|
+
}
|
|
1120
|
+
const subject = buildSubject(commonName);
|
|
1121
|
+
const spkiDer = await subtle.exportKey("spki", publicKey);
|
|
1122
|
+
const subjectPublicKeyInfo = asn1Schema.AsnConvert.parse(spkiDer, asn1X509.SubjectPublicKeyInfo);
|
|
1123
|
+
const attributes = new asn1Csr.Attributes();
|
|
1124
|
+
const sanitizedLogicals = Array.isArray(options.logicals)
|
|
1125
|
+
? options.logicals
|
|
1126
|
+
.map((logical) => logical.trim())
|
|
1127
|
+
.filter((logical) => logical.length > 0)
|
|
1128
|
+
: [];
|
|
1129
|
+
if (sanitizedLogicals.length > 0) {
|
|
1130
|
+
const san = new asn1X509.SubjectAlternativeName(sanitizedLogicals.map((logical) => new asn1X509.GeneralName({
|
|
1131
|
+
uniformResourceIdentifier: `${LOGICAL_URI_PREFIX$1}${logical}`,
|
|
1132
|
+
})));
|
|
1133
|
+
const extensions = new asn1X509.Extensions([
|
|
1134
|
+
new asn1X509.Extension({
|
|
1135
|
+
extnID: asn1X509.id_ce_subjectAltName,
|
|
1136
|
+
critical: false,
|
|
612
1137
|
extnValue: new asn1Schema.OctetString(asn1Schema.AsnConvert.serialize(san)),
|
|
613
1138
|
}),
|
|
614
1139
|
]);
|
|
@@ -688,7 +1213,7 @@ const NODE_ID_OID = "1.3.6.1.4.1.58530.4";
|
|
|
688
1213
|
* Provides async HTTP client to request certificates from the CA signing service.
|
|
689
1214
|
*/
|
|
690
1215
|
// Simple logger for now - TODO: integrate with runtime logging
|
|
691
|
-
const logger$
|
|
1216
|
+
const logger$f = {
|
|
692
1217
|
debug: (_event, _meta) => {
|
|
693
1218
|
// console.log(`[DEBUG] ${event}`, meta);
|
|
694
1219
|
},
|
|
@@ -1160,13 +1685,13 @@ class CAServiceClient {
|
|
|
1160
1685
|
const result = await response.json();
|
|
1161
1686
|
const certificatePem = result.certificate_pem;
|
|
1162
1687
|
const certificateChainPem = result.certificate_chain_pem || certificatePem;
|
|
1163
|
-
logger$
|
|
1688
|
+
logger$f.debug("certificate_request_successful", {
|
|
1164
1689
|
requester_id: requesterId,
|
|
1165
1690
|
expires_at: result.expires_at,
|
|
1166
1691
|
});
|
|
1167
1692
|
// Extract and log certificate information with structured logging
|
|
1168
1693
|
const certInfo = extractCertificateInfo(certificatePem);
|
|
1169
|
-
logger$
|
|
1694
|
+
logger$f.debug("certificate_details", {
|
|
1170
1695
|
requester_id: requesterId,
|
|
1171
1696
|
certificate_type: "issued_certificate",
|
|
1172
1697
|
...certInfo,
|
|
@@ -1185,7 +1710,7 @@ class CAServiceClient {
|
|
|
1185
1710
|
// First cert in chain is usually the issued certificate
|
|
1186
1711
|
if (certPemBlock.trim() !== certificatePem.trim()) {
|
|
1187
1712
|
const chainCertInfo = extractCertificateInfo(certPemBlock);
|
|
1188
|
-
logger$
|
|
1713
|
+
logger$f.debug("certificate_chain_details", {
|
|
1189
1714
|
requester_id: requesterId,
|
|
1190
1715
|
certificate_type: "certificate_chain",
|
|
1191
1716
|
chain_index: i,
|
|
@@ -1196,7 +1721,7 @@ class CAServiceClient {
|
|
|
1196
1721
|
else {
|
|
1197
1722
|
// Subsequent certs are intermediate/root CAs
|
|
1198
1723
|
const caCertInfo = extractCertificateInfo(certPemBlock);
|
|
1199
|
-
logger$
|
|
1724
|
+
logger$f.debug("certificate_chain_details", {
|
|
1200
1725
|
requester_id: requesterId,
|
|
1201
1726
|
certificate_type: "ca_certificate",
|
|
1202
1727
|
chain_index: i,
|
|
@@ -1224,7 +1749,7 @@ class CAServiceClient {
|
|
|
1224
1749
|
// Body read failed entirely
|
|
1225
1750
|
errorDetail = `HTTP ${response.status}`;
|
|
1226
1751
|
}
|
|
1227
|
-
logger$
|
|
1752
|
+
logger$f.error("certificate_request_failed", {
|
|
1228
1753
|
requester_id: requesterId,
|
|
1229
1754
|
status_code: response.status,
|
|
1230
1755
|
error: errorDetail,
|
|
@@ -1241,13 +1766,13 @@ class CAServiceClient {
|
|
|
1241
1766
|
throw error;
|
|
1242
1767
|
}
|
|
1243
1768
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1244
|
-
logger$
|
|
1769
|
+
logger$f.error("certificate_request_timeout", {
|
|
1245
1770
|
requester_id: requesterId,
|
|
1246
1771
|
timeout_seconds: this.timeoutSeconds,
|
|
1247
1772
|
});
|
|
1248
1773
|
throw new CertificateRequestError(`Certificate request timed out after ${this.timeoutSeconds} seconds`);
|
|
1249
1774
|
}
|
|
1250
|
-
logger$
|
|
1775
|
+
logger$f.error("certificate_request_network_error", {
|
|
1251
1776
|
requester_id: requesterId,
|
|
1252
1777
|
error: String(error),
|
|
1253
1778
|
});
|
|
@@ -1256,7 +1781,7 @@ class CAServiceClient {
|
|
|
1256
1781
|
}
|
|
1257
1782
|
}
|
|
1258
1783
|
|
|
1259
|
-
const logger$
|
|
1784
|
+
const logger$e = runtime.getLogger("naylence.fame.security.encryption.sealed.x25519_encryption_manager");
|
|
1260
1785
|
class X25519EncryptionManager {
|
|
1261
1786
|
constructor({ keyProvider, nodeLike = null, cryptoProvider = null, }) {
|
|
1262
1787
|
this.pendingEnvelopes = new Map();
|
|
@@ -1273,7 +1798,7 @@ class X25519EncryptionManager {
|
|
|
1273
1798
|
// KeyManagementHandler will queue the envelope and send KeyRequest.
|
|
1274
1799
|
// X25519 should NOT queue here to avoid dual queueing.
|
|
1275
1800
|
if (opts?.requestAddress) {
|
|
1276
|
-
logger$
|
|
1801
|
+
logger$e.debug("key_not_found_delegating_to_key_management", {
|
|
1277
1802
|
envelope_id: envelope.id,
|
|
1278
1803
|
request_address: String(opts.requestAddress),
|
|
1279
1804
|
});
|
|
@@ -1289,7 +1814,7 @@ class X25519EncryptionManager {
|
|
|
1289
1814
|
return await this.encryptWithKey(envelope, recipPub, recipKid);
|
|
1290
1815
|
}
|
|
1291
1816
|
catch (error) {
|
|
1292
|
-
logger$
|
|
1817
|
+
logger$e.error("x25519_encryption_failed", {
|
|
1293
1818
|
error: error instanceof Error ? error.message : String(error),
|
|
1294
1819
|
});
|
|
1295
1820
|
return runtime.EncryptionResult.skipped(envelope);
|
|
@@ -1327,20 +1852,20 @@ class X25519EncryptionManager {
|
|
|
1327
1852
|
return envelope;
|
|
1328
1853
|
}
|
|
1329
1854
|
catch (error) {
|
|
1330
|
-
logger$
|
|
1855
|
+
logger$e.error("x25519_decryption_failed", {
|
|
1331
1856
|
error: error instanceof Error ? error.message : String(error),
|
|
1332
1857
|
});
|
|
1333
1858
|
return envelope;
|
|
1334
1859
|
}
|
|
1335
1860
|
}
|
|
1336
1861
|
async notifyKeyAvailable(keyId) {
|
|
1337
|
-
logger$
|
|
1862
|
+
logger$e.debug("x25519_notify_key_available_called", {
|
|
1338
1863
|
key_id: keyId,
|
|
1339
1864
|
pending_keys: Array.from(this.pendingEnvelopes.keys()),
|
|
1340
1865
|
});
|
|
1341
1866
|
const queued = this.pendingEnvelopes.get(keyId);
|
|
1342
1867
|
if (!queued || queued.length === 0) {
|
|
1343
|
-
logger$
|
|
1868
|
+
logger$e.debug("no_queued_envelopes_for_key", {
|
|
1344
1869
|
key_id: keyId,
|
|
1345
1870
|
has_queue: this.pendingEnvelopes.has(keyId),
|
|
1346
1871
|
queue_length: queued?.length ?? 0,
|
|
@@ -1352,13 +1877,13 @@ class X25519EncryptionManager {
|
|
|
1352
1877
|
this.keyRequestsInProgress.delete(keyId);
|
|
1353
1878
|
const node = this.nodeLike;
|
|
1354
1879
|
if (!node) {
|
|
1355
|
-
logger$
|
|
1880
|
+
logger$e.debug("discarding_queued_envelopes_no_node", {
|
|
1356
1881
|
key_id: keyId,
|
|
1357
1882
|
count: queued.length,
|
|
1358
1883
|
});
|
|
1359
1884
|
return;
|
|
1360
1885
|
}
|
|
1361
|
-
logger$
|
|
1886
|
+
logger$e.debug("replaying_envelopes_for_key", {
|
|
1362
1887
|
key_id: keyId,
|
|
1363
1888
|
count: queued.length,
|
|
1364
1889
|
});
|
|
@@ -1367,7 +1892,7 @@ class X25519EncryptionManager {
|
|
|
1367
1892
|
await node.deliver(envelope);
|
|
1368
1893
|
}
|
|
1369
1894
|
catch (error) {
|
|
1370
|
-
logger$
|
|
1895
|
+
logger$e.error("failed_to_replay_envelope", {
|
|
1371
1896
|
key_id: keyId,
|
|
1372
1897
|
envelope_id: envelope.id,
|
|
1373
1898
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1468,7 +1993,7 @@ class X25519EncryptionManager {
|
|
|
1468
1993
|
? this.extractPrivateKeyFromRecord(providerRecord)
|
|
1469
1994
|
: null;
|
|
1470
1995
|
if (providerRecordKey) {
|
|
1471
|
-
logger$
|
|
1996
|
+
logger$e.debug("using_provider_key_record_private_key", {
|
|
1472
1997
|
kid,
|
|
1473
1998
|
provider_key_id: providerKeyId,
|
|
1474
1999
|
mismatched_kid: kid && providerKeyId !== kid ? kid : null,
|
|
@@ -1478,7 +2003,7 @@ class X25519EncryptionManager {
|
|
|
1478
2003
|
}
|
|
1479
2004
|
if (!providerPem) {
|
|
1480
2005
|
if (kid && providerKeyId && providerKeyId !== kid) {
|
|
1481
|
-
logger$
|
|
2006
|
+
logger$e.debug("crypto_provider_key_id_mismatch_no_private_key", {
|
|
1482
2007
|
kid,
|
|
1483
2008
|
provider_key_id: providerKeyId,
|
|
1484
2009
|
});
|
|
@@ -1490,13 +2015,13 @@ class X25519EncryptionManager {
|
|
|
1490
2015
|
return null;
|
|
1491
2016
|
}
|
|
1492
2017
|
if (!kid || providerKeyId === kid) {
|
|
1493
|
-
logger$
|
|
2018
|
+
logger$e.debug("using_crypto_provider_private_key_fallback", {
|
|
1494
2019
|
kid: kid ?? null,
|
|
1495
2020
|
provider_key_id: providerKeyId,
|
|
1496
2021
|
});
|
|
1497
2022
|
}
|
|
1498
2023
|
else {
|
|
1499
|
-
logger$
|
|
2024
|
+
logger$e.warning("crypto_provider_key_id_mismatch_using_private_key", {
|
|
1500
2025
|
kid,
|
|
1501
2026
|
provider_key_id: providerKeyId,
|
|
1502
2027
|
key_record_present: Boolean(record),
|
|
@@ -1505,7 +2030,7 @@ class X25519EncryptionManager {
|
|
|
1505
2030
|
return fallbackKey;
|
|
1506
2031
|
}
|
|
1507
2032
|
async queueEnvelopeForKey(envelope, opts, recipientKeyId) {
|
|
1508
|
-
logger$
|
|
2033
|
+
logger$e.debug("queueing_envelope_for_sealed_encryption", {
|
|
1509
2034
|
envelope_id: envelope.id,
|
|
1510
2035
|
recipient_key_id: recipientKeyId,
|
|
1511
2036
|
request_address: opts?.requestAddress
|
|
@@ -1553,7 +2078,7 @@ class X25519EncryptionManager {
|
|
|
1553
2078
|
await node.deliver(keyRequestEnvelope, context);
|
|
1554
2079
|
}
|
|
1555
2080
|
catch (error) {
|
|
1556
|
-
logger$
|
|
2081
|
+
logger$e.error("failed_to_request_recipient_key", {
|
|
1557
2082
|
recipient_key_id: recipientKeyId,
|
|
1558
2083
|
error: error instanceof Error ? error.message : String(error),
|
|
1559
2084
|
});
|
|
@@ -1566,7 +2091,7 @@ class X25519EncryptionManager {
|
|
|
1566
2091
|
return this.extractPublicKeyFromRecord(record);
|
|
1567
2092
|
}
|
|
1568
2093
|
catch (error) {
|
|
1569
|
-
logger$
|
|
2094
|
+
logger$e.debug("recipient_key_lookup_failed", {
|
|
1570
2095
|
kid,
|
|
1571
2096
|
error: error instanceof Error ? error.message : String(error),
|
|
1572
2097
|
});
|
|
@@ -1581,7 +2106,7 @@ class X25519EncryptionManager {
|
|
|
1581
2106
|
return await this.keyProvider.getKey(kid);
|
|
1582
2107
|
}
|
|
1583
2108
|
catch (error) {
|
|
1584
|
-
logger$
|
|
2109
|
+
logger$e.debug("private_key_lookup_failed", {
|
|
1585
2110
|
kid,
|
|
1586
2111
|
error: error instanceof Error ? error.message : String(error),
|
|
1587
2112
|
});
|
|
@@ -1652,7 +2177,7 @@ class X25519EncryptionManager {
|
|
|
1652
2177
|
const base64 = base64Lines.join("");
|
|
1653
2178
|
const der = this.decodeBase64Flexible(base64);
|
|
1654
2179
|
if (!der) {
|
|
1655
|
-
logger$
|
|
2180
|
+
logger$e.debug("pem_decode_failed", {
|
|
1656
2181
|
key_type: keyType,
|
|
1657
2182
|
});
|
|
1658
2183
|
return null;
|
|
@@ -1885,7 +2410,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
1885
2410
|
X25519EncryptionManagerFactory: X25519EncryptionManagerFactory
|
|
1886
2411
|
});
|
|
1887
2412
|
|
|
1888
|
-
const logger$
|
|
2413
|
+
const logger$d = runtime.getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager");
|
|
1889
2414
|
const SUPPORTED_CHANNEL_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
1890
2415
|
const CHANNEL_ENCRYPTION_ALGORITHM = "chacha20-poly1305-channel";
|
|
1891
2416
|
const HANDSHAKE_ALGORITHM = "CHACHA20P1305";
|
|
@@ -2036,13 +2561,13 @@ class ChannelEncryptionManager {
|
|
|
2036
2561
|
const destination = opts?.destination ?? envelope.to ?? null;
|
|
2037
2562
|
const destinationStr = toDestinationString(destination);
|
|
2038
2563
|
if (!destinationStr) {
|
|
2039
|
-
logger$
|
|
2564
|
+
logger$d.warning("no_destination_for_channel_encryption", {
|
|
2040
2565
|
envelope_id: envelope.id,
|
|
2041
2566
|
});
|
|
2042
2567
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2043
2568
|
}
|
|
2044
2569
|
if (!this.secureChannelManager) {
|
|
2045
|
-
logger$
|
|
2570
|
+
logger$d.warning("no_secure_channel_manager_available", {
|
|
2046
2571
|
envelope_id: envelope.id,
|
|
2047
2572
|
});
|
|
2048
2573
|
return runtime.EncryptionResult.skipped(envelope);
|
|
@@ -2053,7 +2578,7 @@ class ChannelEncryptionManager {
|
|
|
2053
2578
|
return this.encryptWithChannel(envelope, existingChannelId);
|
|
2054
2579
|
}
|
|
2055
2580
|
catch (error) {
|
|
2056
|
-
logger$
|
|
2581
|
+
logger$d.error("channel_encryption_failed", {
|
|
2057
2582
|
error: error instanceof Error ? error.message : String(error),
|
|
2058
2583
|
channel_id: existingChannelId,
|
|
2059
2584
|
});
|
|
@@ -2080,35 +2605,35 @@ class ChannelEncryptionManager {
|
|
|
2080
2605
|
}
|
|
2081
2606
|
const channelId = encHeader.kid;
|
|
2082
2607
|
if (!channelId) {
|
|
2083
|
-
logger$
|
|
2608
|
+
logger$d.error("missing_channel_id_in_encryption_header", {
|
|
2084
2609
|
envelope_id: envelope.id,
|
|
2085
2610
|
});
|
|
2086
2611
|
return envelope;
|
|
2087
2612
|
}
|
|
2088
2613
|
const nonce = this.decodeNonceValue(encHeader.val ?? "");
|
|
2089
2614
|
if (!nonce) {
|
|
2090
|
-
logger$
|
|
2615
|
+
logger$d.error("invalid_nonce_in_encryption_header", {
|
|
2091
2616
|
envelope_id: envelope.id,
|
|
2092
2617
|
value_present: Boolean(encHeader.val),
|
|
2093
2618
|
});
|
|
2094
2619
|
return envelope;
|
|
2095
2620
|
}
|
|
2096
2621
|
if (!this.secureChannelManager) {
|
|
2097
|
-
logger$
|
|
2622
|
+
logger$d.warning("no_secure_channel_manager_for_decryption", {
|
|
2098
2623
|
envelope_id: envelope.id,
|
|
2099
2624
|
});
|
|
2100
2625
|
return envelope;
|
|
2101
2626
|
}
|
|
2102
2627
|
const channelState = this.getChannelState(channelId);
|
|
2103
2628
|
if (!channelState) {
|
|
2104
|
-
logger$
|
|
2629
|
+
logger$d.error("channel_not_available_for_decryption", {
|
|
2105
2630
|
channel_id: channelId,
|
|
2106
2631
|
});
|
|
2107
2632
|
return envelope;
|
|
2108
2633
|
}
|
|
2109
2634
|
const ciphertext = this.extractCiphertext(frame.payload);
|
|
2110
2635
|
if (!ciphertext) {
|
|
2111
|
-
logger$
|
|
2636
|
+
logger$d.error("invalid_ciphertext_payload", { envelope_id: envelope.id });
|
|
2112
2637
|
return envelope;
|
|
2113
2638
|
}
|
|
2114
2639
|
try {
|
|
@@ -2133,7 +2658,7 @@ class ChannelEncryptionManager {
|
|
|
2133
2658
|
return envelope;
|
|
2134
2659
|
}
|
|
2135
2660
|
catch (error) {
|
|
2136
|
-
logger$
|
|
2661
|
+
logger$d.error("channel_decryption_failed", {
|
|
2137
2662
|
channel_id: channelId,
|
|
2138
2663
|
error: error instanceof Error ? error.message : String(error),
|
|
2139
2664
|
});
|
|
@@ -2141,24 +2666,24 @@ class ChannelEncryptionManager {
|
|
|
2141
2666
|
}
|
|
2142
2667
|
}
|
|
2143
2668
|
async notifyChannelEstablished(channelId) {
|
|
2144
|
-
logger$
|
|
2669
|
+
logger$d.debug("channel_encryption_manager_notified", {
|
|
2145
2670
|
channel_id: channelId,
|
|
2146
2671
|
manager_type: "channel",
|
|
2147
2672
|
});
|
|
2148
2673
|
if (!channelId.startsWith("auto-")) {
|
|
2149
|
-
logger$
|
|
2674
|
+
logger$d.warning("unexpected_channel_id_format", { channel_id: channelId });
|
|
2150
2675
|
return;
|
|
2151
2676
|
}
|
|
2152
2677
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2153
2678
|
if (!destinationStr) {
|
|
2154
|
-
logger$
|
|
2679
|
+
logger$d.warning("cannot_parse_destination_from_channel_id", {
|
|
2155
2680
|
channel_id: channelId,
|
|
2156
2681
|
});
|
|
2157
2682
|
return;
|
|
2158
2683
|
}
|
|
2159
2684
|
this.handshakeInProgress.delete(destinationStr);
|
|
2160
2685
|
if (!this.pendingEnvelopes.has(destinationStr)) {
|
|
2161
|
-
logger$
|
|
2686
|
+
logger$d.debug("no_pending_queue_for_destination", {
|
|
2162
2687
|
destination: destinationStr,
|
|
2163
2688
|
});
|
|
2164
2689
|
return;
|
|
@@ -2166,7 +2691,7 @@ class ChannelEncryptionManager {
|
|
|
2166
2691
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2167
2692
|
this.pendingEnvelopes.delete(destinationStr);
|
|
2168
2693
|
if (!this.secureChannelManager) {
|
|
2169
|
-
logger$
|
|
2694
|
+
logger$d.error("no_secure_channel_manager_for_queue_drain", {
|
|
2170
2695
|
channel_id: channelId,
|
|
2171
2696
|
});
|
|
2172
2697
|
return;
|
|
@@ -2175,7 +2700,7 @@ class ChannelEncryptionManager {
|
|
|
2175
2700
|
try {
|
|
2176
2701
|
const result = this.encryptWithChannel(envelope, channelId);
|
|
2177
2702
|
if (!result.envelope) {
|
|
2178
|
-
logger$
|
|
2703
|
+
logger$d.warning("failed_to_encrypt_queued_envelope", {
|
|
2179
2704
|
envelope_id: envelope.id,
|
|
2180
2705
|
channel_id: channelId,
|
|
2181
2706
|
});
|
|
@@ -2185,7 +2710,7 @@ class ChannelEncryptionManager {
|
|
|
2185
2710
|
this.runAsyncTask(() => this.deliverEnvelope(encryptedEnvelope), `deliver-queued-${envelope.id}`);
|
|
2186
2711
|
}
|
|
2187
2712
|
catch (error) {
|
|
2188
|
-
logger$
|
|
2713
|
+
logger$d.error("failed_to_encrypt_queued_envelope", {
|
|
2189
2714
|
envelope_id: envelope.id,
|
|
2190
2715
|
error: error instanceof Error ? error.message : String(error),
|
|
2191
2716
|
});
|
|
@@ -2193,19 +2718,19 @@ class ChannelEncryptionManager {
|
|
|
2193
2718
|
}
|
|
2194
2719
|
}
|
|
2195
2720
|
async notifyChannelFailed(channelId, reason = "handshake_failed") {
|
|
2196
|
-
logger$
|
|
2721
|
+
logger$d.debug("channel_encryption_manager_notified_failure", {
|
|
2197
2722
|
channel_id: channelId,
|
|
2198
2723
|
reason,
|
|
2199
2724
|
});
|
|
2200
2725
|
if (!channelId.startsWith("auto-")) {
|
|
2201
|
-
logger$
|
|
2726
|
+
logger$d.warning("unexpected_channel_id_format_on_failure", {
|
|
2202
2727
|
channel_id: channelId,
|
|
2203
2728
|
});
|
|
2204
2729
|
return;
|
|
2205
2730
|
}
|
|
2206
2731
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2207
2732
|
if (!destinationStr) {
|
|
2208
|
-
logger$
|
|
2733
|
+
logger$d.warning("cannot_parse_destination_from_channel_id_on_failure", {
|
|
2209
2734
|
channel_id: channelId,
|
|
2210
2735
|
});
|
|
2211
2736
|
return;
|
|
@@ -2215,14 +2740,14 @@ class ChannelEncryptionManager {
|
|
|
2215
2740
|
const cachedChannelId = this.addrChannelMap.get(destinationStr);
|
|
2216
2741
|
if (cachedChannelId === channelId) {
|
|
2217
2742
|
this.addrChannelMap.delete(destinationStr);
|
|
2218
|
-
logger$
|
|
2743
|
+
logger$d.debug("cleared_channel_cache_for_failed_channel", {
|
|
2219
2744
|
destination: destinationStr,
|
|
2220
2745
|
channel_id: channelId,
|
|
2221
2746
|
});
|
|
2222
2747
|
}
|
|
2223
2748
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr);
|
|
2224
2749
|
if (!queuedEnvelopes || queuedEnvelopes.length === 0) {
|
|
2225
|
-
logger$
|
|
2750
|
+
logger$d.debug("no_pending_queue_for_failed_destination", {
|
|
2226
2751
|
destination: destinationStr,
|
|
2227
2752
|
});
|
|
2228
2753
|
return;
|
|
@@ -2241,7 +2766,7 @@ class ChannelEncryptionManager {
|
|
|
2241
2766
|
const cached = this.addrChannelMap.get(destination);
|
|
2242
2767
|
if (cached) {
|
|
2243
2768
|
this.addrChannelMap.delete(destination);
|
|
2244
|
-
logger$
|
|
2769
|
+
logger$d.debug("cleared_channel_cache_for_destination", {
|
|
2245
2770
|
destination,
|
|
2246
2771
|
cached_channel_id: cached,
|
|
2247
2772
|
});
|
|
@@ -2259,14 +2784,14 @@ class ChannelEncryptionManager {
|
|
|
2259
2784
|
}
|
|
2260
2785
|
const cached = this.addrChannelMap.get(destination);
|
|
2261
2786
|
if (cached && this.getChannelState(cached)) {
|
|
2262
|
-
logger$
|
|
2787
|
+
logger$d.debug("using_cached_channel", { destination, channel_id: cached });
|
|
2263
2788
|
return cached;
|
|
2264
2789
|
}
|
|
2265
2790
|
const channels = this.secureChannelManager.channels;
|
|
2266
2791
|
for (const channelId of Object.keys(channels)) {
|
|
2267
2792
|
if (channelId.startsWith(`auto-${destination}-`)) {
|
|
2268
2793
|
this.addrChannelMap.set(destination, channelId);
|
|
2269
|
-
logger$
|
|
2794
|
+
logger$d.debug("using_existing_channel", {
|
|
2270
2795
|
destination,
|
|
2271
2796
|
channel_id: channelId,
|
|
2272
2797
|
});
|
|
@@ -2279,12 +2804,12 @@ class ChannelEncryptionManager {
|
|
|
2279
2804
|
const queue = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2280
2805
|
queue.push(envelope);
|
|
2281
2806
|
this.pendingEnvelopes.set(destinationStr, queue);
|
|
2282
|
-
logger$
|
|
2807
|
+
logger$d.debug("queued_envelope_for_channel_handshake", {
|
|
2283
2808
|
envelope_id: envelope.id,
|
|
2284
2809
|
destination: destinationStr,
|
|
2285
2810
|
});
|
|
2286
2811
|
if (this.handshakeInProgress.has(destinationStr)) {
|
|
2287
|
-
logger$
|
|
2812
|
+
logger$d.debug("handshake_already_in_progress", {
|
|
2288
2813
|
destination: destinationStr,
|
|
2289
2814
|
});
|
|
2290
2815
|
return;
|
|
@@ -2302,7 +2827,7 @@ class ChannelEncryptionManager {
|
|
|
2302
2827
|
}
|
|
2303
2828
|
async initiateChannelHandshakeAsync(destination, destinationStr, opts) {
|
|
2304
2829
|
if (!this.secureChannelManager) {
|
|
2305
|
-
logger$
|
|
2830
|
+
logger$d.error("no_secure_channel_manager_for_async_handshake_initiation");
|
|
2306
2831
|
return;
|
|
2307
2832
|
}
|
|
2308
2833
|
const channelId = this.generateChannelId(destinationStr);
|
|
@@ -2310,19 +2835,19 @@ class ChannelEncryptionManager {
|
|
|
2310
2835
|
const openFrame = this.secureChannelManager.generateOpenFrame(channelId, HANDSHAKE_ALGORITHM);
|
|
2311
2836
|
const success = await this.sendSecureOpenFrameAsync(openFrame, destination);
|
|
2312
2837
|
if (success) {
|
|
2313
|
-
logger$
|
|
2838
|
+
logger$d.debug("sent_secure_open_frame_async", {
|
|
2314
2839
|
channel_id: channelId,
|
|
2315
2840
|
destination: destinationStr,
|
|
2316
2841
|
});
|
|
2317
2842
|
}
|
|
2318
2843
|
else {
|
|
2319
|
-
logger$
|
|
2844
|
+
logger$d.warning("failed_to_send_secure_open_frame_async", {
|
|
2320
2845
|
channel_id: channelId,
|
|
2321
2846
|
});
|
|
2322
2847
|
}
|
|
2323
2848
|
}
|
|
2324
2849
|
catch (error) {
|
|
2325
|
-
logger$
|
|
2850
|
+
logger$d.error("async_channel_handshake_initiation_failed", {
|
|
2326
2851
|
destination: destinationStr,
|
|
2327
2852
|
error: error instanceof Error ? error.message : String(error),
|
|
2328
2853
|
});
|
|
@@ -2331,22 +2856,22 @@ class ChannelEncryptionManager {
|
|
|
2331
2856
|
async sendSecureOpenFrameAsync(openFrame, destination) {
|
|
2332
2857
|
const node = this.nodeLike;
|
|
2333
2858
|
if (!node) {
|
|
2334
|
-
logger$
|
|
2859
|
+
logger$d.error("no_node_available_for_sending_secure_open_async");
|
|
2335
2860
|
return false;
|
|
2336
2861
|
}
|
|
2337
2862
|
const envelopeFactory = node.envelopeFactory;
|
|
2338
2863
|
if (!envelopeFactory) {
|
|
2339
|
-
logger$
|
|
2864
|
+
logger$d.error("no_envelope_factory_available_for_secure_open_async");
|
|
2340
2865
|
return false;
|
|
2341
2866
|
}
|
|
2342
2867
|
const replyTo = this.buildSystemReplyTo();
|
|
2343
2868
|
if (!replyTo) {
|
|
2344
|
-
logger$
|
|
2869
|
+
logger$d.error("no_physical_path_available_for_reply_to_async");
|
|
2345
2870
|
return false;
|
|
2346
2871
|
}
|
|
2347
2872
|
const toAddress = toFameAddress(destination);
|
|
2348
2873
|
if (!toAddress) {
|
|
2349
|
-
logger$
|
|
2874
|
+
logger$d.error("invalid_destination_for_secure_open", {
|
|
2350
2875
|
destination: String(destination),
|
|
2351
2876
|
});
|
|
2352
2877
|
return false;
|
|
@@ -2358,7 +2883,7 @@ class ChannelEncryptionManager {
|
|
|
2358
2883
|
corrId: core.generateId(),
|
|
2359
2884
|
});
|
|
2360
2885
|
await this.deliverEnvelope(envelope);
|
|
2361
|
-
logger$
|
|
2886
|
+
logger$d.debug("delivered_secure_open_frame_async", {
|
|
2362
2887
|
channel_id: openFrame.cid,
|
|
2363
2888
|
});
|
|
2364
2889
|
return true;
|
|
@@ -2366,7 +2891,7 @@ class ChannelEncryptionManager {
|
|
|
2366
2891
|
async deliverEnvelope(envelope) {
|
|
2367
2892
|
const node = this.nodeLike;
|
|
2368
2893
|
if (!node) {
|
|
2369
|
-
logger$
|
|
2894
|
+
logger$d.error("no_node_available_for_delivery", {
|
|
2370
2895
|
envelope_id: envelope.id,
|
|
2371
2896
|
});
|
|
2372
2897
|
return;
|
|
@@ -2376,19 +2901,19 @@ class ChannelEncryptionManager {
|
|
|
2376
2901
|
}
|
|
2377
2902
|
encryptWithChannel(envelope, channelId) {
|
|
2378
2903
|
if (!this.secureChannelManager) {
|
|
2379
|
-
logger$
|
|
2904
|
+
logger$d.error("no_secure_channel_manager_for_encryption");
|
|
2380
2905
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2381
2906
|
}
|
|
2382
2907
|
const frame = envelope.frame;
|
|
2383
2908
|
if (!this.isDataFrame(frame)) {
|
|
2384
|
-
logger$
|
|
2909
|
+
logger$d.error("attempted_to_encrypt_non_dataframe", {
|
|
2385
2910
|
frame_type: frame.type ?? typeof frame,
|
|
2386
2911
|
});
|
|
2387
2912
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2388
2913
|
}
|
|
2389
2914
|
const channelState = this.getChannelState(channelId);
|
|
2390
2915
|
if (!channelState) {
|
|
2391
|
-
logger$
|
|
2916
|
+
logger$d.error("channel_not_in_channels", { channel_id: channelId });
|
|
2392
2917
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2393
2918
|
}
|
|
2394
2919
|
const payloadBytes = this.serializePayload(frame.payload);
|
|
@@ -2447,7 +2972,7 @@ class ChannelEncryptionManager {
|
|
|
2447
2972
|
return decodeBase64$1(payload);
|
|
2448
2973
|
}
|
|
2449
2974
|
catch (error) {
|
|
2450
|
-
logger$
|
|
2975
|
+
logger$d.error("failed_to_decode_base64_ciphertext", {
|
|
2451
2976
|
error: error instanceof Error ? error.message : String(error),
|
|
2452
2977
|
});
|
|
2453
2978
|
return null;
|
|
@@ -2477,7 +3002,7 @@ class ChannelEncryptionManager {
|
|
|
2477
3002
|
return parts.slice(1, -1).join("-");
|
|
2478
3003
|
}
|
|
2479
3004
|
async handleFailedEnvelope(envelope, destinationStr, channelId, reason) {
|
|
2480
|
-
logger$
|
|
3005
|
+
logger$d.warning("envelope_failed_due_to_channel_handshake_failure", {
|
|
2481
3006
|
envelope_id: envelope.id,
|
|
2482
3007
|
destination: destinationStr,
|
|
2483
3008
|
channel_id: channelId,
|
|
@@ -2485,14 +3010,14 @@ class ChannelEncryptionManager {
|
|
|
2485
3010
|
});
|
|
2486
3011
|
const frame = envelope.frame;
|
|
2487
3012
|
if (!this.isDataFrame(frame)) {
|
|
2488
|
-
logger$
|
|
3013
|
+
logger$d.debug("skipping_nack_for_non_dataframe", {
|
|
2489
3014
|
envelope_id: envelope.id,
|
|
2490
3015
|
frame_type: frame.type ?? typeof frame,
|
|
2491
3016
|
});
|
|
2492
3017
|
return;
|
|
2493
3018
|
}
|
|
2494
3019
|
if (!envelope.replyTo) {
|
|
2495
|
-
logger$
|
|
3020
|
+
logger$d.debug("skipping_nack_no_reply_to", { envelope_id: envelope.id });
|
|
2496
3021
|
return;
|
|
2497
3022
|
}
|
|
2498
3023
|
await this.sendDeliveryNack(envelope, `channel_handshake_failed: ${reason}`);
|
|
@@ -2500,17 +3025,17 @@ class ChannelEncryptionManager {
|
|
|
2500
3025
|
async sendDeliveryNack(envelope, failureReason) {
|
|
2501
3026
|
const node = this.nodeLike;
|
|
2502
3027
|
if (!node) {
|
|
2503
|
-
logger$
|
|
3028
|
+
logger$d.error("no_node_available_for_sending_delivery_nack");
|
|
2504
3029
|
return;
|
|
2505
3030
|
}
|
|
2506
3031
|
const envelopeFactory = node.envelopeFactory;
|
|
2507
3032
|
if (!envelopeFactory) {
|
|
2508
|
-
logger$
|
|
3033
|
+
logger$d.error("no_envelope_factory_available_for_delivery_nack");
|
|
2509
3034
|
return;
|
|
2510
3035
|
}
|
|
2511
3036
|
const replyTo = toFameAddress(envelope.replyTo ?? null);
|
|
2512
3037
|
if (!replyTo) {
|
|
2513
|
-
logger$
|
|
3038
|
+
logger$d.error("invalid_reply_to_for_delivery_nack", {
|
|
2514
3039
|
reply_to: envelope.replyTo,
|
|
2515
3040
|
});
|
|
2516
3041
|
return;
|
|
@@ -2527,7 +3052,7 @@ class ChannelEncryptionManager {
|
|
|
2527
3052
|
corrId: envelope.corrId ?? core.generateId(),
|
|
2528
3053
|
});
|
|
2529
3054
|
await this.deliverEnvelope(nackEnvelope);
|
|
2530
|
-
logger$
|
|
3055
|
+
logger$d.debug("delivered_delivery_nack", {
|
|
2531
3056
|
original_envelope_id: envelope.id,
|
|
2532
3057
|
nack_envelope_id: nackEnvelope.id,
|
|
2533
3058
|
});
|
|
@@ -2565,7 +3090,7 @@ class ChannelEncryptionManager {
|
|
|
2565
3090
|
await task();
|
|
2566
3091
|
}
|
|
2567
3092
|
catch (error) {
|
|
2568
|
-
logger$
|
|
3093
|
+
logger$d.error("async_task_failed", {
|
|
2569
3094
|
task_name: name,
|
|
2570
3095
|
error: error instanceof Error ? error.message : String(error),
|
|
2571
3096
|
});
|
|
@@ -2619,7 +3144,7 @@ class ChannelEncryptionManager {
|
|
|
2619
3144
|
}
|
|
2620
3145
|
}
|
|
2621
3146
|
|
|
2622
|
-
const logger$
|
|
3147
|
+
const logger$c = runtime.getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager_factory");
|
|
2623
3148
|
const DEFAULT_SUPPORTED_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
2624
3149
|
const FACTORY_META$c = {
|
|
2625
3150
|
base: runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE,
|
|
@@ -2651,7 +3176,7 @@ class ChannelEncryptionManagerFactory extends runtime.EncryptionManagerFactory {
|
|
|
2651
3176
|
async create(_config, ...factoryArgs) {
|
|
2652
3177
|
const [dependencies] = factoryArgs;
|
|
2653
3178
|
const resolvedDependencies = this.resolveDependencies(dependencies);
|
|
2654
|
-
logger$
|
|
3179
|
+
logger$c.debug("creating_channel_encryption_manager", {
|
|
2655
3180
|
has_secure_channel_manager: Boolean(resolvedDependencies.secureChannelManager),
|
|
2656
3181
|
has_node_like: Boolean(resolvedDependencies.nodeLike),
|
|
2657
3182
|
has_task_spawner: Boolean(resolvedDependencies.taskSpawner),
|
|
@@ -2713,7 +3238,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2713
3238
|
ChannelEncryptionManagerFactory: ChannelEncryptionManagerFactory
|
|
2714
3239
|
});
|
|
2715
3240
|
|
|
2716
|
-
const logger$
|
|
3241
|
+
const logger$b = runtime.getLogger("naylence.fame.security.encryption.default_secure_channel_manager");
|
|
2717
3242
|
const DEFAULT_ALGORITHM = "CHACHA20P1305";
|
|
2718
3243
|
const CHANNEL_KEY_LENGTH = 32;
|
|
2719
3244
|
const NONCE_PREFIX_LENGTH = 4;
|
|
@@ -2760,7 +3285,7 @@ class DefaultSecureChannelManager {
|
|
|
2760
3285
|
const privateKey = ed25519_js.x25519.utils.randomSecretKey();
|
|
2761
3286
|
const publicKey = ed25519_js.x25519.scalarMultBase(privateKey);
|
|
2762
3287
|
this.ephemeralKeys.set(channelId, privateKey);
|
|
2763
|
-
logger$
|
|
3288
|
+
logger$b.debug("generated_channel_open", { cid: channelId, algorithm });
|
|
2764
3289
|
return {
|
|
2765
3290
|
type: "SecureOpen",
|
|
2766
3291
|
cid: channelId,
|
|
@@ -2773,7 +3298,7 @@ class DefaultSecureChannelManager {
|
|
|
2773
3298
|
runtime.requireCryptoSupport();
|
|
2774
3299
|
const algorithm = frame.alg || DEFAULT_ALGORITHM;
|
|
2775
3300
|
if (!this.isSupportedAlgorithm(algorithm)) {
|
|
2776
|
-
logger$
|
|
3301
|
+
logger$b.warning("unsupported_channel_algorithm", {
|
|
2777
3302
|
cid: frame.cid,
|
|
2778
3303
|
alg: algorithm,
|
|
2779
3304
|
});
|
|
@@ -2791,7 +3316,7 @@ class DefaultSecureChannelManager {
|
|
|
2791
3316
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2792
3317
|
}
|
|
2793
3318
|
catch (error) {
|
|
2794
|
-
logger$
|
|
3319
|
+
logger$b.warning("invalid_peer_public_key", {
|
|
2795
3320
|
cid: frame.cid,
|
|
2796
3321
|
error: error instanceof Error ? error.message : String(error),
|
|
2797
3322
|
});
|
|
@@ -2813,7 +3338,7 @@ class DefaultSecureChannelManager {
|
|
|
2813
3338
|
algorithm,
|
|
2814
3339
|
});
|
|
2815
3340
|
this.channelsMap.set(frame.cid, channelState);
|
|
2816
|
-
logger$
|
|
3341
|
+
logger$b.debug("channel_established", { cid: frame.cid, algorithm });
|
|
2817
3342
|
myPrivateKey.fill(0);
|
|
2818
3343
|
sharedSecret.fill(0);
|
|
2819
3344
|
return {
|
|
@@ -2827,7 +3352,7 @@ class DefaultSecureChannelManager {
|
|
|
2827
3352
|
async handleAcceptFrame(frame) {
|
|
2828
3353
|
runtime.requireCryptoSupport();
|
|
2829
3354
|
if (frame.ok === false) {
|
|
2830
|
-
logger$
|
|
3355
|
+
logger$b.warning("channel_rejected", {
|
|
2831
3356
|
cid: frame.cid,
|
|
2832
3357
|
error: frame.reason,
|
|
2833
3358
|
});
|
|
@@ -2836,7 +3361,7 @@ class DefaultSecureChannelManager {
|
|
|
2836
3361
|
}
|
|
2837
3362
|
const privateKey = this.ephemeralKeys.get(frame.cid);
|
|
2838
3363
|
if (!privateKey) {
|
|
2839
|
-
logger$
|
|
3364
|
+
logger$b.error("no_ephemeral_key", { cid: frame.cid });
|
|
2840
3365
|
return false;
|
|
2841
3366
|
}
|
|
2842
3367
|
let peerPublicKey;
|
|
@@ -2844,7 +3369,7 @@ class DefaultSecureChannelManager {
|
|
|
2844
3369
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2845
3370
|
}
|
|
2846
3371
|
catch (error) {
|
|
2847
|
-
logger$
|
|
3372
|
+
logger$b.warning("invalid_accept_public_key", {
|
|
2848
3373
|
cid: frame.cid,
|
|
2849
3374
|
error: error instanceof Error ? error.message : String(error),
|
|
2850
3375
|
});
|
|
@@ -2859,17 +3384,17 @@ class DefaultSecureChannelManager {
|
|
|
2859
3384
|
algorithm,
|
|
2860
3385
|
});
|
|
2861
3386
|
this.channelsMap.set(frame.cid, channelState);
|
|
2862
|
-
logger$
|
|
3387
|
+
logger$b.debug("channel_completed", { cid: frame.cid, algorithm });
|
|
2863
3388
|
sharedSecret.fill(0);
|
|
2864
3389
|
this.cleanupEphemeralKey(frame.cid);
|
|
2865
3390
|
return true;
|
|
2866
3391
|
}
|
|
2867
3392
|
handleCloseFrame(frame) {
|
|
2868
3393
|
if (this.channelsMap.delete(frame.cid)) {
|
|
2869
|
-
logger$
|
|
3394
|
+
logger$b.debug("channel_closed", { cid: frame.cid, reason: frame.reason });
|
|
2870
3395
|
}
|
|
2871
3396
|
else {
|
|
2872
|
-
logger$
|
|
3397
|
+
logger$b.warning("close_unknown_channel", { cid: frame.cid });
|
|
2873
3398
|
}
|
|
2874
3399
|
this.cleanupEphemeralKey(frame.cid);
|
|
2875
3400
|
}
|
|
@@ -2896,7 +3421,7 @@ class DefaultSecureChannelManager {
|
|
|
2896
3421
|
}
|
|
2897
3422
|
closeChannel(channelId, reason = "User requested") {
|
|
2898
3423
|
if (this.channelsMap.delete(channelId)) {
|
|
2899
|
-
logger$
|
|
3424
|
+
logger$b.debug("channel_closed_by_user", { cid: channelId, reason });
|
|
2900
3425
|
}
|
|
2901
3426
|
this.cleanupEphemeralKey(channelId);
|
|
2902
3427
|
return {
|
|
@@ -2913,7 +3438,7 @@ class DefaultSecureChannelManager {
|
|
|
2913
3438
|
this.channelsMap.delete(channelId);
|
|
2914
3439
|
this.cleanupEphemeralKey(channelId);
|
|
2915
3440
|
removed += 1;
|
|
2916
|
-
logger$
|
|
3441
|
+
logger$b.debug("channel_expired_cleanup", { cid: channelId });
|
|
2917
3442
|
}
|
|
2918
3443
|
}
|
|
2919
3444
|
return removed;
|
|
@@ -2942,7 +3467,7 @@ class DefaultSecureChannelManager {
|
|
|
2942
3467
|
if (channelId.startsWith(prefix)) {
|
|
2943
3468
|
if (this.removeChannel(channelId)) {
|
|
2944
3469
|
removed += 1;
|
|
2945
|
-
logger$
|
|
3470
|
+
logger$b.debug("removed_channel_for_destination", {
|
|
2946
3471
|
channel_id: channelId,
|
|
2947
3472
|
destination,
|
|
2948
3473
|
});
|
|
@@ -2950,7 +3475,7 @@ class DefaultSecureChannelManager {
|
|
|
2950
3475
|
}
|
|
2951
3476
|
}
|
|
2952
3477
|
if (removed > 0) {
|
|
2953
|
-
logger$
|
|
3478
|
+
logger$b.info("cleanup_channels_for_destination", {
|
|
2954
3479
|
destination,
|
|
2955
3480
|
channels_removed: removed,
|
|
2956
3481
|
});
|
|
@@ -3022,187 +3547,26 @@ class DefaultSecureChannelManagerFactory extends runtime.SecureChannelManagerFac
|
|
|
3022
3547
|
}
|
|
3023
3548
|
return undefined;
|
|
3024
3549
|
}
|
|
3025
|
-
toPositiveNumber(value) {
|
|
3026
|
-
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3027
|
-
return value;
|
|
3028
|
-
}
|
|
3029
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
3030
|
-
const parsed = Number(value);
|
|
3031
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3032
|
-
return parsed;
|
|
3033
|
-
}
|
|
3034
|
-
}
|
|
3035
|
-
return undefined;
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
|
|
3039
|
-
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3040
|
-
__proto__: null,
|
|
3041
|
-
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3042
|
-
FACTORY_META: FACTORY_META$b,
|
|
3043
|
-
default: DefaultSecureChannelManagerFactory
|
|
3044
|
-
});
|
|
3045
|
-
|
|
3046
|
-
const logger$b = runtime.getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
3047
|
-
class EncryptionManagerFactoryRegistry {
|
|
3048
|
-
constructor(autoDiscover = true) {
|
|
3049
|
-
this.factories = [];
|
|
3050
|
-
this.algorithmToFactory = new Map();
|
|
3051
|
-
this.typeToFactories = new Map();
|
|
3052
|
-
this.factorySet = new Set();
|
|
3053
|
-
this.autoDiscoveredFactories = new Set();
|
|
3054
|
-
this.autoDiscovered = false;
|
|
3055
|
-
if (autoDiscover) {
|
|
3056
|
-
this.autoDiscoverFactories();
|
|
3057
|
-
}
|
|
3058
|
-
}
|
|
3059
|
-
autoDiscoverFactories() {
|
|
3060
|
-
if (this.autoDiscovered) {
|
|
3061
|
-
return;
|
|
3062
|
-
}
|
|
3063
|
-
try {
|
|
3064
|
-
const extensionInfos = factory.ExtensionManager.getExtensionsByType(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
3065
|
-
let registeredCount = 0;
|
|
3066
|
-
for (const [factoryName, info] of extensionInfos) {
|
|
3067
|
-
if (factoryName === "CompositeEncryptionManager") {
|
|
3068
|
-
logger$b.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
3069
|
-
factory_name: factoryName,
|
|
3070
|
-
});
|
|
3071
|
-
continue;
|
|
3072
|
-
}
|
|
3073
|
-
try {
|
|
3074
|
-
const factoryInstance = (info.instance ??
|
|
3075
|
-
factory.ExtensionManager.getGlobalFactory(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
3076
|
-
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
3077
|
-
registeredCount += 1;
|
|
3078
|
-
logger$b.debug("auto_discovered_factory", {
|
|
3079
|
-
factory_name: factoryName,
|
|
3080
|
-
factory_class: factoryInstance.constructor.name,
|
|
3081
|
-
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
3082
|
-
encryption_type: factoryInstance.getEncryptionType(),
|
|
3083
|
-
priority: factoryInstance.getPriority(),
|
|
3084
|
-
});
|
|
3085
|
-
}
|
|
3086
|
-
catch (error) {
|
|
3087
|
-
logger$b.warning("failed_to_auto_register_factory", {
|
|
3088
|
-
factory_name: factoryName,
|
|
3089
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3090
|
-
});
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
this.autoDiscovered = true;
|
|
3094
|
-
logger$b.debug("completed_auto_discovery", {
|
|
3095
|
-
registered_factories: registeredCount,
|
|
3096
|
-
total_discovered: extensionInfos.size,
|
|
3097
|
-
skipped_composite: true,
|
|
3098
|
-
});
|
|
3099
|
-
}
|
|
3100
|
-
catch (error) {
|
|
3101
|
-
logger$b.warning("failed_auto_discovery_of_factories", {
|
|
3102
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3103
|
-
});
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
|
-
registerFactory(factory, options = {}) {
|
|
3107
|
-
if (this.factorySet.has(factory)) {
|
|
3108
|
-
return;
|
|
3109
|
-
}
|
|
3110
|
-
this.factorySet.add(factory);
|
|
3111
|
-
this.factories.push(factory);
|
|
3112
|
-
if (options.autoDiscovered) {
|
|
3113
|
-
this.autoDiscoveredFactories.add(factory);
|
|
3114
|
-
}
|
|
3115
|
-
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
3116
|
-
const existing = this.algorithmToFactory.get(algorithm);
|
|
3117
|
-
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
3118
|
-
this.algorithmToFactory.set(algorithm, factory);
|
|
3119
|
-
logger$b.debug("registered_algorithm_mapping", {
|
|
3120
|
-
algorithm,
|
|
3121
|
-
factory: factory.constructor.name,
|
|
3122
|
-
priority: factory.getPriority(),
|
|
3123
|
-
});
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
const encryptionType = factory.getEncryptionType();
|
|
3127
|
-
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
3128
|
-
typeFactories.push(factory);
|
|
3129
|
-
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
3130
|
-
this.typeToFactories.set(encryptionType, typeFactories);
|
|
3131
|
-
logger$b.debug("registered_encryption_manager_factory", {
|
|
3132
|
-
factory: factory.constructor.name,
|
|
3133
|
-
encryption_type: encryptionType,
|
|
3134
|
-
algorithms: factory.getSupportedAlgorithms(),
|
|
3135
|
-
priority: factory.getPriority(),
|
|
3136
|
-
auto_discovered: options.autoDiscovered ?? false,
|
|
3137
|
-
});
|
|
3138
|
-
}
|
|
3139
|
-
getFactoryForAlgorithm(algorithm) {
|
|
3140
|
-
this.ensureAutoDiscovery();
|
|
3141
|
-
return this.algorithmToFactory.get(algorithm);
|
|
3142
|
-
}
|
|
3143
|
-
getFactoryForOptions(opts) {
|
|
3144
|
-
this.ensureAutoDiscovery();
|
|
3145
|
-
for (const factory of this.factories) {
|
|
3146
|
-
if (factory.supportsOptions(opts ?? undefined)) {
|
|
3147
|
-
logger$b.debug("found_factory_for_options", {
|
|
3148
|
-
factory: factory.constructor.name,
|
|
3149
|
-
encryption_type: factory.getEncryptionType(),
|
|
3150
|
-
});
|
|
3151
|
-
return factory;
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
logger$b.debug("no_factory_found_for_options", { opts });
|
|
3155
|
-
return undefined;
|
|
3156
|
-
}
|
|
3157
|
-
getFactoriesByType(encryptionType) {
|
|
3158
|
-
this.ensureAutoDiscovery();
|
|
3159
|
-
return this.typeToFactories.get(encryptionType) ?? [];
|
|
3160
|
-
}
|
|
3161
|
-
getAllSupportedAlgorithms() {
|
|
3162
|
-
this.ensureAutoDiscovery();
|
|
3163
|
-
return Array.from(this.algorithmToFactory.keys());
|
|
3164
|
-
}
|
|
3165
|
-
getRegistryInfo() {
|
|
3166
|
-
return {
|
|
3167
|
-
totalFactories: this.factories.length,
|
|
3168
|
-
autoDiscovered: this.autoDiscovered,
|
|
3169
|
-
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
3170
|
-
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
3171
|
-
encType,
|
|
3172
|
-
factories.map((factory) => factory.constructor.name),
|
|
3173
|
-
])),
|
|
3174
|
-
};
|
|
3175
|
-
}
|
|
3176
|
-
forceRediscovery() {
|
|
3177
|
-
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
3178
|
-
this.autoDiscovered = false;
|
|
3179
|
-
this.algorithmToFactory.clear();
|
|
3180
|
-
this.typeToFactories.clear();
|
|
3181
|
-
this.factories.length = 0;
|
|
3182
|
-
this.factorySet.clear();
|
|
3183
|
-
this.autoDiscoveredFactories.clear();
|
|
3184
|
-
for (const factory of manualFactories) {
|
|
3185
|
-
this.registerFactory(factory);
|
|
3550
|
+
toPositiveNumber(value) {
|
|
3551
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3552
|
+
return value;
|
|
3186
3553
|
}
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
ensureInitialized() {
|
|
3193
|
-
this.ensureAutoDiscovery();
|
|
3194
|
-
}
|
|
3195
|
-
ensureAutoDiscovery() {
|
|
3196
|
-
if (!this.autoDiscovered) {
|
|
3197
|
-
this.autoDiscoverFactories();
|
|
3554
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
3555
|
+
const parsed = Number(value);
|
|
3556
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3557
|
+
return parsed;
|
|
3558
|
+
}
|
|
3198
3559
|
}
|
|
3560
|
+
return undefined;
|
|
3199
3561
|
}
|
|
3200
3562
|
}
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3563
|
+
|
|
3564
|
+
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3565
|
+
__proto__: null,
|
|
3566
|
+
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3567
|
+
FACTORY_META: FACTORY_META$b,
|
|
3568
|
+
default: DefaultSecureChannelManagerFactory
|
|
3569
|
+
});
|
|
3206
3570
|
|
|
3207
3571
|
const logger$a = runtime.getLogger("naylence.fame.security.encryption.composite_encryption_manager");
|
|
3208
3572
|
const DEFAULT_SEALED_ALGORITHMS = [
|
|
@@ -5117,1275 +5481,953 @@ class AFTLoadBalancerStickinessManager extends runtime.BaseNodeEventListener {
|
|
|
5117
5481
|
}
|
|
5118
5482
|
const verification = await this.verifier.verify(aftToken, envelope.sid ?? undefined);
|
|
5119
5483
|
if (!verification.valid) {
|
|
5120
|
-
this.metrics.verifyFailures += 1;
|
|
5121
|
-
logger$4.warning("aft_verification_failed", {
|
|
5122
|
-
envelope_id: envelope.id,
|
|
5123
|
-
replica_id: replicaId,
|
|
5124
|
-
error: verification.error,
|
|
5125
|
-
});
|
|
5126
|
-
return null;
|
|
5127
|
-
}
|
|
5128
|
-
this.storeAssociation(aftToken, {
|
|
5129
|
-
replicaId,
|
|
5130
|
-
token: aftToken,
|
|
5131
|
-
sid: verification.sid ?? "",
|
|
5132
|
-
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5133
|
-
trustLevel: verification.trustLevel,
|
|
5134
|
-
scope: verification.scope ?? null,
|
|
5135
|
-
clientSid: verification.clientSid ?? null,
|
|
5136
|
-
});
|
|
5137
|
-
if (verification.clientSid) {
|
|
5138
|
-
this.sidCache.set(verification.clientSid, replicaId);
|
|
5139
|
-
logger$4.debug("sid_cache_updated", {
|
|
5140
|
-
envelope_id: envelope.id,
|
|
5141
|
-
client_sid: verification.clientSid,
|
|
5142
|
-
replica_id: replicaId,
|
|
5143
|
-
});
|
|
5144
|
-
}
|
|
5145
|
-
this.metrics.associationsCreated += 1;
|
|
5146
|
-
logger$4.debug("aft_association_created", {
|
|
5147
|
-
envelope_id: envelope.id,
|
|
5148
|
-
replica_id: replicaId,
|
|
5149
|
-
sid: verification.sid,
|
|
5150
|
-
exp: verification.exp,
|
|
5151
|
-
trust_level: verification.trustLevel,
|
|
5152
|
-
scope: verification.scope,
|
|
5153
|
-
});
|
|
5154
|
-
return this.config.clientEcho ? aftToken : null;
|
|
5155
|
-
}
|
|
5156
|
-
getStickyReplicaSegment(envelope, segments) {
|
|
5157
|
-
if (!this.config.enabled) {
|
|
5158
|
-
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5159
|
-
return null;
|
|
5160
|
-
}
|
|
5161
|
-
if (envelope.aft) {
|
|
5162
|
-
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5163
|
-
if (replicaId) {
|
|
5164
|
-
this.metrics.cacheHits += 1;
|
|
5165
|
-
logger$4.debug("aft_routed_envelope", {
|
|
5166
|
-
envelope_id: envelope.id,
|
|
5167
|
-
replica_id: replicaId,
|
|
5168
|
-
routing_type: "aft_direct",
|
|
5169
|
-
});
|
|
5170
|
-
return replicaId;
|
|
5171
|
-
}
|
|
5172
|
-
}
|
|
5173
|
-
if (envelope.sid) {
|
|
5174
|
-
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5175
|
-
if (cachedReplica) {
|
|
5176
|
-
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY) {
|
|
5177
|
-
this.metrics.cacheHits += 1;
|
|
5178
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5179
|
-
envelope_id: envelope.id,
|
|
5180
|
-
replica_id: cachedReplica,
|
|
5181
|
-
sid: envelope.sid,
|
|
5182
|
-
routing_type: "sid_only",
|
|
5183
|
-
});
|
|
5184
|
-
return cachedReplica;
|
|
5185
|
-
}
|
|
5186
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5187
|
-
if (association.replicaId === cachedReplica &&
|
|
5188
|
-
!association.isExpired()) {
|
|
5189
|
-
envelope.aft = token;
|
|
5190
|
-
this.metrics.cacheHits += 1;
|
|
5191
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5192
|
-
envelope_id: envelope.id,
|
|
5193
|
-
replica_id: cachedReplica,
|
|
5194
|
-
sid: envelope.sid,
|
|
5195
|
-
routing_type: "sid_cache_with_aft",
|
|
5196
|
-
});
|
|
5197
|
-
return cachedReplica;
|
|
5198
|
-
}
|
|
5199
|
-
}
|
|
5200
|
-
this.metrics.cacheHits += 1;
|
|
5201
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5202
|
-
envelope_id: envelope.id,
|
|
5203
|
-
replica_id: cachedReplica,
|
|
5204
|
-
sid: envelope.sid,
|
|
5205
|
-
routing_type: "sid_cache_direct",
|
|
5206
|
-
});
|
|
5207
|
-
return cachedReplica;
|
|
5208
|
-
}
|
|
5209
|
-
logger$4.debug("no_cached_replica_for_sid", {
|
|
5210
|
-
envelope_id: envelope.id,
|
|
5211
|
-
sid: envelope.sid,
|
|
5212
|
-
});
|
|
5213
|
-
}
|
|
5214
|
-
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5215
|
-
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5216
|
-
const chosen = segments[index];
|
|
5217
|
-
this.metrics.cacheHits += 1;
|
|
5218
|
-
logger$4.debug("sid_based_deterministic_choice", {
|
|
5219
|
-
envelope_id: envelope.id,
|
|
5220
|
-
sid: envelope.sid,
|
|
5221
|
-
chosen,
|
|
5222
|
-
routing_type: "sid_deterministic",
|
|
5223
|
-
});
|
|
5224
|
-
return chosen;
|
|
5225
|
-
}
|
|
5226
|
-
this.metrics.cacheMisses += 1;
|
|
5227
|
-
logger$4.debug("no_stickiness_routing", {
|
|
5228
|
-
envelope_id: envelope.id,
|
|
5229
|
-
has_aft: Boolean(envelope.aft),
|
|
5230
|
-
has_sid: Boolean(envelope.sid),
|
|
5231
|
-
});
|
|
5232
|
-
return null;
|
|
5233
|
-
}
|
|
5234
|
-
cleanupExpiredAssociations() {
|
|
5235
|
-
const now = Math.floor(Date.now() / 1000);
|
|
5236
|
-
const expiredTokens = [];
|
|
5237
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5238
|
-
if (association.isExpired(now)) {
|
|
5239
|
-
expiredTokens.push(token);
|
|
5240
|
-
}
|
|
5241
|
-
}
|
|
5242
|
-
for (const token of expiredTokens) {
|
|
5243
|
-
this.removeAssociation(token);
|
|
5244
|
-
}
|
|
5245
|
-
if (expiredTokens.length > 0) {
|
|
5246
|
-
this.metrics.associationsExpired += expiredTokens.length;
|
|
5247
|
-
logger$4.debug("cleaned_expired_associations", {
|
|
5248
|
-
count: expiredTokens.length,
|
|
5249
|
-
});
|
|
5250
|
-
}
|
|
5251
|
-
}
|
|
5252
|
-
replicaLeft(replicaId) {
|
|
5253
|
-
const tokensToRemove = [];
|
|
5254
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5255
|
-
if (association.replicaId === replicaId) {
|
|
5256
|
-
tokensToRemove.push(token);
|
|
5257
|
-
}
|
|
5258
|
-
}
|
|
5259
|
-
for (const token of tokensToRemove) {
|
|
5260
|
-
this.removeAssociation(token);
|
|
5261
|
-
}
|
|
5262
|
-
if (tokensToRemove.length > 0) {
|
|
5263
|
-
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5264
|
-
replica_id: replicaId,
|
|
5265
|
-
count: tokensToRemove.length,
|
|
5266
|
-
});
|
|
5267
|
-
}
|
|
5268
|
-
}
|
|
5269
|
-
handleReplicaLeft(replicaId) {
|
|
5270
|
-
this.replicaLeft(replicaId);
|
|
5271
|
-
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5272
|
-
}
|
|
5273
|
-
getMetrics() {
|
|
5274
|
-
return {
|
|
5275
|
-
...this.metrics,
|
|
5276
|
-
cacheSize: this.aftAssociations.size,
|
|
5277
|
-
sidCacheSize: this.sidCache.size,
|
|
5278
|
-
};
|
|
5279
|
-
}
|
|
5280
|
-
getAssociations() {
|
|
5281
|
-
const result = {};
|
|
5282
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5283
|
-
result[token] = {
|
|
5284
|
-
replica_id: association.replicaId,
|
|
5285
|
-
sid: association.sid,
|
|
5286
|
-
client_sid: association.clientSid,
|
|
5287
|
-
exp: association.exp,
|
|
5288
|
-
trust_level: association.trustLevel,
|
|
5289
|
-
scope: association.scope,
|
|
5290
|
-
created_at: association.createdAt,
|
|
5291
|
-
expired: association.isExpired(),
|
|
5292
|
-
};
|
|
5293
|
-
}
|
|
5294
|
-
return result;
|
|
5295
|
-
}
|
|
5296
|
-
getStickinessMetrics() {
|
|
5297
|
-
return this.getMetrics();
|
|
5298
|
-
}
|
|
5299
|
-
logMetrics() {
|
|
5300
|
-
const hits = this.metrics.cacheHits;
|
|
5301
|
-
const misses = this.metrics.cacheMisses;
|
|
5302
|
-
const total = hits + misses;
|
|
5303
|
-
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5304
|
-
logger$4.info("stickiness_metrics_report", {
|
|
5305
|
-
enabled: this.config.enabled,
|
|
5306
|
-
security_level: this.config.securityLevel,
|
|
5307
|
-
cache_hits: hits,
|
|
5308
|
-
cache_misses: misses,
|
|
5309
|
-
verify_failures: this.metrics.verifyFailures,
|
|
5310
|
-
associations_created: this.metrics.associationsCreated,
|
|
5311
|
-
associations_expired: this.metrics.associationsExpired,
|
|
5312
|
-
active_associations: this.aftAssociations.size,
|
|
5313
|
-
sid_cache_entries: this.sidCache.size,
|
|
5314
|
-
hit_rate: hitRate,
|
|
5484
|
+
this.metrics.verifyFailures += 1;
|
|
5485
|
+
logger$4.warning("aft_verification_failed", {
|
|
5486
|
+
envelope_id: envelope.id,
|
|
5487
|
+
replica_id: replicaId,
|
|
5488
|
+
error: verification.error,
|
|
5489
|
+
});
|
|
5490
|
+
return null;
|
|
5491
|
+
}
|
|
5492
|
+
this.storeAssociation(aftToken, {
|
|
5493
|
+
replicaId,
|
|
5494
|
+
token: aftToken,
|
|
5495
|
+
sid: verification.sid ?? "",
|
|
5496
|
+
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5497
|
+
trustLevel: verification.trustLevel,
|
|
5498
|
+
scope: verification.scope ?? null,
|
|
5499
|
+
clientSid: verification.clientSid ?? null,
|
|
5315
5500
|
});
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5501
|
+
if (verification.clientSid) {
|
|
5502
|
+
this.sidCache.set(verification.clientSid, replicaId);
|
|
5503
|
+
logger$4.debug("sid_cache_updated", {
|
|
5504
|
+
envelope_id: envelope.id,
|
|
5505
|
+
client_sid: verification.clientSid,
|
|
5506
|
+
replica_id: replicaId,
|
|
5507
|
+
});
|
|
5508
|
+
}
|
|
5509
|
+
this.metrics.associationsCreated += 1;
|
|
5510
|
+
logger$4.debug("aft_association_created", {
|
|
5319
5511
|
envelope_id: envelope.id,
|
|
5320
|
-
|
|
5321
|
-
|
|
5512
|
+
replica_id: replicaId,
|
|
5513
|
+
sid: verification.sid,
|
|
5514
|
+
exp: verification.exp,
|
|
5515
|
+
trust_level: verification.trustLevel,
|
|
5516
|
+
scope: verification.scope,
|
|
5322
5517
|
});
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5518
|
+
return this.config.clientEcho ? aftToken : null;
|
|
5519
|
+
}
|
|
5520
|
+
getStickyReplicaSegment(envelope, segments) {
|
|
5521
|
+
if (!this.config.enabled) {
|
|
5522
|
+
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5523
|
+
return null;
|
|
5524
|
+
}
|
|
5525
|
+
if (envelope.aft) {
|
|
5526
|
+
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5527
|
+
if (replicaId) {
|
|
5528
|
+
this.metrics.cacheHits += 1;
|
|
5529
|
+
logger$4.debug("aft_routed_envelope", {
|
|
5327
5530
|
envelope_id: envelope.id,
|
|
5328
|
-
|
|
5531
|
+
replica_id: replicaId,
|
|
5532
|
+
routing_type: "aft_direct",
|
|
5329
5533
|
});
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5534
|
+
return replicaId;
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
if (envelope.sid) {
|
|
5538
|
+
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5539
|
+
if (cachedReplica) {
|
|
5540
|
+
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY) {
|
|
5541
|
+
this.metrics.cacheHits += 1;
|
|
5542
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5335
5543
|
envelope_id: envelope.id,
|
|
5544
|
+
replica_id: cachedReplica,
|
|
5336
5545
|
sid: envelope.sid,
|
|
5337
|
-
|
|
5338
|
-
});
|
|
5339
|
-
}
|
|
5340
|
-
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5341
|
-
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5342
|
-
if (hadInstruction) {
|
|
5343
|
-
logger$4.debug("processed_aft_setter_instruction", {
|
|
5344
|
-
envelope_id: envelope.id,
|
|
5345
|
-
source_route: sourceRoute,
|
|
5346
|
-
client_echo: Boolean(token),
|
|
5546
|
+
routing_type: "sid_only",
|
|
5347
5547
|
});
|
|
5548
|
+
return cachedReplica;
|
|
5348
5549
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5550
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5551
|
+
if (association.replicaId === cachedReplica &&
|
|
5552
|
+
!association.isExpired()) {
|
|
5553
|
+
envelope.aft = token;
|
|
5554
|
+
this.metrics.cacheHits += 1;
|
|
5555
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5556
|
+
envelope_id: envelope.id,
|
|
5557
|
+
replica_id: cachedReplica,
|
|
5558
|
+
sid: envelope.sid,
|
|
5559
|
+
routing_type: "sid_cache_with_aft",
|
|
5560
|
+
});
|
|
5561
|
+
return cachedReplica;
|
|
5562
|
+
}
|
|
5354
5563
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5564
|
+
this.metrics.cacheHits += 1;
|
|
5565
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5358
5566
|
envelope_id: envelope.id,
|
|
5567
|
+
replica_id: cachedReplica,
|
|
5568
|
+
sid: envelope.sid,
|
|
5569
|
+
routing_type: "sid_cache_direct",
|
|
5359
5570
|
});
|
|
5571
|
+
return cachedReplica;
|
|
5360
5572
|
}
|
|
5361
|
-
|
|
5362
|
-
else {
|
|
5363
|
-
logger$4.debug("envelope_not_from_downstream", {
|
|
5573
|
+
logger$4.debug("no_cached_replica_for_sid", {
|
|
5364
5574
|
envelope_id: envelope.id,
|
|
5575
|
+
sid: envelope.sid,
|
|
5365
5576
|
});
|
|
5366
5577
|
}
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
}
|
|
5373
|
-
const association = new AFTAssociation(data);
|
|
5374
|
-
this.aftAssociations.set(token, association);
|
|
5375
|
-
while (this.aftAssociations.size > this.cacheMax) {
|
|
5376
|
-
const oldest = this.aftAssociations.keys().next();
|
|
5377
|
-
if (oldest.done) {
|
|
5378
|
-
break;
|
|
5379
|
-
}
|
|
5380
|
-
const oldestToken = oldest.value;
|
|
5381
|
-
this.removeAssociation(oldestToken);
|
|
5382
|
-
}
|
|
5383
|
-
}
|
|
5384
|
-
removeAssociation(token) {
|
|
5385
|
-
this.aftAssociations.delete(token);
|
|
5386
|
-
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5387
|
-
if (cachedToken === token) {
|
|
5388
|
-
this.sidCache.delete(sid);
|
|
5389
|
-
}
|
|
5390
|
-
}
|
|
5391
|
-
}
|
|
5392
|
-
routeByAft(token, envelope) {
|
|
5393
|
-
const association = this.aftAssociations.get(token);
|
|
5394
|
-
if (!association) {
|
|
5395
|
-
return null;
|
|
5396
|
-
}
|
|
5397
|
-
if (association.isExpired()) {
|
|
5398
|
-
this.metrics.associationsExpired += 1;
|
|
5399
|
-
this.removeAssociation(token);
|
|
5400
|
-
return null;
|
|
5401
|
-
}
|
|
5402
|
-
if (this.verifier.securityLevel === exports.StickinessMode.STRICT &&
|
|
5403
|
-
association.isLowTrust()) {
|
|
5404
|
-
logger$4.warning("rejecting_low_trust_association", {
|
|
5578
|
+
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5579
|
+
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5580
|
+
const chosen = segments[index];
|
|
5581
|
+
this.metrics.cacheHits += 1;
|
|
5582
|
+
logger$4.debug("sid_based_deterministic_choice", {
|
|
5405
5583
|
envelope_id: envelope.id,
|
|
5406
|
-
|
|
5407
|
-
|
|
5584
|
+
sid: envelope.sid,
|
|
5585
|
+
chosen,
|
|
5586
|
+
routing_type: "sid_deterministic",
|
|
5408
5587
|
});
|
|
5409
|
-
return
|
|
5588
|
+
return chosen;
|
|
5410
5589
|
}
|
|
5411
|
-
this.
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
if (!envelope.meta) {
|
|
5590
|
+
this.metrics.cacheMisses += 1;
|
|
5591
|
+
logger$4.debug("no_stickiness_routing", {
|
|
5592
|
+
envelope_id: envelope.id,
|
|
5593
|
+
has_aft: Boolean(envelope.aft),
|
|
5594
|
+
has_sid: Boolean(envelope.sid),
|
|
5595
|
+
});
|
|
5418
5596
|
return null;
|
|
5419
5597
|
}
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
const
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
return 0;
|
|
5436
|
-
}
|
|
5437
|
-
let hash = 0;
|
|
5438
|
-
for (let i = 0; i < key.length; i += 1) {
|
|
5439
|
-
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5440
|
-
}
|
|
5441
|
-
return hash % modulo;
|
|
5442
|
-
}
|
|
5443
|
-
|
|
5444
|
-
const FACTORY_META$6 = {
|
|
5445
|
-
base: runtime.LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5446
|
-
key: "AFTLoadBalancerStickinessManager",
|
|
5447
|
-
};
|
|
5448
|
-
const DEFAULT_VALUES$1 = {
|
|
5449
|
-
enabled: true,
|
|
5450
|
-
clientEcho: false,
|
|
5451
|
-
defaultTtlSec: 30,
|
|
5452
|
-
cacheMax: 100000,
|
|
5453
|
-
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
5454
|
-
maxTtlSec: 7200,
|
|
5455
|
-
};
|
|
5456
|
-
function toBoolean(value, fallback) {
|
|
5457
|
-
return typeof value === "boolean" ? value : fallback;
|
|
5458
|
-
}
|
|
5459
|
-
function toNumber(value, fallback) {
|
|
5460
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5461
|
-
return value;
|
|
5462
|
-
}
|
|
5463
|
-
return fallback;
|
|
5464
|
-
}
|
|
5465
|
-
function normalizeConfig$4(config) {
|
|
5466
|
-
const record = (config ?? {});
|
|
5467
|
-
const normalizedSecurity = record.securityLevel
|
|
5468
|
-
? normalizeStickinessMode(record.securityLevel)
|
|
5469
|
-
: DEFAULT_VALUES$1.securityLevel;
|
|
5470
|
-
return {
|
|
5471
|
-
...record,
|
|
5472
|
-
type: "AFTLoadBalancerStickinessManager",
|
|
5473
|
-
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5474
|
-
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5475
|
-
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5476
|
-
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5477
|
-
securityLevel: normalizedSecurity,
|
|
5478
|
-
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5479
|
-
};
|
|
5480
|
-
}
|
|
5481
|
-
class AFTLoadBalancerStickinessManagerFactory extends runtime.LoadBalancerStickinessManagerFactory {
|
|
5482
|
-
constructor() {
|
|
5483
|
-
super(...arguments);
|
|
5484
|
-
this.type = "AFTLoadBalancerStickinessManager";
|
|
5485
|
-
this.isDefault = false;
|
|
5486
|
-
}
|
|
5487
|
-
async create(config, keyProvider, verifier) {
|
|
5488
|
-
const resolvedConfig = normalizeConfig$4(config);
|
|
5489
|
-
let effectiveVerifier = verifier ?? null;
|
|
5490
|
-
if (!effectiveVerifier && keyProvider) {
|
|
5491
|
-
effectiveVerifier = createAftVerifier({
|
|
5492
|
-
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5493
|
-
keyProvider,
|
|
5494
|
-
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5598
|
+
cleanupExpiredAssociations() {
|
|
5599
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5600
|
+
const expiredTokens = [];
|
|
5601
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5602
|
+
if (association.isExpired(now)) {
|
|
5603
|
+
expiredTokens.push(token);
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
for (const token of expiredTokens) {
|
|
5607
|
+
this.removeAssociation(token);
|
|
5608
|
+
}
|
|
5609
|
+
if (expiredTokens.length > 0) {
|
|
5610
|
+
this.metrics.associationsExpired += expiredTokens.length;
|
|
5611
|
+
logger$4.debug("cleaned_expired_associations", {
|
|
5612
|
+
count: expiredTokens.length,
|
|
5495
5613
|
});
|
|
5496
5614
|
}
|
|
5497
|
-
|
|
5498
|
-
|
|
5615
|
+
}
|
|
5616
|
+
replicaLeft(replicaId) {
|
|
5617
|
+
const tokensToRemove = [];
|
|
5618
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5619
|
+
if (association.replicaId === replicaId) {
|
|
5620
|
+
tokensToRemove.push(token);
|
|
5621
|
+
}
|
|
5622
|
+
}
|
|
5623
|
+
for (const token of tokensToRemove) {
|
|
5624
|
+
this.removeAssociation(token);
|
|
5625
|
+
}
|
|
5626
|
+
if (tokensToRemove.length > 0) {
|
|
5627
|
+
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5628
|
+
replica_id: replicaId,
|
|
5629
|
+
count: tokensToRemove.length,
|
|
5630
|
+
});
|
|
5499
5631
|
}
|
|
5500
|
-
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5501
5632
|
}
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
__proto__: null,
|
|
5506
|
-
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5507
|
-
FACTORY_META: FACTORY_META$6,
|
|
5508
|
-
default: AFTLoadBalancerStickinessManagerFactory
|
|
5509
|
-
});
|
|
5510
|
-
|
|
5511
|
-
const logger$3 = runtime.getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5512
|
-
function isStickinessRequired(context) {
|
|
5513
|
-
if (typeof context.stickinessRequired === "boolean") {
|
|
5514
|
-
return context.stickinessRequired;
|
|
5633
|
+
handleReplicaLeft(replicaId) {
|
|
5634
|
+
this.replicaLeft(replicaId);
|
|
5635
|
+
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5515
5636
|
}
|
|
5516
|
-
|
|
5517
|
-
return
|
|
5637
|
+
getMetrics() {
|
|
5638
|
+
return {
|
|
5639
|
+
...this.metrics,
|
|
5640
|
+
cacheSize: this.aftAssociations.size,
|
|
5641
|
+
sidCacheSize: this.sidCache.size,
|
|
5642
|
+
};
|
|
5518
5643
|
}
|
|
5519
|
-
|
|
5520
|
-
}
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
helper_type: this.aftHelper.signer.constructor.name,
|
|
5533
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5534
|
-
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5535
|
-
});
|
|
5536
|
-
}
|
|
5537
|
-
else {
|
|
5538
|
-
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5539
|
-
security_level: this.securityLevel,
|
|
5540
|
-
max_ttl_sec: this.maxTtlSec,
|
|
5541
|
-
});
|
|
5644
|
+
getAssociations() {
|
|
5645
|
+
const result = {};
|
|
5646
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5647
|
+
result[token] = {
|
|
5648
|
+
replica_id: association.replicaId,
|
|
5649
|
+
sid: association.sid,
|
|
5650
|
+
client_sid: association.clientSid,
|
|
5651
|
+
exp: association.exp,
|
|
5652
|
+
trust_level: association.trustLevel,
|
|
5653
|
+
scope: association.scope,
|
|
5654
|
+
created_at: association.createdAt,
|
|
5655
|
+
expired: association.isExpired(),
|
|
5656
|
+
};
|
|
5542
5657
|
}
|
|
5658
|
+
return result;
|
|
5543
5659
|
}
|
|
5544
|
-
|
|
5545
|
-
return
|
|
5660
|
+
getStickinessMetrics() {
|
|
5661
|
+
return this.getMetrics();
|
|
5546
5662
|
}
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5663
|
+
logMetrics() {
|
|
5664
|
+
const hits = this.metrics.cacheHits;
|
|
5665
|
+
const misses = this.metrics.cacheMisses;
|
|
5666
|
+
const total = hits + misses;
|
|
5667
|
+
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5668
|
+
logger$4.info("stickiness_metrics_report", {
|
|
5669
|
+
enabled: this.config.enabled,
|
|
5670
|
+
security_level: this.config.securityLevel,
|
|
5671
|
+
cache_hits: hits,
|
|
5672
|
+
cache_misses: misses,
|
|
5673
|
+
verify_failures: this.metrics.verifyFailures,
|
|
5674
|
+
associations_created: this.metrics.associationsCreated,
|
|
5675
|
+
associations_expired: this.metrics.associationsExpired,
|
|
5676
|
+
active_associations: this.aftAssociations.size,
|
|
5677
|
+
sid_cache_entries: this.sidCache.size,
|
|
5678
|
+
hit_rate: hitRate,
|
|
5553
5679
|
});
|
|
5554
5680
|
}
|
|
5555
|
-
async
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
if (negotiated.enabled === false ||
|
|
5574
|
-
(negotiated.mode !== null &&
|
|
5575
|
-
negotiated.mode !== undefined &&
|
|
5576
|
-
negotiated.mode !== "aft")) {
|
|
5577
|
-
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5681
|
+
async onDeliver(_node, envelope, context) {
|
|
5682
|
+
logger$4.debug("stickiness_manager_on_deliver", {
|
|
5683
|
+
envelope_id: envelope.id,
|
|
5684
|
+
origin_type: context?.originType ?? "unknown",
|
|
5685
|
+
from_system_id: context?.fromSystemId ?? null,
|
|
5686
|
+
});
|
|
5687
|
+
if (context?.originType === core.DeliveryOriginType.DOWNSTREAM) {
|
|
5688
|
+
const sourceRoute = context.fromSystemId;
|
|
5689
|
+
if (sourceRoute) {
|
|
5690
|
+
logger$4.debug("processing_downstream_envelope", {
|
|
5691
|
+
envelope_id: envelope.id,
|
|
5692
|
+
source_route: sourceRoute,
|
|
5693
|
+
});
|
|
5694
|
+
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY &&
|
|
5695
|
+
envelope.sid &&
|
|
5696
|
+
!this.sidCache.has(envelope.sid)) {
|
|
5697
|
+
this.sidCache.set(envelope.sid, sourceRoute);
|
|
5698
|
+
logger$4.debug("sid_only_association_recorded", {
|
|
5578
5699
|
envelope_id: envelope.id,
|
|
5579
|
-
|
|
5580
|
-
|
|
5700
|
+
sid: envelope.sid,
|
|
5701
|
+
replica_id: sourceRoute,
|
|
5702
|
+
});
|
|
5703
|
+
}
|
|
5704
|
+
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5705
|
+
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5706
|
+
if (hadInstruction) {
|
|
5707
|
+
logger$4.debug("processed_aft_setter_instruction", {
|
|
5708
|
+
envelope_id: envelope.id,
|
|
5709
|
+
source_route: sourceRoute,
|
|
5710
|
+
client_echo: Boolean(token),
|
|
5711
|
+
});
|
|
5712
|
+
}
|
|
5713
|
+
else {
|
|
5714
|
+
logger$4.debug("no_aft_setter_instruction", {
|
|
5715
|
+
envelope_id: envelope.id,
|
|
5716
|
+
source_route: sourceRoute,
|
|
5581
5717
|
});
|
|
5582
|
-
return envelope;
|
|
5583
5718
|
}
|
|
5584
|
-
}
|
|
5585
|
-
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5586
|
-
envelope_id: envelope.id,
|
|
5587
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5588
|
-
delivery_origin: context.originType ?? null,
|
|
5589
|
-
});
|
|
5590
|
-
const success = await helper.requestStickiness(envelope, {
|
|
5591
|
-
ttlSec: null,
|
|
5592
|
-
scope: "node",
|
|
5593
|
-
context: stickinessContext,
|
|
5594
|
-
});
|
|
5595
|
-
if (success) {
|
|
5596
|
-
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5597
|
-
envelope_id: envelope.id,
|
|
5598
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5599
|
-
delivery_origin: context.originType ?? null,
|
|
5600
|
-
});
|
|
5601
5719
|
}
|
|
5602
5720
|
else {
|
|
5603
|
-
logger$
|
|
5721
|
+
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5604
5722
|
envelope_id: envelope.id,
|
|
5605
|
-
delivery_origin: context.originType ?? null,
|
|
5606
|
-
reason: "helper_returned_false",
|
|
5607
5723
|
});
|
|
5608
5724
|
}
|
|
5609
5725
|
}
|
|
5610
|
-
return envelope;
|
|
5611
|
-
}
|
|
5612
|
-
async onNodeStarted(node) {
|
|
5613
|
-
if (!this.isInitialized) {
|
|
5614
|
-
await this.initializeAftHelper(node);
|
|
5615
|
-
return;
|
|
5616
|
-
}
|
|
5617
|
-
if (this.aftHelper && node.sid) {
|
|
5618
|
-
this.updateNodeSid(node.sid);
|
|
5619
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5620
|
-
node_id: node.id ?? "unknown",
|
|
5621
|
-
node_sid: node.sid,
|
|
5622
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5623
|
-
});
|
|
5624
|
-
}
|
|
5625
|
-
else if (!node.sid) {
|
|
5626
|
-
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
5627
|
-
node_id: node.id ?? "unknown",
|
|
5628
|
-
});
|
|
5629
|
-
}
|
|
5630
5726
|
else {
|
|
5631
|
-
logger$
|
|
5632
|
-
|
|
5727
|
+
logger$4.debug("envelope_not_from_downstream", {
|
|
5728
|
+
envelope_id: envelope.id,
|
|
5633
5729
|
});
|
|
5634
5730
|
}
|
|
5731
|
+
return envelope;
|
|
5635
5732
|
}
|
|
5636
|
-
|
|
5637
|
-
if (this.
|
|
5638
|
-
this.
|
|
5639
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5640
|
-
new_sid: nodeSid,
|
|
5641
|
-
});
|
|
5733
|
+
storeAssociation(token, data) {
|
|
5734
|
+
if (this.aftAssociations.has(token)) {
|
|
5735
|
+
this.aftAssociations.delete(token);
|
|
5642
5736
|
}
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
}
|
|
5650
|
-
|
|
5737
|
+
const association = new AFTAssociation(data);
|
|
5738
|
+
this.aftAssociations.set(token, association);
|
|
5739
|
+
while (this.aftAssociations.size > this.cacheMax) {
|
|
5740
|
+
const oldest = this.aftAssociations.keys().next();
|
|
5741
|
+
if (oldest.done) {
|
|
5742
|
+
break;
|
|
5743
|
+
}
|
|
5744
|
+
const oldestToken = oldest.value;
|
|
5745
|
+
this.removeAssociation(oldestToken);
|
|
5651
5746
|
}
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5747
|
+
}
|
|
5748
|
+
removeAssociation(token) {
|
|
5749
|
+
this.aftAssociations.delete(token);
|
|
5750
|
+
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5751
|
+
if (cachedToken === token) {
|
|
5752
|
+
this.sidCache.delete(sid);
|
|
5753
|
+
}
|
|
5658
5754
|
}
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
? cryptoProvider.signingPrivatePem
|
|
5665
|
-
: null;
|
|
5666
|
-
if (this.securityLevel === exports.StickinessMode.STRICT && !privateKeyPem) {
|
|
5667
|
-
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
5668
|
-
node_id: node.id ?? "unknown",
|
|
5669
|
-
error: "Missing signing private key for strict security level",
|
|
5670
|
-
});
|
|
5671
|
-
return;
|
|
5755
|
+
}
|
|
5756
|
+
routeByAft(token, envelope) {
|
|
5757
|
+
const association = this.aftAssociations.get(token);
|
|
5758
|
+
if (!association) {
|
|
5759
|
+
return null;
|
|
5672
5760
|
}
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
kid: keyId,
|
|
5678
|
-
privateKeyPem,
|
|
5679
|
-
maxTtlSec: this.maxTtlSec,
|
|
5680
|
-
});
|
|
5681
|
-
this.aftHelper = helper;
|
|
5682
|
-
this.isInitialized = true;
|
|
5683
|
-
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5684
|
-
node_id: node.id ?? "unknown",
|
|
5685
|
-
node_sid: nodeSid,
|
|
5686
|
-
key_id: keyId,
|
|
5687
|
-
security_level: helper.signer.securityLevel,
|
|
5688
|
-
});
|
|
5761
|
+
if (association.isExpired()) {
|
|
5762
|
+
this.metrics.associationsExpired += 1;
|
|
5763
|
+
this.removeAssociation(token);
|
|
5764
|
+
return null;
|
|
5689
5765
|
}
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5766
|
+
if (this.verifier.securityLevel === exports.StickinessMode.STRICT &&
|
|
5767
|
+
association.isLowTrust()) {
|
|
5768
|
+
logger$4.warning("rejecting_low_trust_association", {
|
|
5769
|
+
envelope_id: envelope.id,
|
|
5770
|
+
replica_id: association.replicaId,
|
|
5771
|
+
reason: "strict mode rejects low-trust associations",
|
|
5694
5772
|
});
|
|
5773
|
+
return null;
|
|
5695
5774
|
}
|
|
5775
|
+
this.aftAssociations.delete(token);
|
|
5776
|
+
this.aftAssociations.set(token, association);
|
|
5777
|
+
return association.replicaId;
|
|
5696
5778
|
}
|
|
5697
|
-
|
|
5698
|
-
|
|
5779
|
+
}
|
|
5780
|
+
function extractAftInstruction(envelope) {
|
|
5781
|
+
if (!envelope.meta) {
|
|
5782
|
+
return null;
|
|
5699
5783
|
}
|
|
5700
|
-
|
|
5701
|
-
|
|
5784
|
+
const meta = envelope.meta;
|
|
5785
|
+
const nested = meta.set;
|
|
5786
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
5787
|
+
const aftValue = nested.aft;
|
|
5788
|
+
if (aftValue !== undefined) {
|
|
5789
|
+
return aftValue;
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5792
|
+
if (meta["set.aft"] !== undefined) {
|
|
5793
|
+
return meta["set.aft"];
|
|
5702
5794
|
}
|
|
5795
|
+
return null;
|
|
5703
5796
|
}
|
|
5704
|
-
function
|
|
5705
|
-
|
|
5797
|
+
function computeDeterministicIndex(key, modulo) {
|
|
5798
|
+
if (modulo <= 0) {
|
|
5799
|
+
return 0;
|
|
5800
|
+
}
|
|
5801
|
+
let hash = 0;
|
|
5802
|
+
for (let i = 0; i < key.length; i += 1) {
|
|
5803
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5804
|
+
}
|
|
5805
|
+
return hash % modulo;
|
|
5706
5806
|
}
|
|
5707
5807
|
|
|
5708
|
-
const FACTORY_META$
|
|
5709
|
-
base: runtime.
|
|
5710
|
-
key: "
|
|
5808
|
+
const FACTORY_META$6 = {
|
|
5809
|
+
base: runtime.LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5810
|
+
key: "AFTLoadBalancerStickinessManager",
|
|
5711
5811
|
};
|
|
5712
|
-
const DEFAULT_VALUES = {
|
|
5812
|
+
const DEFAULT_VALUES$1 = {
|
|
5813
|
+
enabled: true,
|
|
5814
|
+
clientEcho: false,
|
|
5815
|
+
defaultTtlSec: 30,
|
|
5816
|
+
cacheMax: 100000,
|
|
5713
5817
|
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
5714
5818
|
maxTtlSec: 7200,
|
|
5715
5819
|
};
|
|
5716
|
-
function
|
|
5820
|
+
function toBoolean(value, fallback) {
|
|
5821
|
+
return typeof value === "boolean" ? value : fallback;
|
|
5822
|
+
}
|
|
5823
|
+
function toNumber(value, fallback) {
|
|
5824
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5825
|
+
return value;
|
|
5826
|
+
}
|
|
5827
|
+
return fallback;
|
|
5828
|
+
}
|
|
5829
|
+
function normalizeConfig$4(config) {
|
|
5717
5830
|
const record = (config ?? {});
|
|
5718
5831
|
const normalizedSecurity = record.securityLevel
|
|
5719
5832
|
? normalizeStickinessMode(record.securityLevel)
|
|
5720
|
-
: DEFAULT_VALUES.securityLevel;
|
|
5721
|
-
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
5722
|
-
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
5723
|
-
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
5724
|
-
: DEFAULT_VALUES.maxTtlSec;
|
|
5833
|
+
: DEFAULT_VALUES$1.securityLevel;
|
|
5725
5834
|
return {
|
|
5726
5835
|
...record,
|
|
5727
|
-
type: "
|
|
5728
|
-
|
|
5729
|
-
|
|
5836
|
+
type: "AFTLoadBalancerStickinessManager",
|
|
5837
|
+
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5838
|
+
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5839
|
+
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5840
|
+
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5841
|
+
securityLevel: normalizedSecurity,
|
|
5842
|
+
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5730
5843
|
};
|
|
5731
5844
|
}
|
|
5732
|
-
class
|
|
5845
|
+
class AFTLoadBalancerStickinessManagerFactory extends runtime.LoadBalancerStickinessManagerFactory {
|
|
5733
5846
|
constructor() {
|
|
5734
5847
|
super(...arguments);
|
|
5735
|
-
this.type =
|
|
5736
|
-
this.isDefault =
|
|
5848
|
+
this.type = "AFTLoadBalancerStickinessManager";
|
|
5849
|
+
this.isDefault = false;
|
|
5737
5850
|
}
|
|
5738
|
-
async create(config,
|
|
5739
|
-
const resolvedConfig = normalizeConfig$
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
}
|
|
5851
|
+
async create(config, keyProvider, verifier) {
|
|
5852
|
+
const resolvedConfig = normalizeConfig$4(config);
|
|
5853
|
+
let effectiveVerifier = verifier ?? null;
|
|
5854
|
+
if (!effectiveVerifier && keyProvider) {
|
|
5855
|
+
effectiveVerifier = createAftVerifier({
|
|
5856
|
+
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5857
|
+
keyProvider,
|
|
5858
|
+
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5859
|
+
});
|
|
5860
|
+
}
|
|
5861
|
+
if (!effectiveVerifier) {
|
|
5862
|
+
throw new Error("AFTLoadBalancerStickinessManagerFactory requires an AFT verifier or key provider");
|
|
5863
|
+
}
|
|
5864
|
+
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5751
5865
|
}
|
|
5752
5866
|
}
|
|
5753
5867
|
|
|
5754
|
-
var
|
|
5868
|
+
var aftLoadBalancerStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
5755
5869
|
__proto__: null,
|
|
5756
|
-
|
|
5757
|
-
FACTORY_META: FACTORY_META$
|
|
5758
|
-
default:
|
|
5870
|
+
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5871
|
+
FACTORY_META: FACTORY_META$6,
|
|
5872
|
+
default: AFTLoadBalancerStickinessManagerFactory
|
|
5759
5873
|
});
|
|
5760
5874
|
|
|
5761
|
-
const logger$
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
5766
|
-
function nowUtc() {
|
|
5767
|
-
return new Date();
|
|
5768
|
-
}
|
|
5769
|
-
function formatTimestampForConsole() {
|
|
5770
|
-
return runtime.color(runtime.formatTimestamp(), runtime.AnsiColor.GRAY);
|
|
5771
|
-
}
|
|
5772
|
-
function prettyModel(value) {
|
|
5773
|
-
try {
|
|
5774
|
-
return runtime.jsonDumps(value);
|
|
5775
|
-
}
|
|
5776
|
-
catch (error) {
|
|
5777
|
-
return String(error);
|
|
5778
|
-
}
|
|
5779
|
-
}
|
|
5780
|
-
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
5781
|
-
if (!metadata) {
|
|
5782
|
-
return undefined;
|
|
5783
|
-
}
|
|
5784
|
-
const record = metadata;
|
|
5785
|
-
if (record[camelCaseKey] !== undefined) {
|
|
5786
|
-
return record[camelCaseKey];
|
|
5875
|
+
const logger$3 = runtime.getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5876
|
+
function isStickinessRequired(context) {
|
|
5877
|
+
if (typeof context.stickinessRequired === "boolean") {
|
|
5878
|
+
return context.stickinessRequired;
|
|
5787
5879
|
}
|
|
5788
|
-
if (
|
|
5789
|
-
return
|
|
5880
|
+
if (typeof context.stickiness_required === "boolean") {
|
|
5881
|
+
return context.stickiness_required;
|
|
5790
5882
|
}
|
|
5791
|
-
return
|
|
5883
|
+
return false;
|
|
5792
5884
|
}
|
|
5793
|
-
class
|
|
5794
|
-
constructor(options) {
|
|
5795
|
-
|
|
5796
|
-
this.
|
|
5797
|
-
|
|
5798
|
-
this.
|
|
5799
|
-
this.
|
|
5800
|
-
this.
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : core.generateId();
|
|
5815
|
-
const wasAssigned = trimmedSystemId.length === 0;
|
|
5816
|
-
const normalizedHello = {
|
|
5817
|
-
...hello,
|
|
5818
|
-
systemId,
|
|
5819
|
-
};
|
|
5820
|
-
if (showEnvelopes) {
|
|
5821
|
-
// eslint-disable-next-line no-console
|
|
5822
|
-
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Received envelope 📨", runtime.AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
5885
|
+
class AFTReplicaStickinessManager extends runtime.BaseNodeEventListener {
|
|
5886
|
+
constructor(options = {}) {
|
|
5887
|
+
super();
|
|
5888
|
+
this.securityLevel =
|
|
5889
|
+
normalizeStickinessMode(options.securityLevel ?? DEFAULT_STICKINESS_SECURITY_LEVEL) ?? DEFAULT_STICKINESS_SECURITY_LEVEL;
|
|
5890
|
+
this.aftHelper = options.aftHelper ?? null;
|
|
5891
|
+
this.maxTtlSec = options.maxTtlSec ?? 7200;
|
|
5892
|
+
this.isInitialized = this.aftHelper !== null;
|
|
5893
|
+
this.negotiatedStickiness = null;
|
|
5894
|
+
if (this.aftHelper) {
|
|
5895
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5896
|
+
helper_type: this.aftHelper.signer.constructor.name,
|
|
5897
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5898
|
+
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5899
|
+
});
|
|
5900
|
+
}
|
|
5901
|
+
else {
|
|
5902
|
+
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5903
|
+
security_level: this.securityLevel,
|
|
5904
|
+
max_ttl_sec: this.maxTtlSec,
|
|
5905
|
+
});
|
|
5823
5906
|
}
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5907
|
+
}
|
|
5908
|
+
offer() {
|
|
5909
|
+
return { mode: "aft", supportedModes: ["aft", "attr"], version: 1 };
|
|
5910
|
+
}
|
|
5911
|
+
accept(stickiness) {
|
|
5912
|
+
this.negotiatedStickiness = stickiness ?? null;
|
|
5913
|
+
logger$3.debug("replica_stickiness_policy_set", {
|
|
5914
|
+
enabled: stickiness?.enabled ?? null,
|
|
5915
|
+
mode: stickiness?.mode ?? null,
|
|
5916
|
+
ttl: stickiness?.ttlSec ?? null,
|
|
5830
5917
|
});
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
if (
|
|
5834
|
-
|
|
5835
|
-
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
5836
|
-
}
|
|
5837
|
-
if (fullMetadata.instance_id === undefined) {
|
|
5838
|
-
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
5839
|
-
}
|
|
5918
|
+
}
|
|
5919
|
+
async onForwardUpstream(_node, envelope, context) {
|
|
5920
|
+
if (!context) {
|
|
5921
|
+
return envelope;
|
|
5840
5922
|
}
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
logicals: normalizedHello.logicals,
|
|
5923
|
+
const helper = this.aftHelper;
|
|
5924
|
+
if (!helper) {
|
|
5925
|
+
logger$3.debug("aft_helper_not_ready_skip_injection", {
|
|
5926
|
+
envelope_id: envelope.id,
|
|
5927
|
+
delivery_origin: context.originType ?? null,
|
|
5928
|
+
reason: "not_initialized",
|
|
5848
5929
|
});
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5930
|
+
return envelope;
|
|
5931
|
+
}
|
|
5932
|
+
const stickinessContext = context;
|
|
5933
|
+
if (isStickinessRequired(stickinessContext) &&
|
|
5934
|
+
context.originType === core.DeliveryOriginType.LOCAL) {
|
|
5935
|
+
if (this.negotiatedStickiness) {
|
|
5936
|
+
const negotiated = this.negotiatedStickiness;
|
|
5937
|
+
if (negotiated.enabled === false ||
|
|
5938
|
+
(negotiated.mode !== null &&
|
|
5939
|
+
negotiated.mode !== undefined &&
|
|
5940
|
+
negotiated.mode !== "aft")) {
|
|
5941
|
+
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5942
|
+
envelope_id: envelope.id,
|
|
5943
|
+
policy_mode: negotiated.mode ?? null,
|
|
5944
|
+
policy_enabled: negotiated.enabled ?? null,
|
|
5945
|
+
});
|
|
5946
|
+
return envelope;
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5950
|
+
envelope_id: envelope.id,
|
|
5951
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5952
|
+
delivery_origin: context.originType ?? null,
|
|
5953
|
+
});
|
|
5954
|
+
const success = await helper.requestStickiness(envelope, {
|
|
5955
|
+
ttlSec: null,
|
|
5956
|
+
scope: "node",
|
|
5957
|
+
context: stickinessContext,
|
|
5958
|
+
});
|
|
5959
|
+
if (success) {
|
|
5960
|
+
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5961
|
+
envelope_id: envelope.id,
|
|
5962
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5963
|
+
delivery_origin: context.originType ?? null,
|
|
5964
|
+
});
|
|
5965
|
+
}
|
|
5966
|
+
else {
|
|
5967
|
+
logger$3.debug("aft_token_not_applied_upstream", {
|
|
5968
|
+
envelope_id: envelope.id,
|
|
5969
|
+
delivery_origin: context.originType ?? null,
|
|
5970
|
+
reason: "helper_returned_false",
|
|
5854
5971
|
});
|
|
5855
|
-
throw new Error(`Invalid logical format: ${pathError}`);
|
|
5856
5972
|
}
|
|
5857
|
-
logger$2.debug("logicals_validation_successful");
|
|
5858
5973
|
}
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5974
|
+
return envelope;
|
|
5975
|
+
}
|
|
5976
|
+
async onNodeStarted(node) {
|
|
5977
|
+
if (!this.isInitialized) {
|
|
5978
|
+
await this.initializeAftHelper(node);
|
|
5979
|
+
return;
|
|
5980
|
+
}
|
|
5981
|
+
if (this.aftHelper && node.sid) {
|
|
5982
|
+
this.updateNodeSid(node.sid);
|
|
5983
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5984
|
+
node_id: node.id ?? "unknown",
|
|
5985
|
+
node_sid: node.sid,
|
|
5986
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5865
5987
|
});
|
|
5866
|
-
throw new Error(placementResult.reason || "Node not accepted");
|
|
5867
5988
|
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
assignedPath,
|
|
5872
|
-
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
5873
|
-
targetSystemId: placementResult.targetSystemId ?? null,
|
|
5874
|
-
});
|
|
5875
|
-
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
5876
|
-
normalizedHello.capabilities ??
|
|
5877
|
-
null;
|
|
5878
|
-
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
5879
|
-
normalizedHello.logicals ??
|
|
5880
|
-
null;
|
|
5881
|
-
logger$2.debug("processing_placement_result_metadata", {
|
|
5882
|
-
acceptedCapabilities,
|
|
5883
|
-
acceptedLogicals,
|
|
5884
|
-
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
5885
|
-
placementResult.metadata !== null,
|
|
5886
|
-
});
|
|
5887
|
-
const connectionGrants = [];
|
|
5888
|
-
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
5889
|
-
fullMetadata.instanceId) ||
|
|
5890
|
-
(typeof fullMetadata.instance_id === "string" &&
|
|
5891
|
-
fullMetadata.instance_id) ||
|
|
5892
|
-
normalizedHello.instanceId ||
|
|
5893
|
-
core.generateId();
|
|
5894
|
-
if (placementResult.targetSystemId) {
|
|
5895
|
-
logger$2.debug("issuing_node_attach_token", {
|
|
5896
|
-
systemId,
|
|
5897
|
-
assignedPath,
|
|
5989
|
+
else if (!node.sid) {
|
|
5990
|
+
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
5991
|
+
node_id: node.id ?? "unknown",
|
|
5898
5992
|
});
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
assigned_path: placementResult.assignedPath,
|
|
5904
|
-
accepted_logicals: acceptedLogicals,
|
|
5905
|
-
instance_id: metadataInstanceId,
|
|
5993
|
+
}
|
|
5994
|
+
else {
|
|
5995
|
+
logger$3.error("aft_replica_stickiness_manager_node_missing_sid", {
|
|
5996
|
+
node_type: node.constructor?.name ?? typeof node,
|
|
5906
5997
|
});
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
? (transportInfo.connectionGrant.type ??
|
|
5915
|
-
"Unknown")
|
|
5916
|
-
: "Unknown",
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
updateNodeSid(nodeSid) {
|
|
6001
|
+
if (this.aftHelper) {
|
|
6002
|
+
this.aftHelper.nodeSid = nodeSid;
|
|
6003
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
6004
|
+
new_sid: nodeSid,
|
|
5917
6005
|
});
|
|
5918
|
-
connectionGrants.push(transportInfo.connectionGrant);
|
|
5919
6006
|
}
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
const
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
6007
|
+
}
|
|
6008
|
+
async initializeAftHelper(node) {
|
|
6009
|
+
const nodeSid = node.sid;
|
|
6010
|
+
if (!nodeSid) {
|
|
6011
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_sid", {
|
|
6012
|
+
node_id: node.id ?? "unknown",
|
|
6013
|
+
});
|
|
6014
|
+
return;
|
|
6015
|
+
}
|
|
6016
|
+
const cryptoProvider = node.cryptoProvider ?? null;
|
|
6017
|
+
if (!cryptoProvider) {
|
|
6018
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_crypto_provider", {
|
|
6019
|
+
node_id: node.id ?? "unknown",
|
|
6020
|
+
});
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
const keyId = typeof cryptoProvider.signatureKeyId === "string" &&
|
|
6024
|
+
cryptoProvider.signatureKeyId.length > 0
|
|
6025
|
+
? cryptoProvider.signatureKeyId
|
|
6026
|
+
: "default-key-id";
|
|
6027
|
+
const privateKeyPem = typeof cryptoProvider.signingPrivatePem === "string"
|
|
6028
|
+
? cryptoProvider.signingPrivatePem
|
|
6029
|
+
: null;
|
|
6030
|
+
if (this.securityLevel === exports.StickinessMode.STRICT && !privateKeyPem) {
|
|
6031
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6032
|
+
node_id: node.id ?? "unknown",
|
|
6033
|
+
error: "Missing signing private key for strict security level",
|
|
6034
|
+
});
|
|
6035
|
+
return;
|
|
6036
|
+
}
|
|
6037
|
+
try {
|
|
6038
|
+
const helper = createAftHelper({
|
|
6039
|
+
securityLevel: this.securityLevel,
|
|
6040
|
+
nodeSid,
|
|
6041
|
+
kid: keyId,
|
|
6042
|
+
privateKeyPem,
|
|
6043
|
+
maxTtlSec: this.maxTtlSec,
|
|
6044
|
+
});
|
|
6045
|
+
this.aftHelper = helper;
|
|
6046
|
+
this.isInitialized = true;
|
|
6047
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
6048
|
+
node_id: node.id ?? "unknown",
|
|
6049
|
+
node_sid: nodeSid,
|
|
6050
|
+
key_id: keyId,
|
|
6051
|
+
security_level: helper.signer.securityLevel,
|
|
6052
|
+
});
|
|
5965
6053
|
}
|
|
5966
|
-
|
|
6054
|
+
catch (error) {
|
|
6055
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6056
|
+
node_id: node.id ?? "unknown",
|
|
6057
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6058
|
+
});
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
get signer() {
|
|
6062
|
+
return this.aftHelper?.signer ?? null;
|
|
6063
|
+
}
|
|
6064
|
+
getHelper() {
|
|
6065
|
+
return this.aftHelper;
|
|
5967
6066
|
}
|
|
5968
6067
|
}
|
|
6068
|
+
function createAftReplicaStickinessManager(aftHelper) {
|
|
6069
|
+
return new AFTReplicaStickinessManager({ aftHelper });
|
|
6070
|
+
}
|
|
5969
6071
|
|
|
5970
|
-
const FACTORY_META$
|
|
5971
|
-
base: runtime.
|
|
5972
|
-
key: "
|
|
5973
|
-
priority: 100,
|
|
5974
|
-
isDefault: true,
|
|
6072
|
+
const FACTORY_META$5 = {
|
|
6073
|
+
base: runtime.REPLICA_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
6074
|
+
key: "AFTReplicaStickinessManager",
|
|
5975
6075
|
};
|
|
5976
|
-
|
|
6076
|
+
const DEFAULT_VALUES = {
|
|
6077
|
+
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
6078
|
+
maxTtlSec: 7200,
|
|
6079
|
+
};
|
|
6080
|
+
function normalizeConfig$3(config) {
|
|
6081
|
+
const record = (config ?? {});
|
|
6082
|
+
const normalizedSecurity = record.securityLevel
|
|
6083
|
+
? normalizeStickinessMode(record.securityLevel)
|
|
6084
|
+
: DEFAULT_VALUES.securityLevel;
|
|
6085
|
+
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
6086
|
+
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
6087
|
+
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
6088
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6089
|
+
return {
|
|
6090
|
+
...record,
|
|
6091
|
+
type: "AFTReplicaStickinessManager",
|
|
6092
|
+
securityLevel,
|
|
6093
|
+
maxTtlSec: maxTtlSecValue,
|
|
6094
|
+
};
|
|
6095
|
+
}
|
|
6096
|
+
class AFTReplicaStickinessManagerFactory extends runtime.ReplicaStickinessManagerFactory {
|
|
5977
6097
|
constructor() {
|
|
5978
6098
|
super(...arguments);
|
|
5979
|
-
this.type = FACTORY_META$
|
|
5980
|
-
this.isDefault =
|
|
5981
|
-
this.priority = FACTORY_META$4.priority;
|
|
5982
|
-
}
|
|
5983
|
-
async create(config, ...factoryArgs) {
|
|
5984
|
-
const normalized = normalizeConfig$2(config);
|
|
5985
|
-
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
5986
|
-
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
5987
|
-
const placementStrategy = await runtime.NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5988
|
-
const transportProvisioner = await runtime.TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5989
|
-
const tokenIssuer = await runtime.TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5990
|
-
let authorizer = null;
|
|
5991
|
-
if (normalized.authorizerConfig) {
|
|
5992
|
-
authorizer =
|
|
5993
|
-
(await runtime.AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
5994
|
-
factoryArgs,
|
|
5995
|
-
})) ?? null;
|
|
5996
|
-
}
|
|
5997
|
-
const options = {
|
|
5998
|
-
placementStrategy,
|
|
5999
|
-
transportProvisioner,
|
|
6000
|
-
tokenIssuer,
|
|
6001
|
-
authorizer,
|
|
6002
|
-
caServiceUrl: normalized.caServiceUrl,
|
|
6003
|
-
};
|
|
6004
|
-
if (normalized.ttlSec !== undefined) {
|
|
6005
|
-
options.ttlSec = normalized.ttlSec;
|
|
6006
|
-
}
|
|
6007
|
-
return new AdvancedWelcomeService(options);
|
|
6008
|
-
}
|
|
6009
|
-
}
|
|
6010
|
-
function normalizeConfig$2(config) {
|
|
6011
|
-
if (!config) {
|
|
6012
|
-
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6013
|
-
}
|
|
6014
|
-
const source = config;
|
|
6015
|
-
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6016
|
-
? source.ttlSec
|
|
6017
|
-
: typeof source.ttl_sec === "number"
|
|
6018
|
-
? source.ttl_sec
|
|
6019
|
-
: undefined;
|
|
6020
|
-
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6021
|
-
source.caServiceUrl.trim().length > 0
|
|
6022
|
-
? source.caServiceUrl.trim()
|
|
6023
|
-
: typeof source.ca_service_url === "string" &&
|
|
6024
|
-
source.ca_service_url.trim().length > 0
|
|
6025
|
-
? source.ca_service_url.trim()
|
|
6026
|
-
: undefined;
|
|
6027
|
-
if (!caServiceUrlCandidate) {
|
|
6028
|
-
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6029
|
-
}
|
|
6030
|
-
const normalized = {
|
|
6031
|
-
caServiceUrl: caServiceUrlCandidate,
|
|
6032
|
-
};
|
|
6033
|
-
if (source.placement !== undefined) {
|
|
6034
|
-
normalized.placementConfig =
|
|
6035
|
-
source.placement ?? null;
|
|
6036
|
-
}
|
|
6037
|
-
if (source.transport !== undefined) {
|
|
6038
|
-
normalized.transportConfig =
|
|
6039
|
-
source.transport ?? null;
|
|
6040
|
-
}
|
|
6041
|
-
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6042
|
-
? source.tokenIssuer
|
|
6043
|
-
: source.token_issuer !== undefined
|
|
6044
|
-
? source.token_issuer
|
|
6045
|
-
: undefined;
|
|
6046
|
-
if (tokenIssuerConfig !== undefined) {
|
|
6047
|
-
normalized.tokenIssuerConfig =
|
|
6048
|
-
tokenIssuerConfig ?? null;
|
|
6049
|
-
}
|
|
6050
|
-
if (source.authorizer !== undefined) {
|
|
6051
|
-
normalized.authorizerConfig =
|
|
6052
|
-
source.authorizer ?? null;
|
|
6099
|
+
this.type = FACTORY_META$5.key;
|
|
6100
|
+
this.isDefault = true;
|
|
6053
6101
|
}
|
|
6054
|
-
|
|
6055
|
-
|
|
6102
|
+
async create(config, dependencies) {
|
|
6103
|
+
const resolvedConfig = normalizeConfig$3(config);
|
|
6104
|
+
const helper = dependencies?.aftHelper ?? null;
|
|
6105
|
+
const securityLevel = normalizeStickinessMode(resolvedConfig.securityLevel ?? DEFAULT_VALUES.securityLevel) ?? DEFAULT_VALUES.securityLevel;
|
|
6106
|
+
const maxTtlSec = typeof resolvedConfig.maxTtlSec === "number" &&
|
|
6107
|
+
Number.isFinite(resolvedConfig.maxTtlSec)
|
|
6108
|
+
? Math.max(0, Math.floor(resolvedConfig.maxTtlSec))
|
|
6109
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6110
|
+
return new AFTReplicaStickinessManager({
|
|
6111
|
+
securityLevel,
|
|
6112
|
+
maxTtlSec,
|
|
6113
|
+
aftHelper: helper,
|
|
6114
|
+
});
|
|
6056
6115
|
}
|
|
6057
|
-
return normalized;
|
|
6058
6116
|
}
|
|
6059
6117
|
|
|
6060
|
-
var
|
|
6118
|
+
var aftReplicaStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
6061
6119
|
__proto__: null,
|
|
6062
|
-
|
|
6063
|
-
FACTORY_META: FACTORY_META$
|
|
6064
|
-
default:
|
|
6120
|
+
AFTReplicaStickinessManagerFactory: AFTReplicaStickinessManagerFactory,
|
|
6121
|
+
FACTORY_META: FACTORY_META$5,
|
|
6122
|
+
default: AFTReplicaStickinessManagerFactory
|
|
6065
6123
|
});
|
|
6066
6124
|
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
"./security/cert/default-ca-service-factory.js",
|
|
6075
|
-
"./security/cert/default-certificate-manager-factory.js",
|
|
6076
|
-
"./security/cert/trust-store/browser-trust-store-provider-factory.js",
|
|
6077
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
6078
|
-
"./security/encryption/channel/channel-encryption-manager-factory.js",
|
|
6079
|
-
"./security/encryption/composite-encryption-manager-factory.js",
|
|
6080
|
-
"./security/encryption/default-secure-channel-manager-factory.js",
|
|
6081
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
6082
|
-
"./security/keys/x5c-key-manager-factory.js",
|
|
6083
|
-
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
6084
|
-
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
6085
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
6086
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
6087
|
-
"./welcome/advanced-welcome-service-factory.js"
|
|
6088
|
-
];
|
|
6089
|
-
const MODULE_LOADERS = {
|
|
6090
|
-
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
6091
|
-
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
6092
|
-
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
6093
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
6094
|
-
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
6095
|
-
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
6096
|
-
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
6097
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
6098
|
-
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
6099
|
-
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
6100
|
-
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
6101
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
6102
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
6103
|
-
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
6104
|
-
};
|
|
6105
|
-
|
|
6106
|
-
const SECURITY_PREFIX = "./security/";
|
|
6107
|
-
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
6108
|
-
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
6109
|
-
const NODE_ONLY_MODULES = new Set([
|
|
6110
|
-
"./security/cert/default-ca-service-factory.js",
|
|
6111
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
6112
|
-
]);
|
|
6113
|
-
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
6114
|
-
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
6115
|
-
function detectModuleUrl() {
|
|
6116
|
-
if (typeof __filename === "string") {
|
|
6117
|
-
try {
|
|
6118
|
-
return __filename.startsWith("file://")
|
|
6119
|
-
? __filename
|
|
6120
|
-
: `file://${__filename}`;
|
|
6121
|
-
}
|
|
6122
|
-
catch {
|
|
6123
|
-
// fall through to stack parsing
|
|
6124
|
-
}
|
|
6125
|
-
}
|
|
6126
|
-
try {
|
|
6127
|
-
throw new Error();
|
|
6128
|
-
}
|
|
6129
|
-
catch (error) {
|
|
6130
|
-
const stack = typeof error === "object" && error && "stack" in error
|
|
6131
|
-
? String(error.stack ?? "")
|
|
6132
|
-
: "";
|
|
6133
|
-
const lines = stack.split("\n");
|
|
6134
|
-
for (const line of lines) {
|
|
6135
|
-
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
6136
|
-
const candidate = match?.[1];
|
|
6137
|
-
if (!candidate) {
|
|
6138
|
-
continue;
|
|
6139
|
-
}
|
|
6140
|
-
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
6141
|
-
return candidate;
|
|
6142
|
-
}
|
|
6143
|
-
if (candidate.startsWith("file://")) {
|
|
6144
|
-
return candidate;
|
|
6145
|
-
}
|
|
6146
|
-
return `file://${candidate}`;
|
|
6147
|
-
}
|
|
6148
|
-
}
|
|
6149
|
-
return null;
|
|
6125
|
+
const logger$2 = runtime.getLogger("naylence.fame.welcome.advanced_welcome_service");
|
|
6126
|
+
const ENV_VAR_SHOW_ENVELOPES = "FAME_SHOW_ENVELOPES";
|
|
6127
|
+
const DEFAULT_TTL_SEC = 3600;
|
|
6128
|
+
const showEnvelopes = typeof process !== "undefined" &&
|
|
6129
|
+
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
6130
|
+
function nowUtc() {
|
|
6131
|
+
return new Date();
|
|
6150
6132
|
}
|
|
6151
|
-
function
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
const distMarker = "/dist/";
|
|
6158
|
-
if (sanitized.includes(esmMarker)) {
|
|
6159
|
-
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
6160
|
-
}
|
|
6161
|
-
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
6162
|
-
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
6133
|
+
function formatTimestampForConsole() {
|
|
6134
|
+
return runtime.color(runtime.formatTimestamp(), runtime.AnsiColor.GRAY);
|
|
6135
|
+
}
|
|
6136
|
+
function prettyModel(value) {
|
|
6137
|
+
try {
|
|
6138
|
+
return runtime.jsonDumps(value);
|
|
6163
6139
|
}
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
6140
|
+
catch (error) {
|
|
6141
|
+
return String(error);
|
|
6167
6142
|
}
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
return
|
|
6143
|
+
}
|
|
6144
|
+
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
6145
|
+
if (!metadata) {
|
|
6146
|
+
return undefined;
|
|
6172
6147
|
}
|
|
6173
|
-
const
|
|
6174
|
-
if (
|
|
6175
|
-
|
|
6176
|
-
const projectRoot = sanitized.slice(0, index);
|
|
6177
|
-
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
6148
|
+
const record = metadata;
|
|
6149
|
+
if (record[camelCaseKey] !== undefined) {
|
|
6150
|
+
return record[camelCaseKey];
|
|
6178
6151
|
}
|
|
6179
|
-
if (
|
|
6180
|
-
|
|
6181
|
-
const parsed = new URL(rawUrl);
|
|
6182
|
-
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
6183
|
-
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
6184
|
-
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
6185
|
-
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
6186
|
-
}
|
|
6187
|
-
}
|
|
6188
|
-
catch {
|
|
6189
|
-
// ignore
|
|
6190
|
-
}
|
|
6152
|
+
if (record[snakeCaseKey] !== undefined) {
|
|
6153
|
+
return record[snakeCaseKey];
|
|
6191
6154
|
}
|
|
6192
|
-
return
|
|
6155
|
+
return undefined;
|
|
6193
6156
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6157
|
+
class AdvancedWelcomeService {
|
|
6158
|
+
constructor(options) {
|
|
6159
|
+
this.placementStrategy = options.placementStrategy;
|
|
6160
|
+
this.transportProvisioner = options.transportProvisioner;
|
|
6161
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
6162
|
+
this.authorizer = options.authorizer ?? null;
|
|
6163
|
+
this.caServiceUrl = options.caServiceUrl;
|
|
6164
|
+
this.ttlSec =
|
|
6165
|
+
typeof options.ttlSec === "number" && Number.isFinite(options.ttlSec)
|
|
6166
|
+
? Math.max(0, options.ttlSec)
|
|
6167
|
+
: DEFAULT_TTL_SEC;
|
|
6168
|
+
logger$2.debug("initialized_advanced_welcome_service", {
|
|
6169
|
+
ca_service_url: this.caServiceUrl,
|
|
6170
|
+
ttl_sec: this.ttlSec,
|
|
6171
|
+
});
|
|
6203
6172
|
}
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6173
|
+
async handleHello(hello, metadata) {
|
|
6174
|
+
const fullMetadata = metadata
|
|
6175
|
+
? { ...metadata }
|
|
6176
|
+
: {};
|
|
6177
|
+
const trimmedSystemId = typeof hello.systemId === "string" ? hello.systemId.trim() : "";
|
|
6178
|
+
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : core.generateId();
|
|
6179
|
+
const wasAssigned = trimmedSystemId.length === 0;
|
|
6180
|
+
const normalizedHello = {
|
|
6181
|
+
...hello,
|
|
6182
|
+
systemId,
|
|
6183
|
+
};
|
|
6184
|
+
if (showEnvelopes) {
|
|
6185
|
+
// eslint-disable-next-line no-console
|
|
6186
|
+
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Received envelope 📨", runtime.AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
6216
6187
|
}
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6188
|
+
logger$2.debug("starting_hello_frame_processing", {
|
|
6189
|
+
instanceId: normalizedHello.instanceId,
|
|
6190
|
+
systemId,
|
|
6191
|
+
logicals: normalizedHello.logicals,
|
|
6192
|
+
capabilities: normalizedHello.capabilities,
|
|
6193
|
+
ttlSec: this.ttlSec,
|
|
6194
|
+
});
|
|
6195
|
+
const now = nowUtc();
|
|
6196
|
+
const expiry = new Date(now.getTime() + this.ttlSec * 1000);
|
|
6197
|
+
if (normalizedHello.instanceId) {
|
|
6198
|
+
if (fullMetadata.instanceId === undefined) {
|
|
6199
|
+
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
6200
|
+
}
|
|
6201
|
+
if (fullMetadata.instance_id === undefined) {
|
|
6202
|
+
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
6203
|
+
}
|
|
6223
6204
|
}
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6205
|
+
logger$2.debug("system_id_assignment_completed", {
|
|
6206
|
+
systemId,
|
|
6207
|
+
wasAssigned,
|
|
6208
|
+
});
|
|
6209
|
+
if (normalizedHello.logicals?.length) {
|
|
6210
|
+
logger$2.debug("validating_logicals_for_dns_compatibility", {
|
|
6211
|
+
logicals: normalizedHello.logicals,
|
|
6212
|
+
});
|
|
6213
|
+
const [pathsValid, pathError] = runtime.validateHostLogicals(normalizedHello.logicals);
|
|
6214
|
+
if (!pathsValid) {
|
|
6215
|
+
logger$2.error("logical_validation_failed", {
|
|
6216
|
+
error: pathError,
|
|
6217
|
+
logicals: normalizedHello.logicals,
|
|
6218
|
+
});
|
|
6219
|
+
throw new Error(`Invalid logical format: ${pathError}`);
|
|
6231
6220
|
}
|
|
6221
|
+
logger$2.debug("logicals_validation_successful");
|
|
6232
6222
|
}
|
|
6233
|
-
|
|
6234
|
-
|
|
6223
|
+
logger$2.debug("requesting_node_placement", { systemId });
|
|
6224
|
+
const placementResult = await this.placementStrategy.place(normalizedHello);
|
|
6225
|
+
if (!placementResult.accept) {
|
|
6226
|
+
logger$2.error("node_placement_rejected", {
|
|
6227
|
+
systemId,
|
|
6228
|
+
reason: placementResult.reason,
|
|
6229
|
+
});
|
|
6230
|
+
throw new Error(placementResult.reason || "Node not accepted");
|
|
6235
6231
|
}
|
|
6232
|
+
const assignedPath = placementResult.assignedPath;
|
|
6233
|
+
logger$2.debug("node_placement_accepted", {
|
|
6234
|
+
systemId,
|
|
6235
|
+
assignedPath,
|
|
6236
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
6237
|
+
targetSystemId: placementResult.targetSystemId ?? null,
|
|
6238
|
+
});
|
|
6239
|
+
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
6240
|
+
normalizedHello.capabilities ??
|
|
6241
|
+
null;
|
|
6242
|
+
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
6243
|
+
normalizedHello.logicals ??
|
|
6244
|
+
null;
|
|
6245
|
+
logger$2.debug("processing_placement_result_metadata", {
|
|
6246
|
+
acceptedCapabilities,
|
|
6247
|
+
acceptedLogicals,
|
|
6248
|
+
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
6249
|
+
placementResult.metadata !== null,
|
|
6250
|
+
});
|
|
6251
|
+
const connectionGrants = [];
|
|
6252
|
+
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
6253
|
+
fullMetadata.instanceId) ||
|
|
6254
|
+
(typeof fullMetadata.instance_id === "string" &&
|
|
6255
|
+
fullMetadata.instance_id) ||
|
|
6256
|
+
normalizedHello.instanceId ||
|
|
6257
|
+
core.generateId();
|
|
6258
|
+
if (placementResult.targetSystemId) {
|
|
6259
|
+
logger$2.debug("issuing_node_attach_token", {
|
|
6260
|
+
systemId,
|
|
6261
|
+
assignedPath,
|
|
6262
|
+
});
|
|
6263
|
+
const nodeAttachToken = await this.tokenIssuer.issue({
|
|
6264
|
+
aud: placementResult.targetPhysicalPath,
|
|
6265
|
+
system_id: systemId,
|
|
6266
|
+
parent_path: placementResult.targetPhysicalPath,
|
|
6267
|
+
assigned_path: placementResult.assignedPath,
|
|
6268
|
+
accepted_logicals: acceptedLogicals,
|
|
6269
|
+
instance_id: metadataInstanceId,
|
|
6270
|
+
});
|
|
6271
|
+
logger$2.debug("token_issued_successfully");
|
|
6272
|
+
logger$2.debug("provisioning_transport", { systemId });
|
|
6273
|
+
const transportInfo = await this.transportProvisioner.provision(placementResult, normalizedHello, fullMetadata, nodeAttachToken);
|
|
6274
|
+
logger$2.debug("transport_provisioned_successfully", {
|
|
6275
|
+
systemId,
|
|
6276
|
+
directiveType: transportInfo.connectionGrant &&
|
|
6277
|
+
typeof transportInfo.connectionGrant === "object"
|
|
6278
|
+
? (transportInfo.connectionGrant.type ??
|
|
6279
|
+
"Unknown")
|
|
6280
|
+
: "Unknown",
|
|
6281
|
+
});
|
|
6282
|
+
connectionGrants.push(transportInfo.connectionGrant);
|
|
6283
|
+
}
|
|
6284
|
+
const caSignToken = await this.tokenIssuer.issue({
|
|
6285
|
+
aud: "ca",
|
|
6286
|
+
system_id: systemId,
|
|
6287
|
+
assigned_path: assignedPath,
|
|
6288
|
+
accepted_logicals: acceptedLogicals,
|
|
6289
|
+
instance_id: metadataInstanceId,
|
|
6290
|
+
});
|
|
6291
|
+
const caGrant = {
|
|
6292
|
+
type: runtime.HTTP_CONNECTION_GRANT_TYPE,
|
|
6293
|
+
purpose: GRANT_PURPOSE_CA_SIGN,
|
|
6294
|
+
url: this.caServiceUrl,
|
|
6295
|
+
auth: {
|
|
6296
|
+
type: "BearerTokenHeaderAuth",
|
|
6297
|
+
tokenProvider: {
|
|
6298
|
+
type: "StaticTokenProvider",
|
|
6299
|
+
token: caSignToken,
|
|
6300
|
+
},
|
|
6301
|
+
},
|
|
6302
|
+
};
|
|
6303
|
+
connectionGrants.push(caGrant);
|
|
6304
|
+
const welcomeFrame = {
|
|
6305
|
+
type: "NodeWelcome",
|
|
6306
|
+
systemId,
|
|
6307
|
+
targetSystemId: placementResult.targetSystemId ?? undefined,
|
|
6308
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? undefined,
|
|
6309
|
+
instanceId: normalizedHello.instanceId,
|
|
6310
|
+
assignedPath,
|
|
6311
|
+
acceptedCapabilities: acceptedCapabilities ?? undefined,
|
|
6312
|
+
acceptedLogicals: acceptedLogicals ?? undefined,
|
|
6313
|
+
rejectedLogicals: undefined,
|
|
6314
|
+
connectionGrants,
|
|
6315
|
+
metadata: Object.keys(fullMetadata).length > 0 ? fullMetadata : undefined,
|
|
6316
|
+
expiresAt: expiry.toISOString(),
|
|
6317
|
+
};
|
|
6318
|
+
logger$2.debug("hello_frame_processing_completed_successfully", {
|
|
6319
|
+
systemId,
|
|
6320
|
+
assignedPath,
|
|
6321
|
+
acceptedLogicals,
|
|
6322
|
+
acceptedCapabilities,
|
|
6323
|
+
expiresAt: welcomeFrame.expiresAt,
|
|
6324
|
+
instanceId: normalizedHello.instanceId,
|
|
6325
|
+
});
|
|
6326
|
+
if (showEnvelopes) {
|
|
6327
|
+
// eslint-disable-next-line no-console
|
|
6328
|
+
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Sent envelope", runtime.AnsiColor.BLUE)} 🚀\n${prettyModel(welcomeFrame)}`);
|
|
6329
|
+
}
|
|
6330
|
+
return welcomeFrame;
|
|
6236
6331
|
}
|
|
6237
|
-
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
6238
|
-
addCandidate(packageCandidate);
|
|
6239
|
-
if (packageCandidate?.endsWith(".js")) {
|
|
6240
|
-
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
6241
|
-
}
|
|
6242
|
-
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
6243
|
-
addCandidate(fallback);
|
|
6244
|
-
if (fallback.endsWith(".js")) {
|
|
6245
|
-
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
6246
|
-
}
|
|
6247
|
-
addCandidate(spec);
|
|
6248
|
-
if (spec.endsWith(".js")) {
|
|
6249
|
-
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
6250
|
-
}
|
|
6251
|
-
return candidates;
|
|
6252
|
-
}
|
|
6253
|
-
const registeredModules = new Set();
|
|
6254
|
-
const inflightModules = new Map();
|
|
6255
|
-
const browserSkippedModules = new Set();
|
|
6256
|
-
function isNodeEnvironment$5() {
|
|
6257
|
-
return (typeof process !== "undefined" &&
|
|
6258
|
-
typeof process.release !== "undefined" &&
|
|
6259
|
-
process.release?.name === "node");
|
|
6260
|
-
}
|
|
6261
|
-
function shouldSkipModule(spec) {
|
|
6262
|
-
if (isNodeEnvironment$5()) {
|
|
6263
|
-
return false;
|
|
6264
|
-
}
|
|
6265
|
-
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
6266
|
-
return false;
|
|
6267
|
-
}
|
|
6268
|
-
if (!browserSkippedModules.has(spec)) {
|
|
6269
|
-
// console.warn(
|
|
6270
|
-
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
6271
|
-
// spec,
|
|
6272
|
-
// );
|
|
6273
|
-
browserSkippedModules.add(spec);
|
|
6274
|
-
}
|
|
6275
|
-
return true;
|
|
6276
|
-
}
|
|
6277
|
-
function getDynamicImporter() {
|
|
6278
|
-
if (typeof globalThis === "undefined") {
|
|
6279
|
-
return null;
|
|
6280
|
-
}
|
|
6281
|
-
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
6282
|
-
if (typeof candidate === "function") {
|
|
6283
|
-
return candidate;
|
|
6284
|
-
}
|
|
6285
|
-
return null;
|
|
6286
6332
|
}
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6333
|
+
|
|
6334
|
+
const FACTORY_META$4 = {
|
|
6335
|
+
base: runtime.WELCOME_SERVICE_FACTORY_BASE_TYPE,
|
|
6336
|
+
key: "AdvancedWelcomeService",
|
|
6337
|
+
priority: 100,
|
|
6338
|
+
isDefault: true,
|
|
6339
|
+
};
|
|
6340
|
+
class AdvancedWelcomeServiceFactory extends runtime.WelcomeServiceFactory {
|
|
6341
|
+
constructor() {
|
|
6342
|
+
super(...arguments);
|
|
6343
|
+
this.type = FACTORY_META$4.key;
|
|
6344
|
+
this.isDefault = FACTORY_META$4.isDefault;
|
|
6345
|
+
this.priority = FACTORY_META$4.priority;
|
|
6300
6346
|
}
|
|
6301
|
-
|
|
6302
|
-
const
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
6315
|
-
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
6316
|
-
return true;
|
|
6317
|
-
};
|
|
6318
|
-
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
6319
|
-
try {
|
|
6320
|
-
const mod = await load();
|
|
6321
|
-
return registerFromModule(mod);
|
|
6347
|
+
async create(config, ...factoryArgs) {
|
|
6348
|
+
const normalized = normalizeConfig$2(config);
|
|
6349
|
+
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
6350
|
+
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
6351
|
+
const placementStrategy = await runtime.NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6352
|
+
const transportProvisioner = await runtime.TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6353
|
+
const tokenIssuer = await runtime.TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6354
|
+
let authorizer = null;
|
|
6355
|
+
if (normalized.authorizerConfig) {
|
|
6356
|
+
authorizer =
|
|
6357
|
+
(await runtime.AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
6358
|
+
factoryArgs,
|
|
6359
|
+
})) ?? null;
|
|
6322
6360
|
}
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
if (!moduleNotFound || isLastAttempt) {
|
|
6333
|
-
console.warn("[debug] failed to import candidate", {
|
|
6334
|
-
spec,
|
|
6335
|
-
candidate,
|
|
6336
|
-
message,
|
|
6337
|
-
});
|
|
6338
|
-
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
6339
|
-
return false;
|
|
6340
|
-
}
|
|
6361
|
+
const options = {
|
|
6362
|
+
placementStrategy,
|
|
6363
|
+
transportProvisioner,
|
|
6364
|
+
tokenIssuer,
|
|
6365
|
+
authorizer,
|
|
6366
|
+
caServiceUrl: normalized.caServiceUrl,
|
|
6367
|
+
};
|
|
6368
|
+
if (normalized.ttlSec !== undefined) {
|
|
6369
|
+
options.ttlSec = normalized.ttlSec;
|
|
6341
6370
|
}
|
|
6371
|
+
return new AdvancedWelcomeService(options);
|
|
6342
6372
|
}
|
|
6343
|
-
return false;
|
|
6344
6373
|
}
|
|
6345
|
-
|
|
6346
|
-
if (
|
|
6347
|
-
|
|
6348
|
-
}
|
|
6349
|
-
const inflight = inflightModules.get(spec);
|
|
6350
|
-
if (inflight) {
|
|
6351
|
-
return inflight;
|
|
6374
|
+
function normalizeConfig$2(config) {
|
|
6375
|
+
if (!config) {
|
|
6376
|
+
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6352
6377
|
}
|
|
6353
|
-
const
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6378
|
+
const source = config;
|
|
6379
|
+
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6380
|
+
? source.ttlSec
|
|
6381
|
+
: typeof source.ttl_sec === "number"
|
|
6382
|
+
? source.ttl_sec
|
|
6383
|
+
: undefined;
|
|
6384
|
+
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6385
|
+
source.caServiceUrl.trim().length > 0
|
|
6386
|
+
? source.caServiceUrl.trim()
|
|
6387
|
+
: typeof source.ca_service_url === "string" &&
|
|
6388
|
+
source.ca_service_url.trim().length > 0
|
|
6389
|
+
? source.ca_service_url.trim()
|
|
6390
|
+
: undefined;
|
|
6391
|
+
if (!caServiceUrlCandidate) {
|
|
6392
|
+
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6363
6393
|
}
|
|
6364
|
-
|
|
6365
|
-
|
|
6394
|
+
const normalized = {
|
|
6395
|
+
caServiceUrl: caServiceUrlCandidate,
|
|
6396
|
+
};
|
|
6397
|
+
if (source.placement !== undefined) {
|
|
6398
|
+
normalized.placementConfig =
|
|
6399
|
+
source.placement ?? null;
|
|
6366
6400
|
}
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
return 0;
|
|
6401
|
+
if (source.transport !== undefined) {
|
|
6402
|
+
normalized.transportConfig =
|
|
6403
|
+
source.transport ?? null;
|
|
6371
6404
|
}
|
|
6372
|
-
const
|
|
6373
|
-
|
|
6374
|
-
|
|
6405
|
+
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6406
|
+
? source.tokenIssuer
|
|
6407
|
+
: source.token_issuer !== undefined
|
|
6408
|
+
? source.token_issuer
|
|
6409
|
+
: undefined;
|
|
6410
|
+
if (tokenIssuerConfig !== undefined) {
|
|
6411
|
+
normalized.tokenIssuerConfig =
|
|
6412
|
+
tokenIssuerConfig ?? null;
|
|
6375
6413
|
}
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
async function registerAdvancedSecurityFactories(registrar = factory.Registry, options) {
|
|
6380
|
-
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
6381
|
-
if (newlyRegisteredSecurity > 0) {
|
|
6382
|
-
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
6414
|
+
if (source.authorizer !== undefined) {
|
|
6415
|
+
normalized.authorizerConfig =
|
|
6416
|
+
source.authorizer ?? null;
|
|
6383
6417
|
}
|
|
6384
|
-
{
|
|
6385
|
-
|
|
6418
|
+
if (ttlCandidate !== undefined && Number.isFinite(ttlCandidate)) {
|
|
6419
|
+
normalized.ttlSec = ttlCandidate;
|
|
6386
6420
|
}
|
|
6421
|
+
return normalized;
|
|
6387
6422
|
}
|
|
6388
6423
|
|
|
6424
|
+
var advancedWelcomeServiceFactory = /*#__PURE__*/Object.freeze({
|
|
6425
|
+
__proto__: null,
|
|
6426
|
+
AdvancedWelcomeServiceFactory: AdvancedWelcomeServiceFactory,
|
|
6427
|
+
FACTORY_META: FACTORY_META$4,
|
|
6428
|
+
default: AdvancedWelcomeServiceFactory
|
|
6429
|
+
});
|
|
6430
|
+
|
|
6389
6431
|
/**
|
|
6390
6432
|
* Isomorphic entry point for Naylence Advanced Security.
|
|
6391
6433
|
*
|
|
@@ -6460,7 +6502,16 @@ const __advancedSecurityPluginLoader = ensureAdvancedSecurityPluginLoader();
|
|
|
6460
6502
|
* runtimes lacking Node.js built-ins. Node-specific certificate authority
|
|
6461
6503
|
* helpers and Fastify bindings are intentionally excluded.
|
|
6462
6504
|
*/
|
|
6463
|
-
//
|
|
6505
|
+
// Always register the plugin directly. This ensures it is initialized even if
|
|
6506
|
+
// the dynamic import mechanism fails.
|
|
6507
|
+
(async () => {
|
|
6508
|
+
try {
|
|
6509
|
+
await advancedSecurityPlugin.register();
|
|
6510
|
+
}
|
|
6511
|
+
catch (err) {
|
|
6512
|
+
console.error('[naylence:advanced-security] Failed to auto-register plugin:', err);
|
|
6513
|
+
}
|
|
6514
|
+
})();
|
|
6464
6515
|
// Mark as used so bundlers keep the import
|
|
6465
6516
|
if (typeof __advancedSecurityPluginLoader === "undefined") {
|
|
6466
6517
|
throw new Error("Advanced security plugin loader not initialized");
|
|
@@ -9827,48 +9878,6 @@ var nodeTrustStoreProviderFactory = /*#__PURE__*/Object.freeze({
|
|
|
9827
9878
|
default: EnvTrustStoreProviderFactory
|
|
9828
9879
|
});
|
|
9829
9880
|
|
|
9830
|
-
async function registerAdvancedSecurityPluginFactories(registrar = factory.Registry) {
|
|
9831
|
-
await registerAdvancedSecurityFactories(registrar);
|
|
9832
|
-
}
|
|
9833
|
-
let initialized = false;
|
|
9834
|
-
let initializing = null;
|
|
9835
|
-
const advancedSecurityPlugin = {
|
|
9836
|
-
name: "naylence:advanced-security",
|
|
9837
|
-
version: VERSION,
|
|
9838
|
-
async register() {
|
|
9839
|
-
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
9840
|
-
if (initialized) {
|
|
9841
|
-
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
9842
|
-
return;
|
|
9843
|
-
}
|
|
9844
|
-
if (initializing) {
|
|
9845
|
-
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
9846
|
-
await initializing;
|
|
9847
|
-
return;
|
|
9848
|
-
}
|
|
9849
|
-
initializing = (async () => {
|
|
9850
|
-
try {
|
|
9851
|
-
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
9852
|
-
await registerAdvancedSecurityPluginFactories();
|
|
9853
|
-
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
9854
|
-
initialized = true;
|
|
9855
|
-
}
|
|
9856
|
-
finally {
|
|
9857
|
-
initializing = null;
|
|
9858
|
-
}
|
|
9859
|
-
})();
|
|
9860
|
-
await initializing;
|
|
9861
|
-
},
|
|
9862
|
-
};
|
|
9863
|
-
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
9864
|
-
|
|
9865
|
-
var plugin = /*#__PURE__*/Object.freeze({
|
|
9866
|
-
__proto__: null,
|
|
9867
|
-
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
9868
|
-
default: advancedSecurityPlugin,
|
|
9869
|
-
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
9870
|
-
});
|
|
9871
|
-
|
|
9872
9881
|
exports.ADVANCED_EDDSA_ENVELOPE_SIGNER_FACTORY_META = FACTORY_META$9;
|
|
9873
9882
|
exports.ADVANCED_EDDSA_ENVELOPE_VERIFIER_FACTORY_META = FACTORY_META$8;
|
|
9874
9883
|
exports.ADVANCED_WELCOME_FACTORY_META = FACTORY_META$4;
|