Language and runtime matrix

Status: WS12.1 deliverable. Section 1 is the definitive language/runtime matrix. Section 2 is the per-file port/dedupe plan that WS12.2 executes. All source paths verified against the live trees on 2026-07-02, then adversarially fact-checked (two independent review passes); citations below are to the 0.8.5 SDK tree unless marked otherwise.

Source trees referenced:


1. Language/runtime matrix

Framing: hosts embed a runtime to execute SDN .wasm modules; guests are the languages modules are written in. All SDN modules are standalone-WASI wasm compiled from C/C++ only (emscripten em++/emcc selected in selectCompiler, space-data-module-sdk/src/compiler/compileModule.js:78-84; STANDALONE_WASM=1 at compileModule.js:130). No Rust, AssemblyScript, TinyGo, or Python guests exist anywhere in the codebase.

Host environment / languageModule runtime usedWasmEdge native?Status in this codebaseEvidence
Go node (sdn-server) — GoWasmEdge embedded in-process via CGO (second-state/WasmEdge-go v0.14.0)YES (in-process, production)In use — the production module runtimesdn-server/go.mod:27; internal/wasmrt/runtime.go:12,117-130 (wasmedge.NewVMWithConfig, THREADS/EXCEPTION_HANDLING/WASI); internal/modulert/module.go:167-175
Browser — JS/TS (sdn-js UI, OrbPro)Plain WebAssembly.instantiate + pure-JS WASI shim + the space_data_module_host hostcall bridge (the JS harness)NO — WasmEdge does not exist in browsersIn usecreateBrowserModuleHarness, createWorkerModuleHarness, createFlowRuntimeHostsdn-js/src/ui/runtime/live-delivery.ts:2,204; SDK src/host/wasiShim.js:33, src/host/abi.js:10 (DEFAULT_HOSTCALL_IMPORT_MODULE = "space_data_module_host", matching Go internal/modulert/hostbridge.go:28), src/host/browserModuleHarness.js, src/flow/flowRuntimeHost.js:36
Node.js — JS/TS, subprocess pathWasmEdge out-of-process: spawn("wasmedge", ...) CLI, invoke request over stdin/stdoutYes, but only as an external binaryIn use (Node harness default for standalone artifacts)SDK src/host/isomorphicLoader.js:89,112; src/testing/processInvoke.js:111,131
Node.js — JS/TS, pure-JS pathPlain V8 WebAssembly + the same browser harness (wasiShim + hostcall bridge)NOIn use (vitest, isomorphic artifacts, no-wasmedge-binary environments)wasiShim.js:1-12 ("same .wasm that runs in WasmEdge … instantiated directly"); src/host/browserModuleHarness.js
C — native test runnerWasmEdge embedded via the C API (wasmedge/wasmedge.h)YES (in-process, test-only)In use (pthread module test runner only)SDK src/testing/native/wasmedge_emscripten_pthread_runner.c:9; src/testing/buildWasmEdgeRunner.js
C/C++ — as guest languagen/a (compiles to the wasm modules themselves)n/aIn use — the only guest languageAll module families (propagator/*, foundation/*, comms/*, closed-modules *.cpp); compiler supports exactly .cpp → em++ / else emcc
RustWasmEdge Rust SDK (wasmedge-sdk) exists upstreamWould be nativeN/A — not used (only vendored emsdk test fixtures contain .rs)Zero Cargo.toml outside deps/emsdk/.../test/rust/
PythonWasmEdge Python SDK exists upstream (experimental)Would be nativeN/A — not usedNo Python host or guest anywhere
Javawasmedge-java exists upstreamWould be nativeN/A — not usedNo Java anywhere
Node.js napi binding (wasmedge-core)Deliberately not used — project shells out to the CLI insteadNo napi dep in SDK or sdn-js/package.json

Definitive conclusion


2. Port/dedupe plan — the 10 orbpro-integration/sdk/src/runtime/ files

Conventions for WS12.2:

#File (lines)VerdictTarget / action
1bufferLike.js (28)DROPByte-identical twin already exists at SDK src/runtime/bufferLike.js (same three exports: isArrayBufferLike, hasByteAddressableBuffer, toUint8Array). Rewrite the two internal consumers (codec, streamBridge) to import from ../runtime/bufferLike.js. Zero-risk deletion.
2codec.js (94)PORT (split, with renames)The StreamInvokeRequest/Response + FlowProgram ("FLOW") codecs are a distinct envelope from the SDK's existing PluginInvokeRequest/Response codec (src/invoke/codec.js) — do not conflate them. Port encode/decodeFlowProgram and encode/decodeStreamInvoke{Request,Response} to new SDK file src/flow/flowCodec.js (subsuming src/testing/streamInvokeCodec.js, see precursor). The manifest codec does NOT merge: the SDK's encodePluginManifest (src/manifest/codec.js:258) is encodePlgManifest(legacyManifestToPlg(manifest)) — it emits a $PLG-identified buffer (src/manifest/plgCodec.js:37), consumed by compileModule.js:830,889 and SDK embeddedManifest.js:32 — while the harness's codec emits the PMAN-identified PluginManifest buffer that orbpro-integration's sdk/src/embeddedManifest.js:5 depends on. These are different wire formats; both must exist. Port the PMAN pair with renamed exports encodePluginManifestPman/decodePluginManifestPman (avoids star-export collision with the PLG-flavored names in src/manifest/index.js:7/browser.js:7 re-exported via src/browser.js:1), living in src/flow/flowCodec.js beside the flow codecs. Internal toByteBuffer moves with it. Browser-safe; only dep is flatbuffers (already an SDK dep).
3compiledFlowHost.js (35)MERGE (option) + KEEP compat shimcreateFlowRuntimeHost (0.8.5 src/flow/flowRuntimeHost.js) already runs the compiled flow runtime.wasm with Go-parity ABI. Merge INTO it the one new behavior here: withLegacyHostImportCompat — the stub import module sdn_flow_host with dispatch_current_invocation: () => 0 (caller entries win) so legacy artifacts instantiate under plain WebAssembly.instantiate — as an instantiate option (legacyHostImportCompat: true or an extraImports hook). bindCompiledFlowRuntimeHost is NOT deleted: it has production consumers — orbpro-integration/default-suite.js:631,738 call createDefaultPluginCompiledFlowHost (defaultRuntimeRegistry.js:164-166bindOrbProCompiledFlowHost), passing sdn-flow bound-host options and consuming the bound-host API (host.runEntrypoint, host.dispatchNextReadyNodeWithHost, host.drainWithHost…, default-suite.js:668-700) that the SDK host does not provide. The wrapper (and its sdn-flow/host import) stays in orbpro-integration as a compat adapter; migrating default-suite off the bound-host API onto the SDK flow host is a follow-on task, after which the sdk-package sdn-flow pin can go.
4FlowRuntime.js (275)PORT to src/flow/jsFlowRuntime.jsNew capability: a pure-JS FlowProgram interpreter (no runtime.wasm needed) — the fallback/test-tier scheduler, complementing flowRuntimeHost.js which requires the compiled artifact. Port intact but align contracts with the wasm host: same backpressure policies (queue/drop-oldest/drop-newest, depth ≥1 or Infinity), same yield semantics (response.yielded === true breaks the drain), same default maxInvocationsPerDrain = 1024, and make drain()'s return shape ({invocations, idle, queues}) documented alongside flowRuntimeHost.drain(). It must invoke through the registry surface chosen in #6. Export as space-data-module-sdk/flow alongside createFlowRuntimeHost. Browser-safe; only sibling import is the normalizer (#8).
5index.js (35)DROPThe barrel is orbpro-integration packaging, not code. Its 18 names (8 codec fns + bindOrbProCompiledFlowHost + createDependencyStreamBridge + MethodRegistry + FlowRuntime + 5 sdnCompat exports + generateLegacySdnShimSource) get re-homed: flow pieces + flow/PMAN codecs → ./flow, registry → ./runtime-host, compat → new ./compat (see #7), shim generator → ./compiler (see #9); bindOrbProCompiledFlowHost stays in orbpro-integration (#3). The renamed PMAN exports (#2) prevent barrel collisions with the SDK's existing PLG-flavored encodePluginManifest/decodePluginManifest. Update the SDK package.json exports map and src/browser.js accordingly. orbpro-integration keeps a thin re-export barrel for back-compat (Section 3).
6MethodRegistry.js (40)VENDOR sdn-flow's registry; wrapper stays thinThe file itself is a 40-line wrapper, but the sdn-flow/runtime contract behind it is not small: at the pinned commit (4dd28e2, verified against the local baseline worktree) sdn-flow/src/runtime/MethodRegistry.js is 400 lines + runtime/normalize.js 506 lines, enforcing duplicate-pluginId and missing-per-method-handler rejection (:231/:245 — defaultRuntimeRegistry.js:93-120 relies on these throws for its registration-failed reporting), required-input-port presence (:330), min/max distinct-stream bounds (:339-347), acceptedTypeSets frame-type rejection (:350-357), undeclared-input-port rejection (:362-367), internal-transport aligned-binary typeRef coercion with capability/interface exception sets (:10-28,:140-172), outputStreamCap overflow errors (:176-179), single-output portId defaulting (:181-188), and drainPolicy defaulting (:370-373). Reimplementing "lookup + call + normalization" would silently drop validation the ported specs assert. Plan: vendor sdn-flow's MethodRegistry.js + runtime/normalize.js into the SDK (src/runtime-host/methodRegistry.js + supporting normalize), preserving behavior verbatim (no github pin — vendored source), integrate with createModuleRegistry at the seams (manifest-aware registerPlugin accepting PluginManifestT \flatbuffer bytes \plain object via #8's normalizer), and keep the class-shaped MethodRegistry export. The five orbpro-integration spec files are the parity gate.
7sdnCompat.js (449)PORT to new src/compat/sdnLegacy.js (new ./compat subpath)No SDK equivalent exists for the legacy SDN metadata JSON envelope (buildLegacySdnMetadata — the exact snake_case JSON that plugin_get_metadata returns from a WasmEdge-loaded module) or the cron/protocol dispatch adapters (SdnCompatAdapter.invokeCron/invokeProtocol with legacyBridge context tagging, timer resolution by timerId-or-methodId, default-port stamping). This is the JS twin of the Go host's legacy surface and is consumed by the shim generator (#9). Port whole. Rewrites: generated imports (manifest, stream namespaces) → SDK generated tree; decodePluginManifest import → decodePluginManifestPman (#2); registry type → #6. Browser-safe (TextEncoder only). Do not change any field name, enum casing (UPPER_SNAKE), or the BigInt "<n>ms" interval formatting — this JSON is a wire contract with the Go host.
8sdnFlowAdapter.js (276)PORT (internal) to src/flow/normalize.jsRequired by #4 and #6: normalizeManifestForSdnFlow / normalizeProgramForSdnFlow encode the ABI conventions (numeric flatbuffer enums → kebab-case strings, maxBatch: 1 / drain-until-yield / queue/depth-1 defaults, schemaHash typed-array → plain array). Keep it internal — not in the exports map, exactly as it is today (never re-exported by the harness barrel). Since #6 vendors sdn-flow's registry (the sdn-flow reference stays meaningful), keep the exported names as-is. Rewrites: generated imports + decodePluginManifestPman/decodeFlowProgram from their new SDK homes. Browser-safe.
9sdnShimGenerator.js (323)PORT to src/compiler/sdnShimGenerator.jsThe SDK-surface audit's confirmed gap: no hostcall/legacy-ABI codegen exists in the SDK (only build-side compiler/invokeGlue.js). This generates the C shim exporting the legacy WasmEdge-host ABI (plugin_get_metadata / plugin_handle_request / plugin_cron, caller-allocated buffers, int32 length-or-negative-error). Port whole into the compiler layer, next to its natural consumer (the build pipeline; orbpro-integration's sdnArtifacts.js writes its output as sdn-compat-shim.c). The C template must be moved byte-identically: error constants -1..-5, EMSCRIPTEN_KEEPALIVE exports, \xNN string escaping, memcmp method dispatch, and the single-protocol-method constraint of resolveRequestMethodId are all ABI/behavior contracts. Rewrites: generated manifest import, decodePluginManifestPman + buildLegacySdnMetadata from their new SDK homes (#2/#7). The file is browser-safe (pure string generation) even though it lives under the Node-leaning ./compiler subpath — note that in the module docstring so a future browser build tool can import it.
10streamBridge.js (234)PORT to src/flow/dependencyStreamBridge.jsFills the other confirmed gap: a flow host that can invoke dependency wasm modules itself (guest malloc/free arena staging, TypedArenaBufferT input framing with defaults alignment=8/BORROWED/IMMUTABLE, invokeRawStream with the StreamInvokeRequest envelope, copy-out of (offset,size) outputs via cloneBytes so bytes survive memory growth, guaranteed release() in finally, deduped output-pointer freeing). flowRuntimeHost.drain() currently delegates non-linked-direct nodes to caller-supplied JS handlers; this bridge is what turns a dependency descriptor's instantiated module into such a handler. Port whole; wire an optional integration in flowRuntimeHost so a caller can pass {instantiatedDependency} per dependencyId instead of a raw handler. Merge notes: import toUint8Array/hasByteAddressableBuffer from SDK src/runtime/bufferLike.js (#1), the StreamInvoke codecs from src/flow/flowCodec.js (#2), and converge its output-frame shape with src/invoke/codec.js's TypedArenaBuffer materialization where field names differ (traceToken vs traceId — keep both readable, emit the harness's names for continuity). Do not replace it with the browser harness's direct surface: that speaks the PluginInvokeRequest envelope to plugin_invoke_stream; this speaks StreamInvokeRequest to invokeRawStream. They are different guest ABIs and both must exist.

Summary of verdicts


3. Risks and constraints for WS12.2

  1. Port into 0.8.5, not 0.8.4. The copy at /Users/tj/software/OrbPro/packages/space-data-module-sdk is stale (v0.8.4, missing src/flow/, workerModuleHarness, sabHostcallChannel.js, hostcallWire.js, timerDriver.js). All merges above name 0.8.5-only files. Decide early whether the stale OrbPro copy gets synced or retired; do not let the two copies diverge further.
  2. Do not disturb the existing SDK export surface. sdn-js pins the SDK tarball directly at 53748bc92bdac04a2372fe4a6dafa0e4a0abd43d (sdn-js/package.json:100; v0.8.5, one commit before ./flow existed) and imports these subpaths: root, /compiler, /host/timer-driver, /licensing, /runtime-host, /testing, /testing/browser, /testing/module-flatbuffer-stream-pump, /transport. It does not import ./flow. Additions to the exports map are fine; renames/removals of existing subpaths or src/browser.js re-exports are not.
  3. orbpro-integration consumers must keep working. Three surfaces depend on the harness today: (a) the public subpath @orbpro/integration-sdk/runtime (sdk/package.json:18) and the 18 names re-exported by sdk/index.js:61-80 / sdk/node.js; (b) deep imports in orbpro-integration/defaultRuntimeRegistry.js:1-2,12 (FlowRuntime.js, MethodRegistry.js, lazy compiledFlowHost.js); (c) in-SDK consumers sdk/src/sdnArtifacts.js:3-6 and sdk/src/embeddedManifest.js:5 (PMAN bytes — see #2). WS12.2 must leave compat shims at the old file paths (one-line re-exports from space-data-module-sdk) until OrbPro-side callers are migrated — especially the deep-imported files, which the barrel cannot cover.
  4. Two invoke envelopes coexist; neither may change. PluginInvokeRequest/Response (SDK src/invoke/codec.js, used by plugin_invoke_stream / _start surfaces, the worker harness, and the Go host) vs StreamInvokeRequest/Response (flow dependency invokeRawStream ABI). Keep them in separate files with distinct names; a unification refactor is out of scope and would break compiled artifacts.
  5. Two manifest envelopes coexist; neither may change. The SDK's encodePluginManifest/decodePluginManifest are $PLG-flavored (src/manifest/codec.js:258 via plgCodec.js); the harness's are PMAN-flavored. The ported PMAN pair ships under the renamed encodePluginManifestPman/decodePluginManifestPman exports; consumers of each keep their exact bytes.
  6. Wire/ABI contracts that must stay byte-identical: the "PMAN" and "FLOW" flatbuffer file identifiers (and the fact that StreamInvoke envelopes have no identifier); the legacy metadata JSON field set/casing/"<n>ms" BigInt intervals (buildLegacySdnMetadata); the generated C shim template including error codes −1…−5 and export names; the sdn_flow_host import-module name and dispatch_current_invocation stub in the compat option; TypedArenaBuffer staging defaults (alignment 8, BORROWED, IMMUTABLE, generation 0); the space_data_module_host hostcall import-module name (src/host/abi.js:10 = Go hostbridge.go:28).
  7. Vendoring sdn-flow's registry — test parity is the gate. The vendored MethodRegistry + normalize (#6) must behave identically to the pin; the five orbpro-integration spec files (UnifiedRuntimeSpec, SdnCompatSpec, CompiledFlowHostSpec, StreamInvokeBridgeSpec, SdnShimGeneratorSpec) must be ported alongside the code and pass against the SDK build before the shims land. CompiledFlowHostSpec imports layout constants from sdn-flow/host directly — rewrite those assertions against flowRuntimeHost.js's own struct-layout constants (60/72/48/24/32/24-byte descriptors).
  8. Browser-graph hygiene. Everything ported is browser-safe (verified: zero node: imports across the 10 files). Keep it that way: src/compat/sdnLegacy.js and src/flow/* go into src/browser.js; the shim generator goes only under ./compiler (Node-leaning) even though it is technically browser-safe; nothing ported may import nodeHost.js, sdnArtifacts.js-style fs/path, or the subprocess loaders.
  9. Don't re-port what 0.8.5 already finished: worker/SAB execution (workerModuleHarness + sabHostcallChannel + hostcallWire), cron/timer re-invocation (timerDriver + cron.js), isomorphic load/inspect (isomorphicLoader, detectArtifactProfile), and the compiled-flow wasm host's descriptor decoding (flowRuntimeHost). The port adds the JS interpreter tier, the dependency stream bridge, legacy compat, and the shim generator — nothing else.
  10. Naming collision watch: the SDK already exports createModuleRegistry; the vendored MethodRegistry must be documented as the flow-tier registry (validating, sdn-flow-derived) vs createModuleRegistry (module lifecycle) to avoid a third registry concept. Similarly jsFlowRuntime vs createFlowRuntimeHost need one doc page distinguishing "interpreter (no runtime.wasm)" from "compiled-artifact host (Go parity)".

SDN Stack

Connected sites