@private.me/xbind 1.3.5 → 3.0.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.
Files changed (306) hide show
  1. package/LICENSES.md +212 -0
  2. package/README.md +388 -6
  3. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
  4. package/dist-standalone/_deps/shared/cjs/errors.js +1 -639
  5. package/dist-standalone/_deps/shared/cjs/index.js +1 -496
  6. package/dist-standalone/_deps/shared/cjs/types.js +1 -317
  7. package/dist-standalone/_deps/shared/errors.js +1 -255
  8. package/dist-standalone/_deps/shared/index.js +1 -74
  9. package/dist-standalone/_deps/shared/types.js +1 -90
  10. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  20. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  21. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  28. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  29. package/dist-standalone/_deps/xchange/errors.js +1 -1
  30. package/dist-standalone/_deps/xchange/index.js +1 -1
  31. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  32. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  33. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  34. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  40. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  41. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  42. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  43. package/dist-standalone/_deps/xregistry/index.js +1 -1
  44. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  45. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  46. package/dist-standalone/_deps/xregistry/types.js +1 -1
  47. package/dist-standalone/agent-call.js +1 -642
  48. package/dist-standalone/agent-sdk.js +1 -328
  49. package/dist-standalone/agent.d.ts +95 -5
  50. package/dist-standalone/agent.js +1 -1545
  51. package/dist-standalone/approval.js +1 -193
  52. package/dist-standalone/async-iterators.d.ts +275 -0
  53. package/dist-standalone/async-iterators.js +1 -0
  54. package/dist-standalone/auth.js +1 -219
  55. package/dist-standalone/auto-accept.js +1 -229
  56. package/dist-standalone/backup-config.js +1 -201
  57. package/dist-standalone/backup.d.ts +114 -0
  58. package/dist-standalone/backup.js +1 -0
  59. package/dist-standalone/batch-operations.d.ts +297 -0
  60. package/dist-standalone/batch-operations.js +1 -0
  61. package/dist-standalone/cancellation.d.ts +301 -0
  62. package/dist-standalone/cancellation.js +1 -0
  63. package/dist-standalone/checkpoint.js +1 -186
  64. package/dist-standalone/circuit-breaker.d.ts +351 -0
  65. package/dist-standalone/circuit-breaker.js +1 -0
  66. package/dist-standalone/cjs/agent-call.js +1 -651
  67. package/dist-standalone/cjs/agent-sdk.js +1 -332
  68. package/dist-standalone/cjs/agent.js +1 -1582
  69. package/dist-standalone/cjs/approval.js +1 -199
  70. package/dist-standalone/cjs/async-iterators.js +1 -0
  71. package/dist-standalone/cjs/auth.js +1 -225
  72. package/dist-standalone/cjs/auto-accept.js +1 -233
  73. package/dist-standalone/cjs/backup-config.js +1 -207
  74. package/dist-standalone/cjs/backup.js +1 -0
  75. package/dist-standalone/cjs/batch-operations.js +1 -0
  76. package/dist-standalone/cjs/cancellation.js +1 -0
  77. package/dist-standalone/cjs/checkpoint.js +1 -193
  78. package/dist-standalone/cjs/circuit-breaker.js +1 -0
  79. package/dist-standalone/cjs/cli/init.js +1 -486
  80. package/dist-standalone/cjs/config-validation.js +1 -0
  81. package/dist-standalone/cjs/connect.js +1 -312
  82. package/dist-standalone/cjs/connection-pool.js +1 -0
  83. package/dist-standalone/cjs/correlation-id.js +1 -339
  84. package/dist-standalone/cjs/crypto-utils.js +1 -0
  85. package/dist-standalone/cjs/debug-mode.js +1 -0
  86. package/dist-standalone/cjs/did-document.js +1 -101
  87. package/dist-standalone/cjs/did-privateme.js +1 -130
  88. package/dist-standalone/cjs/did-web.js +1 -201
  89. package/dist-standalone/cjs/discovery.js +1 -462
  90. package/dist-standalone/cjs/dual-mode.js +1 -251
  91. package/dist-standalone/cjs/email-templates.js +1 -313
  92. package/dist-standalone/cjs/email-transport.js +1 -239
  93. package/dist-standalone/cjs/envelope.js +1 -510
  94. package/dist-standalone/cjs/errors.js +1 -826
  95. package/dist-standalone/cjs/event-emitter.js +1 -0
  96. package/dist-standalone/cjs/gateway-state.js +1 -55
  97. package/dist-standalone/cjs/gateway-transport.js +1 -120
  98. package/dist-standalone/cjs/graceful-degradation.js +1 -0
  99. package/dist-standalone/cjs/guardrails.js +1 -223
  100. package/dist-standalone/cjs/health-check.js +1 -0
  101. package/dist-standalone/cjs/http-compat.js +1 -272
  102. package/dist-standalone/cjs/http-status-map.js +1 -571
  103. package/dist-standalone/cjs/identity.js +1 -540
  104. package/dist-standalone/cjs/index.js +1 -237
  105. package/dist-standalone/cjs/invitation.js +1 -421
  106. package/dist-standalone/cjs/invite.js +1 -328
  107. package/dist-standalone/cjs/key-agreement.js +1 -246
  108. package/dist-standalone/cjs/lazy-init.js +1 -300
  109. package/dist-standalone/cjs/logger.js +1 -0
  110. package/dist-standalone/cjs/mdns-discovery.js +1 -202
  111. package/dist-standalone/cjs/nonce-store.js +1 -66
  112. package/dist-standalone/cjs/pairing-manager.js +1 -223
  113. package/dist-standalone/cjs/plugin-system.js +1 -0
  114. package/dist-standalone/cjs/plugins/logging.js +1 -0
  115. package/dist-standalone/cjs/plugins/metrics.js +1 -0
  116. package/dist-standalone/cjs/plugins/validation.js +1 -0
  117. package/dist-standalone/cjs/policy.js +1 -320
  118. package/dist-standalone/cjs/progress-callbacks.js +1 -0
  119. package/dist-standalone/cjs/redis-nonce-store.js +1 -76
  120. package/dist-standalone/cjs/registry-middleware.js +1 -50
  121. package/dist-standalone/cjs/retry-strategies.js +1 -0
  122. package/dist-standalone/cjs/retry-transport.js +1 -102
  123. package/dist-standalone/cjs/runtime/browser.js +1 -0
  124. package/dist-standalone/cjs/runtime/edge.js +1 -0
  125. package/dist-standalone/cjs/runtime/react-native.js +1 -0
  126. package/dist-standalone/cjs/security-policy.js +1 -245
  127. package/dist-standalone/cjs/serialization.js +1 -0
  128. package/dist-standalone/cjs/split-channel.js +1 -177
  129. package/dist-standalone/cjs/subscription-proof.js +1 -230
  130. package/dist-standalone/cjs/succession.js +1 -148
  131. package/dist-standalone/cjs/timeouts.js +1 -0
  132. package/dist-standalone/cjs/trace-context.js +1 -0
  133. package/dist-standalone/cjs/trace-spans.js +1 -0
  134. package/dist-standalone/cjs/transport.js +1 -63
  135. package/dist-standalone/cjs/trust-registry.js +1 -742
  136. package/dist-standalone/cjs/types/error-response.js +1 -56
  137. package/dist-standalone/cjs/vault-auth.js +1 -0
  138. package/dist-standalone/cjs/vault-store-loader.js +1 -0
  139. package/dist-standalone/cjs/verify.js +1 -25
  140. package/dist-standalone/cjs/version-info.js +1 -0
  141. package/dist-standalone/cjs/xfetch.js +1 -252
  142. package/dist-standalone/cli/init.js +1 -449
  143. package/dist-standalone/cli/setup.js +1 -514
  144. package/dist-standalone/cli/types.js +1 -27
  145. package/dist-standalone/cli/xbind.js +1 -148
  146. package/dist-standalone/config-validation.d.ts +185 -0
  147. package/dist-standalone/config-validation.js +1 -0
  148. package/dist-standalone/connect.js +1 -274
  149. package/dist-standalone/connection-pool.d.ts +251 -0
  150. package/dist-standalone/connection-pool.js +1 -0
  151. package/dist-standalone/correlation-id.js +1 -326
  152. package/dist-standalone/crypto-utils.d.ts +60 -0
  153. package/dist-standalone/crypto-utils.js +1 -0
  154. package/dist-standalone/debug-mode.d.ts +286 -0
  155. package/dist-standalone/debug-mode.js +1 -0
  156. package/dist-standalone/did-document.js +1 -96
  157. package/dist-standalone/did-privateme.js +1 -121
  158. package/dist-standalone/did-web.js +1 -196
  159. package/dist-standalone/discovery.js +1 -458
  160. package/dist-standalone/dual-mode.js +1 -247
  161. package/dist-standalone/email-templates.js +1 -309
  162. package/dist-standalone/email-transport.js +1 -232
  163. package/dist-standalone/envelope.d.ts +29 -1
  164. package/dist-standalone/envelope.js +1 -497
  165. package/dist-standalone/errors.d.ts +10 -0
  166. package/dist-standalone/errors.js +1 -811
  167. package/dist-standalone/event-emitter.d.ts +395 -0
  168. package/dist-standalone/event-emitter.js +1 -0
  169. package/dist-standalone/gateway-state.js +1 -51
  170. package/dist-standalone/gateway-transport.js +1 -116
  171. package/dist-standalone/graceful-degradation.d.ts +246 -0
  172. package/dist-standalone/graceful-degradation.js +1 -0
  173. package/dist-standalone/guardrails.js +1 -216
  174. package/dist-standalone/health-check.d.ts +150 -0
  175. package/dist-standalone/health-check.js +1 -0
  176. package/dist-standalone/http-compat.js +1 -267
  177. package/dist-standalone/http-status-map.js +1 -561
  178. package/dist-standalone/identity.d.ts +64 -1
  179. package/dist-standalone/identity.js +1 -515
  180. package/dist-standalone/index.d.ts +45 -3
  181. package/dist-standalone/index.js +1 -52
  182. package/dist-standalone/invitation.js +1 -415
  183. package/dist-standalone/invite.js +1 -324
  184. package/dist-standalone/key-agreement.d.ts +61 -13
  185. package/dist-standalone/key-agreement.js +1 -236
  186. package/dist-standalone/lazy-init.js +1 -295
  187. package/dist-standalone/logger.d.ts +77 -0
  188. package/dist-standalone/logger.js +1 -0
  189. package/dist-standalone/mdns-discovery.js +1 -195
  190. package/dist-standalone/nonce-store.d.ts +16 -3
  191. package/dist-standalone/nonce-store.js +1 -62
  192. package/dist-standalone/package.json +0 -1
  193. package/dist-standalone/pairing-manager.js +1 -219
  194. package/dist-standalone/plugin-system.d.ts +145 -0
  195. package/dist-standalone/plugin-system.js +1 -0
  196. package/dist-standalone/policy.js +1 -315
  197. package/dist-standalone/progress-callbacks.d.ts +394 -0
  198. package/dist-standalone/progress-callbacks.js +1 -0
  199. package/dist-standalone/redis-nonce-store.js +1 -72
  200. package/dist-standalone/registry-middleware.js +1 -47
  201. package/dist-standalone/retry-strategies.d.ts +382 -0
  202. package/dist-standalone/retry-strategies.js +1 -0
  203. package/dist-standalone/retry-transport.js +1 -98
  204. package/dist-standalone/security-policy.js +1 -239
  205. package/dist-standalone/serialization.d.ts +244 -0
  206. package/dist-standalone/serialization.js +1 -0
  207. package/dist-standalone/split-channel.d.ts +49 -1
  208. package/dist-standalone/split-channel.js +1 -171
  209. package/dist-standalone/subscription-proof.js +1 -224
  210. package/dist-standalone/succession.js +1 -142
  211. package/dist-standalone/timeouts.d.ts +275 -0
  212. package/dist-standalone/timeouts.js +1 -0
  213. package/dist-standalone/trace-context.d.ts +252 -0
  214. package/dist-standalone/trace-context.js +1 -0
  215. package/dist-standalone/trace-spans.d.ts +360 -0
  216. package/dist-standalone/trace-spans.js +1 -0
  217. package/dist-standalone/transport.js +1 -59
  218. package/dist-standalone/trust-registry.d.ts +106 -5
  219. package/dist-standalone/trust-registry.js +1 -702
  220. package/dist-standalone/vault-auth.d.ts +91 -0
  221. package/dist-standalone/vault-auth.js +1 -0
  222. package/dist-standalone/vault-store-loader.d.ts +110 -0
  223. package/dist-standalone/vault-store-loader.js +1 -0
  224. package/dist-standalone/verify.js +1 -16
  225. package/dist-standalone/version-info.d.ts +259 -0
  226. package/dist-standalone/version-info.js +1 -0
  227. package/dist-standalone/xfetch.js +1 -247
  228. package/llms.txt +1 -0
  229. package/package.json +65 -5
  230. package/share1.dat +0 -0
  231. package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
  232. package/dist-standalone/_deps/crypto/base64.js +0 -222
  233. package/dist-standalone/_deps/crypto/cjs/base64.js +0 -665
  234. package/dist-standalone/_deps/crypto/cjs/errors.js +0 -675
  235. package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -473
  236. package/dist-standalone/_deps/crypto/cjs/index.js +0 -852
  237. package/dist-standalone/_deps/crypto/cjs/package.json +0 -1
  238. package/dist-standalone/_deps/crypto/cjs/padding.js +0 -511
  239. package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -372
  240. package/dist-standalone/_deps/crypto/cjs/shares.js +0 -874
  241. package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -1021
  242. package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -443
  243. package/dist-standalone/_deps/crypto/cjs/verify.js +0 -414
  244. package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -923
  245. package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
  246. package/dist-standalone/_deps/crypto/errors.js +0 -199
  247. package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
  248. package/dist-standalone/_deps/crypto/hmac.js +0 -134
  249. package/dist-standalone/_deps/crypto/index.d.ts +0 -20
  250. package/dist-standalone/_deps/crypto/index.js +0 -145
  251. package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
  252. package/dist-standalone/_deps/crypto/padding.js +0 -159
  253. package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
  254. package/dist-standalone/_deps/crypto/share-header.js +0 -92
  255. package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
  256. package/dist-standalone/_deps/crypto/shares.js +0 -295
  257. package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
  258. package/dist-standalone/_deps/crypto/tlv.js +0 -364
  259. package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
  260. package/dist-standalone/_deps/crypto/uuid.js +0 -136
  261. package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
  262. package/dist-standalone/_deps/crypto/verify.js +0 -71
  263. package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
  264. package/dist-standalone/_deps/crypto/xorida.js +0 -366
  265. package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
  266. package/dist-standalone/_deps/shared/errors.js.map +0 -1
  267. package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
  268. package/dist-standalone/_deps/shared/index.js.map +0 -1
  269. package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
  270. package/dist-standalone/_deps/shared/types.js.map +0 -1
  271. package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
  272. package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
  273. package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
  274. package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
  275. package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
  276. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
  277. package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
  278. package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
  279. package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
  280. package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
  281. package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
  282. package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
  283. package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
  284. package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
  285. package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
  286. package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
  287. package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
  288. package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
  289. package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
  290. package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
  291. package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
  292. package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
  293. package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
  294. package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
  295. package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
  296. package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
  297. package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
  298. package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
  299. package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
  300. package/dist-standalone/_deps/xregistry/index.js.map +0 -1
  301. package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
  302. package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
  303. package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
  304. package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
  305. package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
  306. package/dist-standalone/_deps/xregistry/types.js.map +0 -1
@@ -1,1582 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.generateSharedKey = exports.Agent = void 0;
37
- exports.parseAgentError = parseAgentError;
38
- const shared_1 = require("../_deps/shared/index.js");
39
- const crypto_1 = require("../_deps/crypto/index.js");
40
- const identity_js_1 = require("./identity.js");
41
- const envelope_js_1 = require("./envelope.js");
42
- Object.defineProperty(exports, "generateSharedKey", { enumerable: true, get: function () { return envelope_js_1.generateSharedKey; } });
43
- const xchange_1 = require("../_deps/xchange/index.js");
44
- const identity_js_2 = require("./identity.js");
45
- const key_agreement_js_1 = require("./key-agreement.js");
46
- const split_channel_js_1 = require("./split-channel.js");
47
- const nonce_store_js_1 = require("./nonce-store.js");
48
- const transport_js_1 = require("./transport.js");
49
- const trust_registry_js_1 = require("./trust-registry.js");
50
- const ux_helpers_1 = require("../_deps/ux-helpers/index.js");
51
- const security_policy_js_1 = require("./security-policy.js");
52
- const backup_config_js_1 = require("./backup-config.js");
53
- /* ── Configuration ── */
54
- /**
55
- * Default relay URL for message transport.
56
- * Override via XBIND_RELAY_URL environment variable.
57
- */
58
- const DEFAULT_RELAY_URL = process.env.XBIND_RELAY_URL || 'https://private.me/relay';
59
- /**
60
- * Default registry URL for DID resolution.
61
- * Override via XBIND_REGISTRY_URL environment variable.
62
- */
63
- const DEFAULT_REGISTRY_URL = process.env.XBIND_REGISTRY_URL || 'https://private.me/registry';
64
- /**
65
- * Parse an AgentError string into structured detail.
66
- *
67
- * Splits colon-separated error codes into base code and sub-code.
68
- *
69
- * @param error - An AgentError string (e.g. 'DECRYPT_FAILED:KEY_AGREEMENT').
70
- * @returns Parsed error detail.
71
- */
72
- function parseAgentError(error) {
73
- const parts = error.split(':');
74
- if (parts.length === 1)
75
- return { code: parts[0] ?? error };
76
- return { code: parts[0] ?? error, subCode: parts.slice(1).join(':') };
77
- }
78
- const TIMESTAMP_WINDOW_MS = 30_000;
79
- /* ── Key Agreement ── */
80
- /** Copy Uint8Array to fresh ArrayBuffer. */
81
- function toArrayBuffer(data) {
82
- const buf = new ArrayBuffer(data.byteLength);
83
- new Uint8Array(buf).set(data);
84
- return buf;
85
- }
86
- // SECURITY FIX (v1.1.6): deriveSharedKey() REMOVED
87
- //
88
- // Previous implementation derived AES-256-GCM keys from public-only inputs:
89
- // AES key = SHA-256(sort(pubA, pubB))
90
- //
91
- // Both public keys appear in the envelope (sender/recipient DIDs). Any passive
92
- // observer can derive the same key and decrypt all messages. Zero forward secrecy.
93
- //
94
- // Fix: All senders/receivers MUST use X25519 ECDH for proper key agreement.
95
- // Recipients without x25519 keys fail with KEY_AGREEMENT_FAILED:RECIPIENT_HAS_NO_X25519_KEY.
96
- //
97
- // Removed: deriveSharedKey() function and export (agent.ts:2305)
98
- // Removed: Sender fallback at line 959
99
- // Removed: Receiver fallbacks at lines 1036, 2209
100
- function compareBytes(a, b) {
101
- const len = Math.min(a.length, b.length);
102
- for (let i = 0; i < len; i++) {
103
- const ai = a[i] ?? 0;
104
- const bi = b[i] ?? 0;
105
- if (ai !== bi)
106
- return ai - bi;
107
- }
108
- return a.length - b.length;
109
- }
110
- function concatBytes(a, b) {
111
- const result = new Uint8Array(a.length + b.length);
112
- result.set(a);
113
- result.set(b, a.length);
114
- return result;
115
- }
116
- /* ── Agent Class ── */
117
- /**
118
- * Top-level Xail Agent SDK API.
119
- *
120
- * Matches xail.io/sdk specification:
121
- * - Agent.create({ name, registry }) — generate identity + register
122
- * - agent.send({ to, payload, scope }) — encrypt, sign, deliver
123
- * - agent.receive(envelope) — verify, decrypt, return message
124
- */
125
- class Agent {
126
- identity;
127
- name;
128
- registry;
129
- transports;
130
- nonceStore;
131
- timestampWindowMs;
132
- securityPolicy;
133
- backupConfig;
134
- /** Accumulates split-channel shares by groupId until threshold is met. */
135
- shareAccumulator = new Map();
136
- /** Diagnostic detail from the last failed verification step. */
137
- lastDetail = '';
138
- /** Last security decision made by the policy (for debugging/logging). */
139
- lastSecurityDecision;
140
- /** Timer for ephemeral agent auto-cleanup. */
141
- cleanupTimer;
142
- /**
143
- * Human-readable diagnostic from the last failed receive/verify call.
144
- *
145
- * Populated during verification with context like age vs max window.
146
- * Empty string if no error has occurred or after a successful call.
147
- */
148
- get lastErrorDetail() {
149
- return this.lastDetail;
150
- }
151
- /**
152
- * Last security decision made by the security policy.
153
- *
154
- * Shows which security mode was selected and why. Useful for debugging
155
- * and understanding when/why Xorida split-channel was activated.
156
- *
157
- * Returns undefined if no send() has been called yet.
158
- */
159
- get lastSecurity() {
160
- return this.lastSecurityDecision;
161
- }
162
- constructor(identity, name, registry, transports, nonceStore, timestampWindowMs, securityPolicy, backupConfig) {
163
- this.identity = identity;
164
- this.name = name;
165
- this.registry = registry;
166
- this.transports = transports;
167
- this.nonceStore = nonceStore;
168
- this.timestampWindowMs = timestampWindowMs;
169
- this.securityPolicy = securityPolicy ?? new security_policy_js_1.DefaultSecurityPolicy();
170
- this.backupConfig = backupConfig ?? backup_config_js_1.DEFAULT_BACKUP_CONFIG;
171
- }
172
- /** The agent's DID. */
173
- get did() {
174
- return this.identity.did;
175
- }
176
- /**
177
- * Check whether the runtime supports the SDK's crypto requirements.
178
- *
179
- * Verifies that `crypto.subtle` is available and supports Ed25519 +
180
- * AES-256-GCM. Call this before lazy-loading the SDK in middleware
181
- * to avoid failing on the first `Agent.create()` call.
182
- *
183
- * @returns `true` if the runtime has the required Web Crypto APIs.
184
- */
185
- static isSupported() {
186
- try {
187
- return (typeof globalThis.crypto !== 'undefined' &&
188
- typeof globalThis.crypto.subtle !== 'undefined' &&
189
- typeof globalThis.crypto.subtle.generateKey === 'function' &&
190
- typeof globalThis.crypto.subtle.sign === 'function' &&
191
- typeof globalThis.crypto.subtle.verify === 'function' &&
192
- typeof globalThis.crypto.subtle.encrypt === 'function' &&
193
- typeof globalThis.crypto.getRandomValues === 'function');
194
- }
195
- catch {
196
- return false;
197
- }
198
- }
199
- /**
200
- * Create an Agent from a persisted identity (skips keygen + registration).
201
- *
202
- * Use this with importFromPKCS8 or importIdentity to restore a
203
- * previously created agent across process restarts.
204
- *
205
- * @param identity - A previously exported AgentIdentity.
206
- * @param opts - Agent options (registry, transport, etc.).
207
- * @returns Agent instance or error.
208
- */
209
- static async fromIdentity(identity, opts) {
210
- const nonceStore = opts.nonceStore ?? new nonce_store_js_1.MemoryNonceStore();
211
- const timestampWindowMs = opts.timestampWindowMs ?? TIMESTAMP_WINDOW_MS;
212
- const transports = Array.isArray(opts.transport) ? opts.transport : [opts.transport];
213
- const agent = new Agent(identity, opts.name ?? identity.did, opts.registry, transports, nonceStore, timestampWindowMs, opts.securityPolicy, opts.backupConfig);
214
- return (0, shared_1.ok)(agent);
215
- }
216
- /**
217
- * Build an Agent from pre-constructed parts (synchronous).
218
- *
219
- * Unlike `create()` this does NOT generate keys or register with the
220
- * registry — the caller is responsible for both. Useful when identity
221
- * is provisioned in a factory step, registration is handled by an
222
- * external system, or transport is wired up at runtime.
223
- *
224
- * @param identity - A previously generated or imported AgentIdentity.
225
- * @param registry - Trust registry the agent will query for peers.
226
- * @param transport - Transport adapter for envelope delivery.
227
- * @param opts - Optional overrides (name, nonceStore, timestampWindowMs).
228
- * @returns A fully wired Agent instance.
229
- */
230
- static fromParts(identity, registry, transport, opts) {
231
- const transports = Array.isArray(transport) ? transport : [transport];
232
- return new Agent(identity, opts?.name ?? identity.did, registry, transports, opts?.nonceStore ?? new nonce_store_js_1.MemoryNonceStore(), opts?.timestampWindowMs ?? TIMESTAMP_WINDOW_MS, opts?.securityPolicy, opts?.backupConfig);
233
- }
234
- /**
235
- * Create an Agent from a deterministic 32-byte seed.
236
- *
237
- * Uses HKDF-SHA256 to derive Ed25519 + X25519 keys from the seed.
238
- * The same seed always produces the same DID and keys.
239
- *
240
- * **Just-in-Time Registration (JITR):** Automatically registers identity
241
- * with the trust registry on first use. Follows industry standards (AWS IoT
242
- * JITR, OAuth DCR, MCP 2025 spec) for zero-config onboarding. Idempotent:
243
- * safe to call multiple times (ignores ALREADY_REGISTERED errors).
244
- *
245
- * Ideal for IoT, servers, AI agents: zero-config deployment with automatic
246
- * registration on first connection.
247
- *
248
- * @param seed - Exactly 32 bytes of high-entropy key material.
249
- * @param opts - Agent options (registry, transport, scopes, etc.).
250
- * @returns Agent instance or error.
251
- */
252
- static async fromSeed(seed, opts) {
253
- const idResult = await (0, identity_js_1.identityFromSeed)(seed, { postQuantumSig: opts.postQuantumSig });
254
- if (!idResult.ok)
255
- return (0, shared_1.err)('IDENTITY_FAILED:KEYGEN');
256
- // JITR: Auto-register with trust registry (zero-config onboarding)
257
- // Includes all keys: Ed25519 (signing), X25519 (encryption), ML-KEM, ML-DSA
258
- const regResult = await opts.registry.register(idResult.value.did, idResult.value.rawPublicKey, opts.name ?? idResult.value.did, opts.scopes, idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, opts.xchange ?? false);
259
- // Idempotent: Ignore ALREADY_REGISTERED (agent may have registered before)
260
- // Fail on other errors (network issues, invalid data, etc.)
261
- if (!regResult.ok && regResult.error !== 'ALREADY_REGISTERED') {
262
- const sub = regResult.error === 'NETWORK_ERROR'
263
- ? 'REGISTRATION_FAILED:NETWORK_ERROR'
264
- : 'REGISTRATION_FAILED';
265
- return (0, shared_1.err)(sub);
266
- }
267
- const nonceStore = opts.nonceStore ?? new nonce_store_js_1.MemoryNonceStore();
268
- const timestampWindowMs = opts.timestampWindowMs ?? TIMESTAMP_WINDOW_MS;
269
- const transports = Array.isArray(opts.transport) ? opts.transport : [opts.transport];
270
- return (0, shared_1.ok)(new Agent(idResult.value, opts.name ?? idResult.value.did, opts.registry, transports, nonceStore, timestampWindowMs, opts.securityPolicy, opts.backupConfig));
271
- }
272
- /**
273
- * Create a lazy agent that initializes on first use (zero-click onboarding).
274
- *
275
- * Defers invite acceptance and identity generation until first send/receive.
276
- * Reads invite code from XBIND_INVITE_CODE environment variable.
277
- * Auto-accepts invite and configures registry/transport automatically.
278
- *
279
- * @param config - Lazy agent configuration (name required).
280
- * @returns Lazy agent wrapper (synchronous).
281
- *
282
- * @example
283
- * ```ts
284
- * // Environment: XBIND_INVITE_CODE=XBD-abc123, XBIND_AUTO_ACCEPT=true
285
- *
286
- * // Synchronous construction (no await):
287
- * const agent = Agent.lazy({ name: 'my-service' });
288
- *
289
- * // Auto-accept happens on first send/receive:
290
- * await agent.send({
291
- * to: 'did:key:z6Mk...',
292
- * payload: { action: 'test' },
293
- * scope: 'test',
294
- * });
295
- * ```
296
- */
297
- static async lazy(config) {
298
- // Dynamic import avoids circular dependency
299
- const { createLazyAgent } = await Promise.resolve().then(() => __importStar(require('./lazy-init.js')));
300
- return createLazyAgent(config);
301
- }
302
- /**
303
- * Check whether this agent is fully initialized and ready to send/receive.
304
- *
305
- * Returns `true` if identity, registry, and transport are all present.
306
- * Construction always produces a ready agent (invalid inputs cause the
307
- * factory to return an error instead), so this is primarily useful in
308
- * middleware that lazily initializes agents and needs a guard.
309
- *
310
- * @returns `true` if the agent can send and receive messages.
311
- */
312
- isReady() {
313
- return (this.identity !== undefined &&
314
- this.registry !== undefined &&
315
- this.transports.length > 0);
316
- }
317
- /** Create a new agent, generate identity, register with trust registry. */
318
- static async create(opts) {
319
- const idResult = await (0, identity_js_1.generateIdentity)({ postQuantumSig: opts.postQuantumSig });
320
- if (!idResult.ok)
321
- return (0, shared_1.err)('IDENTITY_FAILED:KEYGEN');
322
- const regResult = await opts.registry.register(idResult.value.did, idResult.value.rawPublicKey, opts.name, opts.scopes, idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, opts.xchange ?? false);
323
- if (!regResult.ok) {
324
- const sub = regResult.error === 'ALREADY_REGISTERED'
325
- ? 'REGISTRATION_FAILED:ALREADY_REGISTERED'
326
- : regResult.error === 'NETWORK_ERROR'
327
- ? 'REGISTRATION_FAILED:NETWORK_ERROR'
328
- : 'REGISTRATION_FAILED';
329
- return (0, shared_1.err)(sub);
330
- }
331
- const nonceStore = opts.nonceStore ?? new nonce_store_js_1.MemoryNonceStore();
332
- const timestampWindowMs = opts.timestampWindowMs ?? TIMESTAMP_WINDOW_MS;
333
- const transports = Array.isArray(opts.transport) ? opts.transport : [opts.transport];
334
- const agent = new Agent(idResult.value, opts.name, opts.registry, transports, nonceStore, timestampWindowMs, opts.securityPolicy, opts.backupConfig);
335
- return (0, shared_1.ok)(agent);
336
- }
337
- /**
338
- * Quickstart: create an agent with zero configuration.
339
- *
340
- * Creates an ephemeral agent with auto-cleanup after 1 hour. No policy
341
- * restrictions by default (allow all operations). Ideal for getting
342
- * started, prototyping, and simple use cases.
343
- *
344
- * Uses in-memory registry and default transport. Identity auto-expires
345
- * and is deregistered after TTL.
346
- *
347
- * @param opts - Optional configuration (name only)
348
- * @returns Agent instance (throws on error for simpler API)
349
- *
350
- * @example
351
- * ```ts
352
- * // Zero config (ephemeral identity, 1-hour TTL):
353
- * const agent = await Agent.quickstart();
354
- *
355
- * // With custom name:
356
- * const agent = await Agent.quickstart({ name: 'my-service' });
357
- *
358
- * // Agent is ready to use immediately:
359
- * await agent.send({
360
- * to: 'did:key:z6Mk...',
361
- * payload: { action: 'test' },
362
- * scope: 'test',
363
- * });
364
- * ```
365
- */
366
- static async quickstart(opts) {
367
- // Use in-memory registry for ephemeral agents
368
- const registry = new trust_registry_js_1.MemoryTrustRegistry();
369
- // Use default transport
370
- const transport = new transport_js_1.HttpsTransportAdapter({
371
- baseUrl: DEFAULT_RELAY_URL,
372
- });
373
- // Generate ephemeral identity (1-hour TTL)
374
- const idResult = await (0, identity_js_1.generateIdentity)({ postQuantumSig: false });
375
- if (!idResult.ok) {
376
- throw new Error('Failed to generate ephemeral identity');
377
- }
378
- // Auto-generate name if not provided
379
- const name = opts?.name ?? `agent-${Date.now()}`;
380
- // Register ephemeral identity
381
- const regResult = await registry.register(idResult.value.did, idResult.value.rawPublicKey, name, undefined, // scopes - no restrictions (allow all)
382
- idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, false);
383
- if (!regResult.ok) {
384
- throw new Error(`Failed to register ephemeral identity: ${regResult.error}`);
385
- }
386
- // Create agent instance
387
- const agent = new Agent(idResult.value, name, registry, [transport], new nonce_store_js_1.MemoryNonceStore(), TIMESTAMP_WINDOW_MS, undefined, // No policy restrictions (allow all)
388
- undefined);
389
- // Auto-cleanup after 1 hour (ephemeral identity)
390
- const TTL_MS = 3600000; // 1 hour
391
- agent.cleanupTimer = setTimeout(async () => {
392
- await registry.revoke(idResult.value.did);
393
- // Ephemeral agent auto-revoked after TTL (no logging to avoid production noise)
394
- }, TTL_MS);
395
- return agent;
396
- }
397
- /**
398
- * Create agent from simplified options (async factory).
399
- *
400
- * This is the async-safe way to construct agents with AgentOptions.
401
- *
402
- * @param options - Agent configuration
403
- * @returns Agent instance
404
- *
405
- * @example
406
- * ```typescript
407
- * import { Agent } from '@private.me/xbind';
408
- *
409
- * // Ephemeral agent (auto-cleanup after 1 hour)
410
- * const agent = await Agent.from({
411
- * identity: 'ephemeral',
412
- * identityTTL: 3600, // seconds
413
- * });
414
- *
415
- * // Use the agent
416
- * await agent.send({ to, payload, scope: 'read' });
417
- * ```
418
- */
419
- static async from(options = {}) {
420
- const identity = options.identity ?? 'persistent';
421
- const identityTTL = options.identityTTL ?? 3600000; // 1 hour default (ms)
422
- const isEphemeral = identity === 'ephemeral';
423
- // Resolve registry
424
- const registry = typeof options.registry === 'string'
425
- ? new trust_registry_js_1.HttpTrustRegistry({ baseUrl: options.registry })
426
- : options.registry ?? (isEphemeral ? new trust_registry_js_1.MemoryTrustRegistry() : new trust_registry_js_1.HttpTrustRegistry({ baseUrl: DEFAULT_REGISTRY_URL }));
427
- // Resolve transport
428
- const transports = options.transport
429
- ? Array.isArray(options.transport)
430
- ? options.transport
431
- : [options.transport]
432
- : [new transport_js_1.HttpsTransportAdapter({ baseUrl: DEFAULT_RELAY_URL })];
433
- // Generate identity
434
- const idResult = await (0, identity_js_1.generateIdentity)({
435
- postQuantumSig: options.postQuantumSig ?? false,
436
- });
437
- if (!idResult.ok) {
438
- throw new Error('Failed to generate identity');
439
- }
440
- // Register identity (ephemeral agents use auto-generated names)
441
- const name = isEphemeral
442
- ? `ephemeral-agent-${Date.now()}`
443
- : `agent-${Date.now()}`;
444
- const regResult = await registry.register(idResult.value.did, idResult.value.rawPublicKey, name, undefined, // scopes
445
- idResult.value.rawX25519PublicKey, idResult.value.mlKemPublicKey, idResult.value.mlDsaPublicKey, false);
446
- if (!regResult.ok) {
447
- throw new Error(`Failed to register identity: ${regResult.error}`);
448
- }
449
- // Create agent instance
450
- const agent = new Agent(idResult.value, name, registry, transports, new nonce_store_js_1.MemoryNonceStore(), TIMESTAMP_WINDOW_MS, options.securityPolicy, options.backupConfig);
451
- // Setup TTL cleanup for ephemeral identities
452
- if (isEphemeral) {
453
- agent.cleanupTimer = setTimeout(async () => {
454
- // Auto-deregister after TTL expires
455
- await registry.revoke(idResult.value.did);
456
- }, identityTTL);
457
- }
458
- return agent;
459
- }
460
- /** Send a message to a recipient. */
461
- async send(opts) {
462
- const progress = new ux_helpers_1.ProgressReporter(opts.onProgress);
463
- progress.start('Resolving recipient identity...');
464
- const recipientKey = await this.registry.resolve(opts.to);
465
- if (!recipientKey.ok) {
466
- return (0, shared_1.err)(recipientKey.error === 'REVOKED'
467
- ? 'RECIPIENT_REVOKED'
468
- : 'RECIPIENT_NOT_FOUND');
469
- }
470
- // Bilateral authorization: check if recipient accepts this scope
471
- progress.update('Checking recipient authorization...', 10);
472
- const receiverAccepts = await this.registry.hasReceiveScope(opts.to, opts.scope);
473
- if (!receiverAccepts) {
474
- this.lastDetail = `recipient=${opts.to}, scope=${opts.scope}`;
475
- return (0, shared_1.err)('RECEIVER_SCOPE_DENIED');
476
- }
477
- progress.update('Preparing message...', 15);
478
- const plaintext = new TextEncoder().encode(JSON.stringify(opts.payload));
479
- // Security policy: classify action risk and determine security mode
480
- progress.update('Determining security level...', 20);
481
- const securityDecision = this.securityPolicy.classify({
482
- action: opts.action ?? 'send',
483
- params: typeof opts.payload === 'object' && opts.payload !== null
484
- ? opts.payload
485
- : {},
486
- sender: this.did,
487
- recipient: opts.to,
488
- scope: opts.scope,
489
- securityOverride: opts.security,
490
- });
491
- this.lastSecurityDecision = securityDecision;
492
- // Log security decision for transparency
493
- progress.update(`Security: ${(0, security_policy_js_1.describeSecurityMode)(securityDecision.mode)} — ${securityDecision.reason}`, 25);
494
- // Determine whether to use split-channel based on policy decision
495
- // Backward compatibility: explicit splitChannel flag overrides policy
496
- const shouldUseSplitChannel = opts.splitChannel !== undefined
497
- ? opts.splitChannel
498
- : securityDecision.mode.type === 'split';
499
- // Xchange: opt-in via xchange: true on send or policy decision. Faster (single security layer) but
500
- // trades per-share encryption and KEM for ~180x speed. Falls back to V3 if
501
- // recipient doesn't support Xchange.
502
- if (shouldUseSplitChannel && (opts.xchange || securityDecision.mode.type === 'xchange')) {
503
- progress.update('Checking Xchange support...', 20);
504
- const canXchange = await this.canUseXchange(opts.to);
505
- if (canXchange) {
506
- return this.sendXchange(opts, plaintext, progress);
507
- }
508
- // Fall back to V3 split-channel if recipient doesn't support Xchange
509
- }
510
- progress.update('Establishing key agreement...', 30);
511
- const ecdhResult = await this.trySenderECDH(opts.to);
512
- if (ecdhResult) {
513
- if (shouldUseSplitChannel) {
514
- return this.sendSplitChannel(opts, plaintext, ecdhResult.sharedKey, ecdhResult.ephemeralPublicKey, ecdhResult.kemCiphertext, ecdhResult.recipientHasMlDsa, progress);
515
- }
516
- // V3: both sides have ML-DSA + hybrid KEM
517
- if (ecdhResult.kemCiphertext && ecdhResult.recipientHasMlDsa && this.identity.mlDsaSecretKey) {
518
- return this.sendWithHybridV3(opts, plaintext, ecdhResult.sharedKey, ecdhResult.ephemeralPublicKey, ecdhResult.kemCiphertext, progress);
519
- }
520
- if (ecdhResult.kemCiphertext) {
521
- return this.sendWithHybrid(opts, plaintext, ecdhResult.sharedKey, ecdhResult.ephemeralPublicKey, ecdhResult.kemCiphertext, progress);
522
- }
523
- return this.sendWithECDH(opts, plaintext, ecdhResult.sharedKey, ecdhResult.ephemeralPublicKey, progress);
524
- }
525
- // SECURITY FIX (v1.1.6): Removed insecure fallback to deriveSharedKey()
526
- // Recipient must have x25519 public key registered for secure ECDH key agreement
527
- return (0, shared_1.err)('KEY_AGREEMENT_FAILED:RECIPIENT_HAS_NO_X25519_KEY');
528
- }
529
- /**
530
- * Verify and decrypt an incoming encrypted envelope (v1 or v2).
531
- *
532
- * @param envelope - Incoming transport envelope.
533
- * @param opts - Optional receive options (e.g. allowCleartext).
534
- */
535
- async receive(envelope, opts) {
536
- this.lastDetail = '';
537
- const progress = new ux_helpers_1.ProgressReporter(opts?.onProgress);
538
- progress.start('Verifying envelope signature...');
539
- const verified = await this.verifyEnvelope(envelope);
540
- if (!verified.ok)
541
- return verified;
542
- const { senderRawKey, payloadBytes } = verified.value;
543
- let sharedKey;
544
- // V4 Xchange: use receiveXchangeShare() instead
545
- if (envelope.v === 4) {
546
- return (0, shared_1.err)('VERIFICATION_FAILED:UNSUPPORTED_VERSION');
547
- }
548
- progress.update('Deriving shared key...', 30);
549
- // V2/V3 hybrid path: use X25519 + ML-KEM-768
550
- if ((envelope.v === 2 || envelope.v === 3) && 'kemCiphertext' in envelope) {
551
- if (!this.identity.mlKemSecretKey) {
552
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
553
- }
554
- // Type guards to prevent crashes on malformed envelopes
555
- if (typeof envelope.ephemeralPub !== 'string' || typeof envelope.kemCiphertext !== 'string') {
556
- this.lastDetail = 'ephemeralPub or kemCiphertext not string';
557
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
558
- }
559
- const ephPubBytes = (0, crypto_1.fromBase64)(envelope.ephemeralPub);
560
- const kemCtBytes = (0, crypto_1.fromBase64)(envelope.kemCiphertext);
561
- const hybridKey = await (0, key_agreement_js_1.receiverHybridKeyAgreement)(this.identity.x25519PrivateKey, ephPubBytes, kemCtBytes, this.identity.mlKemSecretKey);
562
- if (!hybridKey.ok)
563
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
564
- sharedKey = hybridKey.value;
565
- }
566
- else if (envelope.ephemeralPub) {
567
- // V1 with ECDH forward secrecy
568
- if (typeof envelope.ephemeralPub !== 'string') {
569
- this.lastDetail = 'ephemeralPub not string';
570
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
571
- }
572
- const ephPubBytes = (0, crypto_1.fromBase64)(envelope.ephemeralPub);
573
- const ecdhKey = await (0, key_agreement_js_1.receiverKeyAgreement)(this.identity.x25519PrivateKey, ephPubBytes);
574
- if (!ecdhKey.ok)
575
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
576
- sharedKey = ecdhKey.value;
577
- }
578
- else {
579
- // SECURITY FIX (v1.1.6): Removed insecure V1 SHA-256 fallback
580
- // Envelopes without ephemeralPub are rejected (sender must use X25519 ECDH)
581
- return (0, shared_1.err)('DECRYPT_FAILED:NO_EPHEMERAL_KEY');
582
- }
583
- progress.update('Decrypting payload...', 60);
584
- const decrypted = await (0, envelope_js_1.decryptPayload)(envelope, sharedKey);
585
- if (!decrypted.ok) {
586
- if (opts?.allowCleartext) {
587
- let payload;
588
- try {
589
- payload = JSON.parse(new TextDecoder().decode(payloadBytes));
590
- }
591
- catch {
592
- return (0, shared_1.err)('DECRYPT_FAILED:PARSE');
593
- }
594
- progress.complete();
595
- return (0, shared_1.ok)({
596
- sender: envelope.sender,
597
- payload,
598
- scope: envelope.scope,
599
- timestamp: envelope.timestamp,
600
- });
601
- }
602
- return (0, shared_1.err)('DECRYPT_FAILED:DECRYPTION');
603
- }
604
- progress.update('Parsing message...', 90);
605
- let payload;
606
- try {
607
- payload = JSON.parse(new TextDecoder().decode(decrypted.value));
608
- }
609
- catch {
610
- return (0, shared_1.err)('DECRYPT_FAILED:PARSE');
611
- }
612
- progress.complete();
613
- // Mechanism 2: Protocol information available in metadata for applications to display
614
- // Applications can show this to users if needed (envelope.protocol, envelope.documentationUrl)
615
- return (0, shared_1.ok)({
616
- sender: envelope.sender,
617
- payload,
618
- scope: envelope.scope,
619
- timestamp: envelope.timestamp,
620
- metadata: envelope.protocol && envelope.documentationUrl
621
- ? {
622
- protocol: envelope.protocol,
623
- documentationUrl: envelope.documentationUrl,
624
- }
625
- : undefined,
626
- });
627
- }
628
- /**
629
- * Verify only the signature on an envelope without consuming the nonce.
630
- *
631
- * Does NOT check timestamp, nonce, or scope. Use this for pre-screening
632
- * or audit logging where you need to confirm the sender but don't want
633
- * to consume replay-prevention state.
634
- *
635
- * @param envelope - The envelope to verify.
636
- * @returns `{ sender, valid }` or error if the DID cannot be resolved.
637
- */
638
- async verifySignature(envelope) {
639
- const senderKey = await this.registry.resolve(envelope.sender);
640
- if (!senderKey.ok)
641
- return (0, shared_1.err)('VERIFICATION_FAILED:DID_NOT_IN_REGISTRY');
642
- const pubKey = await (0, identity_js_1.importPublicKey)(senderKey.value);
643
- if (!pubKey.ok)
644
- return (0, shared_1.err)('VERIFICATION_FAILED:KEY_IMPORT_FAILED');
645
- const sigBytes = (0, crypto_1.fromBase64)(envelope.signature);
646
- // SECURITY FIX (v1.1.3): Verify canonical representation (not just payload)
647
- const canonicalData = JSON.stringify({
648
- v: envelope.v,
649
- alg: envelope.alg,
650
- sender: envelope.sender,
651
- recipient: envelope.recipient,
652
- timestamp: envelope.timestamp,
653
- nonce: envelope.nonce,
654
- scope: envelope.scope,
655
- payload: envelope.payload,
656
- });
657
- const canonicalBytes = new TextEncoder().encode(canonicalData);
658
- const sigValid = await (0, identity_js_1.verify)(pubKey.value, sigBytes, canonicalBytes);
659
- if (!sigValid.ok)
660
- return (0, shared_1.err)('VERIFICATION_FAILED:SIGNATURE_MISMATCH');
661
- return (0, shared_1.ok)({ sender: envelope.sender, valid: sigValid.value });
662
- }
663
- /**
664
- * Export the raw 32-byte private key seeds for both Ed25519 and X25519.
665
- *
666
- * These are the raw private key bytes extracted from PKCS8, NOT the
667
- * original HKDF seed (that derivation is one-way). Use these for
668
- * persistence or cross-device transfer.
669
- *
670
- * @returns Two 32-byte Uint8Arrays or error.
671
- */
672
- async exportSeeds() {
673
- const edPkcs8 = await (0, identity_js_1.exportPKCS8)(this.identity.privateKey);
674
- if (!edPkcs8.ok)
675
- return (0, shared_1.err)('IDENTITY_FAILED');
676
- const x25519Pkcs8 = await (0, identity_js_1.exportX25519PKCS8)(this.identity.x25519PrivateKey);
677
- if (!x25519Pkcs8.ok)
678
- return (0, shared_1.err)('IDENTITY_FAILED');
679
- const edRaw = (0, identity_js_1.extractRawEd25519)(edPkcs8.value);
680
- if (!edRaw.ok)
681
- return (0, shared_1.err)('IDENTITY_FAILED');
682
- const x25519Raw = (0, identity_js_1.extractRawX25519)(x25519Pkcs8.value);
683
- if (!x25519Raw.ok)
684
- return (0, shared_1.err)('IDENTITY_FAILED');
685
- return (0, shared_1.ok)({
686
- ed25519: edRaw.value,
687
- x25519: x25519Raw.value,
688
- mlKemSecretKey: this.identity.mlKemSecretKey,
689
- mlKemPublicKey: this.identity.mlKemPublicKey,
690
- });
691
- }
692
- /**
693
- * Split a cryptographic key into backup shares using XorIDA.
694
- *
695
- * Uses the agent's backup configuration (default: k=2, n=3).
696
- * Any `threshold` shares can reconstruct the original key.
697
- * Each share reveals zero information (information-theoretic security).
698
- *
699
- * @param key - The key to split (32 or 64 bytes typical).
700
- * @returns Array of backup shares or error.
701
- *
702
- * @example
703
- * ```typescript
704
- * // Generate a key and split it
705
- * const key = crypto.getRandomValues(new Uint8Array(32));
706
- * const shares = await agent.splitKey(key);
707
- *
708
- * if (shares.ok) {
709
- * // Store shares in separate locations
710
- * shares.value.forEach((share, i) => {
711
- * storeShare(`backup-${i}.json`, JSON.stringify(share));
712
- * });
713
- * }
714
- * ```
715
- */
716
- async splitKey(key) {
717
- // Dynamic import avoids circular dependency
718
- const { splitKeyWithBackup } = await Promise.resolve().then(() => __importStar(require('./backup-config.js')));
719
- const result = await splitKeyWithBackup(key, this.backupConfig);
720
- if (!result.ok) {
721
- return (0, shared_1.err)('ENVELOPE_FAILED:SPLIT');
722
- }
723
- return result;
724
- }
725
- /**
726
- * Reconstruct a cryptographic key from backup shares.
727
- *
728
- * Requires at least `threshold` shares. Verifies HMAC before returning
729
- * the reconstructed key to prevent tampering.
730
- *
731
- * @param shares - Backup shares (must be >= threshold).
732
- * @returns Reconstructed key or error.
733
- *
734
- * @example
735
- * ```typescript
736
- * // Load shares from storage
737
- * const share0 = JSON.parse(loadShare('backup-0.json'));
738
- * const share1 = JSON.parse(loadShare('backup-1.json'));
739
- *
740
- * // Reconstruct from any 2 shares (threshold=2)
741
- * const key = await agent.reconstructKey([share0, share1]);
742
- *
743
- * if (key.ok) {
744
- * // Use reconstructed key
745
- * console.log('Key recovered:', key.value);
746
- * }
747
- * ```
748
- */
749
- async reconstructKey(shares) {
750
- // Dynamic import avoids circular dependency
751
- const { reconstructKeyFromBackup } = await Promise.resolve().then(() => __importStar(require('./backup-config.js')));
752
- const result = await reconstructKeyFromBackup(shares);
753
- if (!result.ok) {
754
- return (0, shared_1.err)('DECRYPT_FAILED');
755
- }
756
- return result;
757
- }
758
- /**
759
- * Verify a signed (unencrypted) envelope and return the message.
760
- *
761
- * Use for envelopes created with createSignedEnvelope().
762
- * Verifies signature, timestamp, nonce, and scope — skips decryption.
763
- *
764
- * @param envelope - A signed (unencrypted) transport envelope.
765
- * @returns Verified message or error with sub-code.
766
- */
767
- async receiveSigned(envelope) {
768
- this.lastDetail = '';
769
- const verified = await this.verifyEnvelope(envelope);
770
- if (!verified.ok)
771
- return verified;
772
- let payload;
773
- try {
774
- payload = JSON.parse(new TextDecoder().decode(verified.value.payloadBytes));
775
- }
776
- catch {
777
- return (0, shared_1.err)('DECRYPT_FAILED:PARSE');
778
- }
779
- // Mechanism 2: Protocol information available in metadata for applications to display
780
- // Applications can show this to users if needed (envelope.protocol, envelope.documentationUrl)
781
- return (0, shared_1.ok)({
782
- sender: envelope.sender,
783
- payload,
784
- scope: envelope.scope,
785
- timestamp: envelope.timestamp,
786
- metadata: envelope.protocol && envelope.documentationUrl
787
- ? {
788
- protocol: envelope.protocol,
789
- documentationUrl: envelope.documentationUrl,
790
- }
791
- : undefined,
792
- });
793
- }
794
- /**
795
- * Discover available tools in the registry.
796
- *
797
- * Query xRegistry for tools matching the given service prefix or capability.
798
- * Returns tool metadata including name, description, schema, and trust level.
799
- *
800
- * @param service - Optional service prefix to filter by (e.g., "payments")
801
- * @returns Array of tool metadata or empty array if none found
802
- *
803
- * @example
804
- * ```typescript
805
- * import { Agent } from '@private.me/xbind';
806
- * import { ToolRegistry } from"../_deps/xregistry/index.js";
807
- * import { setToolRegistry } from '@private.me/xbind/agent-call';
808
- *
809
- * const registry = new ToolRegistry();
810
- * registry.register({
811
- * name: 'payments:createCharge',
812
- * description: 'Create a payment charge',
813
- * schema: { type: 'object', properties: { amount: { type: 'number' } } },
814
- * trustLevel: 'verified',
815
- * endpoint: 'https://api.stripe.com/v1/charges',
816
- * capabilities: ['payment', 'charge']
817
- * });
818
- *
819
- * setToolRegistry(registry);
820
- *
821
- * const agent = await Agent.quickstart();
822
- *
823
- * // Discover all payment tools
824
- * const tools = await agent.discover('payments');
825
- * console.log(tools); // [{ name: 'payments:createCharge', ... }]
826
- *
827
- * // Discover all tools
828
- * const allTools = await agent.discover();
829
- * ```
830
- */
831
- async discover(service) {
832
- // Import dynamically to avoid circular dependency
833
- // SAFETY: Dynamic import allows agent-call.ts to import agent.ts without circular reference
834
- const { getToolRegistry } = await Promise.resolve().then(() => __importStar(require('./agent-call.js')));
835
- const registry = getToolRegistry();
836
- if (!registry) {
837
- // No registry configured - return empty array
838
- return [];
839
- }
840
- if (!service) {
841
- // No filter - return all tools
842
- return registry.listAll();
843
- }
844
- // Filter by service prefix using registry search
845
- // Search handles name, capability, and description matching
846
- const results = registry.search(service);
847
- return results;
848
- }
849
- /** Generate an Express-compatible middleware handler. */
850
- middleware() {
851
- return async (req, res, next) => {
852
- const validated = (0, envelope_js_1.validateEnvelope)(req.body);
853
- if (!validated.ok) {
854
- res.status(400).json({ error: validated.error });
855
- return;
856
- }
857
- // SAFETY: validateEnvelope returns AnyTransportEnvelope
858
- const result = await this.receive(validated.value);
859
- if (!result.ok) {
860
- const code = result.error === 'TIMESTAMP_EXPIRED'
861
- || result.error === 'REPLAY_DETECTED'
862
- ? 403 : 401;
863
- res.status(code).json({ error: result.error });
864
- return;
865
- }
866
- req['agentMessage'] = result.value;
867
- next();
868
- };
869
- }
870
- /**
871
- * Cleanup resources (timers, handlers, etc.)
872
- *
873
- * Call this in tests or when disposing an agent to prevent timer leaks.
874
- * Clears the ephemeral identity auto-cleanup timer if present.
875
- */
876
- cleanup() {
877
- if (this.cleanupTimer) {
878
- clearTimeout(this.cleanupTimer);
879
- this.cleanupTimer = undefined;
880
- }
881
- }
882
- /**
883
- * Dispose of agent resources (alias for cleanup)
884
- *
885
- * Provided for compatibility with test cleanup patterns.
886
- */
887
- dispose() {
888
- this.cleanup();
889
- }
890
- /**
891
- * Attempt sender-side key agreement.
892
- *
893
- * If both sides support ML-KEM-768, uses hybrid (X25519 + ML-KEM-768).
894
- * Otherwise falls back to X25519-only ECDH.
895
- * Returns null if recipient has no X25519 key registered.
896
- * Also returns whether recipient supports ML-DSA-65 for v3 envelopes.
897
- */
898
- async trySenderECDH(recipientDid) {
899
- const entry = await this.registry.getEntry(recipientDid);
900
- if (!entry.ok || !entry.value.x25519PublicKey)
901
- return null;
902
- const recipientHasMlDsa = !!entry.value.mlDsaPublicKey;
903
- const recipientX25519 = await (0, key_agreement_js_1.importX25519PublicKey)(entry.value.x25519PublicKey);
904
- if (!recipientX25519.ok)
905
- return null;
906
- // Try hybrid if both sides have ML-KEM keys
907
- if (entry.value.mlKemPublicKey && this.identity.mlKemSecretKey) {
908
- const hybrid = await (0, key_agreement_js_1.senderHybridKeyAgreement)(recipientX25519.value, entry.value.mlKemPublicKey);
909
- if (hybrid.ok) {
910
- return {
911
- sharedKey: hybrid.value.sharedKey,
912
- ephemeralPublicKey: hybrid.value.ephemeralPublicKey,
913
- kemCiphertext: hybrid.value.kemCiphertext,
914
- recipientHasMlDsa,
915
- };
916
- }
917
- }
918
- // Fallback to X25519-only
919
- const result = await (0, key_agreement_js_1.senderKeyAgreement)(recipientX25519.value);
920
- if (!result.ok)
921
- return null;
922
- return { ...result.value, recipientHasMlDsa };
923
- }
924
- /** Send with ECDH forward secrecy (ephemeral key in envelope). */
925
- async sendWithECDH(opts, plaintext, sharedKey, ephemeralPublicKey, progress) {
926
- progress?.update('Encrypting message with ECDH...', 60);
927
- const envelope = await (0, envelope_js_1.createEnvelope)({
928
- senderDid: this.identity.did,
929
- recipientDid: opts.to,
930
- scope: opts.scope,
931
- plaintext,
932
- privateKey: this.identity.privateKey,
933
- sharedKey,
934
- ephemeralPublicKey,
935
- });
936
- if (!envelope.ok)
937
- return (0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT');
938
- progress?.update('Sending message...', 90);
939
- const result = await this.transports[0].send(envelope.value, opts.to);
940
- if (result.ok) {
941
- progress?.complete();
942
- }
943
- return result;
944
- }
945
- /** Send with hybrid KEM (X25519 + ML-KEM-768) via v2 envelope. */
946
- async sendWithHybrid(opts, plaintext, sharedKey, ephemeralPublicKey, kemCiphertext, progress) {
947
- progress?.update('Encrypting message with hybrid KEM...', 60);
948
- const envelope = await (0, envelope_js_1.createEnvelopeV2)({
949
- senderDid: this.identity.did,
950
- recipientDid: opts.to,
951
- scope: opts.scope,
952
- plaintext,
953
- privateKey: this.identity.privateKey,
954
- sharedKey,
955
- ephemeralPublicKey,
956
- kemCiphertext,
957
- });
958
- if (!envelope.ok)
959
- return (0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT');
960
- progress?.update('Sending message...', 90);
961
- // SAFETY: AnyTransportEnvelope is accepted by transport.send
962
- const result = await this.transports[0].send(envelope.value, opts.to);
963
- if (result.ok) {
964
- progress?.complete();
965
- }
966
- return result;
967
- }
968
- /** Send with hybrid KEM + dual signatures via v3 envelope. */
969
- async sendWithHybridV3(opts, plaintext, sharedKey, ephemeralPublicKey, kemCiphertext, progress) {
970
- if (!this.identity.mlDsaSecretKey) {
971
- return (0, shared_1.err)('ENVELOPE_FAILED:PQ_KEY_MISSING');
972
- }
973
- progress?.update('Encrypting with post-quantum signatures...', 60);
974
- const envelope = await (0, envelope_js_1.createEnvelopeV3)({
975
- senderDid: this.identity.did,
976
- recipientDid: opts.to,
977
- scope: opts.scope,
978
- plaintext,
979
- privateKey: this.identity.privateKey,
980
- sharedKey,
981
- ephemeralPublicKey,
982
- kemCiphertext,
983
- mlDsaSecretKey: this.identity.mlDsaSecretKey,
984
- });
985
- if (!envelope.ok) {
986
- this.lastDetail = `v3 envelope error: ${envelope.error}`;
987
- return (0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT');
988
- }
989
- progress?.update('Sending message...', 90);
990
- // SAFETY: AnyTransportEnvelope is accepted by transport.send
991
- const result = await this.transports[0].send(envelope.value, opts.to);
992
- if (result.ok) {
993
- progress?.complete();
994
- }
995
- return result;
996
- }
997
- async sendDirect(opts, plaintext, sharedKey, progress) {
998
- progress?.update('Encrypting message...', 60);
999
- const envelope = await (0, envelope_js_1.createEnvelope)({
1000
- senderDid: this.identity.did,
1001
- recipientDid: opts.to,
1002
- scope: opts.scope,
1003
- plaintext,
1004
- privateKey: this.identity.privateKey,
1005
- sharedKey,
1006
- });
1007
- if (!envelope.ok)
1008
- return (0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT');
1009
- progress?.update('Sending message...', 90);
1010
- const result = await this.transports[0].send(envelope.value, opts.to);
1011
- if (result.ok) {
1012
- progress?.complete();
1013
- }
1014
- return result;
1015
- }
1016
- /**
1017
- * Check whether the recipient supports Xchange (XorIDA key transport).
1018
- *
1019
- * @param recipientDid - Recipient DID to check.
1020
- * @returns True if recipient has xchange: true in registry.
1021
- */
1022
- async canUseXchange(recipientDid) {
1023
- const entry = await this.registry.getEntry(recipientDid);
1024
- if (!entry.ok)
1025
- return false;
1026
- return entry.value.xchange === true;
1027
- }
1028
- /**
1029
- * Send via Xchange mode: random key + AES-GCM encrypt, bundle, XorIDA split, v4 envelopes.
1030
- *
1031
- * Opt-in performance mode. No KEM, no key agreement — the random key is
1032
- * embedded in the bundle and split across channels. Single security layer
1033
- * (information-theoretic) + Ed25519 authentication. ~180x faster than V3.
1034
- */
1035
- async sendXchange(opts, plaintext, progress) {
1036
- const config = opts.splitChannelConfig ?? split_channel_js_1.DEFAULT_SPLIT_CONFIG;
1037
- if (this.transports.length < config.totalShares) {
1038
- // eslint-disable-next-line no-console
1039
- console.warn(`Split-channel: ${config.totalShares} shares but only ${this.transports.length} transport(s). ` +
1040
- `For channel separation, provide at least ${config.totalShares} transports.`);
1041
- }
1042
- progress?.update('Generating Xchange key...', 40);
1043
- const keyResult = await (0, xchange_1.generateXchangeKey)();
1044
- if (!keyResult.ok)
1045
- return (0, shared_1.err)('KEY_AGREEMENT_FAILED');
1046
- progress?.update('Encrypting message...', 50);
1047
- const bundleResult = await (0, xchange_1.xchangeEncrypt)(plaintext, keyResult.value);
1048
- if (!bundleResult.ok)
1049
- return (0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT');
1050
- const n = config.totalShares;
1051
- const k = config.threshold;
1052
- const p = (0, crypto_1.nextOddPrime)(n);
1053
- const blockSize = p - 1;
1054
- const padded = (0, crypto_1.pkcs7Pad)(bundleResult.value, blockSize);
1055
- const { key: hmacKey, signature: hmacSig } = await (0, crypto_1.generateHMAC)(padded);
1056
- progress?.update('Splitting message into shares...', 60);
1057
- let shareArrays;
1058
- try {
1059
- shareArrays = (0, crypto_1.splitXorIDA)(padded, n, k);
1060
- }
1061
- catch {
1062
- return (0, shared_1.err)('ENVELOPE_FAILED:SPLIT');
1063
- }
1064
- const hmacKeyB64 = (0, crypto_1.toBase64)(hmacKey);
1065
- const hmacSigB64 = (0, crypto_1.toBase64)(hmacSig);
1066
- const groupId = (0, crypto_1.generateUUID)();
1067
- const results = [];
1068
- progress?.update('Sending shares...', 70);
1069
- for (let i = 0; i < shareArrays.length; i++) {
1070
- const shareData = shareArrays[i];
1071
- const shareB64 = (0, crypto_1.formatShareHeader)((0, crypto_1.toBase64)(shareData));
1072
- const shareBytes = new TextEncoder().encode(shareB64);
1073
- const envResult = await (0, envelope_js_1.createEnvelopeV4)({
1074
- senderDid: this.identity.did,
1075
- recipientDid: opts.to,
1076
- scope: opts.scope,
1077
- shareData: shareBytes,
1078
- privateKey: this.identity.privateKey,
1079
- shareIndex: i,
1080
- shareTotal: n,
1081
- shareThreshold: k,
1082
- shareGroupId: groupId,
1083
- shareHmacKey: hmacKeyB64,
1084
- shareHmacSig: hmacSigB64,
1085
- });
1086
- if (!envResult.ok) {
1087
- results.push((0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT'));
1088
- continue;
1089
- }
1090
- // Route share[i] to transports[i % transports.length]
1091
- const transport = this.transports[i % this.transports.length];
1092
- // SAFETY: Transport accepts v1; v4 has same required fields
1093
- const sendResult = await transport.send(envResult.value, opts.to);
1094
- results.push(sendResult);
1095
- // Update progress for each share sent
1096
- const shareProgress = 70 + Math.floor((i + 1) / shareArrays.length * 20);
1097
- progress?.update(`Sent share ${i + 1}/${shareArrays.length}...`, shareProgress);
1098
- }
1099
- const successes = results.filter((r) => r.ok).length;
1100
- if (successes < k) {
1101
- return (0, shared_1.err)('SEND_FAILED:BELOW_THRESHOLD');
1102
- }
1103
- progress?.complete();
1104
- return (0, shared_1.ok)(undefined);
1105
- }
1106
- /**
1107
- * Split plaintext via XorIDA and send each share as a separate V3 envelope.
1108
- *
1109
- * Default split-channel path with three independent cryptographic layers:
1110
- * XorIDA payload split, hybrid PQ KEM, and dual signatures.
1111
- * Returns ok() if at least threshold sends succeed.
1112
- */
1113
- async sendSplitChannel(opts, plaintext, sharedKey, ephemeralPublicKey, kemCiphertext, recipientHasMlDsa, progress) {
1114
- const config = opts.splitChannelConfig ?? split_channel_js_1.DEFAULT_SPLIT_CONFIG;
1115
- if (this.transports.length < config.totalShares) {
1116
- // eslint-disable-next-line no-console
1117
- console.warn(`Split-channel: ${config.totalShares} shares but only ${this.transports.length} transport(s). ` +
1118
- `For channel separation, provide at least ${config.totalShares} transports.`);
1119
- }
1120
- progress?.update('Splitting message into shares...', 50);
1121
- const splitResult = await (0, split_channel_js_1.splitForChannel)(plaintext, config);
1122
- if (!splitResult.ok)
1123
- return (0, shared_1.err)('ENVELOPE_FAILED:SPLIT');
1124
- const shares = splitResult.value;
1125
- progress?.update('Encrypting and sending shares...', 70);
1126
- const results = await this.sendShareEnvelopes(opts, shares, sharedKey, ephemeralPublicKey, kemCiphertext, recipientHasMlDsa, progress);
1127
- const successes = results.filter((r) => r.ok).length;
1128
- if (successes < config.threshold) {
1129
- return (0, shared_1.err)('SEND_FAILED:BELOW_THRESHOLD');
1130
- }
1131
- progress?.complete();
1132
- return (0, shared_1.ok)(undefined);
1133
- }
1134
- /**
1135
- * Create and send an envelope for each share independently.
1136
- *
1137
- * Routes share[i] to transports[i % transports.length] for channel separation.
1138
- */
1139
- async sendShareEnvelopes(opts, shares, sharedKey, ephemeralPublicKey, kemCiphertext, recipientHasMlDsa, progress) {
1140
- const results = [];
1141
- for (let i = 0; i < shares.length; i++) {
1142
- const share = shares[i];
1143
- const shareBytes = new TextEncoder().encode(share.data);
1144
- let envResult;
1145
- if (kemCiphertext && ephemeralPublicKey && recipientHasMlDsa && this.identity.mlDsaSecretKey) {
1146
- envResult = await (0, envelope_js_1.createEnvelopeV3)({
1147
- senderDid: this.identity.did,
1148
- recipientDid: opts.to,
1149
- scope: opts.scope,
1150
- plaintext: shareBytes,
1151
- privateKey: this.identity.privateKey,
1152
- sharedKey,
1153
- ephemeralPublicKey,
1154
- kemCiphertext,
1155
- mlDsaSecretKey: this.identity.mlDsaSecretKey,
1156
- shareIndex: share.index,
1157
- shareTotal: share.total,
1158
- shareThreshold: share.threshold,
1159
- shareGroupId: share.groupId,
1160
- shareHmacKey: share.hmacKey,
1161
- shareHmacSig: share.hmacSig,
1162
- });
1163
- }
1164
- else if (kemCiphertext && ephemeralPublicKey) {
1165
- envResult = await (0, envelope_js_1.createEnvelopeV2)({
1166
- senderDid: this.identity.did,
1167
- recipientDid: opts.to,
1168
- scope: opts.scope,
1169
- plaintext: shareBytes,
1170
- privateKey: this.identity.privateKey,
1171
- sharedKey,
1172
- ephemeralPublicKey,
1173
- kemCiphertext,
1174
- shareIndex: share.index,
1175
- shareTotal: share.total,
1176
- shareThreshold: share.threshold,
1177
- shareGroupId: share.groupId,
1178
- shareHmacKey: share.hmacKey,
1179
- shareHmacSig: share.hmacSig,
1180
- });
1181
- }
1182
- else {
1183
- envResult = await (0, envelope_js_1.createEnvelope)({
1184
- senderDid: this.identity.did,
1185
- recipientDid: opts.to,
1186
- scope: opts.scope,
1187
- plaintext: shareBytes,
1188
- privateKey: this.identity.privateKey,
1189
- sharedKey,
1190
- ephemeralPublicKey,
1191
- shareIndex: share.index,
1192
- shareTotal: share.total,
1193
- shareThreshold: share.threshold,
1194
- shareGroupId: share.groupId,
1195
- shareHmacKey: share.hmacKey,
1196
- shareHmacSig: share.hmacSig,
1197
- });
1198
- }
1199
- if (!envResult.ok) {
1200
- results.push((0, shared_1.err)('ENVELOPE_FAILED:ENCRYPT'));
1201
- continue;
1202
- }
1203
- // Route share[i] to transports[i % transports.length]
1204
- const transport = this.transports[i % this.transports.length];
1205
- // SAFETY: Transport accepts v1; v2/v3 is a superset of v1 fields
1206
- const sendResult = await transport.send(envResult.value, opts.to);
1207
- results.push(sendResult);
1208
- // Update progress for each share sent
1209
- const shareProgress = 70 + Math.floor((i + 1) / shares.length * 20);
1210
- progress?.update(`Sent share ${i + 1}/${shares.length}...`, shareProgress);
1211
- }
1212
- return results;
1213
- }
1214
- /**
1215
- * Receive and accumulate a split-channel share envelope.
1216
- *
1217
- * Verifies the envelope, extracts share metadata, accumulates shares.
1218
- * When threshold is reached, reconstructs the original plaintext.
1219
- *
1220
- * @param envelope - A share envelope with split-channel metadata
1221
- * @returns AgentMessage if threshold met, null if more shares needed
1222
- */
1223
- async receiveSplitShare(envelope) {
1224
- if (envelope.shareGroupId === undefined) {
1225
- return (0, shared_1.err)('VERIFICATION_FAILED');
1226
- }
1227
- const receiveResult = await this.receiveRaw(envelope);
1228
- if (!receiveResult.ok)
1229
- return receiveResult;
1230
- const { sender, decryptedText, scope, timestamp } = receiveResult.value;
1231
- const share = {
1232
- data: decryptedText,
1233
- index: envelope.shareIndex ?? 0,
1234
- total: envelope.shareTotal ?? 2,
1235
- threshold: envelope.shareThreshold ?? 2,
1236
- groupId: envelope.shareGroupId,
1237
- hmacKey: envelope.shareHmacKey ?? '',
1238
- hmacSig: envelope.shareHmacSig ?? '',
1239
- };
1240
- return this.accumulateShare(share, sender, scope, timestamp);
1241
- }
1242
- /**
1243
- * Receive and accumulate a v4 Xchange share envelope.
1244
- *
1245
- * Verifies Ed25519 signature, extracts raw share data (no decryption —
1246
- * the XorIDA share IS the payload). When threshold is reached,
1247
- * reconstructs and decrypts via Xchange.
1248
- *
1249
- * @param envelope - A v4 Xchange envelope with share metadata.
1250
- * @returns AgentMessage if threshold met, null if more shares needed.
1251
- */
1252
- async receiveXchangeShare(envelope) {
1253
- this.lastDetail = '';
1254
- const verified = await this.verifyEnvelope(envelope);
1255
- if (!verified.ok)
1256
- return verified;
1257
- // V4: payload is the raw share data (base64 of share bytes), NOT encrypted
1258
- const shareText = new TextDecoder().decode(verified.value.payloadBytes);
1259
- const share = {
1260
- data: shareText,
1261
- index: envelope.shareIndex,
1262
- total: envelope.shareTotal,
1263
- threshold: envelope.shareThreshold,
1264
- groupId: envelope.shareGroupId,
1265
- hmacKey: envelope.shareHmacKey,
1266
- hmacSig: envelope.shareHmacSig,
1267
- };
1268
- return this.accumulateXchangeShare(share, envelope.sender, envelope.scope, envelope.timestamp);
1269
- }
1270
- /**
1271
- * Accumulate a Xchange share and attempt reconstruction + decryption.
1272
- *
1273
- * When threshold is met:
1274
- * 1. Reconstruct padded bundle from XorIDA shares
1275
- * 2. Verify HMAC (BEFORE decrypt — CRITICAL)
1276
- * 3. Unpad → extract bundle → xchangeDecrypt → plaintext
1277
- */
1278
- async accumulateXchangeShare(share, sender, scope, timestamp) {
1279
- const existing = this.shareAccumulator.get(share.groupId) ?? [];
1280
- const isDuplicate = existing.some((s) => s.index === share.index);
1281
- if (!isDuplicate) {
1282
- existing.push(share);
1283
- this.shareAccumulator.set(share.groupId, existing);
1284
- }
1285
- if (existing.length < share.threshold) {
1286
- return (0, shared_1.ok)(null);
1287
- }
1288
- this.shareAccumulator.delete(share.groupId);
1289
- const usedShares = existing.slice(0, share.threshold);
1290
- const n = share.total;
1291
- const k = share.threshold;
1292
- // Decode share data from base64
1293
- let shareData;
1294
- try {
1295
- shareData = usedShares.map((s) => (0, crypto_1.fromBase64)((0, crypto_1.parseShareHeader)(s.data)));
1296
- }
1297
- catch {
1298
- return (0, shared_1.err)('DECRYPT_FAILED');
1299
- }
1300
- const indices = usedShares.map((s) => s.index);
1301
- // Reconstruct padded bundle
1302
- let padded;
1303
- try {
1304
- padded = (0, crypto_1.reconstructXorIDA)(shareData, indices, n, k);
1305
- }
1306
- catch {
1307
- return (0, shared_1.err)('DECRYPT_FAILED');
1308
- }
1309
- // HMAC verification BEFORE decrypt — CRITICAL
1310
- let hmacKey;
1311
- let hmacSig;
1312
- try {
1313
- hmacKey = (0, crypto_1.fromBase64)(usedShares[0].hmacKey);
1314
- hmacSig = (0, crypto_1.fromBase64)(usedShares[0].hmacSig);
1315
- }
1316
- catch {
1317
- return (0, shared_1.err)('DECRYPT_FAILED');
1318
- }
1319
- const hmacValid = await (0, crypto_1.verifyHMAC)(hmacKey, padded, hmacSig);
1320
- if (!hmacValid) {
1321
- this.lastDetail = 'HMAC verification failed before decrypt';
1322
- return (0, shared_1.err)('DECRYPT_FAILED');
1323
- }
1324
- // Unpad
1325
- const p = (0, crypto_1.nextOddPrime)(n);
1326
- const blockSize = p - 1;
1327
- const unpadResult = (0, crypto_1.pkcs7Unpad)(padded, blockSize);
1328
- if (!unpadResult.ok)
1329
- return (0, shared_1.err)('DECRYPT_FAILED');
1330
- // Xchange decrypt: extract K, IV, ciphertext from bundle
1331
- const decryptResult = await (0, xchange_1.xchangeDecrypt)(unpadResult.value);
1332
- if (!decryptResult.ok)
1333
- return (0, shared_1.err)('DECRYPT_FAILED:DECRYPTION');
1334
- let payload;
1335
- try {
1336
- payload = JSON.parse(new TextDecoder().decode(decryptResult.value));
1337
- }
1338
- catch {
1339
- return (0, shared_1.err)('DECRYPT_FAILED:PARSE');
1340
- }
1341
- // Mechanism 2: Protocol information available in return value
1342
- // Applications can display: "Secured with PRIVATE.ME/xBind/v3. Learn more: https://private.me/docs/xbind.html"
1343
- return (0, shared_1.ok)({ sender, payload, scope, timestamp });
1344
- }
1345
- /**
1346
- * Accumulate a share and attempt reconstruction when threshold is met.
1347
- */
1348
- async accumulateShare(share, sender, scope, timestamp) {
1349
- const existing = this.shareAccumulator.get(share.groupId) ?? [];
1350
- const isDuplicate = existing.some((s) => s.index === share.index);
1351
- if (!isDuplicate) {
1352
- existing.push(share);
1353
- this.shareAccumulator.set(share.groupId, existing);
1354
- }
1355
- if (existing.length < share.threshold) {
1356
- return (0, shared_1.ok)(null);
1357
- }
1358
- this.shareAccumulator.delete(share.groupId);
1359
- const result = await (0, split_channel_js_1.reconstructFromChannel)(existing);
1360
- if (!result.ok)
1361
- return (0, shared_1.err)('DECRYPT_FAILED');
1362
- let payload;
1363
- try {
1364
- payload = JSON.parse(new TextDecoder().decode(result.value));
1365
- }
1366
- catch {
1367
- return (0, shared_1.err)('DECRYPT_FAILED');
1368
- }
1369
- // Mechanism 2: Protocol information available in return value
1370
- // Applications can display: "Secured with PRIVATE.ME/xBind/v3. Learn more: https://private.me/docs/xbind.html"
1371
- return (0, shared_1.ok)({ sender, payload, scope, timestamp });
1372
- }
1373
- /**
1374
- * Common envelope verification: version, timestamp, nonce, DID, signature, scope.
1375
- *
1376
- * Used by receive(), receiveSigned(), and receiveRaw() to avoid duplication.
1377
- * Consumes the nonce on success (replay prevention).
1378
- */
1379
- async verifyEnvelope(envelope) {
1380
- // Runtime null guard: catch null/undefined despite TypeScript types
1381
- if (!envelope || typeof envelope !== 'object') {
1382
- this.lastDetail = 'envelope is null or not an object';
1383
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
1384
- }
1385
- if ((envelope.v !== 1 && envelope.v !== 2 && envelope.v !== 3 && envelope.v !== 4) || envelope.alg !== 'Ed25519') {
1386
- this.lastDetail = `v=${String(envelope.v)}, alg=${String(envelope.alg)}`;
1387
- return (0, shared_1.err)('VERIFICATION_FAILED:UNSUPPORTED_VERSION');
1388
- }
1389
- // SECURITY FIX (v1.1.3): Type guard for timestamp (prevent type confusion)
1390
- // Attack: Send timestamp: "soon" → Date.now() - "soon" = NaN → window check bypassed
1391
- if (typeof envelope.timestamp !== 'number' || !Number.isFinite(envelope.timestamp)) {
1392
- this.lastDetail = `timestamp=${String(envelope.timestamp)} (must be finite number)`;
1393
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
1394
- }
1395
- const age = Math.abs(Date.now() - envelope.timestamp);
1396
- if (age > this.timestampWindowMs) {
1397
- this.lastDetail = `age=${age}ms, max=${this.timestampWindowMs}ms`;
1398
- return (0, shared_1.err)('TIMESTAMP_EXPIRED');
1399
- }
1400
- const nonceOk = await this.nonceStore.check(envelope.nonce, envelope.sender);
1401
- if (!nonceOk) {
1402
- this.lastDetail = `nonce=${envelope.nonce}`;
1403
- return (0, shared_1.err)('REPLAY_DETECTED');
1404
- }
1405
- const senderKey = await this.registry.resolve(envelope.sender);
1406
- if (!senderKey.ok) {
1407
- this.lastDetail = `did=${envelope.sender}`;
1408
- return (0, shared_1.err)('VERIFICATION_FAILED:DID_NOT_IN_REGISTRY');
1409
- }
1410
- const pubKey = await (0, identity_js_1.importPublicKey)(senderKey.value);
1411
- if (!pubKey.ok) {
1412
- this.lastDetail = `did=${envelope.sender}`;
1413
- return (0, shared_1.err)('VERIFICATION_FAILED:KEY_IMPORT_FAILED');
1414
- }
1415
- // SECURITY FIX (v1.1.3): Guard for missing signature field (prevent TypeError crash)
1416
- // Attack: Send envelope without signature field → fromBase64(undefined) throws → DoS
1417
- if (!envelope.signature || typeof envelope.signature !== 'string') {
1418
- this.lastDetail = 'signature field missing or invalid';
1419
- return (0, shared_1.err)('VERIFICATION_FAILED:SIGNATURE_MISMATCH');
1420
- }
1421
- // SECURITY FIX (v1.1.3): Verify signature over canonical envelope representation
1422
- // (not just payload) to prevent replay attacks via nonce substitution
1423
- const sigBytes = (0, crypto_1.fromBase64)(envelope.signature);
1424
- // Create canonical representation for signature verification
1425
- const canonicalData = JSON.stringify({
1426
- v: envelope.v,
1427
- alg: envelope.alg,
1428
- sender: envelope.sender,
1429
- recipient: envelope.recipient,
1430
- timestamp: envelope.timestamp,
1431
- nonce: envelope.nonce,
1432
- scope: envelope.scope,
1433
- payload: envelope.payload,
1434
- });
1435
- const canonicalBytes = new TextEncoder().encode(canonicalData);
1436
- // Verify canonical signature (v1.1.3+)
1437
- // SECURITY: No legacy fallback. Pre-v1.1.3 envelopes use payload-only signatures
1438
- // that don't cover nonce, allowing unlimited replay attacks. All senders must upgrade.
1439
- const sigValid = await (0, identity_js_1.verify)(pubKey.value, sigBytes, canonicalBytes);
1440
- if (!sigValid.ok || !sigValid.value) {
1441
- this.lastDetail = 'signature does not match canonical envelope (v1.1.3+ required)';
1442
- return (0, shared_1.err)('VERIFICATION_FAILED:SIGNATURE_MISMATCH');
1443
- }
1444
- // V3: verify ML-DSA-65 post-quantum signature (both sigs must pass)
1445
- if (envelope.v === 3 && 'pqSignature' in envelope) {
1446
- // SECURITY FIX (v1.1.5): Guard for non-string pqSignature field
1447
- if (typeof envelope.pqSignature !== 'string') {
1448
- this.lastDetail = 'pqSignature field not a string';
1449
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
1450
- }
1451
- const senderEntry = await this.registry.getEntry(envelope.sender);
1452
- if (!senderEntry.ok || !senderEntry.value.mlDsaPublicKey) {
1453
- this.lastDetail = `did=${envelope.sender} missing ML-DSA public key`;
1454
- return (0, shared_1.err)('VERIFICATION_FAILED:PQ_KEY_MISSING');
1455
- }
1456
- const pqSigBytes = (0, crypto_1.fromBase64)(envelope.pqSignature);
1457
- // Verify ML-DSA-65 post-quantum signature (canonical only, v1.1.3+)
1458
- // SECURITY: No legacy fallback for same replay protection reasons as Ed25519
1459
- const pqValid = await (0, identity_js_2.verifyMlDsa65)(senderEntry.value.mlDsaPublicKey, pqSigBytes, canonicalBytes);
1460
- if (!pqValid.ok || !pqValid.value) {
1461
- this.lastDetail = 'ML-DSA-65 signature does not match canonical envelope (v1.1.3+ required)';
1462
- return (0, shared_1.err)('VERIFICATION_FAILED:PQ_SIGNATURE_MISMATCH');
1463
- }
1464
- }
1465
- const hasScope = await this.registry.hasScope(envelope.sender, envelope.scope);
1466
- if (!hasScope) {
1467
- this.lastDetail = `scope=${envelope.scope}`;
1468
- return (0, shared_1.err)('SCOPE_DENIED');
1469
- }
1470
- // SECURITY FIX (v1.1.5): Guard for non-string payload field
1471
- if (typeof envelope.payload !== 'string') {
1472
- this.lastDetail = 'payload field not a string';
1473
- return (0, shared_1.err)('VERIFICATION_FAILED:INVALID_ENVELOPE');
1474
- }
1475
- // Return decoded payload bytes for decryption
1476
- const payloadBytes = (0, crypto_1.fromBase64)(envelope.payload);
1477
- return (0, shared_1.ok)({ senderRawKey: senderKey.value, payloadBytes });
1478
- }
1479
- /**
1480
- * Verify and decrypt an envelope, returning raw text (no JSON parse).
1481
- * Used by receiveSplitShare to get the raw decrypted share data.
1482
- */
1483
- async receiveRaw(envelope) {
1484
- const verified = await this.verifyEnvelope(envelope);
1485
- if (!verified.ok)
1486
- return verified;
1487
- const { senderRawKey } = verified.value;
1488
- // V4 Xchange: use receiveXchangeShare() instead
1489
- if (envelope.v === 4) {
1490
- return (0, shared_1.err)('VERIFICATION_FAILED:UNSUPPORTED_VERSION');
1491
- }
1492
- let sharedKey;
1493
- // V2/V3 hybrid path
1494
- if ((envelope.v === 2 || envelope.v === 3) && 'kemCiphertext' in envelope) {
1495
- if (!this.identity.mlKemSecretKey) {
1496
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
1497
- }
1498
- // SECURITY FIX (v1.1.5): Guard for non-string fields
1499
- if (typeof envelope.ephemeralPub !== 'string' || typeof envelope.kemCiphertext !== 'string') {
1500
- return (0, shared_1.err)('DECRYPT_FAILED:INVALID_ENVELOPE');
1501
- }
1502
- const ephPubBytes = (0, crypto_1.fromBase64)(envelope.ephemeralPub);
1503
- const kemCtBytes = (0, crypto_1.fromBase64)(envelope.kemCiphertext);
1504
- const hybridKey = await (0, key_agreement_js_1.receiverHybridKeyAgreement)(this.identity.x25519PrivateKey, ephPubBytes, kemCtBytes, this.identity.mlKemSecretKey);
1505
- if (!hybridKey.ok)
1506
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
1507
- sharedKey = hybridKey.value;
1508
- }
1509
- else if (envelope.ephemeralPub) {
1510
- // SECURITY FIX (v1.1.5): Guard for non-string field
1511
- if (typeof envelope.ephemeralPub !== 'string') {
1512
- return (0, shared_1.err)('DECRYPT_FAILED:INVALID_ENVELOPE');
1513
- }
1514
- const ephPubBytes = (0, crypto_1.fromBase64)(envelope.ephemeralPub);
1515
- const ecdhKey = await (0, key_agreement_js_1.receiverKeyAgreement)(this.identity.x25519PrivateKey, ephPubBytes);
1516
- if (!ecdhKey.ok)
1517
- return (0, shared_1.err)('DECRYPT_FAILED:KEY_AGREEMENT');
1518
- sharedKey = ecdhKey.value;
1519
- }
1520
- else {
1521
- // SECURITY FIX (v1.1.6): Removed insecure fallback
1522
- // Envelopes without ephemeralPub rejected (sender must use X25519 ECDH)
1523
- return (0, shared_1.err)('DECRYPT_FAILED:NO_EPHEMERAL_KEY');
1524
- }
1525
- const decrypted = await (0, envelope_js_1.decryptPayload)(envelope, sharedKey);
1526
- if (!decrypted.ok)
1527
- return (0, shared_1.err)('DECRYPT_FAILED:DECRYPTION');
1528
- const decryptedText = new TextDecoder().decode(decrypted.value);
1529
- return (0, shared_1.ok)({
1530
- sender: envelope.sender,
1531
- decryptedText,
1532
- scope: envelope.scope,
1533
- timestamp: envelope.timestamp,
1534
- });
1535
- }
1536
- /**
1537
- * Send email invitation to establish connection.
1538
- *
1539
- * Uses email transport to send branded invitation with one-click acceptance.
1540
- * Requires EmailTransport to be configured in agent options.
1541
- *
1542
- * @param opts - Invitation options
1543
- * @returns Result with success status
1544
- *
1545
- * @example
1546
- * ```ts
1547
- * await agent.invite({
1548
- * to: 'fulfillment@acme.com',
1549
- * message: 'Connect our payment systems'
1550
- * });
1551
- * ```
1552
- */
1553
- async invite(opts) {
1554
- if (!this.transports || this.transports.length === 0) {
1555
- return (0, shared_1.err)('SEND_FAILED');
1556
- }
1557
- // Convert raw public key to base64 for transmission
1558
- const publicKeyBase64 = Buffer.from(this.identity.rawPublicKey).toString('base64');
1559
- // Create invitation envelope
1560
- const envelope = {
1561
- from: this.identity.did,
1562
- to: opts.to,
1563
- payload: {
1564
- agentName: this.name,
1565
- message: opts.message,
1566
- publicKey: publicKeyBase64,
1567
- endpoint: '', // Email-based invites don't need endpoint
1568
- },
1569
- };
1570
- // Send via first transport (assumed to be EmailTransport for invite())
1571
- const transport = this.transports[0];
1572
- if (!transport) {
1573
- return (0, shared_1.err)('SEND_FAILED');
1574
- }
1575
- const result = await transport.send(envelope, opts.to);
1576
- if (!result.ok) {
1577
- return (0, shared_1.err)('SEND_FAILED');
1578
- }
1579
- return (0, shared_1.ok)(undefined);
1580
- }
1581
- }
1582
- exports.Agent = Agent;
1
+ "use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,i)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(){var e=function(t){return e=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r);return t},e(t)};return function(t){if(t&&t.__esModule)return t;var r={};if(null!=t)for(var s=e(t),i=0;i<s.length;i++)"default"!==s[i]&&__createBinding(r,t,s[i]);return __setModuleDefault(r,t),r}}();Object.defineProperty(exports,"__esModule",{value:!0}),exports.generateSharedKey=exports.Agent=void 0,exports.parseAgentError=parseAgentError;const shared_1=require("../_deps/shared/index.js"),crypto_utils_js_1=require("./crypto-utils.js"),vault_store_loader_js_1=require("./vault-store-loader.js"),errors_js_1=require("./errors.js"),identity_js_1=require("./identity.js"),envelope_js_1=require("./envelope.js");Object.defineProperty(exports,"generateSharedKey",{enumerable:!0,get:function(){return envelope_js_1.generateSharedKey}});const xchange_1=require("../_deps/xchange/index.js"),identity_js_2=require("./identity.js"),key_agreement_js_1=require("./key-agreement.js"),split_channel_js_1=require("./split-channel.js"),nonce_store_js_1=require("./nonce-store.js"),transport_js_1=require("./transport.js"),trust_registry_js_1=require("./trust-registry.js"),ux_helpers_1=require("../_deps/ux-helpers/index.js"),security_policy_js_1=require("./security-policy.js"),backup_config_js_1=require("./backup-config.js"),DEFAULT_RELAY_URL=process.env.XBIND_RELAY_URL||"https://private.me/relay",DEFAULT_REGISTRY_URL=process.env.XBIND_REGISTRY_URL||"https://private.me/registry";function parseAgentError(e){const t=e.split(":");return 1===t.length?{code:t[0]??e}:{code:t[0]??e,subCode:t.slice(1).join(":")}}const TIMESTAMP_WINDOW_MS=3e4;function toArrayBuffer(e){const t=new ArrayBuffer(e.byteLength);return new Uint8Array(t).set(e),t}function compareBytes(e,t){const r=Math.min(e.length,t.length);for(let s=0;s<r;s++){const r=e[s]??0,i=t[s]??0;if(r!==i)return r-i}return e.length-t.length}function concatBytes(e,t){const r=new Uint8Array(e.length+t.length);return r.set(e),r.set(t,e.length),r}class Agent{identity;name;registry;transports;nonceStore;timestampWindowMs;securityPolicy;backupConfig;shareAccumulator=new Map;lastDetail="";lastSecurityDecision;cleanupTimer;cryptoModule=null;get lastErrorDetail(){return this.lastDetail}get lastSecurity(){return this.lastSecurityDecision}constructor(e,t,r,s,i,a,n,o){this.identity=e,this.name=t,this.registry=r,this.transports=s,this.nonceStore=i,this.timestampWindowMs=a,this.securityPolicy=n??new security_policy_js_1.DefaultSecurityPolicy,this.backupConfig=o??backup_config_js_1.DEFAULT_BACKUP_CONFIG}get did(){return this.identity.did}getTransports(){return this.transports}async ensureCrypto(){if(this.cryptoModule)return this.cryptoModule;const e=(0,vault_store_loader_js_1.getCrypto)();if(e)return this.cryptoModule=e,e;const t=await(0,vault_store_loader_js_1.loadCryptoPackage)(this.identity);if(!t.ok){if("VAULT_QUOTA_EXCEEDED"===t.error){const e="https://private.me/subscribe?product=xbind&tier=pro";throw new errors_js_1.QuotaExceededError(`Monthly usage quota exceeded (Free tier: 100K operations/month). Upgrade to Pro tier for unlimited access at $5 per 100K operations. Visit: ${e}`,e)}throw new errors_js_1.VaultStoreError(t.error,`Failed to load crypto package: ${t.error}`)}return this.cryptoModule=t.value,t.value}static isSupported(){try{return void 0!==globalThis.crypto&&void 0!==globalThis.crypto.subtle&&"function"==typeof globalThis.crypto.subtle.generateKey&&"function"==typeof globalThis.crypto.subtle.sign&&"function"==typeof globalThis.crypto.subtle.verify&&"function"==typeof globalThis.crypto.subtle.encrypt&&"function"==typeof globalThis.crypto.getRandomValues}catch{return!1}}static async fromIdentity(e,t){const r=t.nonceStore??new nonce_store_js_1.MemoryNonceStore,s=t.timestampWindowMs??3e4,i=Array.isArray(t.transport)?t.transport:[t.transport],a=new Agent(e,t.name??e.did,t.registry,i,r,s,t.securityPolicy,t.backupConfig);return(0,shared_1.ok)(a)}static fromParts(e,t,r,s){const i=Array.isArray(r)?r:[r];return new Agent(e,s?.name??e.did,t,i,s?.nonceStore??new nonce_store_js_1.MemoryNonceStore,s?.timestampWindowMs??3e4,s?.securityPolicy,s?.backupConfig)}static async fromSeed(e,t){const r=await(0,identity_js_1.identityFromSeed)(e,{postQuantumSig:t.postQuantumSig});if(!r.ok)return(0,shared_1.err)("IDENTITY_FAILED:KEYGEN");const s=await t.registry.register(r.value.did,r.value.rawPublicKey,t.name??r.value.did,t.scopes,r.value.rawX25519PublicKey,r.value.mlKemPublicKey,r.value.mlDsaPublicKey,t.xchange??!1);if(!s.ok&&"ALREADY_REGISTERED"!==s.error){const e="NETWORK_ERROR"===s.error?"REGISTRATION_FAILED:NETWORK_ERROR":"REGISTRATION_FAILED";return(0,shared_1.err)(e)}const i=t.nonceStore??new nonce_store_js_1.MemoryNonceStore,a=t.timestampWindowMs??3e4,n=Array.isArray(t.transport)?t.transport:[t.transport];return(0,shared_1.ok)(new Agent(r.value,t.name??r.value.did,t.registry,n,i,a,t.securityPolicy,t.backupConfig))}static async lazy(e){const{createLazyAgent:t}=await Promise.resolve().then(()=>__importStar(require("./lazy-init.js")));return t(e)}isReady(){return void 0!==this.identity&&void 0!==this.registry&&this.transports.length>0}static async create(e){const t=await(0,identity_js_1.generateIdentity)({postQuantumSig:e.postQuantumSig});if(!t.ok)return(0,shared_1.err)("IDENTITY_FAILED:KEYGEN");const r=await e.registry.register(t.value.did,t.value.rawPublicKey,e.name,e.scopes,t.value.rawX25519PublicKey,t.value.mlKemPublicKey,t.value.mlDsaPublicKey,e.xchange??!1);if(!r.ok){const e="ALREADY_REGISTERED"===r.error?"REGISTRATION_FAILED:ALREADY_REGISTERED":"NETWORK_ERROR"===r.error?"REGISTRATION_FAILED:NETWORK_ERROR":"REGISTRATION_FAILED";return(0,shared_1.err)(e)}const s=e.nonceStore??new nonce_store_js_1.MemoryNonceStore,i=e.timestampWindowMs??3e4,a=Array.isArray(e.transport)?e.transport:[e.transport],n=new Agent(t.value,e.name,e.registry,a,s,i,e.securityPolicy,e.backupConfig);try{await n.ensureCrypto()}catch(e){return e instanceof errors_js_1.QuotaExceededError?(0,shared_1.err)("QUOTA_EXCEEDED"):e instanceof errors_js_1.VaultStoreError?(0,shared_1.err)("IDENTITY_FAILED:VAULT_STORE"):(0,shared_1.err)("IDENTITY_FAILED")}return(0,shared_1.ok)(n)}static async quickstart(e){const t=new trust_registry_js_1.MemoryTrustRegistry,r=new transport_js_1.HttpsTransportAdapter({baseUrl:DEFAULT_RELAY_URL}),s=await(0,identity_js_1.generateIdentity)({postQuantumSig:!1});if(!s.ok)throw new Error("Failed to generate ephemeral identity");const i=e?.name??`agent-${Date.now()}`,a=await t.register(s.value.did,s.value.rawPublicKey,i,void 0,s.value.rawX25519PublicKey,s.value.mlKemPublicKey,s.value.mlDsaPublicKey,!1);if(!a.ok)throw new Error(`Failed to register ephemeral identity: ${a.error}`);const n=new Agent(s.value,i,t,[r],new nonce_store_js_1.MemoryNonceStore,3e4,void 0,void 0);return n.cleanupTimer=setTimeout(async()=>{await t.revoke(s.value.did)},36e5),n}static async from(e={}){const t=e.identity??"persistent",r=e.identityTTL??36e5,s="ephemeral"===t,i="string"==typeof e.registry?new trust_registry_js_1.HttpTrustRegistry({baseUrl:e.registry}):e.registry??(s?new trust_registry_js_1.MemoryTrustRegistry:new trust_registry_js_1.HttpTrustRegistry({baseUrl:DEFAULT_REGISTRY_URL})),a=e.transport?Array.isArray(e.transport)?e.transport:[e.transport]:[new transport_js_1.HttpsTransportAdapter({baseUrl:DEFAULT_RELAY_URL})],n=await(0,identity_js_1.generateIdentity)({postQuantumSig:e.postQuantumSig??!1});if(!n.ok)throw new Error("Failed to generate identity");const o=s?`ephemeral-agent-${Date.now()}`:`agent-${Date.now()}`,c=await i.register(n.value.did,n.value.rawPublicKey,o,void 0,n.value.rawX25519PublicKey,n.value.mlKemPublicKey,n.value.mlDsaPublicKey,!1);if(!c.ok)throw new Error(`Failed to register identity: ${c.error}`);const l=new Agent(n.value,o,i,a,new nonce_store_js_1.MemoryNonceStore,3e4,e.securityPolicy,e.backupConfig);return s&&(l.cleanupTimer=setTimeout(async()=>{await i.revoke(n.value.did)},r)),l}async send(e){const t=new ux_helpers_1.ProgressReporter(e.onProgress);t.start("Resolving recipient identity...");const r=await this.registry.resolve(e.to);if(!r.ok)return(0,shared_1.err)("REVOKED"===r.error?"RECIPIENT_REVOKED":"RECIPIENT_NOT_FOUND");t.update("Checking recipient authorization...",10);if(!await this.registry.hasReceiveScope(e.to,e.scope))return this.lastDetail=`recipient=${e.to}, scope=${e.scope}`,(0,shared_1.err)("RECEIVER_SCOPE_DENIED");t.update("Preparing message...",15);const s=(new TextEncoder).encode(JSON.stringify(e.payload));t.update("Determining security level...",20);const i=this.securityPolicy.classify({action:e.action??"send",params:"object"==typeof e.payload&&null!==e.payload?e.payload:{},sender:this.did,recipient:e.to,scope:e.scope,securityOverride:e.security});this.lastSecurityDecision=i,t.update(`Security: ${(0,security_policy_js_1.describeSecurityMode)(i.mode)} — ${i.reason}`,25);const a=void 0!==e.splitChannel?e.splitChannel:"split"===i.mode.type;if(a&&(e.xchange||"xchange"===i.mode.type)){t.update("Checking Xchange support...",20);if(await this.canUseXchange(e.to))return this.sendXchange(e,s,t)}t.update("Establishing key agreement...",30);const n=await this.trySenderECDH(e.to);return n?a?this.sendSplitChannel(e,s,n.sharedKey,n.ephemeralPublicKey,n.kemCiphertext,n.recipientHasMlDsa,t):n.kemCiphertext&&n.recipientHasMlDsa&&this.identity.mlDsaSecretKey?this.sendWithHybridV3(e,s,n.sharedKey,n.ephemeralPublicKey,n.kemCiphertext,t):n.kemCiphertext?this.sendWithHybrid(e,s,n.sharedKey,n.ephemeralPublicKey,n.kemCiphertext,t):this.sendWithECDH(e,s,n.sharedKey,n.ephemeralPublicKey,t):(0,shared_1.err)("KEY_AGREEMENT_FAILED:RECIPIENT_HAS_NO_X25519_KEY")}async receive(e,t){this.lastDetail="";const r=new ux_helpers_1.ProgressReporter(t?.onProgress);r.start("Verifying envelope signature...");const s=await this.verifyEnvelope(e);if(!s.ok)return s;const{senderRawKey:i,payloadBytes:a}=s.value;let n;if(4===e.v)return(0,shared_1.err)("VERIFICATION_FAILED:UNSUPPORTED_VERSION");if(r.update("Deriving shared key...",30),2!==e.v&&3!==e.v||!("kemCiphertext"in e)){if(!e.ephemeralPub){if(t?.allowCleartext){let t;try{t=JSON.parse((new TextDecoder).decode(a))}catch{return(0,shared_1.err)("DECRYPT_FAILED:PARSE")}return r.complete(),(0,shared_1.ok)({sender:e.sender,payload:t,scope:e.scope,timestamp:e.timestamp})}return(0,shared_1.err)("DECRYPT_FAILED:NO_EPHEMERAL_KEY")}{if("string"!=typeof e.ephemeralPub)return this.lastDetail="ephemeralPub not string",(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");const t=(0,crypto_utils_js_1.fromBase64)(e.ephemeralPub),s=await(0,key_agreement_js_1.receiverKeyAgreement)(this.identity.x25519PrivateKey,t);if(s.ok)n=s.value;else{if(this.identity.rotatedKeys&&this.identity.rotatedKeys.length>0)for(const e of this.identity.rotatedKeys){const s=await(0,key_agreement_js_1.receiverKeyAgreement)(e.x25519PrivateKey,t);if(s.ok){n=s.value,r.update("Decrypting with rotated keys...",45);break}}if(!n)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT")}}}else{if(!this.identity.mlKemSecretKey)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT");if("string"!=typeof e.ephemeralPub||"string"!=typeof e.kemCiphertext)return this.lastDetail="ephemeralPub or kemCiphertext not string",(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");const t=(0,crypto_utils_js_1.fromBase64)(e.ephemeralPub),s=(0,crypto_utils_js_1.fromBase64)(e.kemCiphertext);if(!this.identity.mlKemPublicKey||!this.identity.mlKemSecretKey)return this.lastDetail="ML-KEM keys not available in identity",(0,shared_1.err)("DECRYPT_FAILED:MISSING_MLKEM_KEYS");const i=await(0,key_agreement_js_1.receiverHybridKeyAgreement)(this.identity.x25519PrivateKey,this.identity.rawX25519PublicKey,t,s,this.identity.mlKemSecretKey,this.identity.mlKemPublicKey);if(i.ok)n=i.value;else{if(this.identity.rotatedKeys&&this.identity.rotatedKeys.length>0)for(const e of this.identity.rotatedKeys){if(!e.mlKemSecretKey)continue;const i=await(0,key_agreement_js_1.receiverHybridKeyAgreement)(e.x25519PrivateKey,this.identity.rawX25519PublicKey,t,s,e.mlKemSecretKey,this.identity.mlKemPublicKey);if(i.ok){n=i.value,r.update("Decrypting with rotated keys...",45);break}}if(!n)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT")}}if(r.update("Decrypting payload...",60),!n)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT");const o=await(0,envelope_js_1.decryptPayload)(e,n);if(!o.ok)return(0,shared_1.err)("DECRYPT_FAILED:DECRYPTION");let c;r.update("Parsing message...",90);try{c=JSON.parse((new TextDecoder).decode(o.value))}catch{return(0,shared_1.err)("DECRYPT_FAILED:PARSE")}return r.complete(),(0,shared_1.ok)({sender:e.sender,payload:c,scope:e.scope,timestamp:e.timestamp,metadata:e.protocol&&e.documentationUrl?{protocol:e.protocol,documentationUrl:e.documentationUrl}:void 0})}async verifySignature(e){const t=await this.registry.resolve(e.sender);if(!t.ok)return(0,shared_1.err)("VERIFICATION_FAILED:DID_NOT_IN_REGISTRY");const r=await(0,identity_js_1.importPublicKey)(t.value);if(!r.ok)return(0,shared_1.err)("VERIFICATION_FAILED:KEY_IMPORT_FAILED");const s=(0,crypto_utils_js_1.fromBase64)(e.signature),i=JSON.stringify({v:e.v,alg:e.alg,sender:e.sender,recipient:e.recipient,timestamp:e.timestamp,nonce:e.nonce,scope:e.scope,payload:e.payload}),a=(new TextEncoder).encode(i),n=await(0,identity_js_1.verify)(r.value,s,a);return n.ok?(0,shared_1.ok)({sender:e.sender,valid:n.value}):(0,shared_1.err)("VERIFICATION_FAILED:SIGNATURE_MISMATCH")}async exportSeeds(){const e=await(0,identity_js_1.exportPKCS8)(this.identity.privateKey);if(!e.ok)return(0,shared_1.err)("IDENTITY_FAILED");const t=await(0,identity_js_1.exportX25519PKCS8)(this.identity.x25519PrivateKey);if(!t.ok)return(0,shared_1.err)("IDENTITY_FAILED");const r=(0,identity_js_1.extractRawEd25519)(e.value);if(!r.ok)return(0,shared_1.err)("IDENTITY_FAILED");const s=(0,identity_js_1.extractRawX25519)(t.value);return s.ok?(0,shared_1.ok)({ed25519:r.value,x25519:s.value,mlKemSecretKey:this.identity.mlKemSecretKey,mlKemPublicKey:this.identity.mlKemPublicKey}):(0,shared_1.err)("IDENTITY_FAILED")}async splitKey(e){const{splitKeyWithBackup:t}=await Promise.resolve().then(()=>__importStar(require("./backup-config.js"))),r=await t(e,this.backupConfig);return r.ok?r:(0,shared_1.err)("ENVELOPE_FAILED:SPLIT")}async reconstructKey(e){const{reconstructKeyFromBackup:t}=await Promise.resolve().then(()=>__importStar(require("./backup-config.js"))),r=await t(e);return r.ok?r:(0,shared_1.err)("DECRYPT_FAILED")}async receiveSigned(e){this.lastDetail="";const t=await this.verifyEnvelope(e);if(!t.ok)return t;let r;try{r=JSON.parse((new TextDecoder).decode(t.value.payloadBytes))}catch{return(0,shared_1.err)("DECRYPT_FAILED:PARSE")}return(0,shared_1.ok)({sender:e.sender,payload:r,scope:e.scope,timestamp:e.timestamp,metadata:e.protocol&&e.documentationUrl?{protocol:e.protocol,documentationUrl:e.documentationUrl}:void 0})}async discover(e){const{getToolRegistry:t}=await Promise.resolve().then(()=>__importStar(require("./agent-call.js"))),r=t();if(!r)return[];if(!e)return r.listAll();return r.search(e)}middleware(){return async(e,t,r)=>{const s=(0,envelope_js_1.validateEnvelope)(e.body);if(!s.ok)return void t.status(400).json({error:s.error});const i=await this.receive(s.value);if(!i.ok){const e="TIMESTAMP_EXPIRED"===i.error||"REPLAY_DETECTED"===i.error?403:401;return void t.status(e).json({error:i.error})}e.agentMessage=i.value,r()}}cleanup(){this.cleanupTimer&&(clearTimeout(this.cleanupTimer),this.cleanupTimer=void 0)}dispose(){this.cleanup()}async trySenderECDH(e){const t=await this.registry.getEntry(e);if(!t.ok||!t.value.x25519PublicKey)return null;const r=!!t.value.mlDsaPublicKey,s=await(0,key_agreement_js_1.importX25519PublicKey)(t.value.x25519PublicKey);if(!s.ok)return null;if(t.value.mlKemPublicKey&&this.identity.mlKemSecretKey){const e=await(0,key_agreement_js_1.senderHybridKeyAgreement)(s.value,t.value.mlKemPublicKey);if(e.ok)return{sharedKey:e.value.sharedKey,ephemeralPublicKey:e.value.ephemeralPublicKey,kemCiphertext:e.value.kemCiphertext,recipientHasMlDsa:r}}const i=await(0,key_agreement_js_1.senderKeyAgreement)(s.value);return i.ok?{...i.value,recipientHasMlDsa:r}:null}async sendWithECDH(e,t,r,s,i){i?.update("Encrypting message with ECDH...",60);const a=await(0,envelope_js_1.createEnvelope)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:t,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s});if(!a.ok)return(0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT");i?.update("Sending message...",90);const n=await this.transports[0].send(a.value,e.to);return n.ok&&i?.complete(),n}async sendWithHybrid(e,t,r,s,i,a){a?.update("Encrypting message with hybrid KEM...",60);const n=await(0,envelope_js_1.createEnvelopeV2)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:t,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s,kemCiphertext:i});if(!n.ok)return(0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT");a?.update("Sending message...",90);const o=await this.transports[0].send(n.value,e.to);return o.ok&&a?.complete(),o}async sendWithHybridV3(e,t,r,s,i,a){if(!this.identity.mlDsaSecretKey)return(0,shared_1.err)("ENVELOPE_FAILED:PQ_KEY_MISSING");a?.update("Encrypting with post-quantum signatures...",60);const n=await(0,envelope_js_1.createEnvelopeV3)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:t,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s,kemCiphertext:i,mlDsaSecretKey:this.identity.mlDsaSecretKey});if(!n.ok)return this.lastDetail=`v3 envelope error: ${n.error}`,(0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT");a?.update("Sending message...",90);const o=await this.transports[0].send(n.value,e.to);return o.ok&&a?.complete(),o}async sendDirect(e,t,r,s){s?.update("Encrypting message...",60);const i=await(0,envelope_js_1.createEnvelope)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:t,privateKey:this.identity.privateKey,sharedKey:r});if(!i.ok)return(0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT");s?.update("Sending message...",90);const a=await this.transports[0].send(i.value,e.to);return a.ok&&s?.complete(),a}async canUseXchange(e){const t=await this.registry.getEntry(e);return!!t.ok&&!0===t.value.xchange}async sendXchange(e,t,r){const s=e.splitChannelConfig??split_channel_js_1.DEFAULT_SPLIT_CONFIG;this.transports.length<s.totalShares&&console.warn(`Split-channel: ${s.totalShares} shares but only ${this.transports.length} transport(s). For channel separation, provide at least ${s.totalShares} transports.`),r?.update("Generating Xchange key...",40);const i=await(0,xchange_1.generateXchangeKey)();if(!i.ok)return(0,shared_1.err)("KEY_AGREEMENT_FAILED");r?.update("Encrypting message...",50);const a=await(0,xchange_1.xchangeEncrypt)(t,i.value);if(!a.ok)return(0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT");const n=await this.ensureCrypto(),o=s.totalShares,c=s.threshold,l=n.nextOddPrime(o)-1,d=n.pkcs7Pad(a.value,l),{key:u,signature:h}=await n.generateHMAC(d);let y;r?.update("Splitting message into shares...",60);try{y=n.splitXorIDA(d,o,c)}catch{return(0,shared_1.err)("ENVELOPE_FAILED:SPLIT")}const _=(0,crypto_utils_js_1.toBase64)(u),p=(0,crypto_utils_js_1.toBase64)(h),E=(0,crypto_utils_js_1.generateUUID)(),m=[];r?.update("Sending shares...",70);for(let t=0;t<y.length;t++){const s=y[t],i=(0,crypto_utils_js_1.formatShareHeader)((0,crypto_utils_js_1.toBase64)(s)),a=(new TextEncoder).encode(i),n=await(0,envelope_js_1.createEnvelopeV4)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,shareData:a,privateKey:this.identity.privateKey,shareIndex:t,shareTotal:o,shareThreshold:c,shareGroupId:E,shareHmacKey:_,shareHmacSig:p});if(!n.ok){m.push((0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT"));continue}const l=this.transports[t%this.transports.length],d=await l.send(n.value,e.to);m.push(d);const u=70+Math.floor((t+1)/y.length*20);r?.update(`Sent share ${t+1}/${y.length}...`,u)}return m.filter(e=>e.ok).length<c?(0,shared_1.err)("SEND_FAILED:BELOW_THRESHOLD"):(r?.complete(),(0,shared_1.ok)(void 0))}async sendSplitChannel(e,t,r,s,i,a,n){const o=e.splitChannelConfig??split_channel_js_1.DEFAULT_SPLIT_CONFIG;this.transports.length<o.totalShares&&console.warn(`Split-channel: ${o.totalShares} shares but only ${this.transports.length} transport(s). For channel separation, provide at least ${o.totalShares} transports.`),n?.update("Splitting message into shares...",50);const c=await(0,split_channel_js_1.splitForChannel)(t,o);if(!c.ok)return(0,shared_1.err)("ENVELOPE_FAILED:SPLIT");const l=c.value;n?.update("Encrypting and sending shares...",70);return(await this.sendShareEnvelopes(e,l,r,s,i,a,n)).filter(e=>e.ok).length<o.threshold?(0,shared_1.err)("SEND_FAILED:BELOW_THRESHOLD"):(n?.complete(),(0,shared_1.ok)(void 0))}async sendShareEnvelopes(e,t,r,s,i,a,n){const o=[];for(let c=0;c<t.length;c++){const l=t[c],d=(new TextEncoder).encode(l.data);let u;if(u=i&&s&&a&&this.identity.mlDsaSecretKey?await(0,envelope_js_1.createEnvelopeV3)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:d,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s,kemCiphertext:i,mlDsaSecretKey:this.identity.mlDsaSecretKey,shareIndex:l.index,shareTotal:l.total,shareThreshold:l.threshold,shareGroupId:l.groupId,shareHmacKey:l.hmacKey,shareHmacSig:l.hmacSig}):i&&s?await(0,envelope_js_1.createEnvelopeV2)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:d,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s,kemCiphertext:i,shareIndex:l.index,shareTotal:l.total,shareThreshold:l.threshold,shareGroupId:l.groupId,shareHmacKey:l.hmacKey,shareHmacSig:l.hmacSig}):await(0,envelope_js_1.createEnvelope)({senderDid:this.identity.did,recipientDid:e.to,scope:e.scope,plaintext:d,privateKey:this.identity.privateKey,sharedKey:r,ephemeralPublicKey:s,shareIndex:l.index,shareTotal:l.total,shareThreshold:l.threshold,shareGroupId:l.groupId,shareHmacKey:l.hmacKey,shareHmacSig:l.hmacSig}),!u.ok){o.push((0,shared_1.err)("ENVELOPE_FAILED:ENCRYPT"));continue}const h=this.transports[c%this.transports.length],y=await h.send(u.value,e.to);o.push(y);const _=70+Math.floor((c+1)/t.length*20);n?.update(`Sent share ${c+1}/${t.length}...`,_)}return o}async receiveSplitShare(e){if(void 0===e.shareGroupId)return(0,shared_1.err)("VERIFICATION_FAILED");const t=await this.receiveRaw(e);if(!t.ok)return t;const{sender:r,decryptedText:s,scope:i,timestamp:a}=t.value,n={data:s,index:e.shareIndex??0,total:e.shareTotal??2,threshold:e.shareThreshold??2,groupId:e.shareGroupId,hmacKey:e.shareHmacKey??"",hmacSig:e.shareHmacSig??""};return this.accumulateShare(n,r,i,a)}async receiveXchangeShare(e){this.lastDetail="";const t=await this.verifyEnvelope(e);if(!t.ok)return t;const r={data:(new TextDecoder).decode(t.value.payloadBytes),index:e.shareIndex,total:e.shareTotal,threshold:e.shareThreshold,groupId:e.shareGroupId,hmacKey:e.shareHmacKey,hmacSig:e.shareHmacSig};return this.accumulateXchangeShare(r,e.sender,e.scope,e.timestamp)}async accumulateXchangeShare(e,t,r,s){const i=this.shareAccumulator.get(e.groupId)??[];if(i.some(t=>t.index===e.index)||(i.push(e),this.shareAccumulator.set(e.groupId,i)),i.length<e.threshold)return(0,shared_1.ok)(null);this.shareAccumulator.delete(e.groupId);const a=i.slice(0,e.threshold),n=e.total,o=e.threshold;let c;try{c=a.map(e=>(0,crypto_utils_js_1.fromBase64)((0,crypto_utils_js_1.parseShareHeader)(e.data)))}catch{return(0,shared_1.err)("DECRYPT_FAILED")}const l=a.map(e=>e.index),d=await this.ensureCrypto();let u,h,y;try{u=d.reconstructXorIDA(c,l,n,o)}catch{return(0,shared_1.err)("DECRYPT_FAILED")}try{h=(0,crypto_utils_js_1.fromBase64)(a[0].hmacKey),y=(0,crypto_utils_js_1.fromBase64)(a[0].hmacSig)}catch{return(0,shared_1.err)("DECRYPT_FAILED")}if(!await d.verifyHMAC(h,u,y))return this.lastDetail="HMAC verification failed before decrypt",(0,shared_1.err)("DECRYPT_FAILED");const _=d.nextOddPrime(n)-1,p=d.pkcs7Unpad(u,_);if(!p.ok)return(0,shared_1.err)("DECRYPT_FAILED");const E=await(0,xchange_1.xchangeDecrypt)(p.value);if(!E.ok)return(0,shared_1.err)("DECRYPT_FAILED:DECRYPTION");let m;try{m=JSON.parse((new TextDecoder).decode(E.value))}catch{return(0,shared_1.err)("DECRYPT_FAILED:PARSE")}return(0,shared_1.ok)({sender:t,payload:m,scope:r,timestamp:s})}async accumulateShare(e,t,r,s){const i=this.shareAccumulator.get(e.groupId)??[];if(i.some(t=>t.index===e.index)||(i.push(e),this.shareAccumulator.set(e.groupId,i)),i.length<e.threshold)return(0,shared_1.ok)(null);this.shareAccumulator.delete(e.groupId);const a=await(0,split_channel_js_1.reconstructFromChannel)(i);if(!a.ok)return(0,shared_1.err)("DECRYPT_FAILED");let n;try{n=JSON.parse((new TextDecoder).decode(a.value))}catch{return(0,shared_1.err)("DECRYPT_FAILED")}return(0,shared_1.ok)({sender:t,payload:n,scope:r,timestamp:s})}async verifyEnvelope(e){if(!e||"object"!=typeof e)return this.lastDetail="envelope is null or not an object",(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");if(1!==e.v&&2!==e.v&&3!==e.v&&4!==e.v||"Ed25519"!==e.alg)return this.lastDetail=`v=${String(e.v)}, alg=${String(e.alg)}`,(0,shared_1.err)("VERIFICATION_FAILED:UNSUPPORTED_VERSION");if("number"!=typeof e.timestamp||!Number.isFinite(e.timestamp))return this.lastDetail=`timestamp=${String(e.timestamp)} (must be finite number)`,(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");const t=Math.abs(Date.now()-e.timestamp);if(t>this.timestampWindowMs)return this.lastDetail=`age=${t}ms, max=${this.timestampWindowMs}ms`,(0,shared_1.err)("TIMESTAMP_EXPIRED");const r=void 0!==e.shareGroupId?{shareGroupId:e.shareGroupId,shareIndex:e.shareIndex}:void 0;if(!await this.nonceStore.check(e.nonce,e.sender,r))return this.lastDetail=`nonce=${e.nonce}`,(0,shared_1.err)("REPLAY_DETECTED");const s=await this.registry.resolve(e.sender);if(!s.ok)return this.lastDetail=`did=${e.sender}`,(0,shared_1.err)("VERIFICATION_FAILED:DID_NOT_IN_REGISTRY");const i=await(0,identity_js_1.importPublicKey)(s.value);if(!i.ok)return this.lastDetail=`did=${e.sender}`,(0,shared_1.err)("VERIFICATION_FAILED:KEY_IMPORT_FAILED");if(!e.signature||"string"!=typeof e.signature)return this.lastDetail="signature field missing or invalid",(0,shared_1.err)("VERIFICATION_FAILED:SIGNATURE_MISMATCH");const a=(0,crypto_utils_js_1.fromBase64)(e.signature),n=JSON.stringify({v:e.v,alg:e.alg,sender:e.sender,recipient:e.recipient,timestamp:e.timestamp,nonce:e.nonce,scope:e.scope,payload:e.payload}),o=(new TextEncoder).encode(n),c=await(0,identity_js_1.verify)(i.value,a,o);if(!c.ok||!c.value)return this.lastDetail="signature does not match canonical envelope (v1.1.3+ required)",(0,shared_1.err)("VERIFICATION_FAILED:SIGNATURE_MISMATCH");if(3===e.v&&"pqSignature"in e){if("string"!=typeof e.pqSignature)return this.lastDetail="pqSignature field not a string",(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");const t=await this.registry.getEntry(e.sender);if(!t.ok||!t.value.mlDsaPublicKey)return this.lastDetail=`did=${e.sender} missing ML-DSA public key`,(0,shared_1.err)("VERIFICATION_FAILED:PQ_KEY_MISSING");const r=(0,crypto_utils_js_1.fromBase64)(e.pqSignature),s=await(0,identity_js_2.verifyMlDsa65)(t.value.mlDsaPublicKey,r,o);if(!s.ok||!s.value)return this.lastDetail="ML-DSA-65 signature does not match canonical envelope (v1.1.3+ required)",(0,shared_1.err)("VERIFICATION_FAILED:PQ_SIGNATURE_MISMATCH")}if(!await this.registry.hasScope(e.sender,e.scope))return this.lastDetail=`scope=${e.scope}`,(0,shared_1.err)("SCOPE_DENIED");if("string"!=typeof e.payload)return this.lastDetail="payload field not a string",(0,shared_1.err)("VERIFICATION_FAILED:INVALID_ENVELOPE");const l=(0,crypto_utils_js_1.fromBase64)(e.payload);return(0,shared_1.ok)({senderRawKey:s.value,payloadBytes:l})}async receiveRaw(e){const t=await this.verifyEnvelope(e);if(!t.ok)return t;const{senderRawKey:r}=t.value;if(4===e.v)return(0,shared_1.err)("VERIFICATION_FAILED:UNSUPPORTED_VERSION");let s;if(2!==e.v&&3!==e.v||!("kemCiphertext"in e)){if(!e.ephemeralPub)return(0,shared_1.err)("DECRYPT_FAILED:NO_EPHEMERAL_KEY");{if("string"!=typeof e.ephemeralPub)return(0,shared_1.err)("DECRYPT_FAILED:INVALID_ENVELOPE");const t=(0,crypto_utils_js_1.fromBase64)(e.ephemeralPub),r=await(0,key_agreement_js_1.receiverKeyAgreement)(this.identity.x25519PrivateKey,t);if(r.ok)s=r.value;else{if(this.identity.rotatedKeys&&this.identity.rotatedKeys.length>0)for(const e of this.identity.rotatedKeys){const r=await(0,key_agreement_js_1.receiverKeyAgreement)(e.x25519PrivateKey,t);if(r.ok){s=r.value;break}}if(!s)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT")}}}else{if(!this.identity.mlKemSecretKey)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT");if("string"!=typeof e.ephemeralPub||"string"!=typeof e.kemCiphertext)return(0,shared_1.err)("DECRYPT_FAILED:INVALID_ENVELOPE");const t=(0,crypto_utils_js_1.fromBase64)(e.ephemeralPub),r=(0,crypto_utils_js_1.fromBase64)(e.kemCiphertext);if(!this.identity.mlKemPublicKey||!this.identity.mlKemSecretKey)return this.lastDetail="ML-KEM keys not available in identity",(0,shared_1.err)("DECRYPT_FAILED:MISSING_MLKEM_KEYS");const i=await(0,key_agreement_js_1.receiverHybridKeyAgreement)(this.identity.x25519PrivateKey,this.identity.rawX25519PublicKey,t,r,this.identity.mlKemSecretKey,this.identity.mlKemPublicKey);if(i.ok)s=i.value;else{if(this.identity.rotatedKeys&&this.identity.rotatedKeys.length>0)for(const e of this.identity.rotatedKeys){if(!e.mlKemSecretKey)continue;const i=await(0,key_agreement_js_1.receiverHybridKeyAgreement)(e.x25519PrivateKey,this.identity.rawX25519PublicKey,t,r,e.mlKemSecretKey,this.identity.mlKemPublicKey);if(i.ok){s=i.value;break}}if(!s)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT")}}if(!s)return(0,shared_1.err)("DECRYPT_FAILED:KEY_AGREEMENT");const i=await(0,envelope_js_1.decryptPayload)(e,s);if(!i.ok)return(0,shared_1.err)("DECRYPT_FAILED:DECRYPTION");const a=(new TextDecoder).decode(i.value);return(0,shared_1.ok)({sender:e.sender,decryptedText:a,scope:e.scope,timestamp:e.timestamp})}async createTestEnvelope(e,t,r){const s=await this.registry.getEntry(e);if(!s.ok||!s.value.x25519PublicKey)return null;const i=await(0,key_agreement_js_1.importX25519PublicKey)(s.value.x25519PublicKey);if(!i.ok)return null;const a=await(0,key_agreement_js_1.senderKeyAgreement)(i.value);if(!a.ok)return null;const n=(new TextEncoder).encode(JSON.stringify(t)),o=await(0,envelope_js_1.createEnvelope)({senderDid:this.identity.did,recipientDid:e,scope:r,plaintext:n,privateKey:this.identity.privateKey,sharedKey:a.value.sharedKey,ephemeralPublicKey:a.value.ephemeralPublicKey});return o.ok?o.value:null}async invite(e){if(!this.transports||0===this.transports.length)return(0,shared_1.err)("SEND_FAILED");const t=Buffer.from(this.identity.rawPublicKey).toString("base64"),r={from:this.identity.did,to:e.to,payload:{agentName:this.name,message:e.message,publicKey:t,endpoint:""}},s=this.transports[0];if(!s)return(0,shared_1.err)("SEND_FAILED");return(await s.send(r,e.to)).ok?(0,shared_1.ok)(void 0):(0,shared_1.err)("SEND_FAILED")}}exports.Agent=Agent;