@dnsid-ai/sdk 0.22.0 → 0.23.1
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/README.md +23 -6
- package/dist/node.cjs +281 -225
- package/dist/node.d.cts +69 -136
- package/dist/node.d.ts +69 -136
- package/dist/node.js +279 -219
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ The root `@dnsid-ai/sdk` entrypoint is runtime-neutral: callers inject DNS resol
|
|
|
7
7
|
## Package layout
|
|
8
8
|
|
|
9
9
|
- **Root export (`@dnsid-ai/sdk`)** — runtime-neutral. No Node built-ins, `Buffer`, filesystem, or `undici` imports; you inject `dnsResolver`, `fetchJson`, and key providers. Safe to bundle for browsers and other non-Node runtimes. Its managed C2SP workflows use the portable writer-only binding entrypoint.
|
|
10
|
-
- **`@dnsid-ai/sdk/node` subpath** — Node conveniences: `LocalKeyProvider`, `
|
|
10
|
+
- **`@dnsid-ai/sdk/node` subpath** — Node conveniences: `LocalKeyProvider`, configuration loaders (`loadEnvironment`, `loadFile`, `loadCliDirectory`, `mergeLoadedConfig`, `constructIdentityManager`), `createNodeIdentityManager`, and the one-call `createNodeIdentityManagerFromEnvironment` / `FromDnsid` / `FromFile`. Loads the optional `@dnsid-ai/transport` peer when HTTPS defaults are needed.
|
|
11
11
|
- **OIDC lives in `@dnsid-ai/oidc`** — deliberately not re-exported from the root because its default transport is Node-bound, and private-key token minting belongs server-side. Import it directly.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
@@ -71,12 +71,27 @@ rotation state; already-completed activation or supersession is not repeated.
|
|
|
71
71
|
## Node.js convenience usage
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
|
-
import {
|
|
74
|
+
import { createNodeIdentityManagerFromEnvironment } from '@dnsid-ai/sdk/node';
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
// Loaders parse; constructors default. Identity from DNSID_*, keys from DNSID_CONFIG_DIR or
|
|
77
|
+
// DNSID_KEY_STORE, log trust from DNSID_LOG_POLICY_URL / _FILE / DNSID_LOG_TRUST_PROFILE_FILE.
|
|
78
|
+
// Without DNSID_DOMAIN the result is a verification-only manager.
|
|
79
|
+
const idm = await createNodeIdentityManagerFromEnvironment();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`createNodeIdentityManagerFromEnvironment(env?, overlay?, deps?)` is exactly
|
|
83
|
+
`constructIdentityManager(mergeLoadedConfig(await loadEnvironment(env), { dnsid: overlay }), deps)`;
|
|
84
|
+
`createNodeIdentityManagerFromDnsid(dir?)` and `createNodeIdentityManagerFromFile(path)` are the same
|
|
85
|
+
shape over `loadCliDirectory` and `loadFile`. Compose sources yourself with `mergeLoadedConfig`
|
|
86
|
+
(field-wise, presence wins, lists replace, `logTrust` atomic). Supplied `deps` always win over loaded
|
|
87
|
+
`logTrust` and `keySource`. The environment schema is in the repository README.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { createNodeIdentityManager, LocalKeyProvider } from '@dnsid-ai/sdk/node';
|
|
91
|
+
|
|
92
|
+
const keyProvider = await LocalKeyProvider.load('.dnsid/keys.json', true);
|
|
78
93
|
const entityKeyProvider = await LocalKeyProvider.load('.dnsid/entity.keys.json', true);
|
|
79
|
-
const idm = await createNodeIdentityManager(
|
|
94
|
+
const idm = await createNodeIdentityManager({ identity, verification }, { keyProvider, entityKeyProvider });
|
|
80
95
|
```
|
|
81
96
|
|
|
82
97
|
`config.transport.dnsServer`/`caBundlePath` configure only the SDK-managed default resolver and
|
|
@@ -102,9 +117,11 @@ If the registry CLI has already written `~/.dnsid/config.json` and `~/.dnsid/<fq
|
|
|
102
117
|
```ts
|
|
103
118
|
import { createNodeIdentityManagerFromDnsid } from '@dnsid-ai/sdk/node';
|
|
104
119
|
|
|
105
|
-
const idm = await createNodeIdentityManagerFromDnsid();
|
|
120
|
+
const idm = await createNodeIdentityManagerFromDnsid(); // ~/.dnsid by default; never reads DNSID_CONFIG_DIR
|
|
106
121
|
```
|
|
107
122
|
|
|
123
|
+
The CLI loader maps persisted fields as written: a missing `status_url` or `log_ref` fails construction with `ArgumentError` unless the overlay supplies it.
|
|
124
|
+
|
|
108
125
|
DNSSEC modes are: `auto` (default), which rejects `FAILED` and permits `VALID`, `UNSIGNED`, or `UNKNOWN`; `validated`, which permits `VALID` or `UNSIGNED`; and `required`, which permits only `VALID`.
|
|
109
126
|
|
|
110
127
|
## Included surfaces
|
package/dist/node.cjs
CHANGED
|
@@ -31,20 +31,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var node_exports = {};
|
|
32
32
|
__export(node_exports, {
|
|
33
33
|
LocalKeyProvider: () => LocalKeyProvider,
|
|
34
|
-
|
|
34
|
+
constructIdentityManager: () => constructIdentityManager,
|
|
35
35
|
createNodeIdentityManager: () => createNodeIdentityManager,
|
|
36
36
|
createNodeIdentityManagerFromDnsid: () => createNodeIdentityManagerFromDnsid,
|
|
37
|
+
createNodeIdentityManagerFromEnvironment: () => createNodeIdentityManagerFromEnvironment,
|
|
38
|
+
createNodeIdentityManagerFromFile: () => createNodeIdentityManagerFromFile,
|
|
37
39
|
createNodeIdentityVerifier: () => createNodeIdentityVerifier,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
createRegistryClientFromEnvironment: () => createRegistryClientFromEnvironment,
|
|
41
|
+
loadCliDirectory: () => loadCliDirectory,
|
|
42
|
+
loadEnvironment: () => loadEnvironment,
|
|
43
|
+
loadFile: () => loadFile,
|
|
44
|
+
mergeLoadedConfig: () => mergeLoadedConfig
|
|
41
45
|
});
|
|
42
46
|
module.exports = __toCommonJS(node_exports);
|
|
43
|
-
var fs3 = __toESM(require("fs/promises"), 1);
|
|
44
|
-
var os2 = __toESM(require("os"), 1);
|
|
45
|
-
var path2 = __toESM(require("path"), 1);
|
|
46
|
-
var import_protocol3 = require("@dnsid-ai/protocol");
|
|
47
|
-
var import_registry2 = require("@dnsid-ai/registry");
|
|
48
47
|
|
|
49
48
|
// src/local-key-provider.ts
|
|
50
49
|
var fs2 = __toESM(require("fs/promises"), 1);
|
|
@@ -100,9 +99,6 @@ var LocalKeyProvider = class _LocalKeyProvider {
|
|
|
100
99
|
}
|
|
101
100
|
return new _LocalKeyProvider(store, filePath);
|
|
102
101
|
}
|
|
103
|
-
static async fromEnvironment(env, options = {}) {
|
|
104
|
-
return _LocalKeyProvider.load(keyStorePathFromEnvironment(env, options.defaultPath), options.createIfMissing, options.algorithm);
|
|
105
|
-
}
|
|
106
102
|
static async generate(algorithm = "EdDSA") {
|
|
107
103
|
return new _LocalKeyProvider(await buildInitialStore(algorithm), null);
|
|
108
104
|
}
|
|
@@ -229,9 +225,6 @@ async function syncDirectory(dir) {
|
|
|
229
225
|
await directory.close();
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
|
-
function keyStorePathFromEnvironment(env = process.env, defaultPath = ".dnsid/keys.json") {
|
|
233
|
-
return env["DNSID_KEY_STORE"] || env["DNSID_KEY_STORE_PATH"] || env["DNSID_KEYSTORE_PATH"] || defaultPath;
|
|
234
|
-
}
|
|
235
228
|
function toPublicJwk(key) {
|
|
236
229
|
const { d: _d, ...pub } = key;
|
|
237
230
|
return pub;
|
|
@@ -304,226 +297,259 @@ async function importPrivateKey(key) {
|
|
|
304
297
|
);
|
|
305
298
|
}
|
|
306
299
|
|
|
307
|
-
// src/
|
|
300
|
+
// src/node-identity-manager.ts
|
|
308
301
|
var import_protocol2 = require("@dnsid-ai/protocol");
|
|
309
|
-
var import_registry = require("@dnsid-ai/registry");
|
|
310
|
-
var dnsidEnvironmentVariables = {
|
|
311
|
-
domain: "DNSID_DOMAIN",
|
|
312
|
-
governanceId: "DNSID_GOVERNANCE_ID",
|
|
313
|
-
registryUrl: "DNSID_REGISTRY_URL",
|
|
314
|
-
apiKey: "DNSID_API_KEY",
|
|
315
|
-
statusUrl: "DNSID_STATUS_URL",
|
|
316
|
-
logRef: "DNSID_LOG_REF",
|
|
317
|
-
ekUrl: "DNSID_EK_URL",
|
|
318
|
-
kuUrl: "DNSID_KU_URL",
|
|
319
|
-
dnsServer: "DNSID_DNS_SERVER",
|
|
320
|
-
caBundlePath: "DNSID_CA_BUNDLE",
|
|
321
|
-
privateAddressHosts: "DNSID_PRIVATE_HOSTS",
|
|
322
|
-
dnssecMode: "DNSID_DNSSEC_MODE",
|
|
323
|
-
publicUrl: "DNSID_PUBLIC_URL",
|
|
324
|
-
keyStorePath: "DNSID_KEY_STORE",
|
|
325
|
-
agentPort: "DNSID_AGENT_PORT",
|
|
326
|
-
agentName: "DNSID_AGENT_NAME"
|
|
327
|
-
};
|
|
328
|
-
var environmentSchema = {
|
|
329
|
-
domain: stringField(dnsidEnvironmentVariables.domain),
|
|
330
|
-
governanceId: stringField(dnsidEnvironmentVariables.governanceId),
|
|
331
|
-
registryUrl: stringField(dnsidEnvironmentVariables.registryUrl),
|
|
332
|
-
apiKey: stringField(dnsidEnvironmentVariables.apiKey),
|
|
333
|
-
statusUrl: stringField(dnsidEnvironmentVariables.statusUrl),
|
|
334
|
-
logRef: stringField(dnsidEnvironmentVariables.logRef),
|
|
335
|
-
ekUrl: stringField(dnsidEnvironmentVariables.ekUrl),
|
|
336
|
-
kuUrl: stringField(dnsidEnvironmentVariables.kuUrl),
|
|
337
|
-
dnsServer: stringField(dnsidEnvironmentVariables.dnsServer),
|
|
338
|
-
caBundlePath: stringField(dnsidEnvironmentVariables.caBundlePath),
|
|
339
|
-
privateAddressHosts: listField(dnsidEnvironmentVariables.privateAddressHosts),
|
|
340
|
-
dnssecMode: dnssecModeField(dnsidEnvironmentVariables.dnssecMode),
|
|
341
|
-
publicUrl: stringField(dnsidEnvironmentVariables.publicUrl),
|
|
342
|
-
keyStorePath: stringField(dnsidEnvironmentVariables.keyStorePath),
|
|
343
|
-
agentPort: positiveIntField(dnsidEnvironmentVariables.agentPort),
|
|
344
|
-
agentName: stringField(dnsidEnvironmentVariables.agentName)
|
|
345
|
-
};
|
|
346
|
-
function configFromEnvironment(envOrOptions = process.env, options = {}) {
|
|
347
|
-
const receivedOptions = isConfigFromEnvironmentOptions(envOrOptions);
|
|
348
|
-
const env = receivedOptions ? process.env : envOrOptions ?? process.env;
|
|
349
|
-
const resolvedOptions = receivedOptions ? envOrOptions : options;
|
|
350
|
-
const parsed = parseEnvironment(env);
|
|
351
|
-
requireFields(parsed, ["domain", "governanceId", ...resolvedOptions.require ?? []]);
|
|
352
|
-
const registryUrl = parsed.registryUrl ?? import_registry.DEFAULT_REGISTRY_URL;
|
|
353
|
-
const statusUrl = parsed.statusUrl ?? protocolStatusUrl(registryUrl, parsed.domain);
|
|
354
|
-
const config = {
|
|
355
|
-
identity: defined({
|
|
356
|
-
domain: parsed.domain,
|
|
357
|
-
governanceId: parsed.governanceId,
|
|
358
|
-
logRef: parsed.logRef ?? "noop:0",
|
|
359
|
-
statusUrl,
|
|
360
|
-
ekUrl: parsed.ekUrl,
|
|
361
|
-
kuUrl: parsed.kuUrl
|
|
362
|
-
}),
|
|
363
|
-
verification: defined({ dnssecMode: parsed.dnssecMode }),
|
|
364
|
-
transport: defined({ dnsServer: parsed.dnsServer, caBundlePath: parsed.caBundlePath, privateAddressHosts: parsed.privateAddressHosts })
|
|
365
|
-
};
|
|
366
|
-
return {
|
|
367
|
-
config,
|
|
368
|
-
registryUrl,
|
|
369
|
-
apiKey: parsed.apiKey,
|
|
370
|
-
publicUrl: parsed.publicUrl,
|
|
371
|
-
keyStorePath: parsed.keyStorePath,
|
|
372
|
-
agentName: parsed.agentName,
|
|
373
|
-
agentPort: parsed.agentPort
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
function registryClientOptionsFromEnvironment(env = process.env) {
|
|
377
|
-
const parsed = parseEnvironment(env);
|
|
378
|
-
return defined({ baseUrl: parsed.registryUrl ?? import_registry.DEFAULT_REGISTRY_URL, token: parsed.apiKey });
|
|
379
|
-
}
|
|
380
|
-
function defined(value) {
|
|
381
|
-
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
382
|
-
}
|
|
383
|
-
function isConfigFromEnvironmentOptions(value) {
|
|
384
|
-
if (value === void 0) return false;
|
|
385
|
-
const keys = Object.keys(value);
|
|
386
|
-
if (keys.length === 0) return true;
|
|
387
|
-
return keys.every((key) => key === "require") && Array.isArray(value.require);
|
|
388
|
-
}
|
|
389
|
-
function parseEnvironment(env) {
|
|
390
|
-
const parsed = {};
|
|
391
|
-
for (const fieldName of Object.keys(environmentSchema)) {
|
|
392
|
-
const value = readEnvironmentField(env, fieldName);
|
|
393
|
-
if (value !== void 0) parsed[fieldName] = value;
|
|
394
|
-
}
|
|
395
|
-
return parsed;
|
|
396
|
-
}
|
|
397
|
-
function readEnvironmentField(env, fieldName) {
|
|
398
|
-
const field = environmentSchema[fieldName];
|
|
399
|
-
const raw = env[field.variable];
|
|
400
|
-
if (raw === void 0 || raw === "") return void 0;
|
|
401
|
-
return field.parse(raw, field.variable);
|
|
402
|
-
}
|
|
403
|
-
function requireFields(parsed, fields) {
|
|
404
|
-
for (const field of fields) {
|
|
405
|
-
if (parsed[field] === void 0) throw new Error(`${environmentSchema[field].variable} is required`);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
function stringField(variable) {
|
|
409
|
-
return { variable, parse: (value) => value };
|
|
410
|
-
}
|
|
411
|
-
function listField(variable) {
|
|
412
|
-
return {
|
|
413
|
-
variable,
|
|
414
|
-
parse(value) {
|
|
415
|
-
const items = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
416
|
-
return items.length > 0 ? items : void 0;
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
function positiveIntField(variable) {
|
|
421
|
-
return {
|
|
422
|
-
variable,
|
|
423
|
-
parse(value, name) {
|
|
424
|
-
const parsed = Number(value);
|
|
425
|
-
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
426
|
-
throw new Error(`${name} must be a positive integer`);
|
|
427
|
-
}
|
|
428
|
-
return parsed;
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
function dnssecModeField(variable) {
|
|
433
|
-
return {
|
|
434
|
-
variable,
|
|
435
|
-
parse(value, name) {
|
|
436
|
-
if (!Object.values(import_protocol2.DNSSECMode).includes(value)) {
|
|
437
|
-
throw new Error(`invalid ${name} "${value}"; expected one of: ${Object.values(import_protocol2.DNSSECMode).join(", ")}`);
|
|
438
|
-
}
|
|
439
|
-
return value;
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
function protocolStatusUrl(registryUrl, domain) {
|
|
444
|
-
return `${registryUrl.replace(/\/$/, "")}/v1/status/${encodeURIComponent(domain)}`;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// src/node.ts
|
|
448
302
|
async function createNodeIdentityManager(config, deps = {}) {
|
|
449
|
-
const { transport, ...core } = (0,
|
|
303
|
+
const { transport, ...core } = (0, import_protocol2.validateDnsidConfig)(config);
|
|
450
304
|
const { dnsServer, caBundlePath, privateAddressHosts } = transport;
|
|
451
305
|
if (dnsServer !== void 0 && deps.dnsResolver && deps.fetchJson) {
|
|
452
|
-
throw new
|
|
306
|
+
throw new import_protocol2.ArgumentError("config.transport.dnsServer has no SDK-managed consumer when both dnsResolver and fetchJson are injected");
|
|
453
307
|
}
|
|
454
308
|
if (caBundlePath !== void 0 && deps.fetchJson) {
|
|
455
|
-
throw new
|
|
309
|
+
throw new import_protocol2.ArgumentError("config.transport.caBundlePath has no SDK-managed consumer when fetchJson is injected");
|
|
456
310
|
}
|
|
457
311
|
if (privateAddressHosts !== void 0 && deps.fetchJson) {
|
|
458
|
-
throw new
|
|
312
|
+
throw new import_protocol2.ArgumentError("config.transport.privateAddressHosts has no SDK-managed consumer when fetchJson is injected");
|
|
459
313
|
}
|
|
460
314
|
const transportModule = deps.fetchJson && deps.dnsResolver ? null : await loadNodeTransport();
|
|
461
315
|
const fetchJson = deps.fetchJson ?? ((url, fetchOptions) => transportModule.fetchJson(url, { ...fetchOptions, dnsServer, caBundlePath, privateAddressHosts }));
|
|
462
316
|
const dnsResolver = deps.dnsResolver ?? transportModule.createDefaultDnsResolver({ dnsServer });
|
|
463
|
-
return new
|
|
317
|
+
return new import_protocol2.IdentityManager(core, { ...deps, dnsResolver, fetchJson });
|
|
464
318
|
}
|
|
465
319
|
async function createNodeIdentityVerifier(config = {}, deps = {}) {
|
|
466
320
|
return createNodeIdentityManager(config, deps);
|
|
467
321
|
}
|
|
468
|
-
async function
|
|
469
|
-
|
|
322
|
+
async function loadNodeTransport() {
|
|
323
|
+
try {
|
|
324
|
+
return await import("@dnsid-ai/transport");
|
|
325
|
+
} catch (e) {
|
|
326
|
+
const err = e;
|
|
327
|
+
if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
|
|
328
|
+
throw new Error("`@dnsid-ai/sdk/node` HTTPS defaults require optional peer `@dnsid-ai/transport`; install it or inject fetchJson.");
|
|
329
|
+
}
|
|
330
|
+
throw e;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/config-loading.ts
|
|
335
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
336
|
+
var os2 = __toESM(require("os"), 1);
|
|
337
|
+
var path2 = __toESM(require("path"), 1);
|
|
338
|
+
var import_protocol3 = require("@dnsid-ai/protocol");
|
|
339
|
+
var import_log_c2sp_tlog = require("@dnsid-ai/log-c2sp-tlog");
|
|
340
|
+
var import_registry = require("@dnsid-ai/registry");
|
|
341
|
+
var IDENTITY_VARIABLES = {
|
|
342
|
+
DNSID_DOMAIN: "domain",
|
|
343
|
+
DNSID_GOVERNANCE_ID: "governanceId",
|
|
344
|
+
DNSID_STATUS_URL: "statusUrl",
|
|
345
|
+
DNSID_LOG_REF: "logRef",
|
|
346
|
+
DNSID_EK_URL: "ekUrl",
|
|
347
|
+
DNSID_KU_URL: "kuUrl",
|
|
348
|
+
DNSID_PUBLISH_PROFILE: "publishProfile",
|
|
349
|
+
DNSID_CAPABILITIES_URL: "capabilitiesUrl"
|
|
350
|
+
};
|
|
351
|
+
async function loadEnvironment(env = process.env) {
|
|
352
|
+
const get = (name) => {
|
|
353
|
+
const value = env[name]?.trim();
|
|
354
|
+
return value ? value : void 0;
|
|
355
|
+
};
|
|
356
|
+
const identity = compact(Object.fromEntries(
|
|
357
|
+
Object.entries(IDENTITY_VARIABLES).map(([variable, field]) => [field, get(variable)])
|
|
358
|
+
));
|
|
359
|
+
const dnssecMode = get("DNSID_DNSSEC_MODE");
|
|
360
|
+
if (dnssecMode !== void 0 && !Object.values(import_protocol3.DNSSECMode).includes(dnssecMode)) {
|
|
361
|
+
throw new import_protocol3.ArgumentError(`invalid DNSID_DNSSEC_MODE "${dnssecMode}"; expected one of: ${Object.values(import_protocol3.DNSSECMode).join(", ")}`);
|
|
362
|
+
}
|
|
363
|
+
const hosts = get("DNSID_PRIVATE_HOSTS")?.split(",").map((h) => h.trim()).filter(Boolean);
|
|
364
|
+
const dnsid = compact({
|
|
365
|
+
identity,
|
|
366
|
+
verification: compact({ dnssecMode }),
|
|
367
|
+
transport: compact({
|
|
368
|
+
dnsServer: get("DNSID_DNS_SERVER"),
|
|
369
|
+
caBundlePath: get("DNSID_CA_BUNDLE"),
|
|
370
|
+
privateAddressHosts: hosts?.length ? hosts : void 0
|
|
371
|
+
})
|
|
372
|
+
});
|
|
373
|
+
const policyFile = get("DNSID_LOG_POLICY_FILE");
|
|
374
|
+
const profileFile = get("DNSID_LOG_TRUST_PROFILE_FILE");
|
|
375
|
+
const logTrust = compact({
|
|
376
|
+
policyUrl: get("DNSID_LOG_POLICY_URL"),
|
|
377
|
+
policyDocument: policyFile === void 0 ? void 0 : new Uint8Array(await fs3.readFile(policyFile)),
|
|
378
|
+
profile: profileFile === void 0 ? void 0 : jsonObject(await fs3.readFile(profileFile), profileFile)
|
|
379
|
+
});
|
|
380
|
+
return top({
|
|
381
|
+
dnsid,
|
|
382
|
+
logTrust,
|
|
383
|
+
registry: compact({ registryUrl: get("DNSID_REGISTRY_URL") }),
|
|
384
|
+
keySource: compact({ cliDirectory: get("DNSID_CONFIG_DIR"), keyStorePath: get("DNSID_KEY_STORE") })
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async function loadFile(filePath) {
|
|
388
|
+
const root = jsonObject(await fs3.readFile(filePath), filePath);
|
|
389
|
+
rejectUnknown(root, filePath, ["dnsid", "logTrust", "registry"]);
|
|
390
|
+
const loaded = {};
|
|
391
|
+
if (root.dnsid !== void 0) loaded.dnsid = loadedDnsid(root.dnsid, `${filePath}: dnsid`);
|
|
392
|
+
if (root.logTrust !== void 0) {
|
|
393
|
+
const trust = object(root.logTrust, `${filePath}: logTrust`);
|
|
394
|
+
rejectUnknown(trust, `${filePath}: logTrust`, ["managed", "profile", "policyUrl"]);
|
|
395
|
+
loaded.logTrust = compact({
|
|
396
|
+
managed: optional(trust, "managed", "boolean", `${filePath}: logTrust`),
|
|
397
|
+
profile: trust.profile === void 0 ? void 0 : object(trust.profile, `${filePath}: logTrust.profile`),
|
|
398
|
+
policyUrl: optional(trust, "policyUrl", "string", `${filePath}: logTrust`)
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (root.registry !== void 0) {
|
|
402
|
+
const registry = object(root.registry, `${filePath}: registry`);
|
|
403
|
+
rejectUnknown(registry, `${filePath}: registry`, ["registryUrl"]);
|
|
404
|
+
loaded.registry = compact({ registryUrl: optional(registry, "registryUrl", "string", `${filePath}: registry`) });
|
|
405
|
+
}
|
|
406
|
+
return loaded;
|
|
407
|
+
}
|
|
408
|
+
function loadedDnsid(value, source) {
|
|
409
|
+
const raw = object(value, source);
|
|
410
|
+
rejectUnknown(raw, source, ["identity", "verification", "transport"]);
|
|
411
|
+
if (raw.identity !== void 0) {
|
|
412
|
+
const identity = object(raw.identity, `${source}.identity`);
|
|
413
|
+
const keys = ["domain", "governanceId", "logRef", "statusUrl", "policyFlags", "maxKeyAge", "ekUrl", "kuUrl", "publishProfile", "capabilitiesUrl"];
|
|
414
|
+
rejectUnknown(identity, `${source}.identity`, keys);
|
|
415
|
+
for (const key of keys) optional(identity, key, "string", `${source}.identity`);
|
|
416
|
+
}
|
|
417
|
+
if (raw.verification !== void 0) {
|
|
418
|
+
const verification = object(raw.verification, `${source}.verification`);
|
|
419
|
+
rejectUnknown(verification, `${source}.verification`, ["statusCheckInterval", "dnssecMode", "trustedEntities"]);
|
|
420
|
+
if (verification.statusCheckInterval !== void 0 && (typeof verification.statusCheckInterval !== "number" || !Number.isFinite(verification.statusCheckInterval))) {
|
|
421
|
+
throw new import_protocol3.ArgumentError(`${source}.verification.statusCheckInterval must be a finite number`);
|
|
422
|
+
}
|
|
423
|
+
const mode = optional(verification, "dnssecMode", "string", `${source}.verification`);
|
|
424
|
+
if (mode !== void 0 && !Object.values(import_protocol3.DNSSECMode).includes(mode)) {
|
|
425
|
+
throw new import_protocol3.ArgumentError(`${source}.verification.dnssecMode is invalid`);
|
|
426
|
+
}
|
|
427
|
+
if (verification.trustedEntities !== void 0) {
|
|
428
|
+
if (!Array.isArray(verification.trustedEntities)) throw new import_protocol3.ArgumentError(`${source}.verification.trustedEntities must be an array`);
|
|
429
|
+
for (const [index, entry] of verification.trustedEntities.entries()) {
|
|
430
|
+
const label = `${source}.verification.trustedEntities[${index}]`;
|
|
431
|
+
const entity = object(entry, label);
|
|
432
|
+
rejectUnknown(entity, label, ["governanceId", "entityKeyThumbprints"]);
|
|
433
|
+
if (typeof entity.governanceId !== "string") throw new import_protocol3.ArgumentError(`${label}.governanceId must be a string`);
|
|
434
|
+
if (entity.entityKeyThumbprints !== void 0 && (!Array.isArray(entity.entityKeyThumbprints) || entity.entityKeyThumbprints.some((pin) => typeof pin !== "string"))) {
|
|
435
|
+
throw new import_protocol3.ArgumentError(`${label}.entityKeyThumbprints must be an array of strings`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (raw.transport !== void 0) {
|
|
441
|
+
const transport = object(raw.transport, `${source}.transport`);
|
|
442
|
+
rejectUnknown(transport, `${source}.transport`, ["dnsServer", "caBundlePath", "privateAddressHosts"]);
|
|
443
|
+
for (const key of ["dnsServer", "caBundlePath"]) optional(transport, key, "string", `${source}.transport`);
|
|
444
|
+
if (transport.privateAddressHosts !== void 0 && (!Array.isArray(transport.privateAddressHosts) || transport.privateAddressHosts.some((host) => typeof host !== "string"))) {
|
|
445
|
+
throw new import_protocol3.ArgumentError(`${source}.transport.privateAddressHosts must be an array of strings`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return raw;
|
|
449
|
+
}
|
|
450
|
+
var CLI_IDENTITY_FIELDS = {
|
|
451
|
+
domain: "domain",
|
|
452
|
+
governance_id: "governanceId",
|
|
453
|
+
status_url: "statusUrl",
|
|
454
|
+
log_ref: "logRef",
|
|
455
|
+
ek_url: "ekUrl",
|
|
456
|
+
ku_url: "kuUrl",
|
|
457
|
+
capabilities_url: "capabilitiesUrl",
|
|
458
|
+
publish_profile: "publishProfile",
|
|
459
|
+
max_key_age: "maxKeyAge"
|
|
460
|
+
};
|
|
461
|
+
async function loadCliDirectory(dnsidDir = path2.join(os2.homedir(), ".dnsid")) {
|
|
470
462
|
let configPath = path2.join(dnsidDir, "config.json");
|
|
471
|
-
let raw
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
463
|
+
let raw;
|
|
464
|
+
try {
|
|
465
|
+
raw = jsonObject(await fs3.readFile(configPath), configPath);
|
|
466
|
+
} catch (e) {
|
|
467
|
+
if (e.code !== "ENOENT") throw e;
|
|
468
|
+
throw new Error(`${configPath} not found; run the DNSid CLI to register your domain first`);
|
|
469
|
+
}
|
|
470
|
+
if (typeof raw.domain === "string" && raw.domain !== "") {
|
|
471
|
+
const identityConfigPath = path2.join(dnsidDir, (0, import_protocol3.normalizeFQDN)(raw.domain, true), "config.json");
|
|
475
472
|
try {
|
|
476
|
-
raw = await
|
|
473
|
+
raw = jsonObject(await fs3.readFile(identityConfigPath), identityConfigPath);
|
|
477
474
|
configPath = identityConfigPath;
|
|
478
475
|
} catch (e) {
|
|
479
476
|
if (e.code !== "ENOENT") throw e;
|
|
480
477
|
}
|
|
481
478
|
}
|
|
482
|
-
const
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
};
|
|
494
|
-
const identity = { ...definedOnly(loaded), ...definedOnly(options.config?.identity ?? {}) };
|
|
495
|
-
if (!identity.domain) throw new Error(`${configPath} must include domain`);
|
|
496
|
-
if (!identity.governanceId) throw new Error(`${configPath} must include governanceId or governance_id`);
|
|
497
|
-
identity.logRef ??= "noop:0";
|
|
498
|
-
identity.statusUrl ??= protocolStatusUrl2(field("registryUrl", "registry_url", "server_url") ?? import_registry2.DEFAULT_REGISTRY_URL, identity.domain);
|
|
499
|
-
const keyProvider = deps.keyProvider ?? await LocalKeyProvider.fromDirectory(await identityKeyDir(dnsidDir, identity.domain));
|
|
500
|
-
const entityKeyProvider = deps.entityKeyProvider ?? await optionalEntityKeyProvider(
|
|
501
|
-
path2.dirname(configPath),
|
|
502
|
-
field("entityKeyPath", "entity_key_path"),
|
|
503
|
-
field("entityKeyDir", "entity_key_dir")
|
|
504
|
-
);
|
|
505
|
-
return createNodeIdentityManager(
|
|
506
|
-
{ identity, verification: options.config?.verification, transport: options.config?.transport },
|
|
507
|
-
{ ...deps, keyProvider, entityKeyProvider }
|
|
508
|
-
);
|
|
479
|
+
const str = (key) => optional(raw, key, "string", configPath) || void 0;
|
|
480
|
+
const identity = compact(Object.fromEntries(
|
|
481
|
+
Object.entries(CLI_IDENTITY_FIELDS).map(([key, field]) => [field, str(key)])
|
|
482
|
+
));
|
|
483
|
+
const entityKeyPath = str("entity_key_path");
|
|
484
|
+
return top({
|
|
485
|
+
dnsid: compact({ identity }),
|
|
486
|
+
keySource: compact({
|
|
487
|
+
cliDirectory: dnsidDir,
|
|
488
|
+
entityKeyPath: entityKeyPath === void 0 ? void 0 : path2.resolve(path2.dirname(configPath), entityKeyPath)
|
|
489
|
+
})
|
|
490
|
+
});
|
|
509
491
|
}
|
|
510
|
-
function
|
|
511
|
-
return
|
|
492
|
+
function mergeLoadedConfig(base, overlay) {
|
|
493
|
+
return top({
|
|
494
|
+
dnsid: mergeDnsid(base.dnsid, overlay.dnsid),
|
|
495
|
+
logTrust: overlay.logTrust ?? base.logTrust,
|
|
496
|
+
registry: mergeSection(base.registry, overlay.registry),
|
|
497
|
+
keySource: mergeSection(base.keySource, overlay.keySource)
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function mergeDnsid(base, overlay) {
|
|
501
|
+
if (!base || !overlay) return overlay ?? base;
|
|
502
|
+
return compact({
|
|
503
|
+
identity: mergeSection(base.identity, overlay.identity),
|
|
504
|
+
verification: mergeSection(base.verification, overlay.verification),
|
|
505
|
+
transport: mergeSection(base.transport, overlay.transport)
|
|
506
|
+
});
|
|
512
507
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
508
|
+
function mergeSection(base, overlay) {
|
|
509
|
+
if (!base || !overlay) return overlay ?? base;
|
|
510
|
+
return { ...base, ...definedEntries(overlay) };
|
|
511
|
+
}
|
|
512
|
+
var MANAGED_DEFAULTS_MS = 10 * 60 * 1e3;
|
|
513
|
+
async function constructIdentityManager(loaded, deps = {}) {
|
|
514
|
+
(0, import_protocol3.validateDnsidConfig)(loaded.dnsid ?? {});
|
|
515
|
+
const filled = { ...deps };
|
|
516
|
+
if (!filled.logRegistry && loaded.logTrust) {
|
|
517
|
+
filled.logRegistry = await logRegistryFromTrust(loaded.logTrust, loaded.dnsid?.transport);
|
|
518
|
+
}
|
|
519
|
+
const identity = loaded.dnsid?.identity;
|
|
520
|
+
if (identity && loaded.keySource) {
|
|
521
|
+
if (!filled.keyProvider) filled.keyProvider = await operationalKeyProvider(loaded.keySource, identity.domain);
|
|
522
|
+
if (!filled.entityKeyProvider && loaded.keySource.entityKeyPath !== void 0) {
|
|
523
|
+
filled.entityKeyProvider = await LocalKeyProvider.fromFile(loaded.keySource.entityKeyPath);
|
|
517
524
|
}
|
|
518
|
-
|
|
525
|
+
}
|
|
526
|
+
return createNodeIdentityManager(loaded.dnsid ?? {}, filled);
|
|
527
|
+
}
|
|
528
|
+
async function logRegistryFromTrust(trust, transport) {
|
|
529
|
+
const variants = ["managed", "profile", "policyDocument", "policyUrl"].filter((k) => trust[k] !== void 0);
|
|
530
|
+
if (variants.length !== 1) {
|
|
531
|
+
throw new import_protocol3.ArgumentError(`logTrust requires exactly one of managed, profile, policyDocument, policyUrl; got ${variants.length}`);
|
|
532
|
+
}
|
|
533
|
+
if (trust.managed !== void 0) {
|
|
534
|
+
if (trust.managed !== true) throw new import_protocol3.ArgumentError("logTrust.managed must be true when present");
|
|
535
|
+
return (0, import_log_c2sp_tlog.createDnsidManagedVerificationRegistry)();
|
|
536
|
+
}
|
|
537
|
+
return (0, import_log_c2sp_tlog.createC2spTlogVerificationRegistry)({
|
|
538
|
+
trustProfile: trust.profile && (0, import_log_c2sp_tlog.parseC2spTlogTrustProfile)(new TextEncoder().encode(JSON.stringify(trust.profile))),
|
|
539
|
+
policyDocument: trust.policyDocument,
|
|
540
|
+
policyUrl: trust.policyUrl,
|
|
541
|
+
transport,
|
|
542
|
+
checkpointMaxAge: MANAGED_DEFAULTS_MS,
|
|
543
|
+
maxBundleLifetimeMs: trust.profile ? MANAGED_DEFAULTS_MS : void 0,
|
|
544
|
+
allowedClockSkew: 0
|
|
519
545
|
});
|
|
520
546
|
}
|
|
521
|
-
async function
|
|
522
|
-
if (
|
|
523
|
-
return LocalKeyProvider.
|
|
547
|
+
async function operationalKeyProvider(source, domain) {
|
|
548
|
+
if (source.cliDirectory !== void 0) {
|
|
549
|
+
return LocalKeyProvider.fromDirectory(await identityKeyDir(source.cliDirectory, domain));
|
|
524
550
|
}
|
|
525
|
-
if (
|
|
526
|
-
return
|
|
551
|
+
if (source.keyStorePath !== void 0) return LocalKeyProvider.load(source.keyStorePath);
|
|
552
|
+
return void 0;
|
|
527
553
|
}
|
|
528
554
|
async function identityKeyDir(dnsidDir, domain) {
|
|
529
555
|
for (const filename of ["private.jwk", "private.pem"]) {
|
|
@@ -536,36 +562,66 @@ async function identityKeyDir(dnsidDir, domain) {
|
|
|
536
562
|
}
|
|
537
563
|
return path2.join(dnsidDir, (0, import_protocol3.normalizeFQDN)(domain, true));
|
|
538
564
|
}
|
|
539
|
-
function
|
|
540
|
-
return
|
|
565
|
+
function createNodeIdentityManagerFromEnvironment(env, overlay, deps) {
|
|
566
|
+
return loadEnvironment(env).then((loaded) => constructIdentityManager(mergeLoadedConfig(loaded, top({ dnsid: overlay })), deps));
|
|
541
567
|
}
|
|
542
|
-
function
|
|
543
|
-
|
|
544
|
-
if (["24h", "7d", "30d", "90d"].includes(String(value))) return value;
|
|
545
|
-
throw new Error(`${configPath} has invalid maxKeyAge "${String(value)}"`);
|
|
568
|
+
function createNodeIdentityManagerFromDnsid(dnsidDir, overlay, deps) {
|
|
569
|
+
return loadCliDirectory(dnsidDir).then((loaded) => constructIdentityManager(mergeLoadedConfig(loaded, top({ dnsid: overlay })), deps));
|
|
546
570
|
}
|
|
547
|
-
function
|
|
548
|
-
return
|
|
571
|
+
function createNodeIdentityManagerFromFile(filePath, overlay, deps) {
|
|
572
|
+
return loadFile(filePath).then((loaded) => constructIdentityManager(mergeLoadedConfig(loaded, top({ dnsid: overlay })), deps));
|
|
549
573
|
}
|
|
550
|
-
async function
|
|
574
|
+
async function createRegistryClientFromEnvironment(env) {
|
|
575
|
+
const source = env ?? process.env;
|
|
576
|
+
const loaded = await loadEnvironment(source);
|
|
577
|
+
return new import_registry.RegistryClient(compact({ baseUrl: loaded.registry?.registryUrl, token: source.DNSID_API_KEY?.trim() || void 0 }));
|
|
578
|
+
}
|
|
579
|
+
function definedEntries(value) {
|
|
580
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
581
|
+
}
|
|
582
|
+
function top(value) {
|
|
583
|
+
return definedEntries(value);
|
|
584
|
+
}
|
|
585
|
+
function compact(value) {
|
|
586
|
+
const defined = definedEntries(value);
|
|
587
|
+
return Object.keys(defined).length > 0 ? defined : void 0;
|
|
588
|
+
}
|
|
589
|
+
function jsonObject(bytes, source) {
|
|
590
|
+
let value;
|
|
551
591
|
try {
|
|
552
|
-
|
|
592
|
+
value = (0, import_protocol3.parseJsonNoDuplicateMembers)(bytes);
|
|
553
593
|
} catch (e) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
594
|
+
throw new import_protocol3.ArgumentError(`${source}: ${e.message}`);
|
|
595
|
+
}
|
|
596
|
+
return object(value, source);
|
|
597
|
+
}
|
|
598
|
+
function object(value, source) {
|
|
599
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new import_protocol3.ArgumentError(`${source} must be a JSON object`);
|
|
600
|
+
return value;
|
|
601
|
+
}
|
|
602
|
+
function rejectUnknown(raw, source, allowed) {
|
|
603
|
+
for (const key of Object.keys(raw)) {
|
|
604
|
+
if (!allowed.includes(key)) throw new import_protocol3.ArgumentError(`${source} has unknown member "${key}"`);
|
|
559
605
|
}
|
|
560
606
|
}
|
|
607
|
+
function optional(raw, key, type, source) {
|
|
608
|
+
const value = raw[key];
|
|
609
|
+
if (value === void 0) return void 0;
|
|
610
|
+
if (typeof value !== type) throw new import_protocol3.ArgumentError(`${source}.${key} must be a ${type}`);
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
561
613
|
// Annotate the CommonJS export names for ESM import in node:
|
|
562
614
|
0 && (module.exports = {
|
|
563
615
|
LocalKeyProvider,
|
|
564
|
-
|
|
616
|
+
constructIdentityManager,
|
|
565
617
|
createNodeIdentityManager,
|
|
566
618
|
createNodeIdentityManagerFromDnsid,
|
|
619
|
+
createNodeIdentityManagerFromEnvironment,
|
|
620
|
+
createNodeIdentityManagerFromFile,
|
|
567
621
|
createNodeIdentityVerifier,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
622
|
+
createRegistryClientFromEnvironment,
|
|
623
|
+
loadCliDirectory,
|
|
624
|
+
loadEnvironment,
|
|
625
|
+
loadFile,
|
|
626
|
+
mergeLoadedConfig
|
|
571
627
|
});
|