@swal/edge-mesh 0.0.0-stage → 0.1.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 (464) hide show
  1. package/LICENSE +128 -0
  2. package/NOTICE +37 -0
  3. package/README.md +470 -2
  4. package/dist/adapters/gpu-agent/index.d.ts +61 -0
  5. package/dist/adapters/gpu-agent/index.d.ts.map +1 -0
  6. package/dist/adapters/gpu-agent/index.js +364 -0
  7. package/dist/adapters/gpu-agent/index.js.map +1 -0
  8. package/dist/adapters/maloca-salud/citas-distribuidas.d.ts +19 -0
  9. package/dist/adapters/maloca-salud/citas-distribuidas.d.ts.map +1 -0
  10. package/dist/adapters/maloca-salud/citas-distribuidas.js +51 -0
  11. package/dist/adapters/maloca-salud/citas-distribuidas.js.map +1 -0
  12. package/dist/adapters/maloca-salud/eps-bridge.d.ts +15 -0
  13. package/dist/adapters/maloca-salud/eps-bridge.d.ts.map +1 -0
  14. package/dist/adapters/maloca-salud/eps-bridge.js +27 -0
  15. package/dist/adapters/maloca-salud/eps-bridge.js.map +1 -0
  16. package/dist/adapters/maloca-salud/patient-profile.d.ts +20 -0
  17. package/dist/adapters/maloca-salud/patient-profile.d.ts.map +1 -0
  18. package/dist/adapters/maloca-salud/patient-profile.js +36 -0
  19. package/dist/adapters/maloca-salud/patient-profile.js.map +1 -0
  20. package/dist/adapters/maloca-salud/types.d.ts +40 -0
  21. package/dist/adapters/maloca-salud/types.d.ts.map +1 -0
  22. package/dist/adapters/maloca-salud/types.js +2 -0
  23. package/dist/adapters/maloca-salud/types.js.map +1 -0
  24. package/dist/adapters/maloca-veeduria/contract-bridge.d.ts +31 -0
  25. package/dist/adapters/maloca-veeduria/contract-bridge.d.ts.map +1 -0
  26. package/dist/adapters/maloca-veeduria/contract-bridge.js +103 -0
  27. package/dist/adapters/maloca-veeduria/contract-bridge.js.map +1 -0
  28. package/dist/adapters/maloca-veeduria/evidentia-veeduria.d.ts +14 -0
  29. package/dist/adapters/maloca-veeduria/evidentia-veeduria.d.ts.map +1 -0
  30. package/dist/adapters/maloca-veeduria/evidentia-veeduria.js +40 -0
  31. package/dist/adapters/maloca-veeduria/evidentia-veeduria.js.map +1 -0
  32. package/dist/adapters/maloca-veeduria/licitante-profile.d.ts +21 -0
  33. package/dist/adapters/maloca-veeduria/licitante-profile.d.ts.map +1 -0
  34. package/dist/adapters/maloca-veeduria/licitante-profile.js +58 -0
  35. package/dist/adapters/maloca-veeduria/licitante-profile.js.map +1 -0
  36. package/dist/adapters/maloca-veeduria/types.d.ts +30 -0
  37. package/dist/adapters/maloca-veeduria/types.d.ts.map +1 -0
  38. package/dist/adapters/maloca-veeduria/types.js +2 -0
  39. package/dist/adapters/maloca-veeduria/types.js.map +1 -0
  40. package/dist/adapters/maloca-xavier/agent-profile.d.ts +32 -0
  41. package/dist/adapters/maloca-xavier/agent-profile.d.ts.map +1 -0
  42. package/dist/adapters/maloca-xavier/agent-profile.js +56 -0
  43. package/dist/adapters/maloca-xavier/agent-profile.js.map +1 -0
  44. package/dist/adapters/maloca-xavier/codegraph-bridge.d.ts +27 -0
  45. package/dist/adapters/maloca-xavier/codegraph-bridge.d.ts.map +1 -0
  46. package/dist/adapters/maloca-xavier/codegraph-bridge.js +58 -0
  47. package/dist/adapters/maloca-xavier/codegraph-bridge.js.map +1 -0
  48. package/dist/adapters/maloca-xavier/llm-router.d.ts +25 -0
  49. package/dist/adapters/maloca-xavier/llm-router.d.ts.map +1 -0
  50. package/dist/adapters/maloca-xavier/llm-router.js +58 -0
  51. package/dist/adapters/maloca-xavier/llm-router.js.map +1 -0
  52. package/dist/authz/index.d.ts +81 -0
  53. package/dist/authz/index.d.ts.map +1 -0
  54. package/dist/authz/index.js +271 -0
  55. package/dist/authz/index.js.map +1 -0
  56. package/dist/chat/index.d.ts +138 -0
  57. package/dist/chat/index.d.ts.map +1 -0
  58. package/dist/chat/index.js +394 -0
  59. package/dist/chat/index.js.map +1 -0
  60. package/dist/chat/offline-queue.d.ts +32 -0
  61. package/dist/chat/offline-queue.d.ts.map +1 -0
  62. package/dist/chat/offline-queue.js +93 -0
  63. package/dist/chat/offline-queue.js.map +1 -0
  64. package/dist/core/DefaultEdgeMeshNode.d.ts +17 -0
  65. package/dist/core/DefaultEdgeMeshNode.d.ts.map +1 -0
  66. package/dist/core/DefaultEdgeMeshNode.js +57 -0
  67. package/dist/core/DefaultEdgeMeshNode.js.map +1 -0
  68. package/dist/core/node.d.ts +26 -0
  69. package/dist/core/node.d.ts.map +1 -0
  70. package/dist/core/node.js +16 -0
  71. package/dist/core/node.js.map +1 -0
  72. package/dist/edge-mesh.d.ts +139 -0
  73. package/dist/edge-mesh.d.ts.map +1 -0
  74. package/dist/edge-mesh.js +1146 -0
  75. package/dist/edge-mesh.js.map +1 -0
  76. package/dist/gos/GosBridge.d.ts +49 -0
  77. package/dist/gos/GosBridge.d.ts.map +1 -0
  78. package/dist/gos/GosBridge.js +133 -0
  79. package/dist/gos/GosBridge.js.map +1 -0
  80. package/dist/governance/authority.d.ts +44 -0
  81. package/dist/governance/authority.d.ts.map +1 -0
  82. package/dist/governance/authority.js +123 -0
  83. package/dist/governance/authority.js.map +1 -0
  84. package/dist/governance/index.d.ts +66 -0
  85. package/dist/governance/index.d.ts.map +1 -0
  86. package/dist/governance/index.js +210 -0
  87. package/dist/governance/index.js.map +1 -0
  88. package/dist/governance/merge.d.ts +31 -0
  89. package/dist/governance/merge.d.ts.map +1 -0
  90. package/dist/governance/merge.js +167 -0
  91. package/dist/governance/merge.js.map +1 -0
  92. package/dist/identity/index.d.ts +199 -0
  93. package/dist/identity/index.d.ts.map +1 -0
  94. package/dist/identity/index.js +415 -0
  95. package/dist/identity/index.js.map +1 -0
  96. package/dist/index.d.ts +71 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +56 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/maloca/backoffice.d.ts +31 -0
  101. package/dist/maloca/backoffice.d.ts.map +1 -0
  102. package/dist/maloca/backoffice.js +40 -0
  103. package/dist/maloca/backoffice.js.map +1 -0
  104. package/dist/maloca/event-bus.d.ts +60 -0
  105. package/dist/maloca/event-bus.d.ts.map +1 -0
  106. package/dist/maloca/event-bus.js +175 -0
  107. package/dist/maloca/event-bus.js.map +1 -0
  108. package/dist/maloca/evidentia.d.ts +136 -0
  109. package/dist/maloca/evidentia.d.ts.map +1 -0
  110. package/dist/maloca/evidentia.js +447 -0
  111. package/dist/maloca/evidentia.js.map +1 -0
  112. package/dist/maloca/gateway/api.d.ts +146 -0
  113. package/dist/maloca/gateway/api.d.ts.map +1 -0
  114. package/dist/maloca/gateway/api.js +228 -0
  115. package/dist/maloca/gateway/api.js.map +1 -0
  116. package/dist/maloca/gateway/auth.d.ts +20 -0
  117. package/dist/maloca/gateway/auth.d.ts.map +1 -0
  118. package/dist/maloca/gateway/auth.js +115 -0
  119. package/dist/maloca/gateway/auth.js.map +1 -0
  120. package/dist/maloca/gateway/dashboard.d.ts +42 -0
  121. package/dist/maloca/gateway/dashboard.d.ts.map +1 -0
  122. package/dist/maloca/gateway/dashboard.js +67 -0
  123. package/dist/maloca/gateway/dashboard.js.map +1 -0
  124. package/dist/maloca/gateway/websocket.d.ts +34 -0
  125. package/dist/maloca/gateway/websocket.d.ts.map +1 -0
  126. package/dist/maloca/gateway/websocket.js +71 -0
  127. package/dist/maloca/gateway/websocket.js.map +1 -0
  128. package/dist/maloca/karma.d.ts +165 -0
  129. package/dist/maloca/karma.d.ts.map +1 -0
  130. package/dist/maloca/karma.js +307 -0
  131. package/dist/maloca/karma.js.map +1 -0
  132. package/dist/maloca/kernel.d.ts +20 -0
  133. package/dist/maloca/kernel.d.ts.map +1 -0
  134. package/dist/maloca/kernel.js +76 -0
  135. package/dist/maloca/kernel.js.map +1 -0
  136. package/dist/maloca/metadata.d.ts +30 -0
  137. package/dist/maloca/metadata.d.ts.map +1 -0
  138. package/dist/maloca/metadata.js +80 -0
  139. package/dist/maloca/metadata.js.map +1 -0
  140. package/dist/maloca/perfil.d.ts +57 -0
  141. package/dist/maloca/perfil.d.ts.map +1 -0
  142. package/dist/maloca/perfil.js +113 -0
  143. package/dist/maloca/perfil.js.map +1 -0
  144. package/dist/maloca/plugin-registry.d.ts +27 -0
  145. package/dist/maloca/plugin-registry.d.ts.map +1 -0
  146. package/dist/maloca/plugin-registry.js +76 -0
  147. package/dist/maloca/plugin-registry.js.map +1 -0
  148. package/dist/maloca/polygon-bridge.d.ts +63 -0
  149. package/dist/maloca/polygon-bridge.d.ts.map +1 -0
  150. package/dist/maloca/polygon-bridge.js +198 -0
  151. package/dist/maloca/polygon-bridge.js.map +1 -0
  152. package/dist/maloca/types.d.ts +44 -0
  153. package/dist/maloca/types.d.ts.map +1 -0
  154. package/dist/maloca/types.js +2 -0
  155. package/dist/maloca/types.js.map +1 -0
  156. package/dist/medico/aval.d.ts +149 -0
  157. package/dist/medico/aval.d.ts.map +1 -0
  158. package/dist/medico/aval.js +281 -0
  159. package/dist/medico/aval.js.map +1 -0
  160. package/dist/medico/canonica.d.ts +96 -0
  161. package/dist/medico/canonica.d.ts.map +1 -0
  162. package/dist/medico/canonica.js +148 -0
  163. package/dist/medico/canonica.js.map +1 -0
  164. package/dist/medico/credencial.d.ts +160 -0
  165. package/dist/medico/credencial.d.ts.map +1 -0
  166. package/dist/medico/credencial.js +240 -0
  167. package/dist/medico/credencial.js.map +1 -0
  168. package/dist/medico/index.d.ts +36 -0
  169. package/dist/medico/index.d.ts.map +1 -0
  170. package/dist/medico/index.js +39 -0
  171. package/dist/medico/index.js.map +1 -0
  172. package/dist/medico/malla.d.ts +218 -0
  173. package/dist/medico/malla.d.ts.map +1 -0
  174. package/dist/medico/malla.js +347 -0
  175. package/dist/medico/malla.js.map +1 -0
  176. package/dist/medico/registro-profesional.d.ts +84 -0
  177. package/dist/medico/registro-profesional.d.ts.map +1 -0
  178. package/dist/medico/registro-profesional.js +135 -0
  179. package/dist/medico/registro-profesional.js.map +1 -0
  180. package/dist/medico/registro.d.ts +149 -0
  181. package/dist/medico/registro.d.ts.map +1 -0
  182. package/dist/medico/registro.js +173 -0
  183. package/dist/medico/registro.js.map +1 -0
  184. package/dist/medico/types.d.ts +287 -0
  185. package/dist/medico/types.d.ts.map +1 -0
  186. package/dist/medico/types.js +135 -0
  187. package/dist/medico/types.js.map +1 -0
  188. package/dist/medico/verificacion-registro.d.ts +300 -0
  189. package/dist/medico/verificacion-registro.d.ts.map +1 -0
  190. package/dist/medico/verificacion-registro.js +319 -0
  191. package/dist/medico/verificacion-registro.js.map +1 -0
  192. package/dist/mesh/index.d.ts +457 -0
  193. package/dist/mesh/index.d.ts.map +1 -0
  194. package/dist/mesh/index.js +929 -0
  195. package/dist/mesh/index.js.map +1 -0
  196. package/dist/namespaces/encrypted-plugin.d.ts +56 -0
  197. package/dist/namespaces/encrypted-plugin.d.ts.map +1 -0
  198. package/dist/namespaces/encrypted-plugin.js +173 -0
  199. package/dist/namespaces/encrypted-plugin.js.map +1 -0
  200. package/dist/namespaces/index.d.ts +52 -0
  201. package/dist/namespaces/index.d.ts.map +1 -0
  202. package/dist/namespaces/index.js +134 -0
  203. package/dist/namespaces/index.js.map +1 -0
  204. package/dist/namespaces/ivn-proofs.d.ts +54 -0
  205. package/dist/namespaces/ivn-proofs.d.ts.map +1 -0
  206. package/dist/namespaces/ivn-proofs.js +162 -0
  207. package/dist/namespaces/ivn-proofs.js.map +1 -0
  208. package/dist/namespaces/offers-gossip.d.ts +95 -0
  209. package/dist/namespaces/offers-gossip.d.ts.map +1 -0
  210. package/dist/namespaces/offers-gossip.js +213 -0
  211. package/dist/namespaces/offers-gossip.js.map +1 -0
  212. package/dist/node-memory/idb-store.d.ts +22 -0
  213. package/dist/node-memory/idb-store.d.ts.map +1 -0
  214. package/dist/node-memory/idb-store.js +154 -0
  215. package/dist/node-memory/idb-store.js.map +1 -0
  216. package/dist/node-memory/index.d.ts +14 -0
  217. package/dist/node-memory/index.d.ts.map +1 -0
  218. package/dist/node-memory/index.js +159 -0
  219. package/dist/node-memory/index.js.map +1 -0
  220. package/dist/node-memory/sync-flush.d.ts +11 -0
  221. package/dist/node-memory/sync-flush.d.ts.map +1 -0
  222. package/dist/node-memory/sync-flush.js +37 -0
  223. package/dist/node-memory/sync-flush.js.map +1 -0
  224. package/dist/node-memory/types.d.ts +34 -0
  225. package/dist/node-memory/types.d.ts.map +1 -0
  226. package/dist/node-memory/types.js +2 -0
  227. package/dist/node-memory/types.js.map +1 -0
  228. package/dist/node-memory/xavier-store.d.ts +12 -0
  229. package/dist/node-memory/xavier-store.d.ts.map +1 -0
  230. package/dist/node-memory/xavier-store.js +75 -0
  231. package/dist/node-memory/xavier-store.js.map +1 -0
  232. package/dist/op-log/index.d.ts +49 -0
  233. package/dist/op-log/index.d.ts.map +1 -0
  234. package/dist/op-log/index.js +190 -0
  235. package/dist/op-log/index.js.map +1 -0
  236. package/dist/presence/health.d.ts +54 -0
  237. package/dist/presence/health.d.ts.map +1 -0
  238. package/dist/presence/health.js +139 -0
  239. package/dist/presence/health.js.map +1 -0
  240. package/dist/presence/index.d.ts +85 -0
  241. package/dist/presence/index.d.ts.map +1 -0
  242. package/dist/presence/index.js +255 -0
  243. package/dist/presence/index.js.map +1 -0
  244. package/dist/presence/peer-health.d.ts +40 -0
  245. package/dist/presence/peer-health.d.ts.map +1 -0
  246. package/dist/presence/peer-health.js +118 -0
  247. package/dist/presence/peer-health.js.map +1 -0
  248. package/dist/protocol/canonical.d.ts +10 -0
  249. package/dist/protocol/canonical.d.ts.map +1 -0
  250. package/dist/protocol/canonical.js +32 -0
  251. package/dist/protocol/canonical.js.map +1 -0
  252. package/dist/protocol/crypto.d.ts +299 -0
  253. package/dist/protocol/crypto.d.ts.map +1 -0
  254. package/dist/protocol/crypto.js +739 -0
  255. package/dist/protocol/crypto.js.map +1 -0
  256. package/dist/protocol/index.d.ts +30 -0
  257. package/dist/protocol/index.d.ts.map +1 -0
  258. package/dist/protocol/index.js +116 -0
  259. package/dist/protocol/index.js.map +1 -0
  260. package/dist/protocol/utils.d.ts +5 -0
  261. package/dist/protocol/utils.d.ts.map +1 -0
  262. package/dist/protocol/utils.js +27 -0
  263. package/dist/protocol/utils.js.map +1 -0
  264. package/dist/salones/manager.d.ts +100 -0
  265. package/dist/salones/manager.d.ts.map +1 -0
  266. package/dist/salones/manager.js +240 -0
  267. package/dist/salones/manager.js.map +1 -0
  268. package/dist/security/rate-limiter.d.ts +13 -0
  269. package/dist/security/rate-limiter.d.ts.map +1 -0
  270. package/dist/security/rate-limiter.js +32 -0
  271. package/dist/security/rate-limiter.js.map +1 -0
  272. package/dist/snapshot/index.d.ts +90 -0
  273. package/dist/snapshot/index.d.ts.map +1 -0
  274. package/dist/snapshot/index.js +189 -0
  275. package/dist/snapshot/index.js.map +1 -0
  276. package/dist/storage/index.d.ts +49 -0
  277. package/dist/storage/index.d.ts.map +1 -0
  278. package/dist/storage/index.js +176 -0
  279. package/dist/storage/index.js.map +1 -0
  280. package/dist/storage/optimizer.d.ts +28 -0
  281. package/dist/storage/optimizer.d.ts.map +1 -0
  282. package/dist/storage/optimizer.js +50 -0
  283. package/dist/storage/optimizer.js.map +1 -0
  284. package/dist/storage/ydoc-persistence.d.ts +27 -0
  285. package/dist/storage/ydoc-persistence.d.ts.map +1 -0
  286. package/dist/storage/ydoc-persistence.js +128 -0
  287. package/dist/storage/ydoc-persistence.js.map +1 -0
  288. package/dist/sync/ChunkSyncManager.d.ts +41 -0
  289. package/dist/sync/ChunkSyncManager.d.ts.map +1 -0
  290. package/dist/sync/ChunkSyncManager.js +115 -0
  291. package/dist/sync/ChunkSyncManager.js.map +1 -0
  292. package/dist/sync/conflict-resolver.d.ts +33 -0
  293. package/dist/sync/conflict-resolver.d.ts.map +1 -0
  294. package/dist/sync/conflict-resolver.js +54 -0
  295. package/dist/sync/conflict-resolver.js.map +1 -0
  296. package/dist/sync/engine.d.ts +168 -0
  297. package/dist/sync/engine.d.ts.map +1 -0
  298. package/dist/sync/engine.js +315 -0
  299. package/dist/sync/engine.js.map +1 -0
  300. package/dist/sync/index.d.ts +3 -0
  301. package/dist/sync/index.d.ts.map +1 -0
  302. package/dist/sync/index.js +3 -0
  303. package/dist/sync/index.js.map +1 -0
  304. package/dist/transport/TorTransportAdapter.d.ts +70 -0
  305. package/dist/transport/TorTransportAdapter.d.ts.map +1 -0
  306. package/dist/transport/TorTransportAdapter.js +192 -0
  307. package/dist/transport/TorTransportAdapter.js.map +1 -0
  308. package/dist/transport/memory.d.ts +31 -0
  309. package/dist/transport/memory.d.ts.map +1 -0
  310. package/dist/transport/memory.js +114 -0
  311. package/dist/transport/memory.js.map +1 -0
  312. package/dist/transport/peerjs.d.ts +45 -0
  313. package/dist/transport/peerjs.d.ts.map +1 -0
  314. package/dist/transport/peerjs.js +301 -0
  315. package/dist/transport/peerjs.js.map +1 -0
  316. package/dist/transport/pqc-handshake.d.ts +72 -0
  317. package/dist/transport/pqc-handshake.d.ts.map +1 -0
  318. package/dist/transport/pqc-handshake.js +129 -0
  319. package/dist/transport/pqc-handshake.js.map +1 -0
  320. package/dist/transport/relay-config.d.ts +11 -0
  321. package/dist/transport/relay-config.d.ts.map +1 -0
  322. package/dist/transport/relay-config.js +48 -0
  323. package/dist/transport/relay-config.js.map +1 -0
  324. package/dist/transport/relay-server.d.ts +37 -0
  325. package/dist/transport/relay-server.d.ts.map +1 -0
  326. package/dist/transport/relay-server.js +230 -0
  327. package/dist/transport/relay-server.js.map +1 -0
  328. package/dist/transport/tor.d.ts +47 -0
  329. package/dist/transport/tor.d.ts.map +1 -0
  330. package/dist/transport/tor.js +158 -0
  331. package/dist/transport/tor.js.map +1 -0
  332. package/dist/transport/types.d.ts +34 -0
  333. package/dist/transport/types.d.ts.map +1 -0
  334. package/dist/transport/types.js +2 -0
  335. package/dist/transport/types.js.map +1 -0
  336. package/dist/types/index.d.ts +293 -0
  337. package/dist/types/index.d.ts.map +1 -0
  338. package/dist/types/index.js +73 -0
  339. package/dist/types/index.js.map +1 -0
  340. package/dist/web/admission.d.ts +24 -0
  341. package/dist/web/admission.d.ts.map +1 -0
  342. package/dist/web/admission.js +41 -0
  343. package/dist/web/admission.js.map +1 -0
  344. package/dist/web/crypto.d.ts +18 -0
  345. package/dist/web/crypto.d.ts.map +1 -0
  346. package/dist/web/crypto.js +78 -0
  347. package/dist/web/crypto.js.map +1 -0
  348. package/dist/web/exchange.d.ts +23 -0
  349. package/dist/web/exchange.d.ts.map +1 -0
  350. package/dist/web/exchange.js +54 -0
  351. package/dist/web/exchange.js.map +1 -0
  352. package/dist/web/fragment.d.ts +58 -0
  353. package/dist/web/fragment.d.ts.map +1 -0
  354. package/dist/web/fragment.js +198 -0
  355. package/dist/web/fragment.js.map +1 -0
  356. package/dist/web/index.d.ts +25 -0
  357. package/dist/web/index.d.ts.map +1 -0
  358. package/dist/web/index.js +14 -0
  359. package/dist/web/index.js.map +1 -0
  360. package/dist/web/merge/index.d.ts +104 -0
  361. package/dist/web/merge/index.d.ts.map +1 -0
  362. package/dist/web/merge/index.js +236 -0
  363. package/dist/web/merge/index.js.map +1 -0
  364. package/dist/web/oplog/hlc.d.ts +19 -0
  365. package/dist/web/oplog/hlc.d.ts.map +1 -0
  366. package/dist/web/oplog/hlc.js +39 -0
  367. package/dist/web/oplog/hlc.js.map +1 -0
  368. package/dist/web/oplog/index.d.ts +10 -0
  369. package/dist/web/oplog/index.d.ts.map +1 -0
  370. package/dist/web/oplog/index.js +6 -0
  371. package/dist/web/oplog/index.js.map +1 -0
  372. package/dist/web/oplog/log.d.ts +182 -0
  373. package/dist/web/oplog/log.d.ts.map +1 -0
  374. package/dist/web/oplog/log.js +693 -0
  375. package/dist/web/oplog/log.js.map +1 -0
  376. package/dist/web/oplog/store.d.ts +40 -0
  377. package/dist/web/oplog/store.d.ts.map +1 -0
  378. package/dist/web/oplog/store.js +85 -0
  379. package/dist/web/oplog/store.js.map +1 -0
  380. package/dist/web/oplog/sync.d.ts +54 -0
  381. package/dist/web/oplog/sync.d.ts.map +1 -0
  382. package/dist/web/oplog/sync.js +162 -0
  383. package/dist/web/oplog/sync.js.map +1 -0
  384. package/dist/web/oplog/types.d.ts +119 -0
  385. package/dist/web/oplog/types.d.ts.map +1 -0
  386. package/dist/web/oplog/types.js +2 -0
  387. package/dist/web/oplog/types.js.map +1 -0
  388. package/dist/web/pairing.d.ts +186 -0
  389. package/dist/web/pairing.d.ts.map +1 -0
  390. package/dist/web/pairing.js +395 -0
  391. package/dist/web/pairing.js.map +1 -0
  392. package/dist/web/pq.d.ts +36 -0
  393. package/dist/web/pq.d.ts.map +1 -0
  394. package/dist/web/pq.js +59 -0
  395. package/dist/web/pq.js.map +1 -0
  396. package/dist/web/provider.d.ts +188 -0
  397. package/dist/web/provider.d.ts.map +1 -0
  398. package/dist/web/provider.js +2042 -0
  399. package/dist/web/provider.js.map +1 -0
  400. package/dist/web/rooms.d.ts +20 -0
  401. package/dist/web/rooms.d.ts.map +1 -0
  402. package/dist/web/rooms.js +51 -0
  403. package/dist/web/rooms.js.map +1 -0
  404. package/dist/web/rotation.d.ts +73 -0
  405. package/dist/web/rotation.d.ts.map +1 -0
  406. package/dist/web/rotation.js +130 -0
  407. package/dist/web/rotation.js.map +1 -0
  408. package/dist/web/secstate.d.ts +213 -0
  409. package/dist/web/secstate.d.ts.map +1 -0
  410. package/dist/web/secstate.js +789 -0
  411. package/dist/web/secstate.js.map +1 -0
  412. package/dist/web/store.d.ts +9 -0
  413. package/dist/web/store.d.ts.map +1 -0
  414. package/dist/web/store.js +32 -0
  415. package/dist/web/store.js.map +1 -0
  416. package/dist/web/transports/loopback.d.ts +11 -0
  417. package/dist/web/transports/loopback.d.ts.map +1 -0
  418. package/dist/web/transports/loopback.js +97 -0
  419. package/dist/web/transports/loopback.js.map +1 -0
  420. package/dist/web/transports/qr-sdp.d.ts +27 -0
  421. package/dist/web/transports/qr-sdp.d.ts.map +1 -0
  422. package/dist/web/transports/qr-sdp.js +131 -0
  423. package/dist/web/transports/qr-sdp.js.map +1 -0
  424. package/dist/web/transports/ws.d.ts +12 -0
  425. package/dist/web/transports/ws.d.ts.map +1 -0
  426. package/dist/web/transports/ws.js +139 -0
  427. package/dist/web/transports/ws.js.map +1 -0
  428. package/dist/web/trust/canonical.d.ts +8 -0
  429. package/dist/web/trust/canonical.d.ts.map +1 -0
  430. package/dist/web/trust/canonical.js +27 -0
  431. package/dist/web/trust/canonical.js.map +1 -0
  432. package/dist/web/trust/docs.d.ts +51 -0
  433. package/dist/web/trust/docs.d.ts.map +1 -0
  434. package/dist/web/trust/docs.js +170 -0
  435. package/dist/web/trust/docs.js.map +1 -0
  436. package/dist/web/trust/index.d.ts +10 -0
  437. package/dist/web/trust/index.d.ts.map +1 -0
  438. package/dist/web/trust/index.js +7 -0
  439. package/dist/web/trust/index.js.map +1 -0
  440. package/dist/web/trust/keys.d.ts +37 -0
  441. package/dist/web/trust/keys.d.ts.map +1 -0
  442. package/dist/web/trust/keys.js +75 -0
  443. package/dist/web/trust/keys.js.map +1 -0
  444. package/dist/web/trust/store.d.ts +150 -0
  445. package/dist/web/trust/store.d.ts.map +1 -0
  446. package/dist/web/trust/store.js +587 -0
  447. package/dist/web/trust/store.js.map +1 -0
  448. package/dist/web/trust/types.d.ts +134 -0
  449. package/dist/web/trust/types.d.ts.map +1 -0
  450. package/dist/web/trust/types.js +4 -0
  451. package/dist/web/trust/types.js.map +1 -0
  452. package/dist/web/types.d.ts +115 -0
  453. package/dist/web/types.d.ts.map +1 -0
  454. package/dist/web/types.js +2 -0
  455. package/dist/web/types.js.map +1 -0
  456. package/dist/web/util.d.ts +12 -0
  457. package/dist/web/util.d.ts.map +1 -0
  458. package/dist/web/util.js +54 -0
  459. package/dist/web/util.js.map +1 -0
  460. package/dist/web/webrtc.d.ts +35 -0
  461. package/dist/web/webrtc.d.ts.map +1 -0
  462. package/dist/web/webrtc.js +304 -0
  463. package/dist/web/webrtc.js.map +1 -0
  464. package/package.json +138 -4
@@ -0,0 +1,789 @@
1
+ // Security state of a mesh (round 5): an append-only, content-addressed set of self-verifying signed documents,
2
+ // kept in each device's local store and exchanged over the authenticated trust channel. Nothing of it lives in the
3
+ // shared Y.Doc: members can only ADD documents (there are no slots to squat, overwrite or delete).
4
+ //
5
+ // Documents
6
+ // - web/trust grants and revocations: membership and authority. The mesh root (owner) is the trust root; a grant
7
+ // with `delegate >= 1` makes an admin, any other grant a member. A revocation is effective only when issued by the
8
+ // root or by a strict ancestor of the revoked grant (an admin revokes the devices it admitted) and always cascades.
9
+ // - key-agreement records (`kex`): a device's P-256 + ML-KEM-768 public keys, signed by its own identity key.
10
+ // - owner rotations (`rot`, see rotation.ts): signed by the root; their `cut` lists are authoritative (executed).
11
+ //
12
+ // Determinism: what a device concludes is a function of the documents it holds, whatever their arrival order.
13
+ import { isDeviceId, isEpoch } from "./admission.js";
14
+ import { identityVerify, kemEncapsulate, ML_KEM_PUBLIC_KEY_BYTES, } from "./pq.js";
15
+ import { isEcdhPublicKey, isRotDoc, rotationId, rotationSigBytes, wrapsHash, } from "./rotation.js";
16
+ import { canonicalBytes, contentId, sha256B64u } from "./trust/canonical.js";
17
+ import { checkIntegrity, issueGrant, issueRevocation } from "./trust/docs.js";
18
+ import { isPublicKey, isSignature, SIG_ALG, signCanonical, verifyCanonical, } from "./trust/keys.js";
19
+ import { createTrustStore } from "./trust/store.js";
20
+ import { b64uDecode } from "./util.js";
21
+ /** Module every mesh grant names (apps may add their own modules to the schema). */
22
+ export const MESH_MODULE = "mesh";
23
+ export const MESH_SCHEMA = {
24
+ modules: [MESH_MODULE],
25
+ roles: {
26
+ admin: { permissions: { [MESH_MODULE]: "administrar" }, delegate: 1 },
27
+ member: { permissions: { [MESH_MODULE]: "editar" }, delegate: 0 },
28
+ },
29
+ maxDepth: 2,
30
+ };
31
+ /** Members an admin's grants can make (its lowest grant ids count; R5-S3: an admin cannot flood the recipient list). */
32
+ export const MAX_MEMBERS_PER_ADMIN = 256;
33
+ /** Grants of one non-root issuer kept at all (beyond: the cap-N smallest ids are kept, see `applyCap`). */
34
+ export const MAX_STORED_PER_ISSUER = 4 * MAX_MEMBERS_PER_ADMIN;
35
+ /** Revocations of one target grant by one issuer kept (beyond: the cap-N smallest ids are kept, see `applyCap`). */
36
+ export const MAX_REVS_PER_ISSUER_TARGET = 4;
37
+ /** Recent owner rotations kept in full (with their wraps) to serve stragglers; their cuts are kept for good. */
38
+ export const MAX_ROTATIONS_KEPT = 32;
39
+ const MAX_DEFERRED = 512;
40
+ /** R6-S5: a count alone does not bound memory: one deferred document may carry a large `upTo`/wrap list. */
41
+ export const MAX_DOC_BYTES = 512 * 1024;
42
+ /** R6-S5: total bytes held by deferred documents (the auditor measured 126 MiB from 64 deferred revocations). */
43
+ export const MAX_DEFERRED_BYTES = 8 * 1024 * 1024;
44
+ const MAX_REMEMBERED = 8192;
45
+ /**
46
+ * R6-S2: refusals that are a property of THIS device's context, not of the document, so they are not permanent failures
47
+ * and are not remembered as `bad`. `pending overflow` is the waiting set of the trust store being full: in the auditor's
48
+ * proof a REAL grant is turned away that way, so its key must stay in `missing()` and whichever peer offers it again
49
+ * must be served — no sender can keep a genuine document out by filling the room first.
50
+ *
51
+ * R6-S2b adds the per-SENDER room: the same refusal, counted on the peer that sent the documents instead of on the
52
+ * issuers they name, since an attacker signs its junk with fresh random keys and so holds one waiting slot per issuer.
53
+ */
54
+ const CONTEXTUAL_REFUSALS = new Set([
55
+ "pending overflow",
56
+ "sender pending overflow",
57
+ "deferred-overflow",
58
+ "issuer over its cap",
59
+ ]);
60
+ /** Bounded set that forgets the least recently used entry. */
61
+ class LruSet {
62
+ cap;
63
+ m = new Map();
64
+ constructor(cap) {
65
+ this.cap = cap;
66
+ }
67
+ has(k) {
68
+ if (!this.m.has(k))
69
+ return false;
70
+ this.m.delete(k);
71
+ this.m.set(k, true);
72
+ return true;
73
+ }
74
+ add(k) {
75
+ this.m.delete(k);
76
+ this.m.set(k, true);
77
+ if (this.m.size > this.cap)
78
+ this.m.delete(this.m.keys().next().value);
79
+ }
80
+ values() {
81
+ return [...this.m.keys()];
82
+ }
83
+ clear() {
84
+ this.m.clear();
85
+ }
86
+ }
87
+ const kexBody = (k) => ({
88
+ t: k.t,
89
+ v: k.v,
90
+ inst: k.inst,
91
+ dev: k.dev,
92
+ ecdh: k.ecdh,
93
+ kem: k.kem,
94
+ n: k.n,
95
+ });
96
+ function isKexShape(x) {
97
+ const k = x;
98
+ return (typeof k === "object" &&
99
+ k !== null &&
100
+ k.t === "kex" &&
101
+ k.v === 1 &&
102
+ typeof k.inst === "string" &&
103
+ isDeviceId(k.dev) &&
104
+ typeof k.ecdh === "string" &&
105
+ k.ecdh.length <= 128 &&
106
+ typeof k.kem === "string" &&
107
+ k.kem.length <= 2048 &&
108
+ Number.isSafeInteger(k.n) &&
109
+ k.n >= 0 &&
110
+ isSignature(k.sig));
111
+ }
112
+ /** Inventory id of a document: kind prefix + content id (rotation id for rotations). */
113
+ async function docKey(x) {
114
+ if (x.t === "grant")
115
+ return `g:${x.id}`;
116
+ if (x.t === "revoke")
117
+ return `r:${x.id}`;
118
+ if (x.t === "kex")
119
+ return `k:${await contentId(kexBody(x))}`;
120
+ return `R:${await rotationId(x)}`;
121
+ }
122
+ export class SecurityState {
123
+ trust;
124
+ root;
125
+ store;
126
+ now;
127
+ kex = new Map();
128
+ rots = new Map();
129
+ cuts = new Set();
130
+ /** inventory: keys of every document held */
131
+ have = new Set();
132
+ /** keys of documents dropped for good (superseded key records, pruned rotations): never asked for again */
133
+ gone;
134
+ /** hashes of whole documents that failed verification (a forged copy never blocks the genuine one) */
135
+ bad = new LruSet(4096);
136
+ /** R6-B1: documents waiting for their parent/target, by the hash of the WHOLE document (never by the id it claims) */
137
+ deferred = new Map();
138
+ /** R6-S2b: who sent each parked document, so the retry charges that sender's waiting slots and not the parent's. */
139
+ deferredFrom = new Map();
140
+ /** R6-S5: bytes held by `deferred` (kept exact: every set and every drop goes through defer/unpark) */
141
+ deferredBytesHeld = 0;
142
+ listeners = new Set();
143
+ queue = Promise.resolve();
144
+ capIndex = null;
145
+ version = 0;
146
+ constructor(trust, root, store, now) {
147
+ this.trust = trust;
148
+ this.root = root;
149
+ this.store = store;
150
+ this.now = now;
151
+ this.gone = new LruSet(MAX_REMEMBERED);
152
+ trust.onChange(() => {
153
+ this.capIndex = null;
154
+ });
155
+ }
156
+ static async open(o) {
157
+ const now = o.now ?? Date.now;
158
+ const trust = await createTrustStore({
159
+ inst: o.root.mid,
160
+ root: o.root.pub,
161
+ rootFingerprint: o.root.deviceId,
162
+ schema: o.schema ?? MESH_SCHEMA,
163
+ now,
164
+ maxPending: 256,
165
+ });
166
+ const s = new SecurityState(trust, o.root, o.store, now);
167
+ await s.load();
168
+ return s;
169
+ }
170
+ get inst() {
171
+ return this.root.mid;
172
+ }
173
+ onChange(cb) {
174
+ this.listeners.add(cb);
175
+ return () => this.listeners.delete(cb);
176
+ }
177
+ // ─── ingest ──────────────────────────────────────────────────────────────
178
+ /**
179
+ * Add documents (any order, any source). Serialized; listeners fire once when anything was accepted.
180
+ * `opts.from` (R6-S2b) is the transport-level id of the peer that sent them: the trust store charges the waiting
181
+ * slots they park to that sender, so one peer cannot take the whole waiting set by signing with fresh random keys.
182
+ * Leave it undefined for local/own documents (a pair's own, ours, restored from storage): no sender is charged.
183
+ */
184
+ addMany(xs, opts) {
185
+ const run = this.queue.then(async () => {
186
+ const before = this.version;
187
+ const out = [];
188
+ for (const x of xs)
189
+ out.push(await this.ingest(x, opts?.from));
190
+ if (this.version !== before) {
191
+ await this.retryDeferred();
192
+ this.scheduleSave();
193
+ for (const cb of [...this.listeners]) {
194
+ try {
195
+ cb();
196
+ }
197
+ catch { }
198
+ }
199
+ }
200
+ return out;
201
+ });
202
+ this.queue = run.catch(() => undefined);
203
+ return run;
204
+ }
205
+ add(x) {
206
+ return this.addMany([x]).then((r) => r[0]);
207
+ }
208
+ async ingest(x, from, retry = false) {
209
+ const t = x?.t;
210
+ if (t !== "grant" && t !== "revoke" && t !== "kex" && t !== "rot")
211
+ return { status: "rejected", reason: "unknown document" };
212
+ const doc = x;
213
+ if (doc.inst !== this.inst)
214
+ return { status: "rejected", reason: "other mesh" };
215
+ // R5-B3: one encoding per signature; ids are content ids, so a copy with another encoding is not "another" doc
216
+ if (!isSignature(doc.sig))
217
+ return { status: "rejected", reason: "non-canonical signature" };
218
+ let key;
219
+ try {
220
+ key = await docKey(doc);
221
+ }
222
+ catch {
223
+ return { status: "rejected", reason: "malformed" };
224
+ }
225
+ if (this.have.has(key))
226
+ return { status: "duplicate", id: key };
227
+ if (this.gone.has(key))
228
+ return { status: "duplicate", id: key, reason: "superseded" };
229
+ const bytes = canonicalBytes(doc);
230
+ // R6-S5: bounded before any crypto, so an oversize document costs nothing and is never parked
231
+ if (bytes.length > MAX_DOC_BYTES)
232
+ return { status: "rejected", id: key, reason: "oversize document" };
233
+ const whole = await sha256B64u(bytes);
234
+ if (this.bad.has(whole))
235
+ return { status: "rejected", id: key, reason: "known bad" };
236
+ let r;
237
+ if (doc.t === "grant" || doc.t === "revoke")
238
+ r = await this.ingestTrust(doc, key, from);
239
+ else if (doc.t === "kex")
240
+ r = await this.ingestKex(doc, key);
241
+ else
242
+ r = await this.ingestRot(doc, key);
243
+ // R6-S2: a context-dependent refusal (the waiting set was full) is NOT a property of the document: it is not
244
+ // remembered as bad, so the id stays in `missing()` and the peer that offers it again is served
245
+ if (r.status === "rejected" && !CONTEXTUAL_REFUSALS.has(r.reason))
246
+ this.bad.add(whole);
247
+ if (r.status === "pending" && !retry) {
248
+ // R6-B1: nothing is parked before the id is shown to be the content address of these very bytes. A forged
249
+ // copy carrying the id of a genuine document (of somebody else) used to be parked under that id and, since a
250
+ // parked id was never asked for again, it withheld the genuine one for good: the owner never re-keyed and the
251
+ // revoked member kept the key. The forgery is refused (a failure for its sender) instead of parked.
252
+ if (doc.t === "grant" || doc.t === "revoke") {
253
+ const why = await checkIntegrity(doc);
254
+ if (why) {
255
+ this.bad.add(whole);
256
+ return { status: "rejected", id: key, reason: why };
257
+ }
258
+ }
259
+ // R6-S5: parking is bounded by bytes; a document that does not fit is dropped and counts as a failure
260
+ // (it is not remembered as bad: the genuine one is still asked for)
261
+ const why = this.defer(whole, doc, bytes.length, from);
262
+ if (why)
263
+ return { status: "rejected", id: key, reason: why };
264
+ }
265
+ return r;
266
+ }
267
+ /**
268
+ * Park a document until its parent/target arrives. Returns a reason if it was dropped instead.
269
+ * R6-B1: keyed by the hash of the WHOLE document received, never by the id it claims, so a forged copy parked under
270
+ * somebody else's id can neither shadow the genuine one nor take its key out of `missing()`.
271
+ * R6-S2b: remembers which peer sent it, so the retry charges that sender's waiting slots and not the parent's.
272
+ */
273
+ defer(whole, doc, bytes, from) {
274
+ if (this.deferred.has(whole))
275
+ return undefined;
276
+ if (this.deferredBytesHeld + bytes > MAX_DEFERRED_BYTES)
277
+ return "deferred budget exhausted";
278
+ if (this.deferred.size >= MAX_DEFERRED)
279
+ this.unpark(this.deferred.keys().next().value);
280
+ this.deferred.set(whole, doc);
281
+ if (from !== undefined)
282
+ this.deferredFrom.set(whole, from);
283
+ this.deferredBytesHeld += bytes;
284
+ return undefined;
285
+ }
286
+ /** Drop a parked document and give its bytes back. */
287
+ unpark(whole) {
288
+ const doc = this.deferred.get(whole);
289
+ if (!doc)
290
+ return;
291
+ this.deferred.delete(whole);
292
+ this.deferredFrom.delete(whole);
293
+ this.deferredBytesHeld -= canonicalBytes(doc).length;
294
+ }
295
+ /** Bytes held by parked documents (R6-S5 accounting). */
296
+ deferredBytes() {
297
+ return this.deferredBytesHeld;
298
+ }
299
+ async retryDeferred() {
300
+ for (let pass = 0; pass < 3 && this.deferred.size; pass++) {
301
+ let progress = false;
302
+ for (const [whole, d] of [...this.deferred]) {
303
+ // R6-S2b: retried under the sender that sent it, so its waiting slots go back where they came from
304
+ const r = await this.ingest(d, this.deferredFrom.get(whole), true);
305
+ if (r.status !== "pending") {
306
+ this.unpark(whole);
307
+ progress ||= r.status === "accepted";
308
+ }
309
+ }
310
+ if (!progress)
311
+ break;
312
+ }
313
+ }
314
+ async ingestTrust(doc, key, from) {
315
+ // a grant needs nothing up front (its cap is applied once it is accepted, see `applyCap`); a revocation does:
316
+ // it counts only against a known grant and from the root or a strict ancestor of it, anything else is not
317
+ // stored (no member can fill anybody's storage with revocations that can never apply)
318
+ if (doc.t === "revoke") {
319
+ const target = this.trust.getGrant(doc.target);
320
+ if (!target)
321
+ return { status: "pending", id: key, reason: "unknown target" };
322
+ if (doc.parent !== undefined && !this.trust.getGrant(doc.parent))
323
+ return { status: "pending", id: key, reason: "unknown issuer grant" };
324
+ if (doc.issuer !== this.root.deviceId &&
325
+ !this.trust.canRevoke(doc.issuer, doc.target, doc.parent))
326
+ return {
327
+ status: "rejected",
328
+ id: key,
329
+ reason: "issuer cannot revoke this grant",
330
+ };
331
+ }
332
+ const r = await this.trust.add(doc, { from });
333
+ if (r.status === "accepted") {
334
+ if (this.applyCap(doc)) {
335
+ // the arriving document sorts last in its bucket: the kept set is already the cap-N smallest ids, so it
336
+ // goes straight back out (nothing better is held yet, and none is lost: it is asked for again)
337
+ return { status: "rejected", id: key, reason: "issuer over its cap" };
338
+ }
339
+ this.have.add(key);
340
+ this.version++;
341
+ return { status: "accepted", id: key };
342
+ }
343
+ if (r.status === "duplicate") {
344
+ this.have.add(key);
345
+ return { status: "duplicate", id: key };
346
+ }
347
+ if (r.status === "pending")
348
+ return { status: "pending", id: key, reason: r.reason };
349
+ return { status: "rejected", id: key, reason: r.reason };
350
+ }
351
+ /**
352
+ * R6-S4 (order-independent caps). A cap used to be "the first N that arrive are kept", so two devices holding the
353
+ * same documents concluded different things: different revocations kept give a different seq cut-off, and different
354
+ * grants kept give a different membership. The rule is now on the set, not on the arrival order: for each bucket
355
+ * (grants per issuer, revocations per (issuer, target)) the kept documents are the cap-N ones with the SMALLEST
356
+ * content address, and a document arriving with a smaller address evicts the largest kept one. Keeping the smallest
357
+ * ids is incremental (each arrival leaves the cap-N smallest of everything seen so far), so every device ends with
358
+ * the same set whatever the order, and it agrees with the existing `withinCap` rule for members.
359
+ * Returns true when the document just accepted is itself the one pushed out.
360
+ */
361
+ applyCap(doc) {
362
+ const cap = doc.t === "grant" ? MAX_STORED_PER_ISSUER : MAX_REVS_PER_ISSUER_TARGET;
363
+ if (cap <= 0)
364
+ return false;
365
+ if (doc.t === "grant" && doc.issuer === this.root.deviceId)
366
+ return false; // the root's own grants are not capped
367
+ const bucket = [];
368
+ for (const d of this.trust.docs()) {
369
+ if (d.issuer !== doc.issuer)
370
+ continue;
371
+ if (doc.t === "grant"
372
+ ? d.t === "grant"
373
+ : d.t === "revoke" && d.target === doc.target)
374
+ bucket.push(d.id);
375
+ }
376
+ if (bucket.length <= cap)
377
+ return false;
378
+ const worst = bucket.reduce((a, b) => (a > b ? a : b));
379
+ this.trust.dropAccepted([worst]);
380
+ const evicted = `${doc.t === "grant" ? "g" : "r"}:${worst}`;
381
+ this.have.delete(evicted);
382
+ // the same cap pushes it out on every device, so it is never asked for again (like a pruned rotation)
383
+ this.gone.add(evicted);
384
+ return worst === doc.id;
385
+ }
386
+ /** Identity key of a device: the root's, or the subject key of any of its grants (revoked ones too). */
387
+ identityOf(dev) {
388
+ return dev === this.root.deviceId ? this.root.pub : this.trust.keyOf(dev);
389
+ }
390
+ async ingestKex(doc, key) {
391
+ if (!isKexShape(doc))
392
+ return { status: "rejected", reason: "malformed key record" };
393
+ const pub = this.identityOf(doc.dev);
394
+ if (!pub)
395
+ return { status: "pending", id: key, reason: "unknown device" };
396
+ // N3: the signature is verified BEFORE anything is written to the persistent superseded list, so a forged record
397
+ // can never retire a real one for good (it would then never be asked for again)
398
+ if (!(await verifyCanonical(pub, kexBody(doc), doc.sig)))
399
+ return { status: "rejected", id: key, reason: "bad signature" };
400
+ const cur = this.kex.get(doc.dev);
401
+ if (cur && (cur.n > doc.n || (cur.n === doc.n && cur.key < key))) {
402
+ this.gone.add(key); // superseded: never asked for again
403
+ return { status: "duplicate", id: key, reason: "superseded" };
404
+ }
405
+ try {
406
+ const kem = b64uDecode(doc.kem);
407
+ if (kem.length !== ML_KEM_PUBLIC_KEY_BYTES ||
408
+ !(await isEcdhPublicKey(b64uDecode(doc.ecdh))))
409
+ return { status: "rejected", id: key, reason: "bad key" };
410
+ kemEncapsulate(kem); // FIPS 203 input check: a malformed key must never make a wrap throw
411
+ }
412
+ catch {
413
+ return { status: "rejected", id: key, reason: "bad key" };
414
+ }
415
+ if (cur) {
416
+ this.have.delete(cur.key);
417
+ this.gone.add(cur.key);
418
+ }
419
+ this.kex.set(doc.dev, { ...doc, key });
420
+ this.have.add(key);
421
+ this.version++;
422
+ return { status: "accepted", id: key };
423
+ }
424
+ async ingestRot(doc, key) {
425
+ if (!isRotDoc(doc))
426
+ return { status: "rejected", reason: "malformed rotation" };
427
+ if (doc.from !== this.root.deviceId)
428
+ return {
429
+ status: "rejected",
430
+ id: key,
431
+ reason: "rotation not from the owner",
432
+ };
433
+ const id = key.slice(2);
434
+ if ((await wrapsHash(doc.wraps)) !== doc.wh)
435
+ return { status: "rejected", id: key, reason: "wrap set does not match" };
436
+ let ok = false;
437
+ try {
438
+ ok = identityVerify(b64uDecode(this.root.pub), rotationSigBytes(id), b64uDecode(doc.sig));
439
+ }
440
+ catch { }
441
+ if (!ok)
442
+ return {
443
+ status: "rejected",
444
+ id: key,
445
+ reason: "rotation signature invalid",
446
+ };
447
+ for (const c of doc.cut)
448
+ this.cuts.add(c); // executed for good, even once the rotation itself is pruned
449
+ const { t, v, inst, epoch, from, to, revoked, cut, revs, n, wh, wraps, sig, } = doc;
450
+ this.rots.set(id, {
451
+ t,
452
+ v,
453
+ inst,
454
+ epoch,
455
+ from,
456
+ to,
457
+ revoked,
458
+ cut,
459
+ revs,
460
+ n,
461
+ wh,
462
+ wraps,
463
+ sig,
464
+ id,
465
+ });
466
+ this.have.add(key);
467
+ while (this.rots.size > MAX_ROTATIONS_KEPT) {
468
+ const oldest = [...this.rots.values()].sort((a, b) => a.epoch - b.epoch || (a.id < b.id ? 1 : -1))[0];
469
+ this.rots.delete(oldest.id);
470
+ this.have.delete(`R:${oldest.id}`);
471
+ this.gone.add(`R:${oldest.id}`);
472
+ }
473
+ this.version++;
474
+ return { status: "accepted", id: key };
475
+ }
476
+ // ─── persistence ─────────────────────────────────────────────────────────
477
+ saving = null;
478
+ dirty = false;
479
+ /** Persist, coalesced: at most one write in flight and one more queued. */
480
+ scheduleSave() {
481
+ this.dirty = true;
482
+ if (this.saving)
483
+ return;
484
+ this.saving = (async () => {
485
+ while (this.dirty) {
486
+ this.dirty = false;
487
+ try {
488
+ await this.save();
489
+ }
490
+ catch { }
491
+ }
492
+ this.saving = null;
493
+ })();
494
+ }
495
+ /** Resolves once everything accepted so far is in the local store. */
496
+ async flush() {
497
+ await this.queue;
498
+ while (this.saving)
499
+ await this.saving;
500
+ }
501
+ async save() {
502
+ await this.store.set("sec/trust", this.trust.docs());
503
+ await this.store.set("sec/kex", [...this.kex.values()].map(({ key: _k, ...d }) => d));
504
+ await this.store.set("sec/rots", [...this.rots.values()].map(({ id: _i, ...d }) => d));
505
+ await this.store.set("sec/cuts", [...this.cuts]);
506
+ await this.store.set("sec/gone", this.gone.values());
507
+ }
508
+ async load() {
509
+ const arr = async (k) => {
510
+ const v = await this.store.get(k);
511
+ return Array.isArray(v) ? v : [];
512
+ };
513
+ for (const c of await arr("sec/cuts"))
514
+ if (typeof c === "string")
515
+ this.cuts.add(c);
516
+ for (const g of await arr("sec/gone"))
517
+ if (typeof g === "string")
518
+ this.gone.add(g);
519
+ // everything is verified again (the local store is the device's, but checks are cheap enough and keep one path)
520
+ await this.addMany([
521
+ ...(await arr("sec/trust")),
522
+ ...(await arr("sec/kex")),
523
+ ...(await arr("sec/rots")),
524
+ ]);
525
+ }
526
+ // ─── queries ─────────────────────────────────────────────────────────────
527
+ /** Grant ids executed by an owner rotation (authoritative cuts). */
528
+ isCut(grantId) {
529
+ return this.cuts.has(grantId);
530
+ }
531
+ executedCuts() {
532
+ return [...this.cuts];
533
+ }
534
+ chainOf(g) {
535
+ const out = [g];
536
+ for (let p = g.parent ? this.trust.getGrant(g.parent) : undefined; p && out.length < 8; p = p.parent ? this.trust.getGrant(p.parent) : undefined)
537
+ out.push(p);
538
+ return out;
539
+ }
540
+ /** The grant was cut off by an owner rotation (itself or an ancestor: revocations cascade). */
541
+ executed(g) {
542
+ return this.chainOf(g).some((x) => this.cuts.has(x.id));
543
+ }
544
+ /** R5-S3: an admin's grants count up to MAX_MEMBERS_PER_ADMIN (its lowest grant ids: the same on every device). */
545
+ withinCap(g) {
546
+ if (g.issuer === this.root.deviceId)
547
+ return true;
548
+ if (!this.capIndex) {
549
+ const by = new Map();
550
+ for (const d of this.trust.docs())
551
+ if (d.t === "grant" && d.issuer !== this.root.deviceId)
552
+ by.set(d.issuer, [...(by.get(d.issuer) ?? []), d.id]);
553
+ this.capIndex = new Map([...by].map(([k, ids]) => [
554
+ k,
555
+ new Set(ids.sort().slice(0, MAX_MEMBERS_PER_ADMIN)),
556
+ ]));
557
+ }
558
+ return this.capIndex.get(g.issuer)?.has(g.id) ?? false;
559
+ }
560
+ /** The grant that makes `dev` a member now (best role), or null. Never null for the root (its own authority). */
561
+ memberGrant(dev) {
562
+ if (dev === this.root.deviceId)
563
+ return null;
564
+ const ok = this.trust
565
+ .activeGrants(dev, { time: this.now() })
566
+ .filter((g) => !this.executed(g) && this.chainOf(g).every((x) => this.withinCap(x)));
567
+ ok.sort((a, b) => b.delegate - a.delegate ||
568
+ this.chainOf(a).length - this.chainOf(b).length ||
569
+ (a.id < b.id ? -1 : 1));
570
+ return ok[0] ?? null;
571
+ }
572
+ roleOf(dev) {
573
+ if (dev === this.root.deviceId)
574
+ return "owner";
575
+ const g = this.memberGrant(dev);
576
+ return g ? (g.delegate >= 1 ? "admin" : "member") : null;
577
+ }
578
+ /** Every device with a grant (members and former members), except the root. */
579
+ knownDevices() {
580
+ const out = new Set();
581
+ for (const d of this.trust.docs())
582
+ if (d.t === "grant")
583
+ out.add(d.subject.fp);
584
+ out.delete(this.root.deviceId);
585
+ return [...out];
586
+ }
587
+ /**
588
+ * Results of the whole-set queries. Valid while no document is added (both counters unchanged)
589
+ * AND within the same second: membership depends on the clock (grants expire / start), so a
590
+ * cache keyed only on versions would keep an expired member listed until the next document.
591
+ */
592
+ memo = { key: "" };
593
+ memoFor() {
594
+ const key = `${this.version}|${this.trust.version}|${Math.floor(this.now() / 1000)}`;
595
+ if (this.memo.key !== key)
596
+ this.memo = { key };
597
+ return this.memo;
598
+ }
599
+ members() {
600
+ const m = this.memoFor();
601
+ if (!m.members)
602
+ m.members = this.computeMembers();
603
+ return m.members;
604
+ }
605
+ computeMembers() {
606
+ const out = [];
607
+ for (const dev of this.knownDevices()) {
608
+ const g = this.memberGrant(dev);
609
+ if (g)
610
+ out.push({
611
+ deviceId: dev,
612
+ pub: g.subject.pub,
613
+ role: g.delegate >= 1 ? "admin" : "member",
614
+ grant: g,
615
+ });
616
+ }
617
+ return out;
618
+ }
619
+ /** Has grants, none of which makes it a member any more. */
620
+ isOut(dev) {
621
+ return (dev !== this.root.deviceId &&
622
+ this.trust.grantsOf(dev).length > 0 &&
623
+ this.memberGrant(dev) === null);
624
+ }
625
+ /**
626
+ * R5-B2: devices that lost membership through a revocation the owner has not executed yet: whoever they are, and
627
+ * whenever they were paired, they may hold the current key. Executed = cut by an owner rotation (itself or an
628
+ * ancestor). Returns the devices, the grant ids to cut and the revocation documents behind them.
629
+ */
630
+ unexecuted(only) {
631
+ if (!only) {
632
+ const m = this.memoFor();
633
+ if (!m.unexecuted)
634
+ m.unexecuted = this.computeUnexecuted();
635
+ return m.unexecuted;
636
+ }
637
+ return this.computeUnexecuted(only);
638
+ }
639
+ computeUnexecuted(only) {
640
+ const devices = new Set();
641
+ const cut = new Set();
642
+ const revs = new Set();
643
+ const revocations = this.trust.revocations();
644
+ for (const dev of this.knownDevices()) {
645
+ if (only && !only.has(dev))
646
+ continue;
647
+ if (this.memberGrant(dev))
648
+ continue;
649
+ for (const g of this.trust.grantsOf(dev)) {
650
+ if (this.trust.cutOf(g.id) === Number.POSITIVE_INFINITY ||
651
+ this.executed(g))
652
+ continue;
653
+ devices.add(dev);
654
+ cut.add(g.id);
655
+ const chain = new Set(this.chainOf(g).map((x) => x.id));
656
+ for (const r of revocations)
657
+ if (chain.has(r.target))
658
+ revs.add(r.id);
659
+ }
660
+ }
661
+ return {
662
+ devices: [...devices].sort(),
663
+ cut: [...cut].sort(),
664
+ revs: [...revs].sort(),
665
+ };
666
+ }
667
+ /** Best key-agreement record of a device (root or one with a grant). */
668
+ keyAgreement(dev) {
669
+ const k = this.kex.get(dev);
670
+ return k ? { ecdh: k.ecdh, kem: k.kem } : null;
671
+ }
672
+ kexDoc(dev) {
673
+ const k = this.kex.get(dev);
674
+ if (!k)
675
+ return null;
676
+ const { key: _k, ...d } = k;
677
+ return d;
678
+ }
679
+ rotations() {
680
+ return [...this.rots.values()].sort((a, b) => a.epoch - b.epoch);
681
+ }
682
+ rotation(id) {
683
+ return this.rots.get(id);
684
+ }
685
+ // ─── exchange ────────────────────────────────────────────────────────────
686
+ inventory() {
687
+ return [...this.have];
688
+ }
689
+ /**
690
+ * Keys among `ids` this device does not hold (nor dropped for good, nor knows to be bad).
691
+ * R6-B1: parked documents do NOT hide a key. Only a VERIFIED (accepted) document with that id stops it from being
692
+ * asked for again: otherwise one forged copy parked under an id withholds the genuine one from every peer.
693
+ */
694
+ missing(ids) {
695
+ return ids.filter((k) => typeof k === "string" &&
696
+ k.length <= 64 &&
697
+ /^[gkrR]:/.test(k) &&
698
+ !this.have.has(k) &&
699
+ !this.gone.has(k));
700
+ }
701
+ async get(keys) {
702
+ const want = new Set(keys);
703
+ const out = [];
704
+ for (const d of this.trust.docs())
705
+ if (want.has(`${d.t === "grant" ? "g" : "r"}:${d.id}`))
706
+ out.push(d);
707
+ for (const k of this.kex.values())
708
+ if (want.has(k.key))
709
+ out.push(this.kexDoc(k.dev));
710
+ for (const r of this.rots.values())
711
+ if (want.has(`R:${r.id}`)) {
712
+ const { id: _i, ...d } = r;
713
+ out.push(d);
714
+ }
715
+ return out;
716
+ }
717
+ // ─── issuing ─────────────────────────────────────────────────────────────
718
+ /** Grant for `subjectPub` signed by `signer` (the root, or a member whose own grant may delegate). */
719
+ async issueGrant(signer, subjectPub, o) {
720
+ if (!isPublicKey(subjectPub))
721
+ throw new Error("subject is not an ML-DSA-65 public key");
722
+ const preset = this.trust.schema.roles?.[o.role] ?? MESH_SCHEMA.roles?.[o.role];
723
+ if (!preset)
724
+ throw new Error(`unknown role "${o.role}"`);
725
+ const parent = signer.fp === this.root.deviceId
726
+ ? undefined
727
+ : this.memberGrant(signer.fp);
728
+ if (signer.fp !== this.root.deviceId && (!parent || parent.delegate < 1))
729
+ throw new Error("this device cannot admit others");
730
+ return issueGrant(signer, {
731
+ subject: { pub: subjectPub },
732
+ role: o.role,
733
+ permissions: o.permissions ?? { ...preset.permissions },
734
+ delegate: preset.delegate ?? 0,
735
+ name: o.name,
736
+ notBefore: 0, // B6: membership never depends on a clock
737
+ epoch: o.epoch,
738
+ issuedAt: o.now,
739
+ }, { inst: this.inst, parent: parent ?? undefined, now: o.now });
740
+ }
741
+ /**
742
+ * Revocations of every grant of `target` that `signer` may revoke (root: all of them; an admin: those it issued,
743
+ * directly or below). `heads` (web/oplog `headIds()`) keep the target's history up to there; omitted = none kept.
744
+ */
745
+ async revocationsFor(signer, target, o = {}) {
746
+ const own = signer.fp === this.root.deviceId
747
+ ? undefined
748
+ : this.memberGrant(signer.fp);
749
+ const out = [];
750
+ for (const g of this.trust.grantsOf(target)) {
751
+ if (this.trust.cutOf(g.id) !== Number.POSITIVE_INFINITY ||
752
+ this.executed(g))
753
+ continue;
754
+ if (own && !this.trust.canRevoke(signer.fp, g.id, own.id))
755
+ continue;
756
+ if (!own && signer.fp !== this.root.deviceId)
757
+ continue;
758
+ const input = this.trust.prepareRevocation(g.id, o.heads ?? {}, o.reason);
759
+ out.push(await issueRevocation(signer, input, {
760
+ inst: this.inst,
761
+ parent: own ?? undefined,
762
+ now: o.now,
763
+ }));
764
+ }
765
+ return out;
766
+ }
767
+ /** This device's own key-agreement record (counter `n` above every record of it seen so far). */
768
+ async kexRecord(signer, ecdh, kem) {
769
+ const cur = this.kex.get(signer.fp);
770
+ if (cur && cur.ecdh === ecdh && cur.kem === kem)
771
+ return this.kexDoc(signer.fp);
772
+ const body = {
773
+ t: "kex",
774
+ v: 1,
775
+ inst: this.inst,
776
+ dev: signer.fp,
777
+ ecdh,
778
+ kem,
779
+ n: (cur?.n ?? -1) + 1,
780
+ };
781
+ return { ...body, sig: await signCanonical(signer, body) };
782
+ }
783
+ }
784
+ /** A web/trust Signer over a mesh vault (ML-DSA-65 identity key). */
785
+ export function vaultSigner(v, pubB64) {
786
+ return { alg: SIG_ALG, fp: v.deviceId, pub: pubB64, sign: (d) => v.sign(d) };
787
+ }
788
+ export { isEpoch };
789
+ //# sourceMappingURL=secstate.js.map