space-data-module-sdk 0.8.3 → 0.8.5

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 (354) hide show
  1. package/README.md +117 -20
  2. package/bin/space-data-module.js +263 -0
  3. package/docs/.nojekyll +1 -0
  4. package/docs/AGENTS.md +34 -0
  5. package/docs/architecture.svg +141 -0
  6. package/docs/browser-wasmedge-isomorphic.md +179 -0
  7. package/docs/flatsql-streaming-standard.md +341 -0
  8. package/docs/gpu-module-abi.md +193 -0
  9. package/docs/index.html +125 -0
  10. package/docs/isomorphic-pthreads.md +188 -0
  11. package/docs/isomorphic-sdn-runtime-plan.md +167 -0
  12. package/docs/language-runtime-matrix.md +82 -0
  13. package/docs/manifest-structure.svg +114 -0
  14. package/docs/module-bundle-runtime-plan.md +19 -0
  15. package/docs/module-lifecycle.svg +70 -0
  16. package/docs/module-publication-standard.md +540 -0
  17. package/docs/node-red-default-node-parity.md +88 -0
  18. package/docs/protocol-installation.md +196 -0
  19. package/docs/secrets-capability.md +152 -0
  20. package/docs/styles.css +288 -0
  21. package/docs/testing-harness.md +408 -0
  22. package/docs/tri-runtime-parity.md +132 -0
  23. package/docs/wallet-callback.html +2 -0
  24. package/package.json +26 -6
  25. package/schemas/HttpRequestAbi.fbs +41 -0
  26. package/schemas/HttpResponseAbi.fbs +36 -0
  27. package/schemas/PluginManifest.fbs +2 -1
  28. package/src/AGENTS.md +1 -1
  29. package/src/app/codec.js +548 -0
  30. package/src/app/index.js +6 -0
  31. package/src/browser.js +5 -0
  32. package/src/bundle/constants.js +1 -0
  33. package/src/bundle/index.js +15 -0
  34. package/src/bundle/sigdomain.js +179 -0
  35. package/src/bundle/signing.js +767 -0
  36. package/src/bundle/wasm.js +3 -2
  37. package/src/capabilities.js +3 -0
  38. package/src/compat/index.js +7 -0
  39. package/src/compat/sdnLegacy.js +449 -0
  40. package/src/compiler/compileModule.js +556 -55
  41. package/src/compiler/flatcSupport.js +41 -27
  42. package/src/compiler/index.d.ts +2 -0
  43. package/src/compiler/index.js +18 -0
  44. package/src/compiler/invokeGlue.js +1212 -183
  45. package/src/compiler/pthreadArtifactGuard.js +910 -0
  46. package/src/compiler/sdnShimGenerator.js +323 -0
  47. package/src/compiler/wasiThreadsToolchain.js +150 -0
  48. package/src/compliance/index.js +11 -1
  49. package/src/compliance/legacyWildcardPorts.js +228 -0
  50. package/src/compliance/pluginCompliance.js +1000 -96
  51. package/src/field-stream/index.d.ts +31 -0
  52. package/src/field-stream/index.js +119 -0
  53. package/src/flow/dependencyStreamBridge.js +234 -0
  54. package/src/flow/flatsqlLinkShim.js +304 -0
  55. package/src/flow/flowCodec.js +107 -0
  56. package/src/flow/flowCompiler.js +2900 -0
  57. package/src/flow/flowRuntimeHost.js +877 -0
  58. package/src/flow/index.d.ts +117 -0
  59. package/src/flow/index.js +31 -0
  60. package/src/flow/isomorphicFlowHost.js +384 -0
  61. package/src/flow/jsFlowRuntime.js +275 -0
  62. package/src/flow/normalize.js +294 -0
  63. package/src/flow/runtime-src/flow_runtime.cpp +1699 -0
  64. package/src/flow/vendor/sdn-flow/MethodRegistry.js +400 -0
  65. package/src/flow/vendor/sdn-flow/constants.js +39 -0
  66. package/src/flow/vendor/sdn-flow/index.js +29 -0
  67. package/src/flow/vendor/sdn-flow/normalize.js +506 -0
  68. package/src/flow/vendor/sdn-flow/wasmCompatibility.js +189 -0
  69. package/src/generated/http/cpp/HttpRequestAbi_generated.h +285 -0
  70. package/src/generated/http/cpp/HttpResponseAbi_generated.h +192 -0
  71. package/src/generated/http/sdn/http/http-header.js +53 -0
  72. package/src/generated/http/sdn/http/http-header.ts +67 -0
  73. package/src/generated/http/sdn/http/http-request.js +132 -0
  74. package/src/generated/http/sdn/http/http-request.ts +179 -0
  75. package/src/generated/http/sdn/http/http-response.js +144 -0
  76. package/src/generated/http/sdn/http/http-response.ts +168 -0
  77. package/src/generated/http/sdn/http.js +11 -0
  78. package/src/generated/http/sdn/http.ts +4 -0
  79. package/src/generated/orbpro/flow/backpressure-policy.d.ts +12 -0
  80. package/src/generated/orbpro/flow/backpressure-policy.d.ts.map +1 -0
  81. package/src/generated/orbpro/flow/backpressure-policy.js +17 -0
  82. package/src/generated/orbpro/flow/backpressure-policy.js.map +1 -0
  83. package/src/generated/orbpro/flow/backpressure-policy.ts +15 -0
  84. package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts +142 -0
  85. package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts.map +1 -0
  86. package/src/generated/orbpro/flow/flow-bundle-manifest.js +304 -0
  87. package/src/generated/orbpro/flow/flow-bundle-manifest.js.map +1 -0
  88. package/src/generated/orbpro/flow/flow-bundle-manifest.ts +399 -0
  89. package/src/generated/orbpro/flow/flow-edge.d.ts +113 -0
  90. package/src/generated/orbpro/flow/flow-edge.d.ts.map +1 -0
  91. package/src/generated/orbpro/flow/flow-edge.js +221 -0
  92. package/src/generated/orbpro/flow/flow-edge.js.map +1 -0
  93. package/src/generated/orbpro/flow/flow-edge.ts +292 -0
  94. package/src/generated/orbpro/flow/flow-node.d.ts +80 -0
  95. package/src/generated/orbpro/flow/flow-node.d.ts.map +1 -0
  96. package/src/generated/orbpro/flow/flow-node.js +158 -0
  97. package/src/generated/orbpro/flow/flow-node.js.map +1 -0
  98. package/src/generated/orbpro/flow/flow-node.ts +202 -0
  99. package/src/generated/orbpro/flow/flow-program.d.ts +164 -0
  100. package/src/generated/orbpro/flow/flow-program.d.ts.map +1 -0
  101. package/src/generated/orbpro/flow/flow-program.js +350 -0
  102. package/src/generated/orbpro/flow/flow-program.js.map +1 -0
  103. package/src/generated/orbpro/flow/flow-program.ts +477 -0
  104. package/src/generated/orbpro/flow/flow-trigger.d.ts +108 -0
  105. package/src/generated/orbpro/flow/flow-trigger.d.ts.map +1 -0
  106. package/src/generated/orbpro/flow/flow-trigger.js +207 -0
  107. package/src/generated/orbpro/flow/flow-trigger.js.map +1 -0
  108. package/src/generated/orbpro/flow/flow-trigger.ts +276 -0
  109. package/src/generated/orbpro/flow/node-kind.d.ts +13 -0
  110. package/src/generated/orbpro/flow/node-kind.d.ts.map +1 -0
  111. package/src/generated/orbpro/flow/node-kind.js +16 -0
  112. package/src/generated/orbpro/flow/node-kind.js.map +1 -0
  113. package/src/generated/orbpro/{invoke.ts → flow/node-kind.ts} +12 -2
  114. package/src/generated/orbpro/flow/plugin-version-binding.d.ts +52 -0
  115. package/src/generated/orbpro/flow/plugin-version-binding.d.ts.map +1 -0
  116. package/src/generated/orbpro/flow/plugin-version-binding.js +85 -0
  117. package/src/generated/orbpro/flow/plugin-version-binding.js.map +1 -0
  118. package/src/generated/orbpro/flow/plugin-version-binding.ts +121 -0
  119. package/src/generated/orbpro/flow/schema-hash-binding.d.ts +70 -0
  120. package/src/generated/orbpro/flow/schema-hash-binding.d.ts.map +1 -0
  121. package/src/generated/orbpro/flow/schema-hash-binding.js +143 -0
  122. package/src/generated/orbpro/flow/schema-hash-binding.js.map +1 -0
  123. package/src/generated/orbpro/flow/schema-hash-binding.ts +189 -0
  124. package/src/generated/orbpro/flow/trigger-binding.d.ts +71 -0
  125. package/src/generated/orbpro/flow/trigger-binding.d.ts.map +1 -0
  126. package/src/generated/orbpro/flow/trigger-binding.js +142 -0
  127. package/src/generated/orbpro/flow/trigger-binding.js.map +1 -0
  128. package/src/generated/orbpro/flow/trigger-binding.ts +181 -0
  129. package/src/generated/orbpro/flow/trigger-kind.d.ts +14 -0
  130. package/src/generated/orbpro/flow/trigger-kind.d.ts.map +1 -0
  131. package/src/generated/orbpro/flow/trigger-kind.js +17 -0
  132. package/src/generated/orbpro/flow/trigger-kind.js.map +1 -0
  133. package/src/generated/orbpro/flow/trigger-kind.ts +17 -0
  134. package/src/generated/orbpro/flow.d.ts +21 -0
  135. package/src/generated/orbpro/flow.d.ts.map +1 -0
  136. package/src/generated/orbpro/flow.js +23 -0
  137. package/src/generated/orbpro/flow.js.map +1 -0
  138. package/src/generated/orbpro/flow.ts +24 -0
  139. package/src/generated/orbpro/manifest/capability-kind.d.ts +2 -1
  140. package/src/generated/orbpro/manifest/capability-kind.js +1 -0
  141. package/src/generated/orbpro/manifest/capability-kind.ts +1 -0
  142. package/src/generated/orbpro/plugin/stream-invoke-request.d.ts +93 -0
  143. package/src/generated/orbpro/plugin/stream-invoke-request.js +160 -0
  144. package/src/generated/orbpro/plugin/stream-invoke-request.ts +211 -0
  145. package/src/generated/orbpro/plugin/stream-invoke-response.d.ts +99 -0
  146. package/src/generated/orbpro/plugin/stream-invoke-response.js +175 -0
  147. package/src/generated/orbpro/plugin/stream-invoke-response.ts +226 -0
  148. package/src/generated/orbpro/plugin.js +8 -0
  149. package/src/generated/orbpro/stream.d.ts +12 -0
  150. package/src/generated/orbpro/stream.js +14 -0
  151. package/src/generated/orbpro/stream.ts +15 -0
  152. package/src/generated/spacedatastandards/plg/EntryFunction.d.ts +52 -0
  153. package/src/generated/spacedatastandards/plg/EntryFunction.d.ts.map +1 -0
  154. package/src/generated/spacedatastandards/plg/EntryFunction.js +106 -0
  155. package/src/generated/spacedatastandards/plg/EntryFunction.ts +162 -0
  156. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts +90 -0
  157. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts.map +1 -0
  158. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.js +189 -0
  159. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.ts +270 -0
  160. package/src/generated/spacedatastandards/plg/PLG.d.ts +492 -0
  161. package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -0
  162. package/src/generated/spacedatastandards/plg/PLG.js +1139 -0
  163. package/src/generated/spacedatastandards/plg/PLG.ts +1600 -0
  164. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts +63 -0
  165. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts.map +1 -0
  166. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.js +138 -0
  167. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.ts +189 -0
  168. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts +41 -0
  169. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts.map +1 -0
  170. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.js +104 -0
  171. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.ts +151 -0
  172. package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts +68 -0
  173. package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts.map +1 -0
  174. package/src/generated/spacedatastandards/plg/PLGFlowEdge.js +124 -0
  175. package/src/generated/spacedatastandards/plg/PLGFlowEdge.ts +184 -0
  176. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts +58 -0
  177. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts.map +1 -0
  178. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.js +126 -0
  179. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.ts +161 -0
  180. package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts +82 -0
  181. package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts.map +1 -0
  182. package/src/generated/spacedatastandards/plg/PLGFlowNode.js +164 -0
  183. package/src/generated/spacedatastandards/plg/PLGFlowNode.ts +244 -0
  184. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts +55 -0
  185. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts.map +1 -0
  186. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.js +105 -0
  187. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.ts +162 -0
  188. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts +42 -0
  189. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts.map +1 -0
  190. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.js +83 -0
  191. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.ts +127 -0
  192. package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts +36 -0
  193. package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts.map +1 -0
  194. package/src/generated/spacedatastandards/plg/PLGHostCapability.js +90 -0
  195. package/src/generated/spacedatastandards/plg/PLGHostCapability.ts +127 -0
  196. package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts +53 -0
  197. package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts.map +1 -0
  198. package/src/generated/spacedatastandards/plg/PLGMethodManifest.js +153 -0
  199. package/src/generated/spacedatastandards/plg/PLGMethodManifest.ts +210 -0
  200. package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts +70 -0
  201. package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts.map +1 -0
  202. package/src/generated/spacedatastandards/plg/PLGPortManifest.js +149 -0
  203. package/src/generated/spacedatastandards/plg/PLGPortManifest.ts +212 -0
  204. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts +73 -0
  205. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts.map +1 -0
  206. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.js +199 -0
  207. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.ts +292 -0
  208. package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts +40 -0
  209. package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts.map +1 -0
  210. package/src/generated/spacedatastandards/plg/PLGTimerSpec.js +103 -0
  211. package/src/generated/spacedatastandards/plg/PLGTimerSpec.ts +148 -0
  212. package/src/generated/spacedatastandards/plg/PluginCapability.d.ts +41 -0
  213. package/src/generated/spacedatastandards/plg/PluginCapability.d.ts.map +1 -0
  214. package/src/generated/spacedatastandards/plg/PluginCapability.js +82 -0
  215. package/src/generated/spacedatastandards/plg/{plugin-capability.ts → PluginCapability.ts} +55 -18
  216. package/src/generated/spacedatastandards/plg/PluginDependency.d.ts +42 -0
  217. package/src/generated/spacedatastandards/plg/PluginDependency.d.ts.map +1 -0
  218. package/src/generated/spacedatastandards/plg/PluginDependency.js +80 -0
  219. package/src/generated/spacedatastandards/plg/PluginDependency.ts +124 -0
  220. package/src/generated/spacedatastandards/plg/TAB.d.ts +85 -0
  221. package/src/generated/spacedatastandards/plg/TAB.d.ts.map +1 -0
  222. package/src/generated/spacedatastandards/plg/TAB.js +173 -0
  223. package/src/generated/spacedatastandards/plg/TAB.ts +228 -0
  224. package/src/generated/spacedatastandards/plg/bufferMutability.d.ts +18 -0
  225. package/src/generated/spacedatastandards/plg/bufferMutability.d.ts.map +1 -0
  226. package/src/generated/spacedatastandards/plg/bufferMutability.js +20 -0
  227. package/src/generated/spacedatastandards/plg/bufferMutability.ts +23 -0
  228. package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts +18 -0
  229. package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts.map +1 -0
  230. package/src/generated/spacedatastandards/plg/bufferOwnership.js +20 -0
  231. package/src/generated/spacedatastandards/plg/bufferOwnership.ts +23 -0
  232. package/src/generated/spacedatastandards/plg/drainBehavior.d.ts +20 -0
  233. package/src/generated/spacedatastandards/plg/drainBehavior.d.ts.map +1 -0
  234. package/src/generated/spacedatastandards/plg/drainBehavior.js +22 -0
  235. package/src/generated/spacedatastandards/plg/drainBehavior.ts +25 -0
  236. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts +10 -0
  237. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts.map +1 -0
  238. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.js +12 -0
  239. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.ts +13 -0
  240. package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts +51 -0
  241. package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts.map +1 -0
  242. package/src/generated/spacedatastandards/plg/hostCapabilityKind.js +53 -0
  243. package/src/generated/spacedatastandards/plg/hostCapabilityKind.ts +55 -0
  244. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts +15 -0
  245. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts.map +1 -0
  246. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.js +17 -0
  247. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.ts +19 -0
  248. package/src/generated/spacedatastandards/plg/main.d.ts +29 -0
  249. package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -0
  250. package/src/generated/spacedatastandards/plg/main.js +30 -0
  251. package/src/generated/spacedatastandards/plg/main.ts +32 -0
  252. package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts +17 -0
  253. package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts.map +1 -0
  254. package/src/generated/spacedatastandards/plg/payloadWireFormat.js +19 -0
  255. package/src/generated/spacedatastandards/plg/payloadWireFormat.ts +21 -0
  256. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +82 -0
  257. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -0
  258. package/src/generated/spacedatastandards/plg/pluginCategory.js +84 -0
  259. package/src/generated/spacedatastandards/plg/pluginCategory.ts +103 -0
  260. package/src/generated/spacedatastandards/plg/publicationState.d.ts +18 -0
  261. package/src/generated/spacedatastandards/plg/publicationState.d.ts.map +1 -0
  262. package/src/generated/spacedatastandards/plg/publicationState.js +20 -0
  263. package/src/generated/spacedatastandards/plg/purchaseTier.d.ts +18 -0
  264. package/src/generated/spacedatastandards/plg/purchaseTier.d.ts.map +1 -0
  265. package/src/generated/spacedatastandards/plg/purchaseTier.js +20 -0
  266. package/src/host/AGENTS.md +2 -2
  267. package/src/host/abi.js +53 -66
  268. package/src/host/browserHost.js +355 -8
  269. package/src/host/cpp/keyslotClient.hpp +231 -0
  270. package/src/host/cpp/secretsClient.hpp +583 -0
  271. package/src/host/hostcallWire.js +183 -0
  272. package/src/host/index.js +2 -0
  273. package/src/host/isomorphicLoader.js +44 -7
  274. package/src/host/nodeHost.js +227 -8
  275. package/src/host/sabHostcallChannel.js +206 -0
  276. package/src/host/timerDriver.js +203 -0
  277. package/src/host/wasiShim.js +41 -1
  278. package/src/host/wasiThreadBrowserWorker.mjs +48 -0
  279. package/src/host/wasiThreadHost.js +393 -0
  280. package/src/host/wasiThreadWorker.mjs +32 -0
  281. package/src/host/wasiThreadWorkerRuntime.js +93 -0
  282. package/src/http/index.js +339 -0
  283. package/src/index.d.ts +716 -10
  284. package/src/index.js +4 -0
  285. package/src/invoke/codec.js +1202 -128
  286. package/src/invoke/index.js +5 -1
  287. package/src/licensing/index.d.ts +2 -0
  288. package/src/licensing/index.js +2 -0
  289. package/src/licensing/records.js +287 -8
  290. package/src/manifest/codec.js +62 -14
  291. package/src/manifest/index.js +2 -0
  292. package/src/manifest/legacyToPlg.js +164 -25
  293. package/src/manifest/normalize.js +1 -0
  294. package/src/manifest/plgCodec.js +1303 -62
  295. package/src/manifest/typeRefs.js +162 -24
  296. package/src/runtime/compiledRuntimeAbi.js +47 -0
  297. package/src/runtime/compiledRuntimeAbi.json +33 -0
  298. package/src/runtime/index.d.ts +32 -0
  299. package/src/runtime/index.js +1 -0
  300. package/src/runtime-host/index.js +3 -0
  301. package/src/runtime-host/methodRegistry.js +55 -0
  302. package/src/standards/index.js +200 -56
  303. package/src/standards/sharedCatalog.js +16 -0
  304. package/src/testing/browserModuleHarness.js +604 -31
  305. package/src/testing/docker/wasmedge-parity.Dockerfile +39 -0
  306. package/src/testing/index.d.ts +151 -0
  307. package/src/testing/index.js +21 -0
  308. package/src/testing/native/wasmedge_emscripten_pthread_runner.c +25 -1
  309. package/src/testing/parityBrowserRunner.js +138 -0
  310. package/src/testing/parityHarness.js +616 -0
  311. package/src/testing/parityLanes.js +537 -0
  312. package/src/testing/processInvoke.js +29 -21
  313. package/src/testing/streamInvokeCodec.js +10 -175
  314. package/src/testing/wasmedgePin.json +6 -0
  315. package/src/testing/workerModuleHarness.js +242 -0
  316. package/src/testing/workerModuleHarnessWorker.js +176 -0
  317. package/src/transport/index.js +3 -0
  318. package/src/transport/pki.js +299 -11
  319. package/src/transport/records.js +16 -7
  320. package/src/utils/ecdsaDer.js +106 -0
  321. package/src/utils/wasmCrypto.js +0 -43
  322. package/templates/gpu-module/README.md +48 -0
  323. package/templates/gpu-module/include/space_data_gpu_abi.h +122 -0
  324. package/templates/gpu-module/manifest.json +84 -0
  325. package/schemas/PluginInvokeRequest.fbs +0 -18
  326. package/schemas/PluginInvokeResponse.fbs +0 -30
  327. package/schemas/spacedatastandards/PLG.fbs +0 -191
  328. package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts +0 -51
  329. package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts.map +0 -1
  330. package/src/generated/orbpro/invoke/plugin-invoke-request.js +0 -131
  331. package/src/generated/orbpro/invoke/plugin-invoke-request.js.map +0 -1
  332. package/src/generated/orbpro/invoke/plugin-invoke-request.ts +0 -173
  333. package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts +0 -76
  334. package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts.map +0 -1
  335. package/src/generated/orbpro/invoke/plugin-invoke-response.js +0 -184
  336. package/src/generated/orbpro/invoke/plugin-invoke-response.js.map +0 -1
  337. package/src/generated/orbpro/invoke/plugin-invoke-response.ts +0 -243
  338. package/src/generated/orbpro/invoke.d.ts +0 -3
  339. package/src/generated/orbpro/invoke.js +0 -5
  340. package/src/generated/spacedatastandards/plg/entry-function.js +0 -86
  341. package/src/generated/spacedatastandards/plg/entry-function.ts +0 -119
  342. package/src/generated/spacedatastandards/plg/index.js +0 -16
  343. package/src/generated/spacedatastandards/plg/index.ts +0 -11
  344. package/src/generated/spacedatastandards/plg/plg.js +0 -657
  345. package/src/generated/spacedatastandards/plg/plg.ts +0 -924
  346. package/src/generated/spacedatastandards/plg/plugin-capability.js +0 -66
  347. package/src/generated/spacedatastandards/plg/plugin-category.js +0 -15
  348. package/src/generated/spacedatastandards/plg/plugin-category.ts +0 -53
  349. package/src/generated/spacedatastandards/plg/plugin-dependency.js +0 -63
  350. package/src/generated/spacedatastandards/plg/plugin-dependency.ts +0 -86
  351. package/src/generated/spacedatastandards/plg/publication-state.js +0 -9
  352. package/src/generated/spacedatastandards/plg/purchase-tier.js +0 -9
  353. /package/src/generated/spacedatastandards/plg/{publication-state.ts → publicationState.ts} +0 -0
  354. /package/src/generated/spacedatastandards/plg/{purchase-tier.ts → purchaseTier.ts} +0 -0
@@ -0,0 +1,767 @@
1
+ import {
2
+ computeCanonicalModuleHash,
3
+ createSingleFileBundle,
4
+ getWasmCustomSections,
5
+ parseSingleFileBundle,
6
+ } from "./wasm.js";
7
+ import { SDS_MANIFEST_SECTION_NAME } from "./constants.js";
8
+ import {
9
+ extractPublicationRecordCollection,
10
+ } from "../transport/records.js";
11
+ import {
12
+ ed25519PublicKey,
13
+ ed25519Sign,
14
+ ed25519Verify,
15
+ } from "../utils/wasmCrypto.js";
16
+ import { sha256Bytes } from "../utils/crypto.js";
17
+ import {
18
+ DOMAIN_MODULE_PUBLICATION_V1,
19
+ statement as buildSignatureStatement,
20
+ trimGoWhitespace,
21
+ } from "./sigdomain.js";
22
+ import { ModuleBundleEntryRole } from "spacedatastandards.org/lib/js/MBL/main.js";
23
+
24
+ export const MODULE_SIGNATURE_ALGORITHM = "ed25519";
25
+ export const MODULE_SIGNATURE_ENTRY_ROLE = "signature";
26
+ export const LEGACY_MODULE_SIGNATURE_HASH_ALGORITHM =
27
+ "sha256-canonical-module-hash";
28
+ export const BUNDLE_SIGNATURE_HASH_ALGORITHM =
29
+ "sha256-sdn-module-bundle-v1";
30
+
31
+ export class ModuleSignatureError extends Error {
32
+ constructor(code, message) {
33
+ super(message);
34
+ this.name = "ModuleSignatureError";
35
+ this.code = code;
36
+ }
37
+ }
38
+
39
+ function bytesToHex(bytes) {
40
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
41
+ }
42
+
43
+ function hexToBytes(hex) {
44
+ const normalized = String(hex ?? "").trim().toLowerCase();
45
+ if (!/^[0-9a-f]*$/.test(normalized) || normalized.length % 2 !== 0) {
46
+ throw new ModuleSignatureError(
47
+ "invalid_hex",
48
+ "signature material must be even-length hex",
49
+ );
50
+ }
51
+ const out = new Uint8Array(normalized.length / 2);
52
+ for (let i = 0; i < out.length; i += 1) {
53
+ out[i] = Number.parseInt(normalized.slice(i * 2, i * 2 + 2), 16);
54
+ }
55
+ return out;
56
+ }
57
+
58
+ function normalizeTrustedPublicKeys(trustedPublicKeys) {
59
+ const list = Array.isArray(trustedPublicKeys)
60
+ ? trustedPublicKeys
61
+ : typeof trustedPublicKeys === "string"
62
+ ? trustedPublicKeys.split(",")
63
+ : [];
64
+ return list
65
+ .map((key) => String(key ?? "").trim().toLowerCase())
66
+ .filter((key) => key.length === 64);
67
+ }
68
+
69
+ function findSignatureEntry(bundle) {
70
+ for (const entry of bundle?.entries ?? []) {
71
+ const role =
72
+ typeof entry.role === "string" ? entry.role.toLowerCase() : entry.role;
73
+ if (
74
+ role === MODULE_SIGNATURE_ENTRY_ROLE ||
75
+ role === ModuleBundleEntryRole.SIGNATURE ||
76
+ entry.entryId === "signature" ||
77
+ entry.sectionName === "sds.signature"
78
+ ) {
79
+ return entry;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+
85
+ // The exact key spellings the SDK writer emits. Go's encoding/json matches
86
+ // field names CASE-INSENSITIVELY and JavaScript's property access does not, so
87
+ // a trailer spelling "StatementDomain" populates the node's struct and is
88
+ // invisible here — the node would take the domain path and verify while this
89
+ // verifier fell to the legacy path and refused. Same artifact, opposite
90
+ // verdicts. Both sides therefore refuse a case variant of a known key outright.
91
+ // Keys that are not a variant of anything known stay tolerated on both sides:
92
+ // this closes a divergence, it is not a new schema.
93
+ const SIGNATURE_PAYLOAD_KEYS = Object.freeze([
94
+ "algorithm",
95
+ "keyId",
96
+ "publicKeyHex",
97
+ "signatureHex",
98
+ "signedHashHex",
99
+ "signedHashAlgorithm",
100
+ "statementDomain",
101
+ ]);
102
+
103
+ function decodeSignaturePayload(entry) {
104
+ let parsed;
105
+ try {
106
+ const payload = entry.payload ?? [];
107
+ const text = new TextDecoder().decode(new Uint8Array(payload));
108
+ parsed = JSON.parse(text);
109
+ } catch {
110
+ throw new ModuleSignatureError(
111
+ "invalid_signature_payload",
112
+ "module signature entry payload is not valid JSON",
113
+ );
114
+ }
115
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
116
+ throw new ModuleSignatureError(
117
+ "invalid_signature_payload",
118
+ "module signature entry payload is not a JSON object",
119
+ );
120
+ }
121
+ for (const key of Object.keys(parsed)) {
122
+ if (SIGNATURE_PAYLOAD_KEYS.includes(key)) continue;
123
+ const variant = SIGNATURE_PAYLOAD_KEYS.find(
124
+ (known) => known.toLowerCase() === key.toLowerCase(),
125
+ );
126
+ if (variant) {
127
+ throw new ModuleSignatureError(
128
+ "invalid_signature_payload",
129
+ `module signature entry payload spells "${key}"; the wire form is the module SDK's camelCase JSON and the correct spelling is "${variant}"`,
130
+ );
131
+ }
132
+ }
133
+ return parsed;
134
+ }
135
+
136
+ function equalBytes(left, right) {
137
+ const a = new Uint8Array(left ?? []);
138
+ const b = new Uint8Array(right ?? []);
139
+ return a.length === b.length && a.every((byte, index) => byte === b[index]);
140
+ }
141
+
142
+ /**
143
+ * The canonical module hash, or null if this artifact's wasm cannot be walked.
144
+ * Used ONLY to populate a reporting field on the node-signed path, where the
145
+ * node itself never parses the wasm: a module whose sections this SDK cannot
146
+ * walk must not become an artifact the node admits and the SDK refuses.
147
+ */
148
+ async function bestEffortCanonicalModuleHashHex(payloadBytes) {
149
+ try {
150
+ return (await computeCanonicalModuleHash(payloadBytes)).hashHex;
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+
156
+ const bundleStatementTextEncoder = new TextEncoder();
157
+
158
+ function compareUtf8Keys(left, right) {
159
+ const leftBytes = bundleStatementTextEncoder.encode(left);
160
+ const rightBytes = bundleStatementTextEncoder.encode(right);
161
+ const length = Math.min(leftBytes.length, rightBytes.length);
162
+ for (let index = 0; index < length; index += 1) {
163
+ if (leftBytes[index] !== rightBytes[index]) {
164
+ return leftBytes[index] - rightBytes[index];
165
+ }
166
+ }
167
+ return leftBytes.length - rightBytes.length;
168
+ }
169
+
170
+ function normalizeBundleStatementValue(value) {
171
+ if (value === undefined) {
172
+ return undefined;
173
+ }
174
+ if (value === null || typeof value !== "object") {
175
+ return value;
176
+ }
177
+ if (Array.isArray(value)) {
178
+ return value.map((item) => {
179
+ const normalized = normalizeBundleStatementValue(item);
180
+ return normalized === undefined ? null : normalized;
181
+ });
182
+ }
183
+ return Object.fromEntries(
184
+ Object.entries(value)
185
+ .filter(([, nestedValue]) => nestedValue !== undefined)
186
+ .sort(([left], [right]) => compareUtf8Keys(left, right))
187
+ .map(([key, nestedValue]) => [
188
+ key,
189
+ normalizeBundleStatementValue(nestedValue),
190
+ ]),
191
+ );
192
+ }
193
+
194
+ function canonicalBundleStatementBytes(statement) {
195
+ // Go's encoding/json orders map keys by their UTF-8 bytes and escapes these
196
+ // five code points by default. Match that host-side representation exactly
197
+ // so a bundle digest is independent of JavaScript's locale configuration.
198
+ const json = JSON.stringify(normalizeBundleStatementValue(statement)).replace(
199
+ /[<>&\u2028\u2029]/g,
200
+ (character) =>
201
+ `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`,
202
+ );
203
+ return bundleStatementTextEncoder.encode(json);
204
+ }
205
+
206
+ function normalizedBundleEntryForSignature(entry) {
207
+ const payload = new Uint8Array(entry.payload ?? []);
208
+ return {
209
+ entryId: entry.entryId ?? null,
210
+ role: Number(entry.role ?? ModuleBundleEntryRole.AUXILIARY),
211
+ sectionName: entry.sectionName ?? null,
212
+ typeRef: entry.typeRef ?? null,
213
+ payloadEncoding: Number(entry.payloadEncoding ?? 0),
214
+ mediaType: entry.mediaType ?? null,
215
+ flags: Number(entry.flags ?? 0),
216
+ sha256Hex: bytesToHex(new Uint8Array(entry.sha256 ?? [])),
217
+ payloadLength: payload.length,
218
+ description: entry.description ?? null,
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Compute the v1 whole-bundle signing digest. Every non-signature entry's
224
+ * payload hash is recomputed before the canonical statement is hashed; an
225
+ * attacker cannot make a modified payload self-consistent merely by changing
226
+ * its MBL sha256 field. Bundle metadata and the portable module hash are bound
227
+ * by the same statement.
228
+ */
229
+ export async function computeModuleBundleSignatureHash(bundle, options = {}) {
230
+ if (!bundle || typeof bundle !== "object") {
231
+ throw new ModuleSignatureError(
232
+ "invalid_bundle",
233
+ "module bundle is missing or malformed",
234
+ );
235
+ }
236
+ const entries = (bundle.entries ?? []).filter(
237
+ (entry) => !findSignatureEntry({ entries: [entry] }),
238
+ );
239
+ const seen = new Set();
240
+ for (const entry of entries) {
241
+ const entryId = String(entry.entryId ?? "");
242
+ if (!entryId) {
243
+ throw new ModuleSignatureError(
244
+ "invalid_bundle",
245
+ "module bundle contains an entry without an entryId",
246
+ );
247
+ }
248
+ if (seen.has(entryId)) {
249
+ throw new ModuleSignatureError(
250
+ "invalid_bundle",
251
+ `module bundle contains duplicate entryId ${JSON.stringify(entryId)}`,
252
+ );
253
+ }
254
+ seen.add(entryId);
255
+ const payloadHash = await sha256Bytes(new Uint8Array(entry.payload ?? []));
256
+ if (!equalBytes(payloadHash, entry.sha256)) {
257
+ throw new ModuleSignatureError(
258
+ "hash_mismatch",
259
+ `module bundle entry ${JSON.stringify(entryId)} payload hash does not match its recorded sha256`,
260
+ );
261
+ }
262
+ }
263
+
264
+ const recordedModuleHash = new Uint8Array(bundle.canonicalModuleHash ?? []);
265
+ if (recordedModuleHash.length !== 32) {
266
+ throw new ModuleSignatureError(
267
+ "invalid_bundle",
268
+ "module bundle canonicalModuleHash must be 32 bytes",
269
+ );
270
+ }
271
+ if (options.wasmBytes) {
272
+ const canonical = await computeCanonicalModuleHash(options.wasmBytes, {
273
+ customSectionPrefix:
274
+ bundle.canonicalization?.strippedCustomSectionPrefix,
275
+ });
276
+ if (!equalBytes(canonical.hashBytes, recordedModuleHash)) {
277
+ throw new ModuleSignatureError(
278
+ "hash_mismatch",
279
+ "module canonical hash does not match the bundle's recorded hash",
280
+ );
281
+ }
282
+ }
283
+
284
+ const manifestEntry = entries.find(
285
+ (entry) =>
286
+ entry.entryId === "manifest" ||
287
+ entry.role === ModuleBundleEntryRole.MANIFEST,
288
+ );
289
+ const recordedManifestHash = new Uint8Array(bundle.manifestHash ?? []);
290
+ if (manifestEntry) {
291
+ const manifestHash = await sha256Bytes(
292
+ new Uint8Array(manifestEntry.payload ?? []),
293
+ );
294
+ if (!equalBytes(manifestHash, recordedManifestHash)) {
295
+ throw new ModuleSignatureError(
296
+ "hash_mismatch",
297
+ "module manifest payload hash does not match the bundle's manifestHash",
298
+ );
299
+ }
300
+ } else if (recordedManifestHash.length !== 0) {
301
+ throw new ModuleSignatureError(
302
+ "hash_mismatch",
303
+ "module bundle records a manifestHash but contains no manifest entry",
304
+ );
305
+ }
306
+
307
+ const statement = {
308
+ version: 1,
309
+ bundleVersion: Number(bundle.bundleVersion ?? 1),
310
+ moduleFormat: bundle.moduleFormat ?? null,
311
+ canonicalization: {
312
+ version: Number(bundle.canonicalization?.version ?? 1),
313
+ strippedCustomSectionPrefix:
314
+ bundle.canonicalization?.strippedCustomSectionPrefix ?? null,
315
+ bundleSectionName:
316
+ bundle.canonicalization?.bundleSectionName ?? null,
317
+ hashAlgorithm: bundle.canonicalization?.hashAlgorithm ?? null,
318
+ },
319
+ canonicalModuleHashHex: bytesToHex(recordedModuleHash),
320
+ manifestHashHex: bytesToHex(recordedManifestHash),
321
+ manifestExportSymbol: bundle.manifestExportSymbol ?? null,
322
+ manifestSizeSymbol: bundle.manifestSizeSymbol ?? null,
323
+ entries: entries
324
+ .map(normalizedBundleEntryForSignature)
325
+ .sort((left, right) => compareUtf8Keys(left.entryId, right.entryId)),
326
+ };
327
+ const hashBytes = await sha256Bytes(canonicalBundleStatementBytes(statement));
328
+ return {
329
+ statement,
330
+ hashBytes,
331
+ hashHex: bytesToHex(hashBytes),
332
+ };
333
+ }
334
+
335
+ /**
336
+ * Sign a module artifact's canonical wasm hash with an Ed25519 key and embed
337
+ * the detached signature in the artifact's MBL bundle (sds.signature entry).
338
+ *
339
+ * Existing bundle entries, the manifest, and any ENC/PNM publication records
340
+ * in the REC trailer are preserved. Any previous signature entry is replaced.
341
+ *
342
+ * @param {Uint8Array|ArrayBuffer} bytes - module artifact (raw wasm or single-file bundle)
343
+ * @param {Object} options
344
+ * @param {string} options.privateKeySeedHex - 32-byte Ed25519 seed, hex
345
+ * @param {string} [options.keyId] - identifier recorded alongside the signature
346
+ * @returns {Promise<{wasmBytes: Uint8Array, signature: Object, canonicalModuleHashHex: string}>}
347
+ */
348
+ export async function signModuleArtifact(bytes, options = {}) {
349
+ const seed = hexToBytes(options.privateKeySeedHex);
350
+ if (seed.length !== 32) {
351
+ throw new ModuleSignatureError(
352
+ "invalid_seed",
353
+ "privateKeySeedHex must be a 32-byte hex Ed25519 seed",
354
+ );
355
+ }
356
+ const protectedArtifact = extractPublicationRecordCollection(bytes);
357
+ const payloadBytes = protectedArtifact?.payloadBytes ?? bytes;
358
+ const canonical = await computeCanonicalModuleHash(payloadBytes);
359
+
360
+ let manifestBytes;
361
+ let preservedEntries = [];
362
+ if (protectedArtifact?.mbl) {
363
+ const parsed = await parseSingleFileBundle(bytes);
364
+ preservedEntries = (parsed.bundle.entries ?? [])
365
+ .filter((entry) => {
366
+ if (findSignatureEntry({ entries: [entry] })) {
367
+ return false;
368
+ }
369
+ if (
370
+ entry.entryId === "manifest" ||
371
+ entry.role === ModuleBundleEntryRole.MANIFEST
372
+ ) {
373
+ manifestBytes = new Uint8Array(entry.payload ?? []);
374
+ return false;
375
+ }
376
+ return true;
377
+ })
378
+ .map((entry) => ({
379
+ ...entry,
380
+ payload: new Uint8Array(entry.payload ?? []),
381
+ }));
382
+ }
383
+ if (!manifestBytes) {
384
+ manifestBytes = getWasmCustomSections(
385
+ payloadBytes,
386
+ SDS_MANIFEST_SECTION_NAME,
387
+ )[0];
388
+ }
389
+
390
+ let signedHashBytes = canonical.hashBytes;
391
+ let signedHashHex = canonical.hashHex;
392
+ let signedHashAlgorithm = LEGACY_MODULE_SIGNATURE_HASH_ALGORITHM;
393
+ if (options.signatureScope === "bundle") {
394
+ const unsigned = await createSingleFileBundle({
395
+ wasmBytes: bytes,
396
+ ...(manifestBytes ? { manifestBytes } : {}),
397
+ entries: preservedEntries,
398
+ });
399
+ const parsedUnsigned = await parseSingleFileBundle(unsigned.wasmBytes);
400
+ const bundleHash = await computeModuleBundleSignatureHash(
401
+ parsedUnsigned.bundle,
402
+ { wasmBytes: parsedUnsigned.wasmBytes },
403
+ );
404
+ signedHashBytes = bundleHash.hashBytes;
405
+ signedHashHex = bundleHash.hashHex;
406
+ signedHashAlgorithm = BUNDLE_SIGNATURE_HASH_ALGORITHM;
407
+ } else if (
408
+ options.signatureScope !== undefined &&
409
+ options.signatureScope !== "module"
410
+ ) {
411
+ throw new ModuleSignatureError(
412
+ "invalid_signature_scope",
413
+ 'signatureScope must be either "module" or "bundle"',
414
+ );
415
+ }
416
+
417
+ const publicKey = await ed25519PublicKey(seed);
418
+ const signatureBytes = await ed25519Sign(signedHashBytes, seed);
419
+ const signature = {
420
+ algorithm: MODULE_SIGNATURE_ALGORITHM,
421
+ keyId: options.keyId ?? null,
422
+ publicKeyHex: bytesToHex(new Uint8Array(publicKey)),
423
+ signatureHex: bytesToHex(new Uint8Array(signatureBytes)),
424
+ signedHashHex,
425
+ signedHashAlgorithm,
426
+ };
427
+
428
+ const rebuilt = await createSingleFileBundle({
429
+ wasmBytes: bytes,
430
+ ...(manifestBytes ? { manifestBytes } : {}),
431
+ signature,
432
+ entries: preservedEntries,
433
+ });
434
+ return {
435
+ wasmBytes: rebuilt.wasmBytes,
436
+ signature,
437
+ canonicalModuleHashHex: rebuilt.canonicalModuleHashHex,
438
+ signedHashHex,
439
+ };
440
+ }
441
+
442
+ /**
443
+ * Verify a module artifact's embedded Ed25519 signature before loading.
444
+ *
445
+ * TWO SIGNED FORMS, and the artifact chooses — not the caller, not a policy:
446
+ *
447
+ * - `statementDomain` PRESENT — the NODE-SIGNED form issued by the node's
448
+ * content-bound signing endpoint. The signature covers the domain-separated
449
+ * statement `domain || 0x00 || sha256(portable)` (see ./sigdomain.js), where
450
+ * `portable` is the trailer-stripped payload — the exact bytes the runtime
451
+ * instantiates and the node's capability policy identifies by content hash.
452
+ * The domain must be EXACTLY {@link DOMAIN_MODULE_PUBLICATION_V1}: not
453
+ * merely "registered". A registered-but-different domain (the reserved
454
+ * update-manifest domain, say) is REFUSED, which is what stops a signature
455
+ * minted for a signed update from being stapled into a module trailer.
456
+ *
457
+ * - `statementDomain` ABSENT — the legacy SDK-signed form: a signature over
458
+ * the bare canonical module (or bundle) digest. Verification is byte-identical
459
+ * to what it has always been, so every artifact already published keeps
460
+ * verifying.
461
+ *
462
+ * THIS MIRRORS THE NODE EXACTLY, deliberately and to the reason code, because
463
+ * the same artifact is verified by the Go loaders
464
+ * (`sdn-server/internal/modulert/publication_signature.go` and its kubo twin)
465
+ * and by this function in the browser and under Node/WasmEdge. An artifact that
466
+ * verifies on the node and is refused here is a cross-runtime defect, not a
467
+ * platform difference — so the checks below run in the node's ORDER (what the
468
+ * signature covers is resolved before who signed it) and the shared vector file
469
+ * `test/support/statement-domain-vectors.json` pins both sides against the same
470
+ * bytes and the same refusal reasons.
471
+ *
472
+ * Note the deliberate omissions on the node-signed path: `signedHashAlgorithm`
473
+ * is not consulted and the bundle's recorded `canonicalModuleHash` is not
474
+ * gated on, because the node consults neither. The domain itself defines the
475
+ * hash basis (portable-payload SHA-256), and that basis binds the entire
476
+ * instantiated payload — a strictly stronger commitment than the canonical
477
+ * hash, which strips `sds.*` custom sections before hashing.
478
+ *
479
+ * A present-but-invalid signature always throws. A missing signature throws
480
+ * only when `requireSignature` is true.
481
+ *
482
+ * @param {Uint8Array|ArrayBuffer} bytes - module artifact bytes
483
+ * @param {Object} options
484
+ * @param {string[]|string} [options.trustedPublicKeys] - allowed signer public keys (hex)
485
+ * @param {boolean} [options.requireSignature=false]
486
+ * @returns {Promise<{verified: boolean, signed: boolean, keyId?: string|null, publicKeyHex?: string, canonicalModuleHashHex?: string, contentHashHex?: string, statementDomain?: string, reason?: string}>}
487
+ */
488
+ export async function verifyModuleArtifact(bytes, options = {}) {
489
+ const requireSignature = options.requireSignature === true;
490
+ const protectedArtifact = extractPublicationRecordCollection(bytes);
491
+ const signatureEntry = protectedArtifact?.mbl
492
+ ? findSignatureEntry(protectedArtifact.mbl)
493
+ : null;
494
+
495
+ if (!signatureEntry) {
496
+ if (requireSignature) {
497
+ throw new ModuleSignatureError(
498
+ "missing_signature",
499
+ "module artifact has no signature entry but signature is required",
500
+ );
501
+ }
502
+ return { verified: false, signed: false, reason: "unsigned" };
503
+ }
504
+
505
+ const payload = decodeSignaturePayload(signatureEntry);
506
+ // Trimmed and case-folded, because the node is: `strings.EqualFold(
507
+ // strings.TrimSpace(payload.Algorithm), "ed25519")`. An exact match here
508
+ // would refuse an "ED25519" the node admits.
509
+ if (trimGoWhitespace(payload.algorithm).toLowerCase() !== MODULE_SIGNATURE_ALGORITHM) {
510
+ throw new ModuleSignatureError(
511
+ "unsupported_algorithm",
512
+ `unsupported module signature algorithm: ${payload.algorithm}`,
513
+ );
514
+ }
515
+ const signatureBytes = hexToBytes(payload.signatureHex);
516
+ if (signatureBytes.length !== 64) {
517
+ throw new ModuleSignatureError(
518
+ "invalid_signature",
519
+ "module signature must be 64 bytes",
520
+ );
521
+ }
522
+ if (signatureBytes.every((byte) => byte === 0)) {
523
+ throw new ModuleSignatureError(
524
+ "invalid_signature",
525
+ "module signature must not be all zeroes",
526
+ );
527
+ }
528
+ const publicKeyHex = String(payload.publicKeyHex ?? "").trim().toLowerCase();
529
+ const publicKeyBytes = hexToBytes(publicKeyHex);
530
+ if (publicKeyBytes.length !== 32) {
531
+ throw new ModuleSignatureError(
532
+ "invalid_public_key",
533
+ "module signer public key must be 32 bytes",
534
+ );
535
+ }
536
+
537
+ const trusted = normalizeTrustedPublicKeys(options.trustedPublicKeys);
538
+ // resolvedStatementDomain is carried onto the thrown error so a refusal
539
+ // reports the same (reason, domain) pair the node's ModuleSignatureStatus
540
+ // does — including for untrusted_signer, where the domain was already
541
+ // resolved before trust was asked.
542
+ const requireTrustedSigner = (resolvedStatementDomain) => {
543
+ if (!trusted.includes(publicKeyHex)) {
544
+ const error = new ModuleSignatureError(
545
+ "untrusted_signer",
546
+ "module signer public key is not in the trusted signer set",
547
+ );
548
+ if (resolvedStatementDomain) {
549
+ error.statementDomain = resolvedStatementDomain;
550
+ }
551
+ throw error;
552
+ }
553
+ };
554
+
555
+ // ---- node-signed form: domain-separated statement over the portable hash --
556
+ //
557
+ // Resolved BEFORE the trusted-signer check, mirroring the node: a wrong
558
+ // statement domain is a property of the ARTIFACT and must be reported as
559
+ // such whether or not the signer happens to be trusted. (The shared vector
560
+ // `foreign-registered-domain-untrusted-signer` pins exactly this ordering.)
561
+ const declaredStatementDomain = trimGoWhitespace(payload.statementDomain);
562
+ // AN ARTIFACT DECLARES EXACTLY ONE BASIS. A statement domain says the
563
+ // signature covers domain||0x00||sha256(portable); the bundle hash algorithm
564
+ // says it covers the canonical whole-bundle statement. Different preimages,
565
+ // so an artifact claiming both has not said what its signature covers.
566
+ // Resolving it either way silently discards the other claim — the node twins
567
+ // resolved it bundle-first, which would have dropped the domain check and
568
+ // with it the cross-domain replay guard. All three verifiers refuse instead
569
+ // (sdn-server + kubo: reason "ambiguous_signature_scope").
570
+ if (
571
+ declaredStatementDomain !== "" &&
572
+ trimGoWhitespace(payload.signedHashAlgorithm) ===
573
+ BUNDLE_SIGNATURE_HASH_ALGORITHM
574
+ ) {
575
+ const error = new ModuleSignatureError(
576
+ "ambiguous_signature_scope",
577
+ `module signature declares statement domain ${JSON.stringify(declaredStatementDomain)} AND hash algorithm ${JSON.stringify(BUNDLE_SIGNATURE_HASH_ALGORITHM)}; these are different preimages and an artifact must declare exactly one`,
578
+ );
579
+ error.statementDomain = declaredStatementDomain;
580
+ throw error;
581
+ }
582
+ if (declaredStatementDomain !== "") {
583
+ const portableBytes = new Uint8Array(protectedArtifact.payloadBytes);
584
+ const contentHashBytes = await sha256Bytes(portableBytes);
585
+ const contentHashHex = bytesToHex(contentHashBytes);
586
+
587
+ const declaredHashHex = trimGoWhitespace(payload.signedHashHex).toLowerCase();
588
+ if (declaredHashHex !== "" && declaredHashHex !== contentHashHex) {
589
+ throw new ModuleSignatureError(
590
+ "hash_mismatch",
591
+ `module signature covers content hash ${declaredHashHex}, portable artifact hashes to ${contentHashHex}`,
592
+ );
593
+ }
594
+
595
+ if (declaredStatementDomain !== DOMAIN_MODULE_PUBLICATION_V1) {
596
+ const error = new ModuleSignatureError(
597
+ "unsupported_statement_domain",
598
+ `module signature declares statement domain ${JSON.stringify(declaredStatementDomain)}; a module artifact must be signed under ${JSON.stringify(DOMAIN_MODULE_PUBLICATION_V1)}`,
599
+ );
600
+ error.statementDomain = declaredStatementDomain;
601
+ throw error;
602
+ }
603
+
604
+ const statementBytes = buildSignatureStatement(
605
+ DOMAIN_MODULE_PUBLICATION_V1,
606
+ contentHashBytes,
607
+ );
608
+
609
+ requireTrustedSigner(DOMAIN_MODULE_PUBLICATION_V1);
610
+
611
+ const validStatement = await ed25519Verify(
612
+ statementBytes,
613
+ signatureBytes,
614
+ publicKeyBytes,
615
+ );
616
+ if (!validStatement) {
617
+ const error = new ModuleSignatureError(
618
+ "invalid_signature",
619
+ "module publication signature verification failed",
620
+ );
621
+ error.statementDomain = DOMAIN_MODULE_PUBLICATION_V1;
622
+ throw error;
623
+ }
624
+
625
+ return {
626
+ verified: true,
627
+ signed: true,
628
+ keyId: payload.keyId ?? null,
629
+ publicKeyHex,
630
+ statementDomain: DOMAIN_MODULE_PUBLICATION_V1,
631
+ contentHashHex,
632
+ // Reported for continuity with the legacy result shape (the CLI prints
633
+ // it); NOT gated on, because the node does not gate on it either.
634
+ canonicalModuleHashHex: await bestEffortCanonicalModuleHashHex(
635
+ protectedArtifact.payloadBytes,
636
+ ),
637
+ signatureScope: "module",
638
+ signedHashHex: contentHashHex,
639
+ };
640
+ }
641
+
642
+ // ---- legacy form: bare canonical module (or bundle) digest ---------------
643
+ const canonical = await computeCanonicalModuleHash(protectedArtifact.payloadBytes);
644
+ const recordedHash = new Uint8Array(
645
+ protectedArtifact.mbl.canonicalModuleHash ?? [],
646
+ );
647
+ if (
648
+ recordedHash.length !== canonical.hashBytes.length ||
649
+ !recordedHash.every((byte, i) => byte === canonical.hashBytes[i])
650
+ ) {
651
+ throw new ModuleSignatureError(
652
+ "hash_mismatch",
653
+ "module canonical hash does not match the bundle's recorded hash",
654
+ );
655
+ }
656
+
657
+ let signedHashBytes = canonical.hashBytes;
658
+ let signedHashHex = canonical.hashHex;
659
+ let signatureScope = "module";
660
+ const signedHashAlgorithm = String(payload.signedHashAlgorithm ?? "");
661
+ if (signedHashAlgorithm === BUNDLE_SIGNATURE_HASH_ALGORITHM) {
662
+ const parsed = await parseSingleFileBundle(bytes);
663
+ const bundleHash = await computeModuleBundleSignatureHash(parsed.bundle, {
664
+ wasmBytes: parsed.wasmBytes,
665
+ });
666
+ signedHashBytes = bundleHash.hashBytes;
667
+ signedHashHex = bundleHash.hashHex;
668
+ signatureScope = "bundle";
669
+ } else if (
670
+ signedHashAlgorithm !== LEGACY_MODULE_SIGNATURE_HASH_ALGORITHM
671
+ ) {
672
+ throw new ModuleSignatureError(
673
+ "unsupported_hash_algorithm",
674
+ `unsupported module signature hash algorithm: ${signedHashAlgorithm}`,
675
+ );
676
+ }
677
+ if (
678
+ String(payload.signedHashHex ?? "").toLowerCase() !== signedHashHex
679
+ ) {
680
+ throw new ModuleSignatureError(
681
+ "hash_mismatch",
682
+ "module or bundle hash does not match the signed digest",
683
+ );
684
+ }
685
+
686
+ // Trust is asked LAST here too, for the same reason as above and so the two
687
+ // forms cannot report different reasons for the same defect.
688
+ requireTrustedSigner();
689
+
690
+ const valid = await ed25519Verify(
691
+ signedHashBytes,
692
+ signatureBytes,
693
+ publicKeyBytes,
694
+ );
695
+ if (!valid) {
696
+ throw new ModuleSignatureError(
697
+ "invalid_signature",
698
+ "module signature verification failed",
699
+ );
700
+ }
701
+ return {
702
+ verified: true,
703
+ signed: true,
704
+ keyId: payload.keyId ?? null,
705
+ publicKeyHex,
706
+ // Null, not absent: the legacy form is a POSITIVE statement that this
707
+ // artifact predates domain separation, which a caller may want to log.
708
+ statementDomain: null,
709
+ contentHashHex: bytesToHex(
710
+ await sha256Bytes(new Uint8Array(protectedArtifact.payloadBytes)),
711
+ ),
712
+ canonicalModuleHashHex: canonical.hashHex,
713
+ signatureScope,
714
+ signedHashHex,
715
+ };
716
+ }
717
+
718
+ function readEnv(name) {
719
+ try {
720
+ if (typeof process !== "undefined" && process?.env?.[name] !== undefined) {
721
+ return process.env[name];
722
+ }
723
+ } catch {
724
+ // no process in this runtime
725
+ }
726
+ return undefined;
727
+ }
728
+
729
+ /**
730
+ * Resolve the effective signature-verification policy for a load operation.
731
+ * Sources, in priority order: explicit `options.verifySignature`, then the
732
+ * `SDM_TRUSTED_MODULE_SIGNERS` / `SDM_REQUIRE_MODULE_SIGNATURE` environment
733
+ * variables, then `globalThis.__SDM_TRUSTED_MODULE_SIGNERS__` /
734
+ * `globalThis.__SDM_REQUIRE_MODULE_SIGNATURE__` (for browser hosts).
735
+ *
736
+ * Returns null when no policy is configured (loading proceeds unverified,
737
+ * preserving existing behavior).
738
+ */
739
+ export function resolveModuleSignaturePolicy(options = {}) {
740
+ if (options.verifySignature === false) {
741
+ return null;
742
+ }
743
+ if (options.verifySignature && typeof options.verifySignature === "object") {
744
+ return {
745
+ trustedPublicKeys: normalizeTrustedPublicKeys(
746
+ options.verifySignature.trustedPublicKeys,
747
+ ),
748
+ requireSignature: options.verifySignature.requireSignature === true,
749
+ };
750
+ }
751
+ const envTrusted = readEnv("SDM_TRUSTED_MODULE_SIGNERS");
752
+ const envRequire = readEnv("SDM_REQUIRE_MODULE_SIGNATURE");
753
+ const globalTrusted = globalThis.__SDM_TRUSTED_MODULE_SIGNERS__;
754
+ const globalRequire = globalThis.__SDM_REQUIRE_MODULE_SIGNATURE__;
755
+ const trustedPublicKeys = normalizeTrustedPublicKeys(
756
+ envTrusted ?? globalTrusted,
757
+ );
758
+ const requireSignature =
759
+ envRequire === "1" ||
760
+ envRequire === "true" ||
761
+ globalRequire === true ||
762
+ globalRequire === "1";
763
+ if (trustedPublicKeys.length === 0 && !requireSignature) {
764
+ return null;
765
+ }
766
+ return { trustedPublicKeys, requireSignature };
767
+ }