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
@@ -2,11 +2,17 @@
2
2
  * Browser host adapter for the SDN host contract.
3
3
  *
4
4
  * Mirrors the NodeHost public interface using browser-native Web APIs.
5
- * Plugs into createJsonHostcallBridge() exactly like NodeHost does.
5
+ * Plugs into createHostcallBridge() exactly like NodeHost does.
6
6
  */
7
7
 
8
8
  import { RuntimeTarget } from "../runtime/constants.js";
9
- import { toUint8Array } from "../utils/encoding.js";
9
+ import {
10
+ base64ToBytes,
11
+ bytesToBase64,
12
+ hexToBytes,
13
+ toUint8Array,
14
+ } from "../utils/encoding.js";
15
+ import { derSignatureToRaw, rawSignatureToDer } from "../utils/ecdsaDer.js";
10
16
  import {
11
17
  parseCronExpression,
12
18
  matchesCronExpression,
@@ -36,6 +42,9 @@ export const BrowserHostSupportedCapabilities = Object.freeze([
36
42
  "crypto_key_agreement",
37
43
  "crypto_kdf",
38
44
  "wallet_sign",
45
+ "pubsub",
46
+ "storage_query",
47
+ "storage_write",
39
48
  "logging",
40
49
  ]);
41
50
 
@@ -72,7 +81,15 @@ export const BrowserHostSupportedOperations = Object.freeze([
72
81
  "protocol_handle.unregister",
73
82
  "protocol_dial.dial",
74
83
  "protocol.request",
75
- "keyslot.get",
84
+ "keyslot.sign",
85
+ "keyslot.unwrap",
86
+ "storage.write",
87
+ "storage.query",
88
+ "storage.delete",
89
+ "pubsub.publish",
90
+ "pubsub.subscribe",
91
+ "pubsub.unsubscribe",
92
+ "pubsub.list_topics",
76
93
  "context.get",
77
94
  "context.set",
78
95
  "context.delete",
@@ -86,6 +103,9 @@ export const BrowserHostSupportedOperations = Object.freeze([
86
103
  "crypto.x25519.generateKeypair",
87
104
  "crypto.x25519.publicKey",
88
105
  "crypto.x25519.sharedSecret",
106
+ "crypto.secp256k1.publicKeyFromPrivate",
107
+ "crypto.secp256k1.sign",
108
+ "crypto.secp256k1.verify",
89
109
  "crypto.ed25519.publicKeyFromSeed",
90
110
  "crypto.ed25519.sign",
91
111
  "crypto.ed25519.verify",
@@ -150,6 +170,13 @@ function resolveCapabilityAdapters(options = {}) {
150
170
  adapters.protocolDial ??
151
171
  adapters.protocol_dial ??
152
172
  null,
173
+ storage:
174
+ options.storage ??
175
+ adapters.storage ??
176
+ adapters.storage_write ??
177
+ adapters.storage_query ??
178
+ null,
179
+ pubsub: options.pubsub ?? adapters.pubsub ?? null,
153
180
  };
154
181
  }
155
182
 
@@ -175,6 +202,194 @@ async function invokeAdapterMethod(adapter, methodName, params, label) {
175
202
  );
176
203
  }
177
204
 
205
+ // keyslotUnwrapHKDFInfo domain-separates the HKDF step of keyslot.unwrap from
206
+ // every other HKDF use in this host (e.g. crypto.hkdf). Must match the Go
207
+ // reference implementation's keyslotUnwrapHKDFInfo constant
208
+ // (sdn-server/internal/modulert/caps/keyslot.go) exactly, or wrapped
209
+ // payloads produced against one host will fail to unwrap on the other.
210
+ const KEYSLOT_UNWRAP_HKDF_INFO = "sdn-server/keyslot.unwrap/v1";
211
+ const keyslotTextEncoder = new TextEncoder();
212
+
213
+ // normalizeKeySlotMaterial accepts the raw key-slot secret returned by a
214
+ // host-supplied walletSign adapter's internal "get" resolver and coerces it
215
+ // to a Uint8Array. This resolver is never exposed to the guest directly
216
+ // (there is no "keyslot.get" hostcall) — it only feeds the host-side
217
+ // keyslot.sign / keyslot.unwrap crypto oracle below, mirroring Go's
218
+ // resolveKeySlot() in keyslot.go. Callers MUST NOT return the result of this
219
+ // function, or any encoding of it, to the guest.
220
+ function normalizeKeySlotMaterial(raw) {
221
+ if (raw instanceof Uint8Array) {
222
+ return raw;
223
+ }
224
+ if (raw instanceof ArrayBuffer) {
225
+ return new Uint8Array(raw);
226
+ }
227
+ if (ArrayBuffer.isView(raw)) {
228
+ return toUint8Array(raw);
229
+ }
230
+ if (typeof raw === "string") {
231
+ return base64ToBytes(raw);
232
+ }
233
+ if (raw && typeof raw === "object") {
234
+ if (raw.base64 !== undefined) {
235
+ return base64ToBytes(raw.base64);
236
+ }
237
+ if (raw.hex !== undefined) {
238
+ return hexToBytes(raw.hex);
239
+ }
240
+ if (raw.value !== undefined) {
241
+ return normalizeKeySlotMaterial(raw.value);
242
+ }
243
+ if (raw.bytes !== undefined) {
244
+ return normalizeKeySlotMaterial(raw.bytes);
245
+ }
246
+ }
247
+ throw new Error(
248
+ "wallet_sign adapter returned an unrecognized key-slot material shape.",
249
+ );
250
+ }
251
+
252
+ async function resolveKeySlotMaterial(walletSignAdapter, slotId) {
253
+ const normalizedSlotId = String(slotId ?? "").trim();
254
+ if (!normalizedSlotId) {
255
+ throw new Error("slotId is required.");
256
+ }
257
+ const record = await invokeAdapterMethod(
258
+ walletSignAdapter,
259
+ "get",
260
+ { slotId: normalizedSlotId },
261
+ "wallet_sign",
262
+ );
263
+ const material = normalizeKeySlotMaterial(record);
264
+ if (material.length === 0) {
265
+ throw new Error(`keyslot "${normalizedSlotId}" not found`);
266
+ }
267
+ return material;
268
+ }
269
+
270
+ // keyslotSign performs a host-side signature over a guest-supplied payload
271
+ // using the named slot's private key. Request:
272
+ // {slotId, payload: base64, algorithm?: "ed25519" | "secp256k1"}
273
+ // algorithm defaults to "ed25519". Response: {signature: base64, algorithm}.
274
+ // The slot's key material never appears in the response. Mirrors
275
+ // handleKeyslotSign() in keyslot.go.
276
+ async function keyslotSign(walletSignAdapter, wasmWallet, params = {}) {
277
+ const slotId = String(params.slotId ?? "").trim();
278
+ if (!slotId) {
279
+ throw new Error("slotId is required.");
280
+ }
281
+ const keyMaterial = await resolveKeySlotMaterial(walletSignAdapter, slotId);
282
+ const payload =
283
+ params.payload === undefined || params.payload === null
284
+ ? new Uint8Array()
285
+ : base64ToBytes(String(params.payload));
286
+ const algorithm = String(params.algorithm ?? "").trim() || "ed25519";
287
+
288
+ if (algorithm === "ed25519") {
289
+ if (!wasmWallet?.curves?.ed25519?.sign) {
290
+ throw new Error(
291
+ "Browser host keyslot.sign requires a preloaded wasmWallet.",
292
+ );
293
+ }
294
+ if (keyMaterial.length !== 32) {
295
+ throw new Error(`keyslot "${slotId}" is not a 32-byte ed25519 seed`);
296
+ }
297
+ const signature = new Uint8Array(
298
+ wasmWallet.curves.ed25519.sign(payload, keyMaterial),
299
+ );
300
+ return { signature: bytesToBase64(signature), algorithm: "ed25519" };
301
+ }
302
+
303
+ if (algorithm === "secp256k1") {
304
+ if (!wasmWallet?.utils?.sha256 || !wasmWallet?.curves?.secp256k1?.sign) {
305
+ throw new Error(
306
+ "Browser host keyslot.sign requires a preloaded wasmWallet.",
307
+ );
308
+ }
309
+ if (keyMaterial.length !== 32) {
310
+ throw new Error(
311
+ `keyslot "${slotId}" is not a 32-byte secp256k1 private key`,
312
+ );
313
+ }
314
+ const digest = new Uint8Array(wasmWallet.utils.sha256(payload));
315
+ const rawSignature = new Uint8Array(
316
+ wasmWallet.curves.secp256k1.sign(digest, keyMaterial),
317
+ );
318
+ return {
319
+ signature: bytesToBase64(rawSignatureToDer(rawSignature)),
320
+ algorithm: "secp256k1",
321
+ };
322
+ }
323
+
324
+ throw new Error(`unsupported keyslot.sign algorithm: ${algorithm}`);
325
+ }
326
+
327
+ // keyslotUnwrap decrypts a payload that was sealed to a slot's X25519 public
328
+ // key, returning only the decrypted plaintext (e.g. a licensing content key)
329
+ // — never the slot's private key. Wrap scheme is ephemeral-sender ECIES:
330
+ // X25519(slotPrivateKey, ephemeralPublicKey) -> HKDF-SHA256 -> AES-256-GCM.
331
+ // Request:
332
+ // {slotId, ephemeralPublicKey: base64, nonce: base64, ciphertext: base64}
333
+ // Response: {plaintext: base64}. Mirrors handleKeyslotUnwrap() in
334
+ // keyslot.go, including the wire format where `ciphertext` is
335
+ // (ciphertext || 16-byte GCM tag), per Go's crypto/cipher.GCM convention.
336
+ async function keyslotUnwrap(walletSignAdapter, wasmWallet, params = {}) {
337
+ const slotId = String(params.slotId ?? "").trim();
338
+ if (!slotId) {
339
+ throw new Error("slotId is required.");
340
+ }
341
+ if (
342
+ !wasmWallet?.curves?.x25519?.ecdh ||
343
+ !wasmWallet?.utils?.hkdf ||
344
+ !wasmWallet?.utils?.aesGcm?.decrypt
345
+ ) {
346
+ throw new Error(
347
+ "Browser host keyslot.unwrap requires a preloaded wasmWallet.",
348
+ );
349
+ }
350
+ const keyMaterial = await resolveKeySlotMaterial(walletSignAdapter, slotId);
351
+ if (keyMaterial.length !== 32) {
352
+ throw new Error(
353
+ `keyslot "${slotId}" is not a 32-byte x25519 private key`,
354
+ );
355
+ }
356
+
357
+ const ephemeralPublicKey = base64ToBytes(
358
+ String(params.ephemeralPublicKey ?? ""),
359
+ );
360
+ const nonce = base64ToBytes(String(params.nonce ?? ""));
361
+ const wireCiphertext = base64ToBytes(String(params.ciphertext ?? ""));
362
+ if (ephemeralPublicKey.length === 0 || wireCiphertext.length === 0) {
363
+ throw new Error(
364
+ "keyslot.unwrap requires ephemeralPublicKey and ciphertext",
365
+ );
366
+ }
367
+ if (wireCiphertext.length < 16) {
368
+ throw new Error(
369
+ "keyslot.unwrap ciphertext is too short to contain a GCM tag",
370
+ );
371
+ }
372
+ const tagStart = wireCiphertext.length - 16;
373
+ const ciphertext = wireCiphertext.subarray(0, tagStart);
374
+ const tag = wireCiphertext.subarray(tagStart);
375
+
376
+ const sharedSecret = new Uint8Array(
377
+ wasmWallet.curves.x25519.ecdh(keyMaterial, ephemeralPublicKey),
378
+ );
379
+ const aesKey = new Uint8Array(
380
+ wasmWallet.utils.hkdf(
381
+ sharedSecret,
382
+ new Uint8Array(),
383
+ keyslotTextEncoder.encode(KEYSLOT_UNWRAP_HKDF_INFO),
384
+ 32,
385
+ ),
386
+ );
387
+ const plaintext = new Uint8Array(
388
+ wasmWallet.utils.aesGcm.decrypt(aesKey, ciphertext, tag, nonce),
389
+ );
390
+ return { plaintext: bytesToBase64(plaintext) };
391
+ }
392
+
178
393
  export class BrowserHost {
179
394
  constructor(options = {}) {
180
395
  this.runtimeTarget = RuntimeTarget.BROWSER;
@@ -214,6 +429,8 @@ export class BrowserHost {
214
429
  const walletSignAdapter = capabilityAdapters.walletSign;
215
430
  const protocolHandleAdapter = edgeShims.protocolHandle;
216
431
  const protocolDialAdapter = edgeShims.protocolDial;
432
+ const storageAdapter = capabilityAdapters.storage;
433
+ const pubsubAdapter = capabilityAdapters.pubsub;
217
434
 
218
435
  this._grantedCapabilities = granted;
219
436
  this._contextStore = options.contextStore ?? new Map();
@@ -440,10 +657,60 @@ export class BrowserHost {
440
657
  },
441
658
  });
442
659
 
660
+ // keyslot is a host-side crypto oracle (parity with the Go node's
661
+ // internal/modulert/caps/keyslot.go): it never returns a slot's private
662
+ // key material to the guest. Guests get the *outputs* of private-key
663
+ // operations (a signature, or the plaintext that was wrapped to a
664
+ // slot's public key) — never the key itself. There is no "keyslot.get"
665
+ // raw-export operation; unknown keyslot operations fail closed via the
666
+ // default branch of invoke()'s switch below.
443
667
  this.keyslot = Object.freeze({
444
- get: async (params = {}) => {
445
- this.#assertCapability("wallet_sign", "keyslot.get");
446
- return invokeAdapterMethod(walletSignAdapter, "get", params, "wallet_sign");
668
+ sign: async (params = {}) => {
669
+ this.#assertCapability("wallet_sign", "keyslot.sign");
670
+ return keyslotSign(walletSignAdapter, wasmWallet, params);
671
+ },
672
+ unwrap: async (params = {}) => {
673
+ this.#assertCapability("wallet_sign", "keyslot.unwrap");
674
+ return keyslotUnwrap(walletSignAdapter, wasmWallet, params);
675
+ },
676
+ });
677
+
678
+ // Storage + pubsub mirror the Go node's module capability contract
679
+ // (sdn-server internal/modulert/caps): storage.write {schema, data:base64}
680
+ // -> {cid}; storage.query {schema, day, entity_id, norad_cat_id, limit};
681
+ // storage.delete {schema, cid}; pubsub.publish {topic, data:utf8};
682
+ // pubsub.subscribe/unsubscribe {topic}; pubsub.list_topics {} -> {topics}.
683
+ this.storage = Object.freeze({
684
+ write: async (params = {}) => {
685
+ this.#assertCapability("storage_write", "storage.write");
686
+ return invokeAdapterMethod(storageAdapter, "write", params, "storage");
687
+ },
688
+ query: async (params = {}) => {
689
+ this.#assertCapability("storage_query", "storage.query");
690
+ return invokeAdapterMethod(storageAdapter, "query", params, "storage");
691
+ },
692
+ delete: async (params = {}) => {
693
+ this.#assertCapability("storage_write", "storage.delete");
694
+ return invokeAdapterMethod(storageAdapter, "delete", params, "storage");
695
+ },
696
+ });
697
+
698
+ this.pubsub = Object.freeze({
699
+ publish: async (params = {}) => {
700
+ this.#assertCapability("pubsub", "pubsub.publish");
701
+ return invokeAdapterMethod(pubsubAdapter, "publish", params, "pubsub");
702
+ },
703
+ subscribe: async (params = {}) => {
704
+ this.#assertCapability("pubsub", "pubsub.subscribe");
705
+ return invokeAdapterMethod(pubsubAdapter, "subscribe", params, "pubsub");
706
+ },
707
+ unsubscribe: async (params = {}) => {
708
+ this.#assertCapability("pubsub", "pubsub.unsubscribe");
709
+ return invokeAdapterMethod(pubsubAdapter, "unsubscribe", params, "pubsub");
710
+ },
711
+ listTopics: async (params = {}) => {
712
+ this.#assertCapability("pubsub", "pubsub.list_topics");
713
+ return invokeAdapterMethod(pubsubAdapter, "list_topics", params, "pubsub");
447
714
  },
448
715
  });
449
716
 
@@ -590,6 +857,60 @@ export class BrowserHost {
590
857
  ),
591
858
  );
592
859
  },
860
+ secp256k1: Object.freeze({
861
+ publicKeyFromPrivate: (privateKey) => {
862
+ this.#assertCapability(
863
+ "crypto_sign",
864
+ "crypto.secp256k1.publicKeyFromPrivate",
865
+ );
866
+ if (!wasmWallet?.curves?.publicKeyFromPrivate) {
867
+ throw new Error(
868
+ "Browser host crypto.secp256k1.publicKeyFromPrivate requires a preloaded wasmWallet.",
869
+ );
870
+ }
871
+ return new Uint8Array(
872
+ wasmWallet.curves.publicKeyFromPrivate(toUint8Array(privateKey)),
873
+ );
874
+ },
875
+ sign: (message, privateKey) => {
876
+ this.#assertCapability("crypto_sign", "crypto.secp256k1.sign");
877
+ if (
878
+ !wasmWallet?.utils?.sha256 ||
879
+ !wasmWallet?.curves?.secp256k1?.sign
880
+ ) {
881
+ throw new Error(
882
+ "Browser host crypto.secp256k1.sign requires a preloaded wasmWallet.",
883
+ );
884
+ }
885
+ const digest = new Uint8Array(
886
+ wasmWallet.utils.sha256(toUint8Array(message)),
887
+ );
888
+ const rawSignature = new Uint8Array(
889
+ wasmWallet.curves.secp256k1.sign(digest, toUint8Array(privateKey)),
890
+ );
891
+ return rawSignatureToDer(rawSignature);
892
+ },
893
+ verify: (message, signature, publicKey) => {
894
+ this.#assertCapability("crypto_verify", "crypto.secp256k1.verify");
895
+ if (
896
+ !wasmWallet?.utils?.sha256 ||
897
+ !wasmWallet?.curves?.secp256k1?.verify
898
+ ) {
899
+ throw new Error(
900
+ "Browser host crypto.secp256k1.verify requires a preloaded wasmWallet.",
901
+ );
902
+ }
903
+ const digest = new Uint8Array(
904
+ wasmWallet.utils.sha256(toUint8Array(message)),
905
+ );
906
+ const result = wasmWallet.curves.secp256k1.verify(
907
+ digest,
908
+ derSignatureToRaw(signature),
909
+ toUint8Array(publicKey),
910
+ );
911
+ return { result: Boolean(result) };
912
+ },
913
+ }),
593
914
  ed25519: Object.freeze({
594
915
  publicKeyFromSeed: (seed) => {
595
916
  this.#assertCapability("crypto_sign", "crypto.ed25519.publicKeyFromSeed");
@@ -774,8 +1095,24 @@ export class BrowserHost {
774
1095
  return this.protocolDial.dial(params);
775
1096
  case "protocol.request":
776
1097
  return this.protocolDial.request(params);
777
- case "keyslot.get":
778
- return this.keyslot.get(params);
1098
+ case "keyslot.sign":
1099
+ return this.keyslot.sign(params);
1100
+ case "keyslot.unwrap":
1101
+ return this.keyslot.unwrap(params);
1102
+ case "storage.write":
1103
+ return this.storage.write(params);
1104
+ case "storage.query":
1105
+ return this.storage.query(params);
1106
+ case "storage.delete":
1107
+ return this.storage.delete(params);
1108
+ case "pubsub.publish":
1109
+ return this.pubsub.publish(params);
1110
+ case "pubsub.subscribe":
1111
+ return this.pubsub.subscribe(params);
1112
+ case "pubsub.unsubscribe":
1113
+ return this.pubsub.unsubscribe(params);
1114
+ case "pubsub.list_topics":
1115
+ return this.pubsub.listTopics(params);
779
1116
  case "context.get":
780
1117
  return this.context.get(params.scope, params.key);
781
1118
  case "context.set":
@@ -805,6 +1142,16 @@ export class BrowserHost {
805
1142
  params.privateKey,
806
1143
  params.publicKey,
807
1144
  );
1145
+ case "crypto.secp256k1.publicKeyFromPrivate":
1146
+ return this.crypto.secp256k1.publicKeyFromPrivate(params.privateKey);
1147
+ case "crypto.secp256k1.sign":
1148
+ return this.crypto.secp256k1.sign(params.message, params.privateKey);
1149
+ case "crypto.secp256k1.verify":
1150
+ return this.crypto.secp256k1.verify(
1151
+ params.message,
1152
+ params.signature,
1153
+ params.publicKey,
1154
+ );
808
1155
  case "crypto.ed25519.publicKeyFromSeed":
809
1156
  return this.crypto.ed25519.publicKeyFromSeed(params.seed);
810
1157
  case "crypto.ed25519.sign":
@@ -0,0 +1,231 @@
1
+ // C++ guest-side wrapper for the `keyslot` host capability.
2
+ //
3
+ // Mirrors the intent of space-data-module-sdk/src/host/hostcallWire.js plus
4
+ // the server-side handlers in
5
+ // sdn-server/internal/modulert/caps/keyslot.go: `keyslot` is a host-side
6
+ // crypto oracle. A module never receives a slot's private key material —
7
+ // only the *outputs* of private-key operations cross the host/guest
8
+ // boundary (a signature; the plaintext of a payload wrapped to a slot's
9
+ // public key). The removed `keyslot.get` raw-export operation must never be
10
+ // reintroduced here.
11
+ //
12
+ // This header assumes the including translation unit has already pulled in
13
+ // the sdm_hostcall wire protocol (sdm_hostcall::call / Segment / Response —
14
+ // see space-data-network-modules/common/sdm_hostcall_wire.hpp) ahead of this
15
+ // file, since that protocol is what actually crosses the WASM import
16
+ // boundary. Keeping that as a precondition (rather than a relative
17
+ // #include reaching across repos) avoids a fragile cross-repo path that
18
+ // would break depending on checkout layout.
19
+ //
20
+ // keyslot.sign request/response wire shape (JSON meta document; the
21
+ // payload bytes travel as a binary segment referenced by "$bin", not
22
+ // inline base64 — the host bridge base64-encodes it for the capability
23
+ // handler on the way through):
24
+ //
25
+ // -> {"slotId":"...", "payload":{"$bin":0}, "algorithm":"ed25519"}
26
+ // <- {"signature":"<base64>", "algorithm":"ed25519"}
27
+ //
28
+ // See sdn-server/internal/modulert/caps/keyslot.go (handleKeyslotSign) for
29
+ // the authoritative host-side implementation.
30
+
31
+ #ifndef SDM_KEYSLOT_CLIENT_HPP
32
+ #define SDM_KEYSLOT_CLIENT_HPP
33
+
34
+ #ifndef SDM_HOSTCALL_WIRE_HPP
35
+ #error "include sdm_hostcall_wire.hpp (sdm_hostcall::call/Segment/Response) before keyslotClient.hpp"
36
+ #endif
37
+
38
+ #include <cstdint>
39
+ #include <string>
40
+ #include <string_view>
41
+ #include <vector>
42
+
43
+ namespace sdm_keyslot {
44
+
45
+ namespace detail {
46
+
47
+ inline std::string escape_json_string(std::string_view value) {
48
+ std::string escaped;
49
+ escaped.reserve(value.size());
50
+ for (const char c : value) {
51
+ switch (c) {
52
+ case '"':
53
+ escaped += "\\\"";
54
+ break;
55
+ case '\\':
56
+ escaped += "\\\\";
57
+ break;
58
+ case '\n':
59
+ escaped += "\\n";
60
+ break;
61
+ case '\r':
62
+ escaped += "\\r";
63
+ break;
64
+ case '\t':
65
+ escaped += "\\t";
66
+ break;
67
+ default:
68
+ escaped.push_back(c);
69
+ break;
70
+ }
71
+ }
72
+ return escaped;
73
+ }
74
+
75
+ inline int decode_base64_char(char c) {
76
+ if (c >= 'A' && c <= 'Z') return c - 'A';
77
+ if (c >= 'a' && c <= 'z') return c - 'a' + 26;
78
+ if (c >= '0' && c <= '9') return c - '0' + 52;
79
+ if (c == '+') return 62;
80
+ if (c == '/') return 63;
81
+ if (c == '=') return -2;
82
+ return -1;
83
+ }
84
+
85
+ inline bool decode_base64_bytes(std::string_view text, std::vector<uint8_t>* bytes_out) {
86
+ if (!bytes_out) {
87
+ return false;
88
+ }
89
+ bytes_out->clear();
90
+ uint32_t accumulator = 0;
91
+ int bits_collected = 0;
92
+ bool padding_seen = false;
93
+ for (const char c : text) {
94
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
95
+ continue;
96
+ }
97
+ const int decoded = decode_base64_char(c);
98
+ if (decoded == -1) {
99
+ return false;
100
+ }
101
+ if (decoded == -2) {
102
+ padding_seen = true;
103
+ continue;
104
+ }
105
+ if (padding_seen) {
106
+ return false;
107
+ }
108
+ accumulator = (accumulator << 6) | static_cast<uint32_t>(decoded);
109
+ bits_collected += 6;
110
+ if (bits_collected >= 8) {
111
+ bits_collected -= 8;
112
+ bytes_out->push_back(static_cast<uint8_t>((accumulator >> bits_collected) & 0xffu));
113
+ }
114
+ }
115
+ return true;
116
+ }
117
+
118
+ inline size_t skip_json_ws(std::string_view text, size_t cursor) {
119
+ while (cursor < text.size()) {
120
+ const char c = text[cursor];
121
+ if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
122
+ break;
123
+ }
124
+ ++cursor;
125
+ }
126
+ return cursor;
127
+ }
128
+
129
+ // Minimal find-key-then-read-string-value JSON scan. Good enough for the
130
+ // small, flat `{"signature":"...","algorithm":"..."}` result documents this
131
+ // header parses; not a general JSON parser.
132
+ inline bool extract_json_string_field(
133
+ std::string_view text,
134
+ std::string_view field,
135
+ std::string* value_out) {
136
+ if (!value_out) {
137
+ return false;
138
+ }
139
+ const std::string marker = "\"" + std::string(field) + "\"";
140
+ const size_t field_pos = text.find(marker);
141
+ if (field_pos == std::string_view::npos) {
142
+ return false;
143
+ }
144
+ const size_t colon_pos = text.find(':', field_pos + marker.size());
145
+ if (colon_pos == std::string_view::npos) {
146
+ return false;
147
+ }
148
+ size_t cursor = skip_json_ws(text, colon_pos + 1);
149
+ if (cursor >= text.size() || text[cursor] != '"') {
150
+ return false;
151
+ }
152
+ ++cursor;
153
+
154
+ std::string out;
155
+ while (cursor < text.size()) {
156
+ const char c = text[cursor++];
157
+ if (c == '"') {
158
+ *value_out = out;
159
+ return true;
160
+ }
161
+ if (c == '\\') {
162
+ if (cursor >= text.size()) {
163
+ return false;
164
+ }
165
+ const char escaped = text[cursor++];
166
+ switch (escaped) {
167
+ case '"':
168
+ case '\\':
169
+ case '/':
170
+ out.push_back(escaped);
171
+ break;
172
+ case 'b':
173
+ out.push_back('\b');
174
+ break;
175
+ case 'f':
176
+ out.push_back('\f');
177
+ break;
178
+ case 'n':
179
+ out.push_back('\n');
180
+ break;
181
+ case 'r':
182
+ out.push_back('\r');
183
+ break;
184
+ case 't':
185
+ out.push_back('\t');
186
+ break;
187
+ default:
188
+ return false;
189
+ }
190
+ continue;
191
+ }
192
+ out.push_back(c);
193
+ }
194
+ return false;
195
+ }
196
+
197
+ } // namespace detail
198
+
199
+ // keyslot_sign asks the host to sign `data` (length `len`) using the
200
+ // private key material held in slot `slot_id`. `algorithm` is "ed25519" (the
201
+ // default) or "secp256k1" — it must match what the slot actually holds. The
202
+ // slot's private key never crosses into guest memory; only the resulting
203
+ // signature does. Returns false on any hostcall, transport, or decode
204
+ // failure and leaves `signature_out` untouched in that case.
205
+ inline bool keyslot_sign(
206
+ std::string_view slot_id,
207
+ const uint8_t* data,
208
+ size_t len,
209
+ std::vector<uint8_t>* signature_out,
210
+ std::string_view algorithm = "ed25519") {
211
+ if (!signature_out || slot_id.empty()) {
212
+ return false;
213
+ }
214
+ const std::string meta =
215
+ "{\"slotId\":\"" + detail::escape_json_string(slot_id) +
216
+ "\",\"payload\":{\"$bin\":0},\"algorithm\":\"" +
217
+ detail::escape_json_string(algorithm) + "\"}";
218
+ sdm_hostcall::Response response;
219
+ if (!sdm_hostcall::call("keyslot.sign", meta, {{data, len}}, &response)) {
220
+ return false;
221
+ }
222
+ std::string signature_base64;
223
+ if (!detail::extract_json_string_field(response.meta, "signature", &signature_base64)) {
224
+ return false;
225
+ }
226
+ return detail::decode_base64_bytes(signature_base64, signature_out);
227
+ }
228
+
229
+ } // namespace sdm_keyslot
230
+
231
+ #endif // SDM_KEYSLOT_CLIENT_HPP