@dnsid-ai/sdk 0.25.0 → 0.26.0
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 +25 -0
- package/dist/node.cjs +79 -7
- package/dist/node.d.cts +25 -6
- package/dist/node.d.ts +25 -6
- package/dist/node.js +80 -7
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -124,6 +124,31 @@ The CLI loader maps persisted fields as written: a missing `status_url` or `log_
|
|
|
124
124
|
|
|
125
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`.
|
|
126
126
|
|
|
127
|
+
## Registration and provider groundwork
|
|
128
|
+
|
|
129
|
+
Deployment files support `registration.organizationId`, optional `governanceId`, and an
|
|
130
|
+
independently selected `entityKeyUrl`. These are setup expectations, not registry-root selectors
|
|
131
|
+
or counterparty acceptance defaults. `RegistryClient.getOrganizationOnboarding()` reads the
|
|
132
|
+
existing authenticated endpoint and exposes organization/GI proof and delegation readiness;
|
|
133
|
+
it does not supply an entity-JWKS URL or establish trust.
|
|
134
|
+
|
|
135
|
+
`keySource` selects file custody or an optional existing AWS KMS key using `provider`, `keyRef`,
|
|
136
|
+
and non-secret `settings` (`region` and EdDSA/ES256 `algorithm`). Injected providers win, and
|
|
137
|
+
only the selected factory is loaded. AWS authentication uses ambient credential chains, not
|
|
138
|
+
secrets in deployment files. Unavailable factories or invalid settings fail without file fallback.
|
|
139
|
+
Ordinary manager construction opens existing keys only; generation requires workflow coordination.
|
|
140
|
+
Configuration-selected file and AWS keys must match the current operational key of the verified published
|
|
141
|
+
identity, including key ID, algorithm, and public material. Missing publication or unavailable
|
|
142
|
+
verification fails construction. Initial setup supplies an injected provider and owns its binding
|
|
143
|
+
checks. Selecting local files emits a production-safety warning.
|
|
144
|
+
Google KMS and Azure factories are unavailable in this binding.
|
|
145
|
+
|
|
146
|
+
This groundwork does not expose automatic managed registration or named recovery. Those require
|
|
147
|
+
a separate SDK workflow and verified permanent server replay/name claims; the existing expiring
|
|
148
|
+
store is insufficient. Existing low-level registration remains available without assuming those
|
|
149
|
+
server capabilities. Moving custody to another provider requires authorized, publicly verified
|
|
150
|
+
rotation, not configuration alone.
|
|
151
|
+
|
|
127
152
|
## Included surfaces
|
|
128
153
|
|
|
129
154
|
`@dnsid-ai/sdk` re-exports the common core and profile surfaces and namespaces:
|
package/dist/node.cjs
CHANGED
|
@@ -386,9 +386,9 @@ async function loadEnvironment(env = process.env) {
|
|
|
386
386
|
}
|
|
387
387
|
async function loadFile(filePath) {
|
|
388
388
|
const root = jsonObject(await fs3.readFile(filePath), filePath);
|
|
389
|
-
rejectUnknown(root, filePath, ["dnsid", "logTrust", "registry"]);
|
|
389
|
+
rejectUnknown(root, filePath, ["dnsid", "logTrust", "registry", "registration", "keySource"]);
|
|
390
390
|
const loaded = {};
|
|
391
|
-
if (root.dnsid !== void 0) loaded.dnsid =
|
|
391
|
+
if (root.dnsid !== void 0) loaded.dnsid = validateLoadedDnsidConfig(root.dnsid, `${filePath}: dnsid`);
|
|
392
392
|
if (root.logTrust !== void 0) {
|
|
393
393
|
const trust = object(root.logTrust, `${filePath}: logTrust`);
|
|
394
394
|
rejectUnknown(trust, `${filePath}: logTrust`, ["managed", "profile", "policyUrl"]);
|
|
@@ -403,9 +403,32 @@ async function loadFile(filePath) {
|
|
|
403
403
|
rejectUnknown(registry, `${filePath}: registry`, ["registryUrl"]);
|
|
404
404
|
loaded.registry = compact({ registryUrl: optional(registry, "registryUrl", "string", `${filePath}: registry`) });
|
|
405
405
|
}
|
|
406
|
+
if (root.registration !== void 0) {
|
|
407
|
+
const registration = object(root.registration, `${filePath}: registration`);
|
|
408
|
+
rejectUnknown(registration, `${filePath}: registration`, ["organizationId", "governanceId", "entityKeyUrl"]);
|
|
409
|
+
loaded.registration = compact({
|
|
410
|
+
organizationId: optional(registration, "organizationId", "string", `${filePath}: registration`),
|
|
411
|
+
governanceId: optional(registration, "governanceId", "string", `${filePath}: registration`),
|
|
412
|
+
entityKeyUrl: optional(registration, "entityKeyUrl", "string", `${filePath}: registration`)
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
if (root.keySource !== void 0) {
|
|
416
|
+
const source = object(root.keySource, `${filePath}: keySource`);
|
|
417
|
+
rejectUnknown(source, "keySource", ["provider", "keyRef", "generation", "settings", "cliDirectory", "keyStorePath", "entityKeyPath"]);
|
|
418
|
+
for (const field of ["provider", "keyRef", "cliDirectory", "keyStorePath", "entityKeyPath"]) optional(source, field, "string", "keySource");
|
|
419
|
+
if (source.settings !== void 0) object(source.settings, "keySource.settings");
|
|
420
|
+
if (source.generation !== void 0) {
|
|
421
|
+
const generation = object(source.generation, "keySource.generation");
|
|
422
|
+
rejectUnknown(generation, "keySource.generation", ["locator", "algorithm"]);
|
|
423
|
+
for (const field of ["locator", "algorithm"]) {
|
|
424
|
+
if (typeof generation[field] !== "string") throw new import_protocol3.ArgumentError(`keySource.generation.${field} must be a string`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
loaded.keySource = source;
|
|
428
|
+
}
|
|
406
429
|
return loaded;
|
|
407
430
|
}
|
|
408
|
-
function
|
|
431
|
+
function validateLoadedDnsidConfig(value, source = "dnsid") {
|
|
409
432
|
const raw = object(value, source);
|
|
410
433
|
rejectUnknown(raw, source, ["identity", "verification", "transport"]);
|
|
411
434
|
if (raw.identity !== void 0) {
|
|
@@ -494,9 +517,15 @@ function mergeLoadedConfig(base, overlay) {
|
|
|
494
517
|
dnsid: mergeDnsid(base.dnsid, overlay.dnsid),
|
|
495
518
|
logTrust: overlay.logTrust ?? base.logTrust,
|
|
496
519
|
registry: mergeSection(base.registry, overlay.registry),
|
|
497
|
-
|
|
520
|
+
registration: mergeSection(base.registration, overlay.registration),
|
|
521
|
+
keySource: mergeKeySource(base.keySource, overlay.keySource)
|
|
498
522
|
});
|
|
499
523
|
}
|
|
524
|
+
function mergeKeySource(base, overlay) {
|
|
525
|
+
if (!overlay) return base;
|
|
526
|
+
const replacesOperational = ["provider", "keyRef", "generation", "settings", "cliDirectory", "keyStorePath"].some((field) => overlay[field] !== void 0);
|
|
527
|
+
return mergeSection(replacesOperational ? compact({ entityKeyPath: base?.entityKeyPath }) : base, overlay);
|
|
528
|
+
}
|
|
500
529
|
function mergeDnsid(base, overlay) {
|
|
501
530
|
if (!base || !overlay) return overlay ?? base;
|
|
502
531
|
return compact({
|
|
@@ -523,7 +552,16 @@ async function constructIdentityManager(loaded, deps = {}) {
|
|
|
523
552
|
filled.entityKeyProvider = await LocalKeyProvider.fromFile(loaded.keySource.entityKeyPath);
|
|
524
553
|
}
|
|
525
554
|
}
|
|
526
|
-
|
|
555
|
+
const manager = await createNodeIdentityManager(loaded.dnsid ?? {}, filled);
|
|
556
|
+
if (identity && !deps.keyProvider && filled.keyProvider) {
|
|
557
|
+
const verified = await manager.verifyPublicationEvidence(manager.config.identity.domain);
|
|
558
|
+
const selected = await filled.keyProvider.signingKey();
|
|
559
|
+
const published = verified.jwks.currentOperationalSigningKey();
|
|
560
|
+
if (selected.kid !== published.kid || selected.alg !== published.alg || await (0, import_protocol3.jwkThumbprint)(selected) !== await (0, import_protocol3.jwkThumbprint)(published)) {
|
|
561
|
+
throw new import_protocol3.ArgumentError("configured operational key does not match the verified identity; use authorized key rotation");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return manager;
|
|
527
565
|
}
|
|
528
566
|
async function logRegistryFromTrust(trust, transport) {
|
|
529
567
|
const variants = ["managed", "profile", "policyDocument", "policyUrl"].filter((k) => trust[k] !== void 0);
|
|
@@ -545,12 +583,46 @@ async function logRegistryFromTrust(trust, transport) {
|
|
|
545
583
|
});
|
|
546
584
|
}
|
|
547
585
|
async function operationalKeyProvider(source, domain) {
|
|
586
|
+
await validateKeySource(source);
|
|
587
|
+
if (source.generation) throw new import_protocol3.ArgumentError("identity-manager construction opens existing keys only; use managed setup for generation");
|
|
588
|
+
if (source.provider === "aws-kms") {
|
|
589
|
+
const { createAwsKmsKeyProvider } = await import("@dnsid-ai/key-aws");
|
|
590
|
+
return createAwsKmsKeyProvider(source.keyRef, source.settings);
|
|
591
|
+
}
|
|
592
|
+
if (source.keyRef !== void 0 || source.cliDirectory !== void 0 || source.keyStorePath !== void 0) {
|
|
593
|
+
console.warn("Local private-key files are unsuitable for production. Configure keySource.provider/keyRef for cloud custody.");
|
|
594
|
+
}
|
|
595
|
+
if (source.keyRef !== void 0) return LocalKeyProvider.load(source.keyRef);
|
|
548
596
|
if (source.cliDirectory !== void 0) {
|
|
549
597
|
return LocalKeyProvider.fromDirectory(await identityKeyDir(source.cliDirectory, domain));
|
|
550
598
|
}
|
|
551
599
|
if (source.keyStorePath !== void 0) return LocalKeyProvider.load(source.keyStorePath);
|
|
552
600
|
return void 0;
|
|
553
601
|
}
|
|
602
|
+
async function validateKeySource(source = {}) {
|
|
603
|
+
const provider = source.provider ?? "file";
|
|
604
|
+
if (!["file", "aws-kms", "google-kms", "azure-key-vault"].includes(provider)) throw new import_protocol3.ArgumentError(`unknown key provider: ${provider}`);
|
|
605
|
+
for (const field of ["keyRef", "keyStorePath", "cliDirectory", "entityKeyPath"]) {
|
|
606
|
+
if (source[field] !== void 0 && (typeof source[field] !== "string" || !source[field].trim())) throw new import_protocol3.ArgumentError(`keySource.${field} must be nonempty`);
|
|
607
|
+
}
|
|
608
|
+
if (source.keyRef && source.generation) throw new import_protocol3.ArgumentError("keyRef and generation are mutually exclusive");
|
|
609
|
+
if ((source.cliDirectory || source.keyStorePath) && (provider !== "file" || source.keyRef || source.generation)) throw new import_protocol3.ArgumentError("CLI/keyStorePath cannot combine with cloud, keyRef, or generation");
|
|
610
|
+
if (source.generation && (typeof source.generation.locator !== "string" || !source.generation.locator.trim() || !["EdDSA", "ES256"].includes(source.generation.algorithm))) throw new import_protocol3.ArgumentError("generation requires a stable locator and supported EdDSA or ES256 algorithm");
|
|
611
|
+
if (provider === "file") {
|
|
612
|
+
if (source.settings && Object.keys(source.settings).length) throw new import_protocol3.ArgumentError("file provider has no settings");
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (provider !== "aws-kms") throw new import_protocol3.ArgumentError(`${provider} is unavailable; install/link a supported provider package (Google KMS and Azure factories are not implemented)`);
|
|
616
|
+
if (!source.keyRef || source.generation) throw new import_protocol3.ArgumentError("AWS KMS requires an existing keyRef; atomic generation discovery is not supported");
|
|
617
|
+
try {
|
|
618
|
+
const factory = await import("@dnsid-ai/key-aws");
|
|
619
|
+
if (typeof factory.createAwsKmsKeyProvider !== "function" || typeof factory.validateAwsKmsSettings !== "function") throw new Error("provider factory unavailable");
|
|
620
|
+
factory.validateAwsKmsSettings(source.settings);
|
|
621
|
+
} catch (cause) {
|
|
622
|
+
if (cause instanceof import_protocol3.ArgumentError) throw cause;
|
|
623
|
+
throw new import_protocol3.ArgumentError("aws-kms unavailable; install a compatible @dnsid-ai/key-aws and include it in the build");
|
|
624
|
+
}
|
|
625
|
+
}
|
|
554
626
|
async function identityKeyDir(dnsidDir, domain) {
|
|
555
627
|
for (const filename of ["private.jwk", "private.pem"]) {
|
|
556
628
|
try {
|
|
@@ -571,10 +643,10 @@ function createNodeIdentityManagerFromDnsid(dnsidDir, overlay, deps) {
|
|
|
571
643
|
function createNodeIdentityManagerFromFile(filePath, overlay, deps) {
|
|
572
644
|
return loadFile(filePath).then((loaded) => constructIdentityManager(mergeLoadedConfig(loaded, top({ dnsid: overlay })), deps));
|
|
573
645
|
}
|
|
574
|
-
async function createRegistryClientFromEnvironment(env) {
|
|
646
|
+
async function createRegistryClientFromEnvironment(env, options = {}) {
|
|
575
647
|
const source = env ?? process.env;
|
|
576
648
|
const loaded = await loadEnvironment(source);
|
|
577
|
-
return new import_registry.RegistryClient(
|
|
649
|
+
return new import_registry.RegistryClient({ baseUrl: loaded.registry?.registryUrl, token: source.DNSID_API_KEY?.trim() || void 0, ...options });
|
|
578
650
|
}
|
|
579
651
|
function definedEntries(value) {
|
|
580
652
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
package/dist/node.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CreateIdentityManagerDependencies } from './index.cjs';
|
|
2
2
|
import * as _dnsid_ai_protocol from '@dnsid-ai/protocol';
|
|
3
3
|
import { KeyProvider, DnsIdJWK, DnsidConfig, IdentityManagerDependencies, IdentityManager, IdentityConfig } from '@dnsid-ai/protocol';
|
|
4
|
-
import { RegistryClient } from '@dnsid-ai/registry';
|
|
4
|
+
import { RegistryClientOptions, RegistryClient } from '@dnsid-ai/registry';
|
|
5
5
|
import '@dnsid-ai/jose';
|
|
6
6
|
import '@dnsid-ai/http-signatures';
|
|
7
7
|
import '@dnsid-ai/web-bot-auth';
|
|
@@ -76,6 +76,15 @@ interface LogTrust {
|
|
|
76
76
|
policyUrl?: string;
|
|
77
77
|
}
|
|
78
78
|
interface KeySource {
|
|
79
|
+
provider?: 'file' | 'aws-kms' | 'google-kms' | 'azure-key-vault';
|
|
80
|
+
/** Existing stable key reference; never combined with generation. */
|
|
81
|
+
keyRef?: string;
|
|
82
|
+
/** File generation locator; cloud factories without atomic discovery require keyRef. */
|
|
83
|
+
generation?: {
|
|
84
|
+
locator: string;
|
|
85
|
+
algorithm: string;
|
|
86
|
+
};
|
|
87
|
+
settings?: Record<string, unknown>;
|
|
79
88
|
/** DNSid CLI identity directory; key files are located under the effective identity domain. */
|
|
80
89
|
cliDirectory?: string;
|
|
81
90
|
/** Accountable-entity private JWK file. */
|
|
@@ -83,6 +92,11 @@ interface KeySource {
|
|
|
83
92
|
/** `LocalKeyProvider` key-store file; used only when `cliDirectory` is absent. */
|
|
84
93
|
keyStorePath?: string;
|
|
85
94
|
}
|
|
95
|
+
interface ManagedRegistrationConfig {
|
|
96
|
+
organizationId?: string;
|
|
97
|
+
governanceId?: string;
|
|
98
|
+
entityKeyUrl: string;
|
|
99
|
+
}
|
|
86
100
|
interface LoadedRegistryConfig {
|
|
87
101
|
registryUrl?: string;
|
|
88
102
|
}
|
|
@@ -95,12 +109,14 @@ interface LoadedConfig {
|
|
|
95
109
|
dnsid?: LoadedDnsidConfig;
|
|
96
110
|
logTrust?: LogTrust;
|
|
97
111
|
registry?: LoadedRegistryConfig;
|
|
112
|
+
/** Setup expectations only; never identity or counterparty acceptance defaults. */
|
|
113
|
+
registration?: Partial<ManagedRegistrationConfig>;
|
|
98
114
|
keySource?: KeySource;
|
|
99
115
|
}
|
|
100
116
|
/** Reads configuration from `DNSID_*`. Secrets such as `DNSID_API_KEY` stay out of `LoadedConfig`. */
|
|
101
117
|
declare function loadEnvironment(env?: EnvironmentSource): Promise<LoadedConfig>;
|
|
102
118
|
/**
|
|
103
|
-
* Reads a JSON deployment file: `{ dnsid?, logTrust?, registry? }`. Unknown members, mistyped
|
|
119
|
+
* Reads a JSON deployment file: `{ dnsid?, logTrust?, registry?, registration? }`. Unknown members, mistyped
|
|
104
120
|
* values, and duplicate members are rejected; semantic `dnsid` validation stays with the constructor.
|
|
105
121
|
*/
|
|
106
122
|
declare function loadFile(filePath: string): Promise<LoadedConfig>;
|
|
@@ -113,18 +129,21 @@ declare function loadFile(filePath: string): Promise<LoadedConfig>;
|
|
|
113
129
|
declare function loadCliDirectory(dnsidDir?: string): Promise<LoadedConfig>;
|
|
114
130
|
/**
|
|
115
131
|
* Field-wise merge; presence wins, not truthiness. Lists replace. `logTrust` is replaced as a
|
|
116
|
-
* whole section when `overlay` sets any variant.
|
|
132
|
+
* whole section when `overlay` sets any variant. Operational key-source fields replace together;
|
|
133
|
+
* `entityKeyPath` merges independently.
|
|
117
134
|
*/
|
|
118
135
|
declare function mergeLoadedConfig(base: LoadedConfig, overlay: LoadedConfig): LoadedConfig;
|
|
119
136
|
/**
|
|
120
137
|
* Fills `deps.logRegistry` from `logTrust` and key providers from `keySource` only when the caller
|
|
121
138
|
* did not supply them, then calls {@link createNodeIdentityManager}. Adds no configuration values.
|
|
139
|
+
* Configuration-selected operational keys must match verified local publication before return.
|
|
140
|
+
* Initial setup injects its provider and owns its binding checks; missing publication fails closed.
|
|
122
141
|
*/
|
|
123
142
|
declare function constructIdentityManager(loaded: LoadedConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
124
143
|
declare function createNodeIdentityManagerFromEnvironment(env?: EnvironmentSource, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
125
144
|
declare function createNodeIdentityManagerFromDnsid(dnsidDir?: string, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
126
145
|
declare function createNodeIdentityManagerFromFile(filePath: string, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
127
|
-
/** `RegistryClient` from `DNSID_REGISTRY_URL` and `DNSID_API_KEY`;
|
|
128
|
-
declare function createRegistryClientFromEnvironment(env?: EnvironmentSource): Promise<RegistryClient>;
|
|
146
|
+
/** `RegistryClient` from `DNSID_REGISTRY_URL` and `DNSID_API_KEY`; explicit options win, including transport overrides. The constructor defaults to the local registry. */
|
|
147
|
+
declare function createRegistryClientFromEnvironment(env?: EnvironmentSource, options?: RegistryClientOptions): Promise<RegistryClient>;
|
|
129
148
|
|
|
130
|
-
export { type EnvironmentSource, type KeySource, type LoadedConfig, type LoadedDnsidConfig, type LoadedRegistryConfig, type LocalKeyAlgorithm, LocalKeyProvider, type LogTrust, constructIdentityManager, createNodeIdentityManager, createNodeIdentityManagerFromDnsid, createNodeIdentityManagerFromEnvironment, createNodeIdentityManagerFromFile, createNodeIdentityVerifier, createRegistryClientFromEnvironment, loadCliDirectory, loadEnvironment, loadFile, mergeLoadedConfig };
|
|
149
|
+
export { type EnvironmentSource, type KeySource, type LoadedConfig, type LoadedDnsidConfig, type LoadedRegistryConfig, type LocalKeyAlgorithm, LocalKeyProvider, type LogTrust, type ManagedRegistrationConfig, constructIdentityManager, createNodeIdentityManager, createNodeIdentityManagerFromDnsid, createNodeIdentityManagerFromEnvironment, createNodeIdentityManagerFromFile, createNodeIdentityVerifier, createRegistryClientFromEnvironment, loadCliDirectory, loadEnvironment, loadFile, mergeLoadedConfig };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CreateIdentityManagerDependencies } from './index.js';
|
|
2
2
|
import * as _dnsid_ai_protocol from '@dnsid-ai/protocol';
|
|
3
3
|
import { KeyProvider, DnsIdJWK, DnsidConfig, IdentityManagerDependencies, IdentityManager, IdentityConfig } from '@dnsid-ai/protocol';
|
|
4
|
-
import { RegistryClient } from '@dnsid-ai/registry';
|
|
4
|
+
import { RegistryClientOptions, RegistryClient } from '@dnsid-ai/registry';
|
|
5
5
|
import '@dnsid-ai/jose';
|
|
6
6
|
import '@dnsid-ai/http-signatures';
|
|
7
7
|
import '@dnsid-ai/web-bot-auth';
|
|
@@ -76,6 +76,15 @@ interface LogTrust {
|
|
|
76
76
|
policyUrl?: string;
|
|
77
77
|
}
|
|
78
78
|
interface KeySource {
|
|
79
|
+
provider?: 'file' | 'aws-kms' | 'google-kms' | 'azure-key-vault';
|
|
80
|
+
/** Existing stable key reference; never combined with generation. */
|
|
81
|
+
keyRef?: string;
|
|
82
|
+
/** File generation locator; cloud factories without atomic discovery require keyRef. */
|
|
83
|
+
generation?: {
|
|
84
|
+
locator: string;
|
|
85
|
+
algorithm: string;
|
|
86
|
+
};
|
|
87
|
+
settings?: Record<string, unknown>;
|
|
79
88
|
/** DNSid CLI identity directory; key files are located under the effective identity domain. */
|
|
80
89
|
cliDirectory?: string;
|
|
81
90
|
/** Accountable-entity private JWK file. */
|
|
@@ -83,6 +92,11 @@ interface KeySource {
|
|
|
83
92
|
/** `LocalKeyProvider` key-store file; used only when `cliDirectory` is absent. */
|
|
84
93
|
keyStorePath?: string;
|
|
85
94
|
}
|
|
95
|
+
interface ManagedRegistrationConfig {
|
|
96
|
+
organizationId?: string;
|
|
97
|
+
governanceId?: string;
|
|
98
|
+
entityKeyUrl: string;
|
|
99
|
+
}
|
|
86
100
|
interface LoadedRegistryConfig {
|
|
87
101
|
registryUrl?: string;
|
|
88
102
|
}
|
|
@@ -95,12 +109,14 @@ interface LoadedConfig {
|
|
|
95
109
|
dnsid?: LoadedDnsidConfig;
|
|
96
110
|
logTrust?: LogTrust;
|
|
97
111
|
registry?: LoadedRegistryConfig;
|
|
112
|
+
/** Setup expectations only; never identity or counterparty acceptance defaults. */
|
|
113
|
+
registration?: Partial<ManagedRegistrationConfig>;
|
|
98
114
|
keySource?: KeySource;
|
|
99
115
|
}
|
|
100
116
|
/** Reads configuration from `DNSID_*`. Secrets such as `DNSID_API_KEY` stay out of `LoadedConfig`. */
|
|
101
117
|
declare function loadEnvironment(env?: EnvironmentSource): Promise<LoadedConfig>;
|
|
102
118
|
/**
|
|
103
|
-
* Reads a JSON deployment file: `{ dnsid?, logTrust?, registry? }`. Unknown members, mistyped
|
|
119
|
+
* Reads a JSON deployment file: `{ dnsid?, logTrust?, registry?, registration? }`. Unknown members, mistyped
|
|
104
120
|
* values, and duplicate members are rejected; semantic `dnsid` validation stays with the constructor.
|
|
105
121
|
*/
|
|
106
122
|
declare function loadFile(filePath: string): Promise<LoadedConfig>;
|
|
@@ -113,18 +129,21 @@ declare function loadFile(filePath: string): Promise<LoadedConfig>;
|
|
|
113
129
|
declare function loadCliDirectory(dnsidDir?: string): Promise<LoadedConfig>;
|
|
114
130
|
/**
|
|
115
131
|
* Field-wise merge; presence wins, not truthiness. Lists replace. `logTrust` is replaced as a
|
|
116
|
-
* whole section when `overlay` sets any variant.
|
|
132
|
+
* whole section when `overlay` sets any variant. Operational key-source fields replace together;
|
|
133
|
+
* `entityKeyPath` merges independently.
|
|
117
134
|
*/
|
|
118
135
|
declare function mergeLoadedConfig(base: LoadedConfig, overlay: LoadedConfig): LoadedConfig;
|
|
119
136
|
/**
|
|
120
137
|
* Fills `deps.logRegistry` from `logTrust` and key providers from `keySource` only when the caller
|
|
121
138
|
* did not supply them, then calls {@link createNodeIdentityManager}. Adds no configuration values.
|
|
139
|
+
* Configuration-selected operational keys must match verified local publication before return.
|
|
140
|
+
* Initial setup injects its provider and owns its binding checks; missing publication fails closed.
|
|
122
141
|
*/
|
|
123
142
|
declare function constructIdentityManager(loaded: LoadedConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
124
143
|
declare function createNodeIdentityManagerFromEnvironment(env?: EnvironmentSource, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
125
144
|
declare function createNodeIdentityManagerFromDnsid(dnsidDir?: string, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
126
145
|
declare function createNodeIdentityManagerFromFile(filePath: string, overlay?: LoadedDnsidConfig, deps?: IdentityManagerDependencies): Promise<_dnsid_ai_protocol.IdentityManager>;
|
|
127
|
-
/** `RegistryClient` from `DNSID_REGISTRY_URL` and `DNSID_API_KEY`;
|
|
128
|
-
declare function createRegistryClientFromEnvironment(env?: EnvironmentSource): Promise<RegistryClient>;
|
|
146
|
+
/** `RegistryClient` from `DNSID_REGISTRY_URL` and `DNSID_API_KEY`; explicit options win, including transport overrides. The constructor defaults to the local registry. */
|
|
147
|
+
declare function createRegistryClientFromEnvironment(env?: EnvironmentSource, options?: RegistryClientOptions): Promise<RegistryClient>;
|
|
129
148
|
|
|
130
|
-
export { type EnvironmentSource, type KeySource, type LoadedConfig, type LoadedDnsidConfig, type LoadedRegistryConfig, type LocalKeyAlgorithm, LocalKeyProvider, type LogTrust, constructIdentityManager, createNodeIdentityManager, createNodeIdentityManagerFromDnsid, createNodeIdentityManagerFromEnvironment, createNodeIdentityManagerFromFile, createNodeIdentityVerifier, createRegistryClientFromEnvironment, loadCliDirectory, loadEnvironment, loadFile, mergeLoadedConfig };
|
|
149
|
+
export { type EnvironmentSource, type KeySource, type LoadedConfig, type LoadedDnsidConfig, type LoadedRegistryConfig, type LocalKeyAlgorithm, LocalKeyProvider, type LogTrust, type ManagedRegistrationConfig, constructIdentityManager, createNodeIdentityManager, createNodeIdentityManagerFromDnsid, createNodeIdentityManagerFromEnvironment, createNodeIdentityManagerFromFile, createNodeIdentityVerifier, createRegistryClientFromEnvironment, loadCliDirectory, loadEnvironment, loadFile, mergeLoadedConfig };
|
package/dist/node.js
CHANGED
|
@@ -292,6 +292,7 @@ import {
|
|
|
292
292
|
ArgumentError as ArgumentError3,
|
|
293
293
|
DNSSECMode,
|
|
294
294
|
normalizeFQDN as normalizeFQDN2,
|
|
295
|
+
jwkThumbprint as jwkThumbprint2,
|
|
295
296
|
parseJsonNoDuplicateMembers,
|
|
296
297
|
validateDnsidConfig as validateDnsidConfig2
|
|
297
298
|
} from "@dnsid-ai/protocol";
|
|
@@ -349,9 +350,9 @@ async function loadEnvironment(env = process.env) {
|
|
|
349
350
|
}
|
|
350
351
|
async function loadFile(filePath) {
|
|
351
352
|
const root = jsonObject(await fs3.readFile(filePath), filePath);
|
|
352
|
-
rejectUnknown(root, filePath, ["dnsid", "logTrust", "registry"]);
|
|
353
|
+
rejectUnknown(root, filePath, ["dnsid", "logTrust", "registry", "registration", "keySource"]);
|
|
353
354
|
const loaded = {};
|
|
354
|
-
if (root.dnsid !== void 0) loaded.dnsid =
|
|
355
|
+
if (root.dnsid !== void 0) loaded.dnsid = validateLoadedDnsidConfig(root.dnsid, `${filePath}: dnsid`);
|
|
355
356
|
if (root.logTrust !== void 0) {
|
|
356
357
|
const trust = object(root.logTrust, `${filePath}: logTrust`);
|
|
357
358
|
rejectUnknown(trust, `${filePath}: logTrust`, ["managed", "profile", "policyUrl"]);
|
|
@@ -366,9 +367,32 @@ async function loadFile(filePath) {
|
|
|
366
367
|
rejectUnknown(registry, `${filePath}: registry`, ["registryUrl"]);
|
|
367
368
|
loaded.registry = compact({ registryUrl: optional(registry, "registryUrl", "string", `${filePath}: registry`) });
|
|
368
369
|
}
|
|
370
|
+
if (root.registration !== void 0) {
|
|
371
|
+
const registration = object(root.registration, `${filePath}: registration`);
|
|
372
|
+
rejectUnknown(registration, `${filePath}: registration`, ["organizationId", "governanceId", "entityKeyUrl"]);
|
|
373
|
+
loaded.registration = compact({
|
|
374
|
+
organizationId: optional(registration, "organizationId", "string", `${filePath}: registration`),
|
|
375
|
+
governanceId: optional(registration, "governanceId", "string", `${filePath}: registration`),
|
|
376
|
+
entityKeyUrl: optional(registration, "entityKeyUrl", "string", `${filePath}: registration`)
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (root.keySource !== void 0) {
|
|
380
|
+
const source = object(root.keySource, `${filePath}: keySource`);
|
|
381
|
+
rejectUnknown(source, "keySource", ["provider", "keyRef", "generation", "settings", "cliDirectory", "keyStorePath", "entityKeyPath"]);
|
|
382
|
+
for (const field of ["provider", "keyRef", "cliDirectory", "keyStorePath", "entityKeyPath"]) optional(source, field, "string", "keySource");
|
|
383
|
+
if (source.settings !== void 0) object(source.settings, "keySource.settings");
|
|
384
|
+
if (source.generation !== void 0) {
|
|
385
|
+
const generation = object(source.generation, "keySource.generation");
|
|
386
|
+
rejectUnknown(generation, "keySource.generation", ["locator", "algorithm"]);
|
|
387
|
+
for (const field of ["locator", "algorithm"]) {
|
|
388
|
+
if (typeof generation[field] !== "string") throw new ArgumentError3(`keySource.generation.${field} must be a string`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
loaded.keySource = source;
|
|
392
|
+
}
|
|
369
393
|
return loaded;
|
|
370
394
|
}
|
|
371
|
-
function
|
|
395
|
+
function validateLoadedDnsidConfig(value, source = "dnsid") {
|
|
372
396
|
const raw = object(value, source);
|
|
373
397
|
rejectUnknown(raw, source, ["identity", "verification", "transport"]);
|
|
374
398
|
if (raw.identity !== void 0) {
|
|
@@ -457,9 +481,15 @@ function mergeLoadedConfig(base, overlay) {
|
|
|
457
481
|
dnsid: mergeDnsid(base.dnsid, overlay.dnsid),
|
|
458
482
|
logTrust: overlay.logTrust ?? base.logTrust,
|
|
459
483
|
registry: mergeSection(base.registry, overlay.registry),
|
|
460
|
-
|
|
484
|
+
registration: mergeSection(base.registration, overlay.registration),
|
|
485
|
+
keySource: mergeKeySource(base.keySource, overlay.keySource)
|
|
461
486
|
});
|
|
462
487
|
}
|
|
488
|
+
function mergeKeySource(base, overlay) {
|
|
489
|
+
if (!overlay) return base;
|
|
490
|
+
const replacesOperational = ["provider", "keyRef", "generation", "settings", "cliDirectory", "keyStorePath"].some((field) => overlay[field] !== void 0);
|
|
491
|
+
return mergeSection(replacesOperational ? compact({ entityKeyPath: base?.entityKeyPath }) : base, overlay);
|
|
492
|
+
}
|
|
463
493
|
function mergeDnsid(base, overlay) {
|
|
464
494
|
if (!base || !overlay) return overlay ?? base;
|
|
465
495
|
return compact({
|
|
@@ -486,7 +516,16 @@ async function constructIdentityManager(loaded, deps = {}) {
|
|
|
486
516
|
filled.entityKeyProvider = await LocalKeyProvider.fromFile(loaded.keySource.entityKeyPath);
|
|
487
517
|
}
|
|
488
518
|
}
|
|
489
|
-
|
|
519
|
+
const manager = await createNodeIdentityManager(loaded.dnsid ?? {}, filled);
|
|
520
|
+
if (identity && !deps.keyProvider && filled.keyProvider) {
|
|
521
|
+
const verified = await manager.verifyPublicationEvidence(manager.config.identity.domain);
|
|
522
|
+
const selected = await filled.keyProvider.signingKey();
|
|
523
|
+
const published = verified.jwks.currentOperationalSigningKey();
|
|
524
|
+
if (selected.kid !== published.kid || selected.alg !== published.alg || await jwkThumbprint2(selected) !== await jwkThumbprint2(published)) {
|
|
525
|
+
throw new ArgumentError3("configured operational key does not match the verified identity; use authorized key rotation");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return manager;
|
|
490
529
|
}
|
|
491
530
|
async function logRegistryFromTrust(trust, transport) {
|
|
492
531
|
const variants = ["managed", "profile", "policyDocument", "policyUrl"].filter((k) => trust[k] !== void 0);
|
|
@@ -508,12 +547,46 @@ async function logRegistryFromTrust(trust, transport) {
|
|
|
508
547
|
});
|
|
509
548
|
}
|
|
510
549
|
async function operationalKeyProvider(source, domain) {
|
|
550
|
+
await validateKeySource(source);
|
|
551
|
+
if (source.generation) throw new ArgumentError3("identity-manager construction opens existing keys only; use managed setup for generation");
|
|
552
|
+
if (source.provider === "aws-kms") {
|
|
553
|
+
const { createAwsKmsKeyProvider } = await import("@dnsid-ai/key-aws");
|
|
554
|
+
return createAwsKmsKeyProvider(source.keyRef, source.settings);
|
|
555
|
+
}
|
|
556
|
+
if (source.keyRef !== void 0 || source.cliDirectory !== void 0 || source.keyStorePath !== void 0) {
|
|
557
|
+
console.warn("Local private-key files are unsuitable for production. Configure keySource.provider/keyRef for cloud custody.");
|
|
558
|
+
}
|
|
559
|
+
if (source.keyRef !== void 0) return LocalKeyProvider.load(source.keyRef);
|
|
511
560
|
if (source.cliDirectory !== void 0) {
|
|
512
561
|
return LocalKeyProvider.fromDirectory(await identityKeyDir(source.cliDirectory, domain));
|
|
513
562
|
}
|
|
514
563
|
if (source.keyStorePath !== void 0) return LocalKeyProvider.load(source.keyStorePath);
|
|
515
564
|
return void 0;
|
|
516
565
|
}
|
|
566
|
+
async function validateKeySource(source = {}) {
|
|
567
|
+
const provider = source.provider ?? "file";
|
|
568
|
+
if (!["file", "aws-kms", "google-kms", "azure-key-vault"].includes(provider)) throw new ArgumentError3(`unknown key provider: ${provider}`);
|
|
569
|
+
for (const field of ["keyRef", "keyStorePath", "cliDirectory", "entityKeyPath"]) {
|
|
570
|
+
if (source[field] !== void 0 && (typeof source[field] !== "string" || !source[field].trim())) throw new ArgumentError3(`keySource.${field} must be nonempty`);
|
|
571
|
+
}
|
|
572
|
+
if (source.keyRef && source.generation) throw new ArgumentError3("keyRef and generation are mutually exclusive");
|
|
573
|
+
if ((source.cliDirectory || source.keyStorePath) && (provider !== "file" || source.keyRef || source.generation)) throw new ArgumentError3("CLI/keyStorePath cannot combine with cloud, keyRef, or generation");
|
|
574
|
+
if (source.generation && (typeof source.generation.locator !== "string" || !source.generation.locator.trim() || !["EdDSA", "ES256"].includes(source.generation.algorithm))) throw new ArgumentError3("generation requires a stable locator and supported EdDSA or ES256 algorithm");
|
|
575
|
+
if (provider === "file") {
|
|
576
|
+
if (source.settings && Object.keys(source.settings).length) throw new ArgumentError3("file provider has no settings");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (provider !== "aws-kms") throw new ArgumentError3(`${provider} is unavailable; install/link a supported provider package (Google KMS and Azure factories are not implemented)`);
|
|
580
|
+
if (!source.keyRef || source.generation) throw new ArgumentError3("AWS KMS requires an existing keyRef; atomic generation discovery is not supported");
|
|
581
|
+
try {
|
|
582
|
+
const factory = await import("@dnsid-ai/key-aws");
|
|
583
|
+
if (typeof factory.createAwsKmsKeyProvider !== "function" || typeof factory.validateAwsKmsSettings !== "function") throw new Error("provider factory unavailable");
|
|
584
|
+
factory.validateAwsKmsSettings(source.settings);
|
|
585
|
+
} catch (cause) {
|
|
586
|
+
if (cause instanceof ArgumentError3) throw cause;
|
|
587
|
+
throw new ArgumentError3("aws-kms unavailable; install a compatible @dnsid-ai/key-aws and include it in the build");
|
|
588
|
+
}
|
|
589
|
+
}
|
|
517
590
|
async function identityKeyDir(dnsidDir, domain) {
|
|
518
591
|
for (const filename of ["private.jwk", "private.pem"]) {
|
|
519
592
|
try {
|
|
@@ -534,10 +607,10 @@ function createNodeIdentityManagerFromDnsid(dnsidDir, overlay, deps) {
|
|
|
534
607
|
function createNodeIdentityManagerFromFile(filePath, overlay, deps) {
|
|
535
608
|
return loadFile(filePath).then((loaded) => constructIdentityManager(mergeLoadedConfig(loaded, top({ dnsid: overlay })), deps));
|
|
536
609
|
}
|
|
537
|
-
async function createRegistryClientFromEnvironment(env) {
|
|
610
|
+
async function createRegistryClientFromEnvironment(env, options = {}) {
|
|
538
611
|
const source = env ?? process.env;
|
|
539
612
|
const loaded = await loadEnvironment(source);
|
|
540
|
-
return new RegistryClient(
|
|
613
|
+
return new RegistryClient({ baseUrl: loaded.registry?.registryUrl, token: source.DNSID_API_KEY?.trim() || void 0, ...options });
|
|
541
614
|
}
|
|
542
615
|
function definedEntries(value) {
|
|
543
616
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dnsid-ai/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Runtime-neutral DNSid SDK entrypoint with optional Node.js convenience helpers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -39,17 +39,21 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@dnsid-ai/protocol": "^0.
|
|
43
|
-
"@dnsid-ai/http-signatures": "^0.
|
|
44
|
-
"@dnsid-ai/jose": "^0.
|
|
45
|
-
"@dnsid-ai/log-c2sp-tlog": "^0.
|
|
46
|
-
"@dnsid-ai/registry": "^0.
|
|
47
|
-
"@dnsid-ai/web-bot-auth": "^0.
|
|
42
|
+
"@dnsid-ai/protocol": "^0.26.0",
|
|
43
|
+
"@dnsid-ai/http-signatures": "^0.26.0",
|
|
44
|
+
"@dnsid-ai/jose": "^0.26.0",
|
|
45
|
+
"@dnsid-ai/log-c2sp-tlog": "^0.26.0",
|
|
46
|
+
"@dnsid-ai/registry": "^0.26.0",
|
|
47
|
+
"@dnsid-ai/web-bot-auth": "^0.26.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@dnsid-ai/
|
|
50
|
+
"@dnsid-ai/key-aws": "^0.26.0",
|
|
51
|
+
"@dnsid-ai/transport": "^0.26.0"
|
|
51
52
|
},
|
|
52
53
|
"peerDependenciesMeta": {
|
|
54
|
+
"@dnsid-ai/key-aws": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
53
57
|
"@dnsid-ai/transport": {
|
|
54
58
|
"optional": true
|
|
55
59
|
}
|