@agent-network-protocol/dsh-anp-identity 0.1.6 → 0.1.8
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/BOUNDARY.md +30 -5
- package/PUBLICATION.md +181 -0
- package/README.md +73 -1
- package/README.zh.md +44 -1
- package/V7-MANAGEMENT.md +18 -0
- package/cordis.patch.yml +9 -0
- package/examples/fresh-bootstrap.cordis.patch.yml +38 -0
- package/lib/authorization-types.d.ts +6 -0
- package/lib/authorization-types.d.ts.map +1 -1
- package/lib/authorization.d.ts.map +1 -1
- package/lib/authorization.js +23 -6
- package/lib/authorization.js.map +1 -1
- package/lib/catalog.d.ts +3 -1
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +15 -3
- package/lib/catalog.js.map +1 -1
- package/lib/client/controller.d.ts +18 -0
- package/lib/client/controller.d.ts.map +1 -1
- package/lib/client/controller.js +89 -5
- package/lib/client/controller.js.map +1 -1
- package/lib/client/views.d.ts.map +1 -1
- package/lib/client/views.js +37 -6
- package/lib/client/views.js.map +1 -1
- package/lib/client-metadata.d.ts +3 -0
- package/lib/client-metadata.d.ts.map +1 -0
- package/lib/client-metadata.js +11 -0
- package/lib/client-metadata.js.map +1 -0
- package/lib/client.js +260 -14
- package/lib/client.js.map +4 -4
- package/lib/host-management-secret.d.ts +41 -0
- package/lib/host-management-secret.d.ts.map +1 -0
- package/lib/host-management-secret.js +92 -0
- package/lib/host-management-secret.js.map +1 -0
- package/lib/index.d.ts +73 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +331 -22
- package/lib/index.js.map +1 -1
- package/lib/initial-registration-proof.d.ts +13 -0
- package/lib/initial-registration-proof.d.ts.map +1 -0
- package/lib/initial-registration-proof.js +48 -0
- package/lib/initial-registration-proof.js.map +1 -0
- package/lib/installed-consumer.d.ts +25 -0
- package/lib/installed-consumer.d.ts.map +1 -0
- package/lib/installed-consumer.js +66 -0
- package/lib/installed-consumer.js.map +1 -0
- package/lib/management-credentials.d.ts +15 -0
- package/lib/management-credentials.d.ts.map +1 -0
- package/lib/management-credentials.js +126 -0
- package/lib/management-credentials.js.map +1 -0
- package/lib/management-driver.d.ts +83 -0
- package/lib/management-driver.d.ts.map +1 -0
- package/lib/management-driver.js +405 -0
- package/lib/management-driver.js.map +1 -0
- package/lib/management-remote.d.ts +27 -1
- package/lib/management-remote.d.ts.map +1 -1
- package/lib/management-remote.js +69 -3
- package/lib/management-remote.js.map +1 -1
- package/lib/management-types.d.ts +7 -0
- package/lib/management-types.d.ts.map +1 -1
- package/lib/pap-callback.d.ts +110 -0
- package/lib/pap-callback.d.ts.map +1 -0
- package/lib/pap-callback.js +51 -0
- package/lib/pap-callback.js.map +1 -0
- package/lib/protocol-bootstrap.d.ts +24 -0
- package/lib/protocol-bootstrap.d.ts.map +1 -0
- package/lib/protocol-bootstrap.js +34 -0
- package/lib/protocol-bootstrap.js.map +1 -0
- package/lib/protocol-client.d.ts +137 -0
- package/lib/protocol-client.d.ts.map +1 -0
- package/lib/protocol-client.js +11 -0
- package/lib/protocol-client.js.map +1 -0
- package/lib/provider-api.d.ts +6 -2
- package/lib/provider-api.d.ts.map +1 -1
- package/lib/provider-api.js.map +1 -1
- package/lib/publication-http.d.ts +19 -0
- package/lib/publication-http.d.ts.map +1 -0
- package/lib/publication-http.js +183 -0
- package/lib/publication-http.js.map +1 -0
- package/lib/publication-types.d.ts +161 -0
- package/lib/publication-types.d.ts.map +1 -0
- package/lib/publication-types.js +35 -0
- package/lib/publication-types.js.map +1 -0
- package/lib/publication.d.ts +21 -0
- package/lib/publication.d.ts.map +1 -0
- package/lib/publication.js +235 -0
- package/lib/publication.js.map +1 -0
- package/lib/registration-bootstrap.d.ts +56 -0
- package/lib/registration-bootstrap.d.ts.map +1 -0
- package/lib/registration-bootstrap.js +523 -0
- package/lib/registration-bootstrap.js.map +1 -0
- package/lib/registration-service-settings.d.ts +12 -0
- package/lib/registration-service-settings.d.ts.map +1 -0
- package/lib/registration-service-settings.js +124 -0
- package/lib/registration-service-settings.js.map +1 -0
- package/lib/registration-service-types.d.ts +11 -0
- package/lib/registration-service-types.d.ts.map +1 -0
- package/lib/registration-service-types.js +3 -0
- package/lib/registration-service-types.js.map +1 -0
- package/lib/remote.d.ts.map +1 -1
- package/lib/remote.js +11 -2
- package/lib/remote.js.map +1 -1
- package/lib/target-policy.d.ts +35 -0
- package/lib/target-policy.d.ts.map +1 -0
- package/lib/target-policy.js +319 -0
- package/lib/target-policy.js.map +1 -0
- package/lib/user-client.d.ts +6 -0
- package/lib/user-client.d.ts.map +1 -1
- package/lib/user-client.js.map +1 -1
- package/lib/user-operation.d.ts.map +1 -1
- package/lib/user-operation.js +9 -0
- package/lib/user-operation.js.map +1 -1
- package/lib/user-service.d.ts +8 -1
- package/lib/user-service.d.ts.map +1 -1
- package/lib/user-service.js +58 -4
- package/lib/user-service.js.map +1 -1
- package/package.json +51 -29
package/lib/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { RegistrationServiceSettingsStore } from './registration-service-settings.js';
|
|
2
|
+
import { createManagedPublicationDriver, registerLocalIdentity } from './management-driver.js';
|
|
3
|
+
import { installedConsumerEntries, resolveInstalledConsumer } from './installed-consumer.js';
|
|
4
|
+
import { ProtocolIdentityAuthority } from './target-policy.js';
|
|
5
|
+
import { PapCallbackManagementError } from './pap-callback.js';
|
|
6
|
+
import { PublicationCoordinator } from './publication.js';
|
|
7
|
+
import { exactHttps } from './publication-http.js';
|
|
1
8
|
/** Host-only ANP Identity service for DeepSeek Harness. */
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
3
10
|
import { homedir } from 'node:os';
|
|
4
11
|
import { isAbsolute, join } from 'node:path';
|
|
5
12
|
import { Service } from '@deepseek-ai/cordis';
|
|
@@ -7,18 +14,21 @@ import z from '@deepseek-ai/schemastery';
|
|
|
7
14
|
import { CatalogStore, CATALOG_SCHEMA, CREATE_INTENT_SCHEMA, emptyCatalog, entryReference, findEntry, normalizeHandle, } from './catalog.js';
|
|
8
15
|
import { pluginError, AnpIdentityPluginError } from './errors.js';
|
|
9
16
|
import { createAuthenticatedHttp } from './http-auth.js';
|
|
10
|
-
import { UserIdentityService,
|
|
17
|
+
import { UserIdentityService, httpSigningKey } from './user-service.js';
|
|
11
18
|
import { ANP_IDENTITY_NATIVE_PROVIDER_PROTOCOL, ANP_IDENTITY_PROVIDER_PROTOCOL, ANP_IDENTITY_SERVICE_PROTOCOL, } from './provider-api.js';
|
|
12
19
|
export { AnpIdentityPluginError } from './errors.js';
|
|
13
20
|
export { ANP_IDENTITY_HTTP_MAX_BODY_BYTES, } from './http-auth.js';
|
|
14
21
|
export { ANP_IDENTITY_PROVIDER_PROTOCOL, ANP_IDENTITY_SERVICE_PROTOCOL, } from './provider-api.js';
|
|
15
22
|
export const Config = z.object({
|
|
23
|
+
signerMode: z.const('local').default('local'),
|
|
24
|
+
protocolPolicies: z.array(z.any()).default([]),
|
|
16
25
|
stateRoot: z.string(),
|
|
17
26
|
allowConsumers: z.array(z.string()).default([]),
|
|
18
27
|
allowProviderConsumers: z.array(z.string()).default([]),
|
|
19
28
|
httpAllowedOrigins: z.dict(z.array(z.string())).default({}),
|
|
20
29
|
recoveryOnOpen: z.boolean().default(true),
|
|
21
30
|
userConsumers: z.array(z.string()).default([]),
|
|
31
|
+
oauthAllowedAudiences: z.dict(z.array(z.string())).default({}),
|
|
22
32
|
});
|
|
23
33
|
/** The Loader retains Registry.plugin's awaitable wrapper, not its raw fiber. */
|
|
24
34
|
function sameFiber(left, right) {
|
|
@@ -54,8 +64,13 @@ const HOST_LEASE_RENEWAL_SKEW_MS = 60_000;
|
|
|
54
64
|
export class AnpIdentityService extends Service {
|
|
55
65
|
static Config = Config;
|
|
56
66
|
resolvedConfig;
|
|
67
|
+
registrationSettings;
|
|
57
68
|
catalogStore;
|
|
58
69
|
providerSlot;
|
|
70
|
+
publications;
|
|
71
|
+
publicationBackends = new Map();
|
|
72
|
+
papCallbackAuthorities = new Map();
|
|
73
|
+
protocols;
|
|
59
74
|
users;
|
|
60
75
|
hostContext;
|
|
61
76
|
disabledFences = new Set();
|
|
@@ -63,8 +78,10 @@ export class AnpIdentityService extends Service {
|
|
|
63
78
|
constructor(ctx, config) {
|
|
64
79
|
super(ctx, 'anpIdentity');
|
|
65
80
|
this.resolvedConfig = resolveConfig(config);
|
|
81
|
+
this.registrationSettings = new RegistrationServiceSettingsStore(this.resolvedConfig.stateRoot);
|
|
66
82
|
this.catalogStore = new CatalogStore(this.resolvedConfig.stateRoot);
|
|
67
83
|
this.hostContext = ctx;
|
|
84
|
+
this.publications = new PublicationCoordinator(this.catalogStore, { withNative: operation => this.withPublicationNative(operation) });
|
|
68
85
|
this.users = new UserIdentityService(this.resolvedConfig.stateRoot, {
|
|
69
86
|
ensureReady: async () => { await this.awaitProvider(this.requireSlot()); },
|
|
70
87
|
resolveCaller: context => this.resolveInstalledCaller(context),
|
|
@@ -73,7 +90,11 @@ export class AnpIdentityService extends Service {
|
|
|
73
90
|
createIdentity: (id, caller, parameters) => this.createConfirmed(id, caller, parameters),
|
|
74
91
|
reconcileCreation: (id, caller, parameters) => this.reconcileConfirmed(id, caller, parameters),
|
|
75
92
|
withNative: operation => this.withUserNative(operation),
|
|
76
|
-
});
|
|
93
|
+
}, this.publications);
|
|
94
|
+
this.protocols = new ProtocolIdentityAuthority(structuredClone(config.protocolPolicies ?? []), {
|
|
95
|
+
resolveCaller: context => this.resolveInstalledCaller(context), assertCaller: caller => this.assertInstalledCaller(caller),
|
|
96
|
+
providerGeneration: () => this.requireSlot().id, withNative: operation => this.withProtocolNative(operation),
|
|
97
|
+
}, this.publications);
|
|
77
98
|
const observe = ctx.on.bind(ctx);
|
|
78
99
|
observe('loader/partial-dispose', (rawEntry, rawPrevious, active) => {
|
|
79
100
|
if (ctx.fiber.state > 2)
|
|
@@ -83,7 +104,7 @@ export class AnpIdentityService extends Service {
|
|
|
83
104
|
if (active === false || entry?.disabled)
|
|
84
105
|
this.cancelDisabledRequests(previous?.name ?? entry?.options?.name);
|
|
85
106
|
try {
|
|
86
|
-
for (const candidate of this.
|
|
107
|
+
for (const candidate of installedConsumerEntries(this.hostContext)) {
|
|
87
108
|
if (candidate.disabled)
|
|
88
109
|
this.cancelDisabledRequests(candidate.options.name);
|
|
89
110
|
}
|
|
@@ -94,7 +115,7 @@ export class AnpIdentityService extends Service {
|
|
|
94
115
|
if (ctx.fiber.state > 2)
|
|
95
116
|
return;
|
|
96
117
|
try {
|
|
97
|
-
for (const entry of this.
|
|
118
|
+
for (const entry of installedConsumerEntries(this.hostContext)) {
|
|
98
119
|
if (sameFiber(entry.fiber, fiber) && entry.disabled)
|
|
99
120
|
this.cancelDisabledRequests(entry.options.name);
|
|
100
121
|
}
|
|
@@ -111,7 +132,7 @@ export class AnpIdentityService extends Service {
|
|
|
111
132
|
}
|
|
112
133
|
async health() {
|
|
113
134
|
const slot = this.providerSlot;
|
|
114
|
-
if (slot === undefined || slot.disposed) {
|
|
135
|
+
if (slot === undefined || slot.disposed || this.hostContext.fiber.state > 2) {
|
|
115
136
|
return {
|
|
116
137
|
status: 'unavailable',
|
|
117
138
|
protocol: ANP_IDENTITY_SERVICE_PROTOCOL,
|
|
@@ -119,7 +140,25 @@ export class AnpIdentityService extends Service {
|
|
|
119
140
|
};
|
|
120
141
|
}
|
|
121
142
|
try {
|
|
122
|
-
|
|
143
|
+
const assertCurrent = () => {
|
|
144
|
+
this.assertSlot(slot);
|
|
145
|
+
if (this.hostContext.fiber.state > 2)
|
|
146
|
+
throw pluginError('provider_disposed');
|
|
147
|
+
};
|
|
148
|
+
const provider = await slot.ready;
|
|
149
|
+
assertCurrent();
|
|
150
|
+
const native = provider.acquireLease({ consumer: '@agent-network-protocol/dsh-anp-identity',
|
|
151
|
+
capabilities: ['IDENTITY_READ'], ttlSeconds: 15 });
|
|
152
|
+
try {
|
|
153
|
+
const info = await native.info();
|
|
154
|
+
assertCurrent();
|
|
155
|
+
// Legacy general health remains compatible; protocol leases require the availability handshake.
|
|
156
|
+
if (info.backendAvailable === false || !info.schemaCompatible || info.health !== 'ready')
|
|
157
|
+
throw pluginError('provider_unavailable');
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
native.dispose();
|
|
161
|
+
}
|
|
123
162
|
}
|
|
124
163
|
catch {
|
|
125
164
|
return {
|
|
@@ -631,13 +670,202 @@ export class AnpIdentityService extends Service {
|
|
|
631
670
|
bindUserConsumer(context) {
|
|
632
671
|
return this.users.bind(context);
|
|
633
672
|
}
|
|
673
|
+
bindProtocolConsumer(context) { return this.protocols.bind(context); }
|
|
674
|
+
/** Bind the actual installed PAP Host. Private callback access additionally requires its current opaque target.
|
|
675
|
+
* @param context - Original Loader-installed PAP plugin Context.
|
|
676
|
+
* @returns Public identity selection and target-bound, closed callback operations.
|
|
677
|
+
*/
|
|
678
|
+
bindPapHostConsumer(context) {
|
|
679
|
+
return this.protocols.bindPapHost(context, async (reference, backend) => {
|
|
680
|
+
const entry = this.papCallbackAuthorities.get(backend);
|
|
681
|
+
if (!entry || entry.reference.storeId !== reference.storeId || entry.reference.identityId !== reference.identityId
|
|
682
|
+
|| entry.reference.did !== reference.did)
|
|
683
|
+
throw new PapCallbackManagementError('management_auth_required');
|
|
684
|
+
await entry.authority.assertActive();
|
|
685
|
+
return entry.authority;
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
/** Install a real device-authorized backend; the document id comes from authenticated resolve. */
|
|
689
|
+
async configureManagedPublicationBackend(context, name, input) {
|
|
690
|
+
this.assertManagementContext(context);
|
|
691
|
+
const slot = this.requireSlot();
|
|
692
|
+
let disposed = false;
|
|
693
|
+
const assertCurrent = () => {
|
|
694
|
+
if (disposed)
|
|
695
|
+
throw pluginError('provider_disposed');
|
|
696
|
+
// Preserve the original installed manager Context even when a different service consumer invokes a capability.
|
|
697
|
+
const managerService = context.get('anpIdentity');
|
|
698
|
+
managerService.assertManagementContext(context);
|
|
699
|
+
managerService.assertSlot(slot);
|
|
700
|
+
};
|
|
701
|
+
const assertActive = async () => { assertCurrent(); };
|
|
702
|
+
const driver = await createManagedPublicationDriver({ ...input,
|
|
703
|
+
withNative: async (operation) => {
|
|
704
|
+
assertCurrent();
|
|
705
|
+
const result = await context.get('anpIdentity').withPublicationNative(async (native) => {
|
|
706
|
+
assertCurrent();
|
|
707
|
+
const value = await operation(native);
|
|
708
|
+
assertCurrent();
|
|
709
|
+
return value;
|
|
710
|
+
});
|
|
711
|
+
assertCurrent();
|
|
712
|
+
return result;
|
|
713
|
+
}, assertActive, assertCurrent });
|
|
714
|
+
this.assertManagementContext(context);
|
|
715
|
+
const dispose = this.registerPublicationBackend(context, name, driver.backend);
|
|
716
|
+
const generation = Symbol('pap-callback-manager');
|
|
717
|
+
const reference = Object.freeze(structuredClone(input.reference));
|
|
718
|
+
const origin = input.origin;
|
|
719
|
+
const prefix = 'pap-host-target:';
|
|
720
|
+
const assertAuthority = async () => {
|
|
721
|
+
await assertActive();
|
|
722
|
+
const managerService = context.get('anpIdentity');
|
|
723
|
+
if (managerService.publicationBackends.get(name) !== driver.backend
|
|
724
|
+
|| managerService.papCallbackAuthorities.get(name)?.generation !== generation)
|
|
725
|
+
throw pluginError('provider_disposed');
|
|
726
|
+
assertCurrent();
|
|
727
|
+
};
|
|
728
|
+
const authority = {
|
|
729
|
+
assertActive: assertAuthority,
|
|
730
|
+
operations: Object.freeze({ execute: driver.execute }),
|
|
731
|
+
reserve: async (transactionId, binding) => {
|
|
732
|
+
await assertAuthority();
|
|
733
|
+
const result = await input.credentials.reserveOperation(reference, origin, prefix + transactionId, binding);
|
|
734
|
+
await assertAuthority();
|
|
735
|
+
return result;
|
|
736
|
+
},
|
|
737
|
+
read: async (transactionId) => {
|
|
738
|
+
await assertAuthority();
|
|
739
|
+
const credential = await input.credentials.get(reference, origin);
|
|
740
|
+
await assertAuthority();
|
|
741
|
+
return credential?.operationBindings?.[prefix + transactionId];
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
this.papCallbackAuthorities.set(name, { reference, generation, authority });
|
|
745
|
+
return { backend: name, documentId: driver.documentId, callbackManagement: Object.freeze({ execute: driver.execute }),
|
|
746
|
+
dispose: () => {
|
|
747
|
+
disposed = true;
|
|
748
|
+
dispose();
|
|
749
|
+
if (this.papCallbackAuthorities.get(name)?.generation === generation)
|
|
750
|
+
this.papCallbackAuthorities.delete(name);
|
|
751
|
+
} };
|
|
752
|
+
}
|
|
753
|
+
/** Register the public local Manifest through User Service and persist its verified management credential. */
|
|
754
|
+
async registerLocalIdentity(context, input) {
|
|
755
|
+
const assertActive = await this.pinManagementContext(context, input.reference, input.assertActive);
|
|
756
|
+
const result = await registerLocalIdentity({ ...input, assertActive, withNative: async (operation) => {
|
|
757
|
+
assertActive();
|
|
758
|
+
const value = await context.get('anpIdentity').withPublicationNative(async (native) => {
|
|
759
|
+
assertActive();
|
|
760
|
+
const output = await operation(native);
|
|
761
|
+
assertActive();
|
|
762
|
+
return output;
|
|
763
|
+
}, true);
|
|
764
|
+
assertActive();
|
|
765
|
+
return value;
|
|
766
|
+
} });
|
|
767
|
+
assertActive();
|
|
768
|
+
return result;
|
|
769
|
+
}
|
|
770
|
+
/** Manager-only registration preferences; ordinary consumer capabilities never expose these methods. */
|
|
771
|
+
registrationServiceSettings(context) {
|
|
772
|
+
return this.registrationSettings.read(() => this.assertManagementContext(context));
|
|
773
|
+
}
|
|
774
|
+
configureRegistrationService(context, input) {
|
|
775
|
+
return this.registrationSettings.update(input, () => this.assertManagementContext(context));
|
|
776
|
+
}
|
|
777
|
+
resolveRegistrationServiceOrigin(context, key, fingerprint) {
|
|
778
|
+
return this.registrationSettings.pin(key, fingerprint, () => this.assertManagementContext(context));
|
|
779
|
+
}
|
|
780
|
+
/** Pin the actual ready provider for an approved local identity and the original manager Context.
|
|
781
|
+
* @param context - Original root-installed manager Context.
|
|
782
|
+
* @param reference - Local identity whose current provider is being pinned.
|
|
783
|
+
* @param additionalCheck - Optional stricter Host lifecycle check.
|
|
784
|
+
* @returns A synchronous fence that never switches to a replacement provider.
|
|
785
|
+
*/
|
|
786
|
+
async pinManagementContext(context, reference, additionalCheck) {
|
|
787
|
+
additionalCheck?.();
|
|
788
|
+
this.assertManagementContext(context);
|
|
789
|
+
const slot = this.requireSlot();
|
|
790
|
+
const assertActive = () => {
|
|
791
|
+
additionalCheck?.();
|
|
792
|
+
const manager = context.get('anpIdentity');
|
|
793
|
+
manager.assertManagementContext(context);
|
|
794
|
+
manager.assertSlot(slot);
|
|
795
|
+
};
|
|
796
|
+
await this.awaitProvider(slot);
|
|
797
|
+
assertActive();
|
|
798
|
+
await context.get('anpIdentity').withPublicationNative(async (native) => {
|
|
799
|
+
assertActive();
|
|
800
|
+
await native.publicIdentity(reference);
|
|
801
|
+
assertActive();
|
|
802
|
+
});
|
|
803
|
+
assertActive();
|
|
804
|
+
return assertActive;
|
|
805
|
+
}
|
|
634
806
|
/** Host-only manager; the ordinary consumer facade never exposes this object. */
|
|
635
|
-
|
|
636
|
-
|
|
807
|
+
registerPublicationBackend(context, name, backend) {
|
|
808
|
+
this.assertManagementContext(context);
|
|
809
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/u.test(name) || this.publicationBackends.has(name))
|
|
810
|
+
throw pluginError('invalid_request');
|
|
811
|
+
exactHttps(backend.origin);
|
|
812
|
+
this.publicationBackends.set(name, backend);
|
|
813
|
+
return () => { if (this.publicationBackends.get(name) === backend)
|
|
814
|
+
this.publicationBackends.delete(name); };
|
|
815
|
+
}
|
|
816
|
+
assertManagementContext(context) {
|
|
817
|
+
if (!context || !sameFiber(this.ctx.fiber, context.fiber))
|
|
637
818
|
throw pluginError('consumer_forbidden');
|
|
638
|
-
|
|
639
|
-
|
|
819
|
+
const manager = this.installedEntries().find(entry => sameFiber(entry.fiber, context.fiber));
|
|
820
|
+
if (!manager || manager.options.name !== '@agent-network-protocol/dsh-anp-identity/management-remote'
|
|
821
|
+
|| manager.disabled || !manager.fiber || ![1, 2].includes(manager.fiber.state))
|
|
822
|
+
throw pluginError('consumer_forbidden');
|
|
823
|
+
}
|
|
824
|
+
acquireManagement(context, additionalCheck) {
|
|
825
|
+
additionalCheck?.();
|
|
826
|
+
this.assertManagementContext(context);
|
|
827
|
+
const ready = async () => {
|
|
828
|
+
additionalCheck?.();
|
|
829
|
+
this.assertManagementContext(context);
|
|
830
|
+
await this.users.ready;
|
|
831
|
+
additionalCheck?.();
|
|
832
|
+
this.assertManagementContext(context);
|
|
833
|
+
const slot = this.requireSlot();
|
|
834
|
+
await this.awaitProvider(slot);
|
|
835
|
+
additionalCheck?.();
|
|
836
|
+
this.assertManagementContext(context);
|
|
837
|
+
this.assertSlot(slot);
|
|
838
|
+
};
|
|
640
839
|
return Object.freeze({
|
|
840
|
+
retryPublication: async (reference) => {
|
|
841
|
+
await ready();
|
|
842
|
+
const entry = findEntry(await this.loadCatalog(this.requireSlot()), reference);
|
|
843
|
+
if (!entry?.publication)
|
|
844
|
+
throw pluginError('provider_unavailable');
|
|
845
|
+
return this.acquireManagement(context, additionalCheck).publishIdentity({ reference, config: entry.publication.config });
|
|
846
|
+
},
|
|
847
|
+
publicationStatus: async (reference) => { await ready(); return this.publications.status(reference); },
|
|
848
|
+
publishIdentity: async (input) => {
|
|
849
|
+
await ready();
|
|
850
|
+
const backend = this.publicationBackends.get(input.config.backend);
|
|
851
|
+
if (!backend)
|
|
852
|
+
throw pluginError('provider_unavailable');
|
|
853
|
+
try {
|
|
854
|
+
return await this.publications.publish(input.reference, input.config, backend, async () => {
|
|
855
|
+
await ready();
|
|
856
|
+
if (this.publicationBackends.get(input.config.backend) !== backend)
|
|
857
|
+
throw pluginError('provider_unavailable');
|
|
858
|
+
const entry = findEntry(await this.loadCatalog(this.requireSlot()), input.reference);
|
|
859
|
+
if (!entry || entry.state !== 'active')
|
|
860
|
+
throw pluginError('identity_not_found');
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
catch (error) {
|
|
864
|
+
if (error instanceof AnpIdentityPluginError)
|
|
865
|
+
throw error;
|
|
866
|
+
throw pluginError('provider_unavailable');
|
|
867
|
+
}
|
|
868
|
+
},
|
|
641
869
|
listIdentities: async () => { await ready(); return this.managementIdentities(); },
|
|
642
870
|
listRequests: async (history = false) => {
|
|
643
871
|
await ready();
|
|
@@ -770,7 +998,7 @@ export class AnpIdentityService extends Service {
|
|
|
770
998
|
return [...loader.entries()];
|
|
771
999
|
}
|
|
772
1000
|
resolveInstalledCaller(context) {
|
|
773
|
-
const entry = this.
|
|
1001
|
+
const entry = resolveInstalledConsumer(this.hostContext, context);
|
|
774
1002
|
if (!entry || entry.disabled || !this.resolvedConfig.userConsumers.has(entry.options.name)
|
|
775
1003
|
|| ![1, 2].includes(entry.fiber.state))
|
|
776
1004
|
throw pluginError('consumer_forbidden');
|
|
@@ -779,7 +1007,7 @@ export class AnpIdentityService extends Service {
|
|
|
779
1007
|
assertInstalledCaller(caller) {
|
|
780
1008
|
if (this.disabledFences.has(caller.consumer))
|
|
781
1009
|
throw pluginError('consumer_forbidden');
|
|
782
|
-
const entry = this.
|
|
1010
|
+
const entry = installedConsumerEntries(this.hostContext).find(value => value.options.name === caller.consumer
|
|
783
1011
|
&& !value.disabled && value.fiber !== undefined && [1, 2].includes(value.fiber.state));
|
|
784
1012
|
if (!entry || !this.resolvedConfig.userConsumers.has(caller.consumer))
|
|
785
1013
|
throw pluginError('consumer_forbidden');
|
|
@@ -802,11 +1030,12 @@ export class AnpIdentityService extends Service {
|
|
|
802
1030
|
throw pluginError('capability_forbidden');
|
|
803
1031
|
}
|
|
804
1032
|
assertLegacyCallerContext(consumer) {
|
|
805
|
-
if (this.resolvedConfig.userConsumers.size === 0
|
|
806
|
-
|| sameFiber(this.ctx.fiber, this.hostContext.fiber) || sameFiber(this.ctx.fiber, this.hostContext.root.fiber))
|
|
1033
|
+
if (this.resolvedConfig.userConsumers.size === 0)
|
|
807
1034
|
return;
|
|
808
1035
|
const installed = this.installedEntries().find(entry => sameFiber(entry.fiber, this.ctx.fiber));
|
|
809
|
-
if (!installed || installed.disabled
|
|
1036
|
+
if (!installed || installed.disabled
|
|
1037
|
+
|| (installed.options.name !== consumer
|
|
1038
|
+
&& !(this.resolvedConfig.allowProviderConsumers.has(consumer) && installed.options.name === consumer + '/provider'))
|
|
810
1039
|
|| !installed.fiber || ![1, 2].includes(installed.fiber.state)
|
|
811
1040
|
|| this.resolvedConfig.userConsumers.has(installed.options.name))
|
|
812
1041
|
throw pluginError('consumer_forbidden');
|
|
@@ -824,6 +1053,42 @@ export class AnpIdentityService extends Service {
|
|
|
824
1053
|
native.dispose();
|
|
825
1054
|
}
|
|
826
1055
|
}
|
|
1056
|
+
async withProtocolNative(operation) {
|
|
1057
|
+
const slot = this.requireSlot();
|
|
1058
|
+
const provider = await this.awaitProvider(slot);
|
|
1059
|
+
const native = provider.acquireLease({ consumer: '@agent-network-protocol/dsh-anp-identity',
|
|
1060
|
+
capabilities: ['IDENTITY_READ', 'IDENTITY_SIGN', 'IDENTITY_PAP_SESSION', 'IDENTITY_PAP_DPOP', 'IDENTITY_KEY_LIFECYCLE'], ttlSeconds: 300 });
|
|
1061
|
+
try {
|
|
1062
|
+
this.assertSlot(slot);
|
|
1063
|
+
const capability = native.providerCapabilities;
|
|
1064
|
+
if (typeof capability !== 'function')
|
|
1065
|
+
throw pluginError('provider_incompatible');
|
|
1066
|
+
const supported = await capability.call(native);
|
|
1067
|
+
if (supported.api_version !== 2 || !supported.algorithms.includes('ES256'))
|
|
1068
|
+
throw pluginError('provider_incompatible');
|
|
1069
|
+
const result = await operation(native);
|
|
1070
|
+
this.assertSlot(slot);
|
|
1071
|
+
return result;
|
|
1072
|
+
}
|
|
1073
|
+
finally {
|
|
1074
|
+
native.dispose();
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
async withPublicationNative(operation, registrationSigning = false) {
|
|
1078
|
+
const slot = this.requireSlot();
|
|
1079
|
+
const provider = await this.awaitProvider(slot);
|
|
1080
|
+
const native = provider.acquireLease({ consumer: '@agent-network-protocol/dsh-anp-identity',
|
|
1081
|
+
capabilities: ['IDENTITY_READ', 'IDENTITY_DOCUMENT_UPDATE', 'IDENTITY_HTTP_SIGNATURE', ...(registrationSigning ? ['IDENTITY_SIGN'] : [])], ttlSeconds: 300 });
|
|
1082
|
+
try {
|
|
1083
|
+
this.assertSlot(slot);
|
|
1084
|
+
if (typeof native.oauthPublicJwk !== 'function')
|
|
1085
|
+
throw pluginError('provider_incompatible');
|
|
1086
|
+
return await operation(native);
|
|
1087
|
+
}
|
|
1088
|
+
finally {
|
|
1089
|
+
native.dispose();
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
827
1092
|
async userSnapshot(caller, reference) {
|
|
828
1093
|
this.assertInstalledCaller(caller);
|
|
829
1094
|
const catalog = await this.loadCatalog(this.requireSlot());
|
|
@@ -836,14 +1101,22 @@ export class AnpIdentityService extends Service {
|
|
|
836
1101
|
const identity = await this.withUserNative(native => native.publicIdentity(reference));
|
|
837
1102
|
if (identity.state !== 'active' || !identity.capabilities.didWba)
|
|
838
1103
|
throw pluginError('provider_incompatible');
|
|
839
|
-
|
|
1104
|
+
const http = httpSigningKey(identity);
|
|
1105
|
+
if (http.httpOnly)
|
|
1106
|
+
return { version: 'ordinary-http/1', capabilities: ['identity:read', 'identity:http-auth'],
|
|
1107
|
+
signingPurposes: [], httpOrigins: [...(this.resolvedConfig.httpAllowedOrigins.get(caller.consumer) ?? [])].sort() };
|
|
840
1108
|
return { version: 'ordinary-use/1', capabilities: ['identity:read', 'identity:sign', 'identity:http-auth'],
|
|
841
|
-
signingPurposes:
|
|
1109
|
+
signingPurposes: this.resolvedConfig.oauthAllowedAudiences.has(caller.consumer)
|
|
1110
|
+
? ['application_assertion', 'oauth_client_assertion'] : ['authentication', 'application_assertion'],
|
|
1111
|
+
...(this.resolvedConfig.oauthAllowedAudiences.has(caller.consumer)
|
|
1112
|
+
? { oauthAudiences: [...this.resolvedConfig.oauthAllowedAudiences.get(caller.consumer)].sort() } : {}),
|
|
842
1113
|
httpOrigins: [...(this.resolvedConfig.httpAllowedOrigins.get(caller.consumer) ?? [])].sort() };
|
|
843
1114
|
}
|
|
844
1115
|
async createConfirmed(operationId, caller, parameters) {
|
|
845
1116
|
this.assertInstalledCaller(caller);
|
|
846
1117
|
return this.catalogStore.withNativeCreateLock(async () => this.withUserNative(async (native) => {
|
|
1118
|
+
// Refresh the namespace generation after document/key operations performed through identity handles.
|
|
1119
|
+
await native.recover();
|
|
847
1120
|
const catalog = await this.loadCatalog(this.requireSlot());
|
|
848
1121
|
const existing = catalog.entries.find(entry => entry.creationOperationId === operationId);
|
|
849
1122
|
if (existing)
|
|
@@ -863,9 +1136,21 @@ export class AnpIdentityService extends Service {
|
|
|
863
1136
|
consumer: caller.consumer, label: parameters.label, createdAt: new Date().toISOString(),
|
|
864
1137
|
...(parameters.handle === undefined ? {} : { handle: parameters.handle }),
|
|
865
1138
|
authorizationMode: 'user', baselineIdentityIds: baseline.map(item => item.reference.identityId) });
|
|
866
|
-
const
|
|
1139
|
+
const profile = parameters.profile ?? 'e1';
|
|
1140
|
+
const protocol = parameters.usage === 'protocol';
|
|
1141
|
+
const identity = await native.create({ profile, domain: parameters.domain,
|
|
867
1142
|
pathSegments: parameters.path.split('/').filter(Boolean), capabilities: { didWba: true },
|
|
868
|
-
managedKeys:
|
|
1143
|
+
managedKeys: protocol ? [
|
|
1144
|
+
...(profile === 'e1' ? [{ fragment: 'root', role: 'root_control' }] : []),
|
|
1145
|
+
{ fragment: 'device', role: 'device_signing' }, { fragment: 'agreement', role: 'e2ee_agreement' },
|
|
1146
|
+
] : [{ fragment: 'root', role: 'root_control' }, { fragment: 'request', role: 'request_signing' }],
|
|
1147
|
+
...(protocol ? { extensions: [{ type: 'device_manifest', value: { devices: [{ deviceId: 'dsh-' + createHash('sha256').update(operationId).digest('hex').slice(0, 24),
|
|
1148
|
+
signingKeyId: '#device', e2eeKeyId: '#agreement', profiles: parameters.identityOnly ? [
|
|
1149
|
+
'anp.core.binding.v1', 'anp.identity.discovery.v1',
|
|
1150
|
+
] : [
|
|
1151
|
+
'anp.core.binding.v1', 'anp.identity.discovery.v1', 'anp.direct.base.v1',
|
|
1152
|
+
'anp.direct.e2ee.v2', 'anp.group.base.v2', 'anp.group.e2ee.v2',
|
|
1153
|
+
] }] } }] } : {}) });
|
|
869
1154
|
await this.catalogStore.updateIntent(operationId, intent => ({ ...intent, reference: identity.reference }));
|
|
870
1155
|
const entry = await this.catalogStore.commitIntent(operationId);
|
|
871
1156
|
await this.catalogStore.deleteIntent(operationId);
|
|
@@ -1108,7 +1393,13 @@ class HostLease {
|
|
|
1108
1393
|
this.#retired.clear();
|
|
1109
1394
|
this.service.removeLease(this.slot, this);
|
|
1110
1395
|
}
|
|
1111
|
-
info = () => this.call('IDENTITY_READ', () =>
|
|
1396
|
+
info = () => this.call('IDENTITY_READ', async () => {
|
|
1397
|
+
const info = await this.native.info();
|
|
1398
|
+
if (info.backendAvailable === false)
|
|
1399
|
+
throw pluginError('provider_unavailable');
|
|
1400
|
+
return { storeId: info.storeId, schemaCompatible: info.schemaCompatible,
|
|
1401
|
+
identityCount: info.identityCount, health: info.health };
|
|
1402
|
+
});
|
|
1112
1403
|
recover = () => this.call('IDENTITY_READ', () => this.native.recover());
|
|
1113
1404
|
list = () => this.call('IDENTITY_READ', () => this.native.list());
|
|
1114
1405
|
publicIdentity = (reference) => this.call('IDENTITY_READ', () => this.native.publicIdentity(reference));
|
|
@@ -1116,6 +1407,8 @@ class HostLease {
|
|
|
1116
1407
|
recoverIdentity = (reference) => this.call('IDENTITY_READ', () => this.native.recoverIdentity(reference));
|
|
1117
1408
|
create = (request) => this.call('IDENTITY_CREATE', () => this.service.createForHost(this.slot, this.native, this.consumer, request));
|
|
1118
1409
|
delete = (reference) => this.call('IDENTITY_DELETE', () => this.service.deleteForHost(this.slot, this.native, this.consumer, reference));
|
|
1410
|
+
oauthPublicJwk = (reference, kid) => this.call('IDENTITY_READ', () => this.native.oauthPublicJwk(reference, kid));
|
|
1411
|
+
signOAuthClientAssertion = (reference, request) => this.call('IDENTITY_SIGN', () => this.native.signOAuthClientAssertion(reference, request));
|
|
1119
1412
|
sign = (reference, request) => this.call('IDENTITY_SIGN', () => this.native.sign(reference, request));
|
|
1120
1413
|
verify = (reference, request) => this.call('IDENTITY_READ', () => this.native.verify(reference, request));
|
|
1121
1414
|
signOriginProof = (reference, request) => this.call('IDENTITY_SIGN', () => this.native.signOriginProof(reference, request));
|
|
@@ -1225,6 +1518,8 @@ function renewalAt(issuedAt, ttlSeconds) {
|
|
|
1225
1518
|
}
|
|
1226
1519
|
export default AnpIdentityService;
|
|
1227
1520
|
function resolveConfig(config) {
|
|
1521
|
+
if (config.signerMode !== undefined && config.signerMode !== 'local')
|
|
1522
|
+
throw new TypeError('Remote signers are unsupported');
|
|
1228
1523
|
const configured = config.stateRoot?.trim();
|
|
1229
1524
|
const dshHome = process.env.DSH_HOME?.trim() || join(homedir(), '.dsh');
|
|
1230
1525
|
const stateRoot = configured === undefined || configured.length === 0
|
|
@@ -1250,14 +1545,24 @@ function resolveConfig(config) {
|
|
|
1250
1545
|
validateConsumer(consumer);
|
|
1251
1546
|
if (!allowConsumers.has(consumer))
|
|
1252
1547
|
throw new TypeError('anp-identity: HTTP consumer must be allowed');
|
|
1253
|
-
|
|
1548
|
+
if (origins.includes('*') && !userConsumers.has(consumer)) {
|
|
1549
|
+
throw new TypeError('anp-identity: wildcard HTTP origins require an explicit User consumer');
|
|
1550
|
+
}
|
|
1551
|
+
const normalized = origins.map(value => value === '*' ? value : normalizeHttpsOrigin(value));
|
|
1254
1552
|
if (new Set(normalized).size !== normalized.length) {
|
|
1255
1553
|
throw new TypeError('anp-identity: HTTP origins must be unique');
|
|
1256
1554
|
}
|
|
1257
1555
|
httpAllowedOrigins.set(consumer, new Set(normalized));
|
|
1258
1556
|
}
|
|
1557
|
+
const oauthAllowedAudiences = new Map();
|
|
1558
|
+
for (const [consumer, audiences] of Object.entries(config.oauthAllowedAudiences ?? {})) {
|
|
1559
|
+
if (!userConsumers.has(consumer) || !audiences.length)
|
|
1560
|
+
throw new TypeError('OAuth consumers must be approved User consumers');
|
|
1561
|
+
oauthAllowedAudiences.set(consumer, new Set(audiences.map(value => exactHttps(value))));
|
|
1562
|
+
}
|
|
1259
1563
|
return {
|
|
1260
1564
|
stateRoot,
|
|
1565
|
+
oauthAllowedAudiences,
|
|
1261
1566
|
allowConsumers,
|
|
1262
1567
|
allowProviderConsumers,
|
|
1263
1568
|
httpAllowedOrigins,
|
|
@@ -1469,4 +1774,8 @@ function readErrorCode(error) {
|
|
|
1469
1774
|
function isPluginCode(error, code) {
|
|
1470
1775
|
return error instanceof AnpIdentityPluginError && error.code === code;
|
|
1471
1776
|
}
|
|
1777
|
+
export { createPublicationBackend } from './publication-http.js';
|
|
1778
|
+
export { EncryptedManagementCredentialBroker } from './management-credentials.js';
|
|
1779
|
+
export { createManagedPublicationDriver, registerLocalIdentity } from './management-driver.js';
|
|
1780
|
+
export { PapCallbackManagementError } from './pap-callback.js';
|
|
1472
1781
|
//# sourceMappingURL=index.js.map
|