@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.
Files changed (116) hide show
  1. package/BOUNDARY.md +30 -5
  2. package/PUBLICATION.md +181 -0
  3. package/README.md +73 -1
  4. package/README.zh.md +44 -1
  5. package/V7-MANAGEMENT.md +18 -0
  6. package/cordis.patch.yml +9 -0
  7. package/examples/fresh-bootstrap.cordis.patch.yml +38 -0
  8. package/lib/authorization-types.d.ts +6 -0
  9. package/lib/authorization-types.d.ts.map +1 -1
  10. package/lib/authorization.d.ts.map +1 -1
  11. package/lib/authorization.js +23 -6
  12. package/lib/authorization.js.map +1 -1
  13. package/lib/catalog.d.ts +3 -1
  14. package/lib/catalog.d.ts.map +1 -1
  15. package/lib/catalog.js +15 -3
  16. package/lib/catalog.js.map +1 -1
  17. package/lib/client/controller.d.ts +18 -0
  18. package/lib/client/controller.d.ts.map +1 -1
  19. package/lib/client/controller.js +89 -5
  20. package/lib/client/controller.js.map +1 -1
  21. package/lib/client/views.d.ts.map +1 -1
  22. package/lib/client/views.js +37 -6
  23. package/lib/client/views.js.map +1 -1
  24. package/lib/client-metadata.d.ts +3 -0
  25. package/lib/client-metadata.d.ts.map +1 -0
  26. package/lib/client-metadata.js +11 -0
  27. package/lib/client-metadata.js.map +1 -0
  28. package/lib/client.js +260 -14
  29. package/lib/client.js.map +4 -4
  30. package/lib/host-management-secret.d.ts +41 -0
  31. package/lib/host-management-secret.d.ts.map +1 -0
  32. package/lib/host-management-secret.js +92 -0
  33. package/lib/host-management-secret.js.map +1 -0
  34. package/lib/index.d.ts +73 -3
  35. package/lib/index.d.ts.map +1 -1
  36. package/lib/index.js +331 -22
  37. package/lib/index.js.map +1 -1
  38. package/lib/initial-registration-proof.d.ts +13 -0
  39. package/lib/initial-registration-proof.d.ts.map +1 -0
  40. package/lib/initial-registration-proof.js +48 -0
  41. package/lib/initial-registration-proof.js.map +1 -0
  42. package/lib/installed-consumer.d.ts +25 -0
  43. package/lib/installed-consumer.d.ts.map +1 -0
  44. package/lib/installed-consumer.js +66 -0
  45. package/lib/installed-consumer.js.map +1 -0
  46. package/lib/management-credentials.d.ts +15 -0
  47. package/lib/management-credentials.d.ts.map +1 -0
  48. package/lib/management-credentials.js +126 -0
  49. package/lib/management-credentials.js.map +1 -0
  50. package/lib/management-driver.d.ts +83 -0
  51. package/lib/management-driver.d.ts.map +1 -0
  52. package/lib/management-driver.js +405 -0
  53. package/lib/management-driver.js.map +1 -0
  54. package/lib/management-remote.d.ts +27 -1
  55. package/lib/management-remote.d.ts.map +1 -1
  56. package/lib/management-remote.js +69 -3
  57. package/lib/management-remote.js.map +1 -1
  58. package/lib/management-types.d.ts +7 -0
  59. package/lib/management-types.d.ts.map +1 -1
  60. package/lib/pap-callback.d.ts +110 -0
  61. package/lib/pap-callback.d.ts.map +1 -0
  62. package/lib/pap-callback.js +51 -0
  63. package/lib/pap-callback.js.map +1 -0
  64. package/lib/protocol-bootstrap.d.ts +24 -0
  65. package/lib/protocol-bootstrap.d.ts.map +1 -0
  66. package/lib/protocol-bootstrap.js +34 -0
  67. package/lib/protocol-bootstrap.js.map +1 -0
  68. package/lib/protocol-client.d.ts +137 -0
  69. package/lib/protocol-client.d.ts.map +1 -0
  70. package/lib/protocol-client.js +11 -0
  71. package/lib/protocol-client.js.map +1 -0
  72. package/lib/provider-api.d.ts +6 -2
  73. package/lib/provider-api.d.ts.map +1 -1
  74. package/lib/provider-api.js.map +1 -1
  75. package/lib/publication-http.d.ts +19 -0
  76. package/lib/publication-http.d.ts.map +1 -0
  77. package/lib/publication-http.js +183 -0
  78. package/lib/publication-http.js.map +1 -0
  79. package/lib/publication-types.d.ts +161 -0
  80. package/lib/publication-types.d.ts.map +1 -0
  81. package/lib/publication-types.js +35 -0
  82. package/lib/publication-types.js.map +1 -0
  83. package/lib/publication.d.ts +21 -0
  84. package/lib/publication.d.ts.map +1 -0
  85. package/lib/publication.js +235 -0
  86. package/lib/publication.js.map +1 -0
  87. package/lib/registration-bootstrap.d.ts +56 -0
  88. package/lib/registration-bootstrap.d.ts.map +1 -0
  89. package/lib/registration-bootstrap.js +523 -0
  90. package/lib/registration-bootstrap.js.map +1 -0
  91. package/lib/registration-service-settings.d.ts +12 -0
  92. package/lib/registration-service-settings.d.ts.map +1 -0
  93. package/lib/registration-service-settings.js +124 -0
  94. package/lib/registration-service-settings.js.map +1 -0
  95. package/lib/registration-service-types.d.ts +11 -0
  96. package/lib/registration-service-types.d.ts.map +1 -0
  97. package/lib/registration-service-types.js +3 -0
  98. package/lib/registration-service-types.js.map +1 -0
  99. package/lib/remote.d.ts.map +1 -1
  100. package/lib/remote.js +11 -2
  101. package/lib/remote.js.map +1 -1
  102. package/lib/target-policy.d.ts +35 -0
  103. package/lib/target-policy.d.ts.map +1 -0
  104. package/lib/target-policy.js +319 -0
  105. package/lib/target-policy.js.map +1 -0
  106. package/lib/user-client.d.ts +6 -0
  107. package/lib/user-client.d.ts.map +1 -1
  108. package/lib/user-client.js.map +1 -1
  109. package/lib/user-operation.d.ts.map +1 -1
  110. package/lib/user-operation.js +9 -0
  111. package/lib/user-operation.js.map +1 -1
  112. package/lib/user-service.d.ts +8 -1
  113. package/lib/user-service.d.ts.map +1 -1
  114. package/lib/user-service.js +58 -4
  115. package/lib/user-service.js.map +1 -1
  116. package/package.json +51 -29
@@ -0,0 +1,126 @@
1
+ /** Encrypted Host-only management credentials, partitioned by service origin and identity. */
2
+ import { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID } from 'node:crypto';
3
+ import { mkdir, open, readFile, rename, stat, unlink } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import lockfile from 'proper-lockfile';
6
+ import { exactHttps } from './publication-http.js';
7
+ import { pluginError } from './errors.js';
8
+ import { syncDirectory } from './sync-directory.js';
9
+ /** Each key resolver invocation must return a fresh, owner-controlled 32-byte encryption key. */
10
+ export class EncryptedManagementCredentialBroker {
11
+ root;
12
+ encryptionKey;
13
+ constructor(root, encryptionKey) {
14
+ this.root = root;
15
+ this.encryptionKey = encryptionKey;
16
+ }
17
+ partition(reference, origin) {
18
+ exactHttps(origin);
19
+ if (new URL(origin).origin !== origin)
20
+ throw pluginError('invalid_request');
21
+ return createHash('sha256').update(JSON.stringify([origin, reference.storeId, reference.identityId, reference.did])).digest('hex');
22
+ }
23
+ async get(reference, origin) {
24
+ const partition = this.partition(reference, origin);
25
+ let bytes;
26
+ try {
27
+ const path = join(this.root, partition + '.json');
28
+ const info = await stat(path);
29
+ if ((info.mode & 0o077) !== 0 || info.size > 4 * 1024 * 1024)
30
+ throw pluginError('provider_unavailable');
31
+ bytes = await readFile(path, 'utf8');
32
+ }
33
+ catch (error) {
34
+ if (error.code === 'ENOENT')
35
+ return undefined;
36
+ throw pluginError('provider_unavailable');
37
+ }
38
+ const key = await this.encryptionKey();
39
+ try {
40
+ const row = JSON.parse(bytes);
41
+ if (row.schema !== 1 || row.partition !== partition)
42
+ throw pluginError('provider_unavailable');
43
+ const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(row.iv, 'base64url'));
44
+ decipher.setAAD(Buffer.from(partition));
45
+ decipher.setAuthTag(Buffer.from(row.tag, 'base64url'));
46
+ const credential = JSON.parse(Buffer.concat([decipher.update(Buffer.from(row.ciphertext, 'base64url')), decipher.final()]).toString('utf8'));
47
+ if (!credential.accessToken || !credential.deviceId || !credential.signingKeyId)
48
+ throw pluginError('provider_unavailable');
49
+ return credential;
50
+ }
51
+ catch {
52
+ throw pluginError('provider_unavailable');
53
+ }
54
+ finally {
55
+ key.fill(0);
56
+ }
57
+ }
58
+ async writeUnlocked(reference, origin, credential) {
59
+ const partition = this.partition(reference, origin);
60
+ await mkdir(this.root, { recursive: true, mode: 0o700 });
61
+ const path = join(this.root, partition + '.json');
62
+ const key = await this.encryptionKey();
63
+ try {
64
+ const iv = randomBytes(12);
65
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
66
+ cipher.setAAD(Buffer.from(partition));
67
+ const ciphertext = Buffer.concat([cipher.update(JSON.stringify(credential)), cipher.final()]);
68
+ const row = { schema: 1, partition, iv: iv.toString('base64url'), tag: cipher.getAuthTag().toString('base64url'), ciphertext: ciphertext.toString('base64url') };
69
+ const temporary = path + '.' + randomUUID();
70
+ const file = await open(temporary, 'wx', 0o600);
71
+ try {
72
+ await file.writeFile(JSON.stringify(row));
73
+ await file.sync();
74
+ }
75
+ finally {
76
+ await file.close();
77
+ }
78
+ try {
79
+ await rename(temporary, path);
80
+ await syncDirectory(this.root);
81
+ }
82
+ finally {
83
+ await unlink(temporary).catch(() => { });
84
+ }
85
+ }
86
+ finally {
87
+ key.fill(0);
88
+ }
89
+ }
90
+ async set(reference, origin, credential) {
91
+ await this.transaction(reference, origin, async () => {
92
+ const previous = await this.get(reference, origin);
93
+ await this.writeUnlocked(reference, origin, { ...credential, operationBindings: previous?.operationBindings ?? {} });
94
+ });
95
+ }
96
+ async reserveOperation(reference, origin, operationId, body) {
97
+ return this.transaction(reference, origin, async () => {
98
+ const credential = await this.get(reference, origin);
99
+ if (!credential)
100
+ throw pluginError('provider_unavailable');
101
+ const operations = credential.operationBindings ?? {};
102
+ const previous = operations[operationId];
103
+ if (previous)
104
+ return structuredClone(previous);
105
+ if (Object.keys(operations).length >= 1024)
106
+ throw pluginError('provider_unavailable');
107
+ operations[operationId] = structuredClone(body);
108
+ if (Buffer.byteLength(JSON.stringify(operations)) > 2 * 1024 * 1024)
109
+ throw pluginError('provider_unavailable');
110
+ await this.writeUnlocked(reference, origin, { ...credential, operationBindings: operations });
111
+ return structuredClone(body);
112
+ });
113
+ }
114
+ async transaction(reference, origin, run) {
115
+ await mkdir(this.root, { recursive: true, mode: 0o700 });
116
+ const path = join(this.root, this.partition(reference, origin) + '.json');
117
+ const release = await lockfile.lock(this.root, { lockfilePath: path + '.lock', realpath: false, retries: { retries: 100, minTimeout: 20, maxTimeout: 100 } });
118
+ try {
119
+ return await run();
120
+ }
121
+ finally {
122
+ await release();
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=management-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"management-credentials.js","sourceRoot":"","sources":["../src/management-credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { IdentityReference, JsonValue, VerifiedRemoteDocument } from '@agent-network-protocol/anp-identity';
2
+ import type { ProviderLease } from '@agent-network-protocol/anp-identity/provider';
3
+ import type { PublicationBackend } from './publication-types.js';
4
+ import type { PapCallbackManagementOperations } from './pap-callback.js';
5
+ export interface DeviceManagementCredential {
6
+ accessToken: string;
7
+ deviceId: string;
8
+ signingKeyId: string;
9
+ audience?: string;
10
+ operationBindings?: Record<string, Record<string, unknown>>;
11
+ }
12
+ /** The implementation must encrypt secrets at rest and partition by exact identity and service origin. */
13
+ export interface ManagementCredentialBroker {
14
+ get(reference: IdentityReference, origin: string): Promise<DeviceManagementCredential | undefined>;
15
+ set(reference: IdentityReference, origin: string, credential: DeviceManagementCredential): Promise<void>;
16
+ reserveOperation(reference: IdentityReference, origin: string, operationId: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
17
+ }
18
+ export interface ManagedPublicationDriver extends PapCallbackManagementOperations {
19
+ readonly documentId: string;
20
+ readonly backend: PublicationBackend;
21
+ readonly reference: IdentityReference;
22
+ readVerifiedDocument(): Promise<VerifiedRemoteDocument>;
23
+ }
24
+ /** Create a device-authorized publication driver from a live Registry and an authenticated resolve.
25
+ * @param input - Host custody, protected credentials and the fixed User Service origin.
26
+ * @returns A driver bound to the server-resolved document id, never a user-supplied id.
27
+ */
28
+ export declare function createManagedPublicationDriver(input: {
29
+ reference: IdentityReference;
30
+ origin: string;
31
+ credentials: ManagementCredentialBroker;
32
+ withNative<T>(operation: (native: ProviderLease) => Promise<T>): Promise<T>;
33
+ fetch?: typeof fetch;
34
+ /** Supplied by the Host installer to revoke this driver with its management Context and provider generation. */
35
+ assertActive?: () => Promise<void>;
36
+ /** Synchronous final epoch check immediately before physical sends and after body/native awaits. */
37
+ assertCurrent?: () => void;
38
+ }): Promise<ManagedPublicationDriver>;
39
+ /** Registration fields supplied through the trusted Host management flow, never persisted in the catalog. */
40
+ export interface LocalRegistrationInput {
41
+ handle: string;
42
+ phone?: string;
43
+ otp_code?: string;
44
+ email?: string;
45
+ invite_code?: string;
46
+ name?: string;
47
+ registration_operation_id?: string;
48
+ }
49
+ /** Register a locally held E1 Manifest and retain only a live, server-verified device credential.
50
+ * @param input - Local identity, registration confirmation and protected broker.
51
+ * @returns Public registration facts. No Access Token is returned.
52
+ */
53
+ export declare function registerLocalIdentity(input: {
54
+ reference: IdentityReference;
55
+ origin: string;
56
+ registration: LocalRegistrationInput;
57
+ credentials: ManagementCredentialBroker;
58
+ withNative<T>(operation: (native: ProviderLease) => Promise<T>): Promise<T>;
59
+ fetch?: typeof fetch;
60
+ managementAudience?: string;
61
+ handleDomain?: string;
62
+ assertActive?: () => void;
63
+ }): Promise<{
64
+ did: string;
65
+ deviceId: string;
66
+ state: 'registered';
67
+ }>;
68
+ /** RFC 8785 serialization for native-produced JSON values.
69
+ * @param value - Finite JSON data, with keys ordered by UTF-16 code units.
70
+ * @returns Canonical UTF-8 text.
71
+ */
72
+ export declare function canonicalManagementJson(value: JsonValue): string;
73
+ /** Construct the exact Web registration business projection; OTP and proof freshness are excluded.
74
+ * @param input - Public local document and the explicit registration account/profile fields.
75
+ * @returns Projection checked by the local native signer and User Service.
76
+ */
77
+ export declare function webRegistrationProjection(input: {
78
+ document: JsonValue;
79
+ registration: LocalRegistrationInput;
80
+ audience: string;
81
+ handleDomain: string;
82
+ }): JsonValue;
83
+ //# sourceMappingURL=management-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"management-driver.d.ts","sourceRoot":"","sources":["../src/management-driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,405 @@
1
+ /** Real User Service device-management transport. Credentials stay in the Host broker. */
2
+ import { randomUUID, createHash } from 'node:crypto';
3
+ import { exactHttps, parsePublicJson } from './publication-http.js';
4
+ import { AnpIdentityPluginError, pluginError } from './errors.js';
5
+ import { prepareInitialRegistrationProof } from './initial-registration-proof.js';
6
+ import { createAuthenticatedHttp } from './http-auth.js';
7
+ import { PapCallbackOperationSchema, PapCallbackRegistrationResultSchema, PapCallbackClaimResultSchema, PapCallbackManagementError, papCallbackManagementError } from './pap-callback.js';
8
+ /** Sign the exact User Service request with the existing ANP HTTP signing port. */
9
+ async function signedRequest(request, input) {
10
+ return input.withNative(native => createAuthenticatedHttp(input.reference, native, new Set([input.origin]), input.assertActive, async () => input.signingKeyId).dispatch(request, input.transport));
11
+ }
12
+ /** Create a device-authorized publication driver from a live Registry and an authenticated resolve.
13
+ * @param input - Host custody, protected credentials and the fixed User Service origin.
14
+ * @returns A driver bound to the server-resolved document id, never a user-supplied id.
15
+ */
16
+ export async function createManagedPublicationDriver(input) {
17
+ const origin = exactHttps(input.origin);
18
+ if (new URL(origin).origin !== origin)
19
+ throw pluginError('invalid_request');
20
+ const transport = input.fetch ?? fetch;
21
+ const send = async (request, init) => {
22
+ await input.assertActive?.();
23
+ input.assertCurrent?.();
24
+ const auth = await credential();
25
+ const response = await signedRequest(new Request(request, init), {
26
+ reference, origin, signingKeyId: auth.signingKeyId, withNative: input.withNative,
27
+ transport: (request, init) => { input.assertCurrent?.(); return transport(request, init); },
28
+ assertActive: async () => { await input.assertActive?.(); input.assertCurrent?.(); },
29
+ });
30
+ try {
31
+ await input.assertActive?.();
32
+ input.assertCurrent?.();
33
+ return response;
34
+ }
35
+ catch (error) {
36
+ await response.body?.cancel();
37
+ throw error;
38
+ }
39
+ };
40
+ const reference = Object.freeze(structuredClone(input.reference));
41
+ const credential = async () => {
42
+ const value = await input.credentials.get(reference, origin);
43
+ if (!value || !value.deviceId || !value.signingKeyId)
44
+ throw pluginError('provider_unavailable');
45
+ return value;
46
+ };
47
+ const read = async (request, callbackErrors = false) => {
48
+ const response = await send(request);
49
+ if ((!callbackErrors && response.status !== 200) || response.redirected || !(response.headers.get('content-type') ?? '').startsWith('application/json')) {
50
+ await response.body?.cancel();
51
+ throw pluginError('provider_unavailable');
52
+ }
53
+ const reader = response.body?.getReader();
54
+ const parts = [];
55
+ let size = 0;
56
+ try {
57
+ while (reader) {
58
+ const part = await reader.read();
59
+ if (part.done)
60
+ break;
61
+ size += part.value.length;
62
+ if (size > 131072)
63
+ throw pluginError('provider_incompatible');
64
+ parts.push(part.value);
65
+ }
66
+ const result = parsePublicJson(new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(parts)));
67
+ if (!result || typeof result !== 'object' || Array.isArray(result))
68
+ throw pluginError('provider_incompatible');
69
+ const object = result;
70
+ input.assertCurrent?.();
71
+ if (response.status !== 200)
72
+ throw papCallbackManagementError(object) ?? pluginError('provider_unavailable');
73
+ return object;
74
+ }
75
+ finally {
76
+ await reader?.cancel();
77
+ reader?.releaseLock();
78
+ }
79
+ };
80
+ const rpc = async (method, params) => {
81
+ const result = await read(new Request(origin + '/user-service/v1/did-auth/rpc', { method: 'POST', redirect: 'manual', signal: AbortSignal.timeout(15000),
82
+ headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: randomUUID(), method, params }) }));
83
+ if (result.error || !result.result || typeof result.result !== 'object' || Array.isArray(result.result))
84
+ throw pluginError('provider_unavailable');
85
+ return result.result;
86
+ };
87
+ const registry = async () => {
88
+ const current = await rpc('device_registry_get', {});
89
+ const auth = await credential();
90
+ const device = current.devices?.find(device => device.device_id === auth.deviceId);
91
+ if (current.did !== reference.did || !device || device.signing_key_id !== auth.signingKeyId
92
+ || device.status !== 'active' || device.role !== 'admin' || !device.management_ready
93
+ || !Number.isSafeInteger(current.checkpoint?.document_version) || !Number.isSafeInteger(current.checkpoint.registry_version)
94
+ || !/^sha256:[A-Za-z0-9_-]{43}$/u.test(current.checkpoint.document_hash))
95
+ throw pluginError('provider_incompatible');
96
+ return current;
97
+ };
98
+ const proof = async (method, params) => {
99
+ const auth = await credential();
100
+ return input.withNative(async (native) => {
101
+ const signer = native.signManagementProof;
102
+ if (typeof signer !== 'function')
103
+ throw pluginError('provider_incompatible');
104
+ return signer.call(native, reference, { key: { kid: auth.signingKeyId }, method, params,
105
+ ...(auth.audience === undefined ? {} : { audience: auth.audience }) });
106
+ });
107
+ };
108
+ const methods = { prepare: 'agent_client_prepare', activate: 'agent_client_activate',
109
+ status: 'agent_client_status', resolve: 'agent_client_resolve',
110
+ 'pap-callback-register': 'agent_client_pap_callback_register', 'pap-callback-claim': 'agent_client_pap_callback_claim' };
111
+ const deviceResponse = async (action, operationId, extra = {}) => {
112
+ await input.assertActive?.();
113
+ const [current, auth] = await Promise.all([registry(), credential()]);
114
+ const httpPath = '/user-service/v1/agent-clients/device/' + action;
115
+ const proposed = { operation_id: operationId, expected_document_version: current.checkpoint.document_version,
116
+ expected_document_hash: current.checkpoint.document_hash, expected_registry_version: current.checkpoint.registry_version,
117
+ authorizing_device_id: auth.deviceId, ...extra };
118
+ const body = action === 'prepare' || action === 'activate' || action === 'pap-callback-register'
119
+ ? await input.credentials.reserveOperation(reference, origin, methods[action] + ':' + operationId, proposed) : proposed;
120
+ if (action === 'pap-callback-register' && JSON.stringify(body.callback) !== JSON.stringify(extra.callback))
121
+ throw pluginError('catalog_conflict');
122
+ const signed = await proof(methods[action], { ...body, http_path: httpPath, target_did: reference.did });
123
+ await input.assertActive?.();
124
+ const result = await read(new Request(origin + httpPath, { method: 'POST', redirect: 'manual', signal: AbortSignal.timeout(15000),
125
+ headers: { 'content-type': 'application/json' }, body: JSON.stringify({ ...body, authorizing_device_proof: signed }) }), action === 'pap-callback-register' || action === 'pap-callback-claim');
126
+ await input.assertActive?.();
127
+ return result;
128
+ };
129
+ const deviceCommand = async (action, operationId, extra = {}) => {
130
+ const result = await deviceResponse(action, operationId, extra);
131
+ if (result.did !== reference.did || typeof result.document_id !== 'string')
132
+ throw pluginError('provider_incompatible');
133
+ return result;
134
+ };
135
+ const resolved = await deviceCommand('resolve', randomUUID());
136
+ const documentId = resolved.document_id;
137
+ if (!/^[A-Za-z0-9_-]{1,128}$/u.test(documentId))
138
+ throw pluginError('provider_incompatible');
139
+ if (typeof resolved.client_id !== 'string' || typeof resolved.jwks_uri !== 'string')
140
+ throw pluginError('provider_incompatible');
141
+ const clientId = exactHttps(resolved.client_id, true);
142
+ const jwksUri = exactHttps(resolved.jwks_uri, true);
143
+ if (new URL(clientId).origin !== origin || new URL(jwksUri).origin !== origin)
144
+ throw pluginError('provider_incompatible');
145
+ const checkpointObservation = async () => {
146
+ const result = await deviceCommand('resolve', randomUUID());
147
+ const checkpoint = result.checkpoint;
148
+ if (!checkpoint || !Number.isSafeInteger(checkpoint.document_version) || !Number.isSafeInteger(checkpoint.registry_version)
149
+ || !/^sha256:[A-Za-z0-9_-]{43}$/u.test(checkpoint.document_hash) || !result.document || typeof result.document !== 'object')
150
+ throw pluginError('provider_incompatible');
151
+ return { document: result.document, evidence: { documentVersion: checkpoint.document_version,
152
+ registryVersion: checkpoint.registry_version, documentDigest: checkpoint.document_hash } };
153
+ };
154
+ const backend = {
155
+ origin, clientId: id => { if (id !== documentId)
156
+ throw pluginError('invalid_request'); return clientId; },
157
+ prepare: async (id, publication) => {
158
+ if (id !== documentId || !('schema_version' in publication))
159
+ throw pluginError('invalid_request');
160
+ return await deviceCommand('prepare', publication.operation_id, { publication: publication });
161
+ },
162
+ activate: async (id, operationId) => {
163
+ if (id !== documentId)
164
+ throw pluginError('invalid_request');
165
+ return await deviceCommand('activate', randomUUID(), { publication_operation_id: operationId });
166
+ },
167
+ readPublic: async (id, part, url) => {
168
+ if (id !== documentId)
169
+ throw pluginError('invalid_request');
170
+ const target = url ?? (part === 'client.json' ? clientId : jwksUri);
171
+ if (new URL(exactHttps(target, true)).origin !== origin)
172
+ throw pluginError('provider_incompatible');
173
+ const response = await send(new Request(target, { redirect: 'manual', signal: AbortSignal.timeout(15000) }));
174
+ if (response.status !== 200 || response.redirected) {
175
+ await response.body?.cancel();
176
+ throw pluginError('provider_unavailable');
177
+ }
178
+ const text = await response.text();
179
+ input.assertCurrent?.();
180
+ if (Buffer.byteLength(text) > 32768)
181
+ throw pluginError('provider_incompatible');
182
+ parsePublicJson(text);
183
+ return text;
184
+ },
185
+ publishDocument: async (requestedReference, session, assertActive) => {
186
+ if (JSON.stringify(requestedReference) !== JSON.stringify(reference))
187
+ throw pluginError('invalid_request');
188
+ await assertActive();
189
+ if (await session.hostPhase() === 'publication_uncertain') {
190
+ await session.reconcile(await checkpointObservation());
191
+ return;
192
+ }
193
+ const candidate = await session.candidate();
194
+ const previous = await input.withNative(native => native.publicIdentity(reference));
195
+ const oldDocument = previous.document;
196
+ const nextDocument = candidate.candidateDocument;
197
+ const methods = (document) => (document.authentication ?? []);
198
+ const oldKeys = methods(oldDocument);
199
+ const nextKeys = methods(nextDocument);
200
+ const add = nextKeys.filter(key => !oldKeys.includes(key));
201
+ const remove = oldKeys.filter(key => !nextKeys.includes(key));
202
+ const [current, auth] = await Promise.all([registry(), credential()]);
203
+ const base = candidate.baseEvidence;
204
+ if (!base || current.checkpoint.document_version !== base.documentVersion || current.checkpoint.registry_version !== base.registryVersion
205
+ || current.checkpoint.document_hash !== base.documentDigest && current.checkpoint.document_hash !== 'sha256:' + base.documentDigest)
206
+ throw pluginError('catalog_conflict');
207
+ const method = add.length || remove.length ? 'device_oauth_keys_update' : 'device_document_update';
208
+ const params = { operation_id: candidate.operationId, expected_document_version: base.documentVersion,
209
+ expected_document_hash: base.documentDigest.startsWith('sha256:') ? base.documentDigest : 'sha256:' + base.documentDigest, expected_registry_version: base.registryVersion,
210
+ authorizing_device_id: auth.deviceId, new_document: candidate.candidateDocument,
211
+ ...(method === 'device_oauth_keys_update' ? { add_key_ids: add, remove_key_ids: remove } : {}) };
212
+ const bound = await input.credentials.reserveOperation(reference, origin, method + ':' + candidate.operationId, params);
213
+ bound.authorizing_device_proof = await proof(method, bound);
214
+ const attempt = await session.beginPublication();
215
+ try {
216
+ await assertActive();
217
+ await rpc(method, bound);
218
+ }
219
+ catch {
220
+ await session.complete(attempt, { result: 'unknown' });
221
+ throw pluginError('provider_unavailable');
222
+ }
223
+ const observation = await checkpointObservation();
224
+ await assertActive();
225
+ // Reconcile compares the authoritative remote digest/document with the native pending candidate.
226
+ await session.complete(attempt, { result: 'unknown' });
227
+ await session.reconcile(observation);
228
+ },
229
+ };
230
+ async function execute(command) {
231
+ try {
232
+ await input.assertActive?.();
233
+ const parsed = PapCallbackOperationSchema.safeParse(command);
234
+ if (!parsed.success)
235
+ throw pluginError('invalid_request');
236
+ const value = parsed.data;
237
+ if (value.action === 'pap-callback-register') {
238
+ exactHttps(value.callback.issuer);
239
+ const expiry = Date.parse(value.callback.expires_at);
240
+ if (expiry <= Date.now() || expiry > Date.now() + 600000)
241
+ throw pluginError('invalid_request');
242
+ if (value.callback.client_id !== clientId || value.callback.redirect_uri !== origin + '/user-service/v1/pap-callback')
243
+ throw pluginError('invalid_request');
244
+ const result = await deviceResponse(value.action, value.operationId, { callback: value.callback });
245
+ const registered = PapCallbackRegistrationResultSchema.safeParse(result);
246
+ if (!registered.success || result.did !== reference.did || result.document_id !== documentId
247
+ || registered.data.transaction_id !== value.callback.transaction_id || registered.data.callback_uri !== value.callback.redirect_uri
248
+ || registered.data.expires_at !== value.callback.expires_at)
249
+ throw pluginError('provider_incompatible');
250
+ return { transaction_id: registered.data.transaction_id, callback_uri: registered.data.callback_uri, expires_at: registered.data.expires_at };
251
+ }
252
+ // Claim results contain private business data and never use the publisher response or replay ledger.
253
+ const result = await deviceResponse(value.action, value.operationId, { callback: value.callback });
254
+ const claimed = PapCallbackClaimResultSchema.safeParse(result);
255
+ if (!claimed.success)
256
+ throw pluginError('provider_incompatible');
257
+ exactHttps(claimed.data.iss);
258
+ return claimed.data;
259
+ }
260
+ catch (error) {
261
+ if (error instanceof PapCallbackManagementError || error instanceof AnpIdentityPluginError)
262
+ throw error;
263
+ throw pluginError('provider_unavailable');
264
+ }
265
+ }
266
+ return { reference: structuredClone(reference), documentId, backend, readVerifiedDocument: checkpointObservation, execute };
267
+ }
268
+ /** Register a locally held E1 Manifest and retain only a live, server-verified device credential.
269
+ * @param input - Local identity, registration confirmation and protected broker.
270
+ * @returns Public registration facts. No Access Token is returned.
271
+ */
272
+ export async function registerLocalIdentity(input) {
273
+ input.assertActive?.();
274
+ const origin = exactHttps(input.origin);
275
+ if (new URL(origin).origin !== origin || !input.reference.did.startsWith('did:wba:') && !input.reference.did.startsWith('did:web:'))
276
+ throw pluginError('provider_incompatible');
277
+ const identity = await input.withNative(native => native.publicIdentity(input.reference));
278
+ input.assertActive?.();
279
+ let registrationDocument = identity.document;
280
+ const document = identity.document;
281
+ // The live Registry below is authoritative; this list only identifies the locally held candidate key.
282
+ const candidates = identity.activeKeys.filter(key => key.purposes.includes('device_assertion'));
283
+ const web = input.reference.did.startsWith('did:web:');
284
+ if (candidates.length !== 1 || !web && !document.proof)
285
+ throw pluginError('provider_incompatible');
286
+ const registration = structuredClone(input.registration);
287
+ let bootstrap;
288
+ let initialProof;
289
+ if (web) {
290
+ if (!registration.registration_operation_id || !input.managementAudience)
291
+ throw pluginError('invalid_request');
292
+ const projection = webRegistrationProjection({ document: identity.document, registration, audience: input.managementAudience, handleDomain: input.handleDomain ?? new URL(origin).hostname });
293
+ bootstrap = await input.withNative(async (native) => {
294
+ const signer = native.signWebRegistrationProof;
295
+ if (typeof signer !== 'function')
296
+ throw pluginError('provider_incompatible');
297
+ return signer.call(native, input.reference, { key: { kid: candidates[0].kid }, business_projection: projection });
298
+ });
299
+ }
300
+ else {
301
+ if (registration.registration_operation_id)
302
+ throw pluginError('invalid_request');
303
+ if (await input.credentials.get(input.reference, origin))
304
+ throw pluginError('catalog_conflict');
305
+ input.assertActive?.();
306
+ initialProof = await input.withNative(native => prepareInitialRegistrationProof(native, identity, origin));
307
+ registrationDocument = initialProof.document;
308
+ input.assertActive?.();
309
+ }
310
+ const transport = input.fetch ?? fetch;
311
+ const send = async (request, init) => {
312
+ input.assertActive?.();
313
+ const response = await transport(request, init);
314
+ try {
315
+ input.assertActive?.();
316
+ return response;
317
+ }
318
+ catch (error) {
319
+ await response.body?.cancel();
320
+ throw error;
321
+ }
322
+ };
323
+ const request = new Request(origin + '/user-service/v1/did-auth/rpc', { method: 'POST', redirect: 'manual', signal: AbortSignal.timeout(15000),
324
+ headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: randomUUID(), method: 'register',
325
+ params: { ...registration, did_document: registrationDocument, is_agent: true, ...(bootstrap ? { bootstrap_proof: bootstrap } : {}) } }) });
326
+ if (initialProof)
327
+ await input.withNative(native => initialProof.assertCurrent(native));
328
+ input.assertActive?.();
329
+ const response = await send(request);
330
+ if (response.status !== 200 || response.redirected) {
331
+ await response.body?.cancel();
332
+ throw pluginError('provider_unavailable');
333
+ }
334
+ const bytes = await response.text();
335
+ input.assertActive?.();
336
+ if (Buffer.byteLength(bytes) > 32768)
337
+ throw pluginError('provider_incompatible');
338
+ const envelope = parsePublicJson(bytes);
339
+ const result = envelope.result;
340
+ if (envelope.error || result?.state !== 'registered' || result.did !== input.reference.did || typeof result.access_token !== 'string')
341
+ throw pluginError('provider_unavailable');
342
+ const token = result.access_token;
343
+ const registryResponse = await signedRequest(new Request(origin + '/user-service/v1/did-auth/rpc', { method: 'POST', redirect: 'manual', signal: AbortSignal.timeout(15000),
344
+ headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: randomUUID(), method: 'device_registry_get', params: {} }) }), {
345
+ reference: input.reference, origin, signingKeyId: candidates[0].kid, withNative: input.withNative,
346
+ transport: send, assertActive: async () => { input.assertActive?.(); },
347
+ });
348
+ if (registryResponse.status !== 200 || registryResponse.redirected) {
349
+ await registryResponse.body?.cancel();
350
+ throw pluginError('provider_unavailable');
351
+ }
352
+ const registryBytes = await registryResponse.text();
353
+ input.assertActive?.();
354
+ if (Buffer.byteLength(registryBytes) > 32768)
355
+ throw pluginError('provider_incompatible');
356
+ const registryEnvelope = parsePublicJson(registryBytes);
357
+ const registry = registryEnvelope.result;
358
+ const device = registry?.devices.find(device => device.signing_key_id === candidates[0].kid);
359
+ if (registryEnvelope.error || registry?.did !== input.reference.did || !device || device.status !== 'active'
360
+ || device.role !== 'admin' || !device.management_ready)
361
+ throw pluginError('provider_unavailable');
362
+ await input.credentials.set(input.reference, origin, { accessToken: token, deviceId: device.device_id, signingKeyId: device.signing_key_id, ...(web ? { audience: input.managementAudience } : {}) });
363
+ input.assertActive?.();
364
+ await input.withNative(native => native.adoptVerifiedDocument(input.reference, { document: registrationDocument,
365
+ evidence: { documentVersion: registry.checkpoint.document_version, registryVersion: registry.checkpoint.registry_version, documentDigest: registry.checkpoint.document_hash } }));
366
+ input.assertActive?.();
367
+ return { did: input.reference.did, deviceId: device.device_id, state: 'registered' };
368
+ }
369
+ /** RFC 8785 serialization for native-produced JSON values.
370
+ * @param value - Finite JSON data, with keys ordered by UTF-16 code units.
371
+ * @returns Canonical UTF-8 text.
372
+ */
373
+ export function canonicalManagementJson(value) {
374
+ if (value === null || typeof value !== 'object') {
375
+ if (typeof value === 'number' && !Number.isFinite(value))
376
+ throw pluginError('invalid_request');
377
+ return JSON.stringify(value);
378
+ }
379
+ if (Array.isArray(value))
380
+ return '[' + value.map(canonicalManagementJson).join(',') + ']';
381
+ return '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + canonicalManagementJson(value[key])).join(',') + '}';
382
+ }
383
+ /** Construct the exact Web registration business projection; OTP and proof freshness are excluded.
384
+ * @param input - Public local document and the explicit registration account/profile fields.
385
+ * @returns Projection checked by the local native signer and User Service.
386
+ */
387
+ export function webRegistrationProjection(input) {
388
+ const document = input.document;
389
+ const manifest = document.deviceManifest;
390
+ const device = manifest?.devices?.[0];
391
+ if (manifest?.devices?.length !== 1 || !device || !input.registration.registration_operation_id)
392
+ throw pluginError('invalid_request');
393
+ const digest = (value) => 'sha256:' + createHash('sha256').update(value).digest('base64url');
394
+ const phone = input.registration.phone;
395
+ if (phone && !/^\+[1-9][0-9]{6,14}$/u.test(phone))
396
+ throw pluginError('invalid_request');
397
+ const handle = input.registration.handle.trim().toLowerCase();
398
+ return { contract: 'awiki.register.web.v1', audience: input.audience, registration_operation_id: input.registration.registration_operation_id,
399
+ full_handle: handle + '.' + input.handleDomain, did: document.id,
400
+ bootstrap_device: { device_id: device.device_id, signing_key_id: device.signing_key_id, e2ee_key_id: device.e2ee_key_id },
401
+ document_hash: digest(canonicalManagementJson(input.document)), account: { phone: phone ?? null, email: input.registration.email?.trim().toLowerCase() ?? null },
402
+ profile: { name: input.registration.name ?? null, avatar: null, role: null, endpoint_url: null, description: null, is_public: false, is_agent: true },
403
+ invite_code_hash: input.registration.invite_code ? digest(input.registration.invite_code.trim()) : null };
404
+ }
405
+ //# sourceMappingURL=management-driver.js.map