space-data-module-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +139 -0
  2. package/bin/space-data-module.js +230 -0
  3. package/package.json +40 -0
  4. package/schemas/PluginManifest.fbs +144 -0
  5. package/schemas/TypedArenaBuffer.fbs +78 -0
  6. package/src/auth/canonicalize.js +72 -0
  7. package/src/auth/index.js +10 -0
  8. package/src/auth/permissions.js +190 -0
  9. package/src/compiler/compileModule.js +237 -0
  10. package/src/compiler/index.js +7 -0
  11. package/src/compliance/index.js +56 -0
  12. package/src/compliance/pluginCompliance.js +574 -0
  13. package/src/embeddedManifest.js +124 -0
  14. package/src/generated/orbpro/manifest/accepted-type-set.d.ts +45 -0
  15. package/src/generated/orbpro/manifest/accepted-type-set.d.ts.map +1 -0
  16. package/src/generated/orbpro/manifest/accepted-type-set.js +100 -0
  17. package/src/generated/orbpro/manifest/accepted-type-set.js.map +1 -0
  18. package/src/generated/orbpro/manifest/accepted-type-set.ts +200 -0
  19. package/src/generated/orbpro/manifest/build-artifact.d.ts +41 -0
  20. package/src/generated/orbpro/manifest/build-artifact.d.ts.map +1 -0
  21. package/src/generated/orbpro/manifest/build-artifact.js +105 -0
  22. package/src/generated/orbpro/manifest/build-artifact.js.map +1 -0
  23. package/src/generated/orbpro/manifest/build-artifact.ts +193 -0
  24. package/src/generated/orbpro/manifest/capability-kind.d.ts +17 -0
  25. package/src/generated/orbpro/manifest/capability-kind.d.ts.map +1 -0
  26. package/src/generated/orbpro/manifest/capability-kind.js +20 -0
  27. package/src/generated/orbpro/manifest/capability-kind.js.map +1 -0
  28. package/src/generated/orbpro/manifest/capability-kind.ts +20 -0
  29. package/src/generated/orbpro/manifest/drain-policy.d.ts +9 -0
  30. package/src/generated/orbpro/manifest/drain-policy.d.ts.map +1 -0
  31. package/src/generated/orbpro/manifest/drain-policy.js +12 -0
  32. package/src/generated/orbpro/manifest/drain-policy.js.map +1 -0
  33. package/src/generated/orbpro/manifest/drain-policy.ts +12 -0
  34. package/src/generated/orbpro/manifest/host-capability.d.ts +36 -0
  35. package/src/generated/orbpro/manifest/host-capability.d.ts.map +1 -0
  36. package/src/generated/orbpro/manifest/host-capability.js +91 -0
  37. package/src/generated/orbpro/manifest/host-capability.js.map +1 -0
  38. package/src/generated/orbpro/manifest/host-capability.ts +161 -0
  39. package/src/generated/orbpro/manifest/method-manifest.d.ts +53 -0
  40. package/src/generated/orbpro/manifest/method-manifest.d.ts.map +1 -0
  41. package/src/generated/orbpro/manifest/method-manifest.js +154 -0
  42. package/src/generated/orbpro/manifest/method-manifest.js.map +1 -0
  43. package/src/generated/orbpro/manifest/method-manifest.ts +306 -0
  44. package/src/generated/orbpro/manifest/plugin-family.d.ts +17 -0
  45. package/src/generated/orbpro/manifest/plugin-family.d.ts.map +1 -0
  46. package/src/generated/orbpro/manifest/plugin-family.js +20 -0
  47. package/src/generated/orbpro/manifest/plugin-family.js.map +1 -0
  48. package/src/generated/orbpro/manifest/plugin-family.ts +20 -0
  49. package/src/generated/orbpro/manifest/plugin-manifest.d.ts +85 -0
  50. package/src/generated/orbpro/manifest/plugin-manifest.d.ts.map +1 -0
  51. package/src/generated/orbpro/manifest/plugin-manifest.js +268 -0
  52. package/src/generated/orbpro/manifest/plugin-manifest.js.map +1 -0
  53. package/src/generated/orbpro/manifest/plugin-manifest.ts +562 -0
  54. package/src/generated/orbpro/manifest/port-manifest.d.ts +70 -0
  55. package/src/generated/orbpro/manifest/port-manifest.d.ts.map +1 -0
  56. package/src/generated/orbpro/manifest/port-manifest.js +150 -0
  57. package/src/generated/orbpro/manifest/port-manifest.js.map +1 -0
  58. package/src/generated/orbpro/manifest/port-manifest.ts +284 -0
  59. package/src/generated/orbpro/manifest/protocol-spec.d.ts +41 -0
  60. package/src/generated/orbpro/manifest/protocol-spec.d.ts.map +1 -0
  61. package/src/generated/orbpro/manifest/protocol-spec.js +105 -0
  62. package/src/generated/orbpro/manifest/protocol-spec.js.map +1 -0
  63. package/src/generated/orbpro/manifest/protocol-spec.ts +205 -0
  64. package/src/generated/orbpro/manifest/timer-spec.d.ts +40 -0
  65. package/src/generated/orbpro/manifest/timer-spec.d.ts.map +1 -0
  66. package/src/generated/orbpro/manifest/timer-spec.js +104 -0
  67. package/src/generated/orbpro/manifest/timer-spec.js.map +1 -0
  68. package/src/generated/orbpro/manifest/timer-spec.ts +195 -0
  69. package/src/generated/orbpro/manifest.js +14 -0
  70. package/src/generated/orbpro/stream/buffer-mutability.d.ts +9 -0
  71. package/src/generated/orbpro/stream/buffer-mutability.d.ts.map +1 -0
  72. package/src/generated/orbpro/stream/buffer-mutability.js +12 -0
  73. package/src/generated/orbpro/stream/buffer-mutability.js.map +1 -0
  74. package/src/generated/orbpro/stream/buffer-mutability.ts +12 -0
  75. package/src/generated/orbpro/stream/buffer-ownership.d.ts +10 -0
  76. package/src/generated/orbpro/stream/buffer-ownership.d.ts.map +1 -0
  77. package/src/generated/orbpro/stream/buffer-ownership.js +13 -0
  78. package/src/generated/orbpro/stream/buffer-ownership.js.map +1 -0
  79. package/src/generated/orbpro/stream/buffer-ownership.ts +13 -0
  80. package/src/generated/orbpro/stream/flat-buffer-type-ref.d.ts +51 -0
  81. package/src/generated/orbpro/stream/flat-buffer-type-ref.d.ts.map +1 -0
  82. package/src/generated/orbpro/stream/flat-buffer-type-ref.js +115 -0
  83. package/src/generated/orbpro/stream/flat-buffer-type-ref.js.map +1 -0
  84. package/src/generated/orbpro/stream/flat-buffer-type-ref.ts +222 -0
  85. package/src/generated/orbpro/stream/typed-arena-buffer.d.ts +100 -0
  86. package/src/generated/orbpro/stream/typed-arena-buffer.d.ts.map +1 -0
  87. package/src/generated/orbpro/stream/typed-arena-buffer.js +215 -0
  88. package/src/generated/orbpro/stream/typed-arena-buffer.js.map +1 -0
  89. package/src/generated/orbpro/stream/typed-arena-buffer.ts +344 -0
  90. package/src/index.js +8 -0
  91. package/src/manifest/codec.js +40 -0
  92. package/src/manifest/index.js +9 -0
  93. package/src/manifest/normalize.js +275 -0
  94. package/src/runtime/bufferLike.js +28 -0
  95. package/src/runtime/constants.js +34 -0
  96. package/src/standards/index.js +153 -0
  97. package/src/standards/sharedCatalog.js +196 -0
  98. package/src/transport/index.js +8 -0
  99. package/src/transport/pki.js +140 -0
  100. package/src/utils/crypto.js +8 -0
  101. package/src/utils/encoding.js +54 -0
  102. package/src/utils/wasmCrypto.js +70 -0
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Space Data Module SDK
2
+
3
+ A unified SDK for building, validating, signing, and deploying **WebAssembly plugin modules** that run anywhere on the [Space Data Network](https://digitalarsenal.github.io/space-data-network/) — from [OrbPro](https://orbpro.ai) desktops to SDN peer nodes, ground stations, and browsers.
4
+
5
+ The space domain has a fragmentation problem: every platform ships its own plugin format, its own manifest schema, its own packaging conventions. A propagator written for one system can't run on another without a rewrite. This SDK solves that by defining a **single canonical module format** — a WebAssembly binary with an embedded [FlatBuffers](https://digitalarsenal.github.io/flatbuffers/) manifest — that every runtime in the ecosystem understands.
6
+
7
+ Modules declare **typed streaming ports** that accept data conforming to [spacedatastandards.org](https://spacedatastandards.org) schemas (OMM, CAT, EPM, CDM, and 40+ others). A propagator that consumes OMM messages and emits state vectors works identically whether it's running inside OrbPro's 3D scene, processing data on an SDN relay node, or executing at the edge on a ground station.
8
+
9
+ <p align="center">
10
+ <img src="docs/architecture.svg" alt="Architecture overview" width="820" />
11
+ </p>
12
+
13
+ ## How It Works
14
+
15
+ The SDK handles the full module lifecycle — from source code to a signed, encrypted, deployment-ready package:
16
+
17
+ <p align="center">
18
+ <img src="docs/module-lifecycle.svg" alt="Module lifecycle" width="820" />
19
+ </p>
20
+
21
+ 1. **Author** a JSON manifest declaring your module's identity, methods, typed I/O ports, host capabilities, and the [spacedatastandards.org](https://spacedatastandards.org) schemas it consumes and produces.
22
+ 2. **Compile** your C/C++ source (via Emscripten) into a `.wasm` binary with the manifest automatically embedded as a FlatBuffers blob that runtimes can read at load time.
23
+ 3. **Validate** the manifest and artifact against compliance rules — correct port declarations, canonical capability IDs, required WASM ABI exports (`plugin_get_manifest_flatbuffer`, `plugin_get_manifest_flatbuffer_size`), and schema resolution against the standards catalog.
24
+ 4. **Sign** the package with an HD-wallet-derived secp256k1 key, producing a deployment authorization that binds the manifest hash, WASM hash, target, and granted capabilities.
25
+ 5. **Protect** the signed package by encrypting it for a specific recipient using X25519 key agreement + AES-256-GCM, so modules can be transported securely across the network.
26
+
27
+ ## Manifest & Typed Ports
28
+
29
+ Every module carries a manifest that declares **what data it can process**. Methods expose typed input and output ports, and each port declares the FlatBuffer schemas it accepts — referencing standards by schema name and file identifier:
30
+
31
+ <p align="center">
32
+ <img src="docs/manifest-structure.svg" alt="Manifest structure" width="820" />
33
+ </p>
34
+
35
+ This means runtimes can **automatically wire modules together** — connecting a propagator's `CAT` output to a conjunction screener's `CAT` input — without any glue code. The type system ensures only compatible modules get connected.
36
+
37
+ ## Ecosystem
38
+
39
+ This SDK is one piece of the Space Data Network stack:
40
+
41
+ | Project | Role |
42
+ |---|---|
43
+ | [Space Data Network](https://digitalarsenal.github.io/space-data-network/) | Peer-to-peer network for space data exchange |
44
+ | [spacedatastandards.org](https://spacedatastandards.org) | 40+ canonical FlatBuffer schemas for space operations data (OMM, EPM, CAT, CDM, etc.) |
45
+ | [FlatBuffers schemas](https://digitalarsenal.github.io/flatbuffers/) | Binary serialization layer used across the entire network |
46
+ | [OrbPro](https://orbpro.ai) | Space domain awareness platform — one of the runtimes that hosts these modules |
47
+ | [hd-wallet-wasm](https://github.com/nicktj-dev/hd-wallet-wasm) | HD wallet primitives for module signing and identity |
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ npm install space-data-module-sdk
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```js
58
+ import {
59
+ encodeManifest, decodeManifest, // FlatBuffers manifest codec
60
+ checkCompliance, // validate against standards
61
+ signManifest, verifyManifest, // HD wallet auth
62
+ encryptPayload, decryptPayload, // X25519 + AES-256-GCM transport
63
+ compileModule, // source-to-wasm compilation
64
+ } from "space-data-module-sdk";
65
+ ```
66
+
67
+ ### Subpath Exports
68
+
69
+ Each subsystem is available as a standalone import:
70
+
71
+ ```js
72
+ import { encodeManifest } from "space-data-module-sdk/manifest";
73
+ import { checkCompliance } from "space-data-module-sdk/compliance";
74
+ import { signManifest } from "space-data-module-sdk/auth";
75
+ import { encryptPayload } from "space-data-module-sdk/transport";
76
+ import { compileModule } from "space-data-module-sdk/compiler";
77
+ import { resolveStandard } from "space-data-module-sdk/standards";
78
+ ```
79
+
80
+ ## CLI
81
+
82
+ Every SDK operation is also available from the command line:
83
+
84
+ ```bash
85
+ # Validate a manifest + wasm pair against compliance rules
86
+ npx space-data-module check --manifest ./manifest.json --wasm ./dist/module.wasm
87
+
88
+ # Compile C/C++ source to a wasm module with embedded manifest
89
+ npx space-data-module compile --manifest ./manifest.json --source ./src/module.c --out ./dist/module.wasm
90
+
91
+ # Sign and encrypt a module package for transport
92
+ npx space-data-module protect --manifest ./manifest.json --wasm ./dist/module.wasm --json
93
+ ```
94
+
95
+ ## Module Lab
96
+
97
+ An interactive browser tool for compiling, validating, and packaging modules — useful for exploring the manifest format and testing modules without touching the CLI.
98
+
99
+ ```bash
100
+ npm run start:lab
101
+ # http://localhost:4318
102
+ ```
103
+
104
+ ## Plugin Families
105
+
106
+ Modules declare a `pluginFamily` that tells runtimes what role they serve:
107
+
108
+ | Family | Purpose |
109
+ |---|---|
110
+ | `sensor` | Ingest raw data feeds (radar, optical, RF) |
111
+ | `propagator` | Orbit propagation and state prediction |
112
+ | `renderer` | 3D visualization and scene rendering |
113
+ | `analysis` | Data processing, filtering, aggregation |
114
+ | `data_source` | External data connectors |
115
+ | `comms` | Communications link modeling |
116
+ | `shader` | GPU shader programs |
117
+ | `sdf` | Signed distance field geometry |
118
+ | `infrastructure` | Network and platform services |
119
+ | `flow` | Multi-module data flow orchestration |
120
+ | `bridge` | Cross-runtime adapters |
121
+
122
+ ## Host Capabilities
123
+
124
+ Modules request host capabilities by name. The runtime grants or denies them at load time based on the deployment authorization:
125
+
126
+ `clock` `random` `timers` `http` `network` `filesystem` `pipe` `pubsub` `protocol_handle` `protocol_dial` `database` `storage_adapter` `storage_query` `storage_write` `wallet_sign` `ipfs` `scene_access` `render_hooks`
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ npm install
132
+ npm test
133
+ ```
134
+
135
+ Requires Node.js >= 20. Compilation requires [Emscripten](https://emscripten.org/) (`emcc`/`em++`) on `PATH`.
136
+
137
+ ## License
138
+
139
+ See [LICENSE](LICENSE).
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { readFile, writeFile } from "node:fs/promises";
6
+
7
+ import {
8
+ compileModuleFromSource,
9
+ loadComplianceConfig,
10
+ protectModuleArtifact,
11
+ resolveManifestFiles,
12
+ validateArtifactWithStandards,
13
+ validateManifestWithStandards,
14
+ loadManifestFromFile,
15
+ } from "../src/index.js";
16
+ import { bytesToBase64 } from "../src/utils/encoding.js";
17
+
18
+ async function main(argv) {
19
+ const [command, ...rest] = argv;
20
+ switch (command) {
21
+ case "check":
22
+ return runCheck(rest);
23
+ case "compile":
24
+ return runCompile(rest);
25
+ case "protect":
26
+ return runProtect(rest);
27
+ default:
28
+ printUsage();
29
+ return command ? 1 : 0;
30
+ }
31
+ }
32
+
33
+ function parseArgs(argv) {
34
+ const options = {
35
+ json: false,
36
+ repoRoot: process.cwd(),
37
+ manifestPath: null,
38
+ wasmPath: null,
39
+ sourcePath: null,
40
+ language: "c",
41
+ outputPath: null,
42
+ recipientPublicKeyHex: null,
43
+ mnemonic: null,
44
+ };
45
+
46
+ for (let index = 0; index < argv.length; index += 1) {
47
+ const value = argv[index];
48
+ switch (value) {
49
+ case "--json":
50
+ options.json = true;
51
+ break;
52
+ case "--repo-root":
53
+ options.repoRoot = path.resolve(requireValue(argv, ++index, value));
54
+ break;
55
+ case "--manifest":
56
+ options.manifestPath = path.resolve(requireValue(argv, ++index, value));
57
+ break;
58
+ case "--wasm":
59
+ options.wasmPath = path.resolve(requireValue(argv, ++index, value));
60
+ break;
61
+ case "--source":
62
+ options.sourcePath = path.resolve(requireValue(argv, ++index, value));
63
+ break;
64
+ case "--language":
65
+ options.language = requireValue(argv, ++index, value);
66
+ break;
67
+ case "--out":
68
+ options.outputPath = path.resolve(requireValue(argv, ++index, value));
69
+ break;
70
+ case "--recipient-public-key":
71
+ options.recipientPublicKeyHex = requireValue(argv, ++index, value);
72
+ break;
73
+ case "--mnemonic":
74
+ options.mnemonic = requireValue(argv, ++index, value);
75
+ break;
76
+ default:
77
+ throw new Error(`Unknown argument: ${value}`);
78
+ }
79
+ }
80
+
81
+ return options;
82
+ }
83
+
84
+ function requireValue(argv, index, flagName) {
85
+ const value = argv[index];
86
+ if (!value) {
87
+ throw new Error(`${flagName} requires a value.`);
88
+ }
89
+ return value;
90
+ }
91
+
92
+ function printUsage() {
93
+ console.log(`Usage:
94
+ space-data-module check --repo-root .
95
+ space-data-module check --manifest ./manifest.json --wasm ./dist/module.wasm
96
+ space-data-module compile --manifest ./manifest.json --source ./src/module.c --out ./dist/module.wasm
97
+ space-data-module protect --manifest ./manifest.json --wasm ./dist/module.wasm --json
98
+ `);
99
+ }
100
+
101
+ function printReport(report) {
102
+ console.log(`${report.ok ? "PASS" : "FAIL"} ${report.sourceName}`);
103
+ for (const issue of report.issues) {
104
+ console.log(
105
+ ` ${issue.severity.toUpperCase()} ${issue.code}: ${issue.message}`,
106
+ );
107
+ }
108
+ if (report.issues.length === 0) {
109
+ console.log(" No issues found.");
110
+ }
111
+ }
112
+
113
+ async function runCheck(argv) {
114
+ const options = parseArgs(argv);
115
+ if (options.manifestPath) {
116
+ const manifest = await loadManifestFromFile(options.manifestPath);
117
+ const report = options.wasmPath
118
+ ? await validateArtifactWithStandards({
119
+ manifest,
120
+ manifestPath: options.manifestPath,
121
+ wasmPath: options.wasmPath,
122
+ })
123
+ : await validateManifestWithStandards(manifest, {
124
+ sourceName: options.manifestPath,
125
+ });
126
+ if (options.json) {
127
+ console.log(JSON.stringify(report, null, 2));
128
+ } else {
129
+ printReport(report);
130
+ }
131
+ return report.ok ? 0 : 1;
132
+ }
133
+
134
+ const manifestPaths = await resolveManifestFiles(options.repoRoot);
135
+ if (manifestPaths.length === 0) {
136
+ const loadedConfig = await loadComplianceConfig(options.repoRoot);
137
+ if (loadedConfig?.config?.allowEmpty === true) {
138
+ if (!options.json) {
139
+ console.log(
140
+ `No manifests configured under ${options.repoRoot}; allowEmpty=true so the check passes.`,
141
+ );
142
+ }
143
+ return 0;
144
+ }
145
+ console.error(`No manifest.json files found under ${options.repoRoot}`);
146
+ return 1;
147
+ }
148
+ const reports = [];
149
+ for (const manifestPath of manifestPaths) {
150
+ const manifest = await loadManifestFromFile(manifestPath);
151
+ reports.push(
152
+ await validateManifestWithStandards(manifest, { sourceName: manifestPath }),
153
+ );
154
+ }
155
+ if (options.json) {
156
+ console.log(JSON.stringify(reports, null, 2));
157
+ } else {
158
+ reports.forEach(printReport);
159
+ }
160
+ return reports.every((report) => report.ok) ? 0 : 1;
161
+ }
162
+
163
+ async function runCompile(argv) {
164
+ const options = parseArgs(argv);
165
+ if (!options.manifestPath || !options.sourcePath || !options.outputPath) {
166
+ throw new Error("compile requires --manifest, --source, and --out.");
167
+ }
168
+ const manifest = await loadManifestFromFile(options.manifestPath);
169
+ const sourceCode = await readFile(options.sourcePath, "utf8");
170
+ const result = await compileModuleFromSource({
171
+ manifest,
172
+ sourceCode,
173
+ language: options.language,
174
+ outputPath: options.outputPath,
175
+ });
176
+ await writeFile(options.outputPath, result.wasmBytes);
177
+ if (options.json) {
178
+ console.log(
179
+ JSON.stringify(
180
+ {
181
+ outputPath: options.outputPath,
182
+ manifestWarnings: result.manifestWarnings,
183
+ report: result.report,
184
+ },
185
+ null,
186
+ 2,
187
+ ),
188
+ );
189
+ } else {
190
+ console.log(`Wrote ${options.outputPath}`);
191
+ result.manifestWarnings.forEach((warning) =>
192
+ console.log(` WARNING ${warning}`),
193
+ );
194
+ printReport(result.report);
195
+ }
196
+ return result.report.ok ? 0 : 1;
197
+ }
198
+
199
+ async function runProtect(argv) {
200
+ const options = parseArgs(argv);
201
+ if (!options.manifestPath || !options.wasmPath) {
202
+ throw new Error("protect requires --manifest and --wasm.");
203
+ }
204
+ const manifest = await loadManifestFromFile(options.manifestPath);
205
+ const wasmBytes = new Uint8Array(await readFile(options.wasmPath));
206
+ const result = await protectModuleArtifact({
207
+ manifest,
208
+ wasmBytes,
209
+ recipientPublicKeyHex: options.recipientPublicKeyHex,
210
+ mnemonic: options.mnemonic,
211
+ });
212
+ if (options.json) {
213
+ console.log(JSON.stringify(result, null, 2));
214
+ } else {
215
+ console.log(`artifactId=${result.payload.artifactId}`);
216
+ console.log(`signingPublicKeyHex=${result.signingPublicKeyHex}`);
217
+ console.log(`encrypted=${result.encrypted}`);
218
+ console.log(`wasmBase64Length=${bytesToBase64(wasmBytes).length}`);
219
+ }
220
+ return 0;
221
+ }
222
+
223
+ main(process.argv.slice(2))
224
+ .then((exitCode) => {
225
+ process.exitCode = exitCode;
226
+ })
227
+ .catch((error) => {
228
+ console.error(error.message);
229
+ process.exitCode = 1;
230
+ });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "space-data-module-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Module SDK for building, validating, signing, and deploying WebAssembly modules on the Space Data Network.",
5
+ "type": "module",
6
+ "bin": {
7
+ "space-data-module": "./bin/space-data-module.js"
8
+ },
9
+ "exports": {
10
+ ".": "./src/index.js",
11
+ "./manifest": "./src/manifest/index.js",
12
+ "./compliance": "./src/compliance/index.js",
13
+ "./auth": "./src/auth/index.js",
14
+ "./transport": "./src/transport/index.js",
15
+ "./compiler": "./src/compiler/index.js",
16
+ "./standards": "./src/standards/index.js",
17
+ "./schemas/*": "./schemas/*"
18
+ },
19
+ "files": [
20
+ "bin/",
21
+ "schemas/",
22
+ "src/"
23
+ ],
24
+ "scripts": {
25
+ "test": "node --test",
26
+ "start:lab": "node ./lab/server.js",
27
+ "check:compliance": "node ./bin/space-data-module.js check --repo-root ."
28
+ },
29
+ "dependencies": {
30
+ "flatbuffers": "^25.9.23",
31
+ "hd-wallet-wasm": "^1.5.4",
32
+ "spacedatastandards.org": "23.3.3-0.3.4"
33
+ },
34
+ "devDependencies": {
35
+ "express": "^4.21.2"
36
+ },
37
+ "engines": {
38
+ "node": ">=20.0.0"
39
+ }
40
+ }
@@ -0,0 +1,144 @@
1
+ // OrbPro Plugin SDK - Plugin Manifest Schema
2
+ //
3
+ // Canonical manifest for unified OrbPro + SDN plugins.
4
+
5
+ include "TypedArenaBuffer.fbs";
6
+
7
+ namespace orbpro.manifest;
8
+
9
+ /// Canonical plugin family.
10
+ enum PluginFamily : ubyte {
11
+ SENSOR = 0,
12
+ PROPAGATOR = 1,
13
+ RENDERER = 2,
14
+ ANALYSIS = 3,
15
+ DATA_SOURCE = 4,
16
+ COMMS = 5,
17
+ SHADER = 6,
18
+ SDF = 7,
19
+ INFRASTRUCTURE = 8,
20
+ FLOW = 9,
21
+ BRIDGE = 10
22
+ }
23
+
24
+ /// Host capability requested by a plugin.
25
+ enum CapabilityKind : ushort {
26
+ CLOCK = 0,
27
+ RANDOM = 1,
28
+ LOGGING = 2,
29
+ TIMERS = 3,
30
+ PUBSUB = 4,
31
+ PROTOCOL_DIAL = 5,
32
+ PROTOCOL_HANDLE = 6,
33
+ STORAGE_QUERY = 7,
34
+ SCENE_ACCESS = 8,
35
+ ENTITY_ACCESS = 9,
36
+ RENDER_HOOKS = 10
37
+ }
38
+
39
+ /// Drain policy for methods that operate over queued stream frames.
40
+ enum DrainPolicy : ubyte {
41
+ SINGLE_SHOT = 0,
42
+ DRAIN_UNTIL_YIELD = 1,
43
+ DRAIN_TO_EMPTY = 2
44
+ }
45
+
46
+ /// Accepted schema family for a port.
47
+ table AcceptedTypeSet {
48
+ /// Stable type-set identifier.
49
+ set_id: string (required);
50
+
51
+ /// Specific FlatBuffer types accepted by the set.
52
+ allowed_types: [orbpro.stream.FlatBufferTypeRef];
53
+
54
+ /// Human-readable explanation of the accepted schema family.
55
+ description: string;
56
+ }
57
+
58
+ /// One input or output port on a method.
59
+ table PortManifest {
60
+ /// Stable port identifier within the method.
61
+ port_id: string (required);
62
+
63
+ /// Human-readable name for UIs.
64
+ display_name: string;
65
+
66
+ /// Type sets accepted on this port.
67
+ accepted_type_sets: [AcceptedTypeSet];
68
+
69
+ /// Minimum number of streams that must be connected.
70
+ min_streams: uint16 = 1;
71
+
72
+ /// Maximum number of streams that may be connected.
73
+ max_streams: uint16 = 1;
74
+
75
+ /// Whether the port must be connected for invocation.
76
+ required: bool = true;
77
+
78
+ /// Optional human-readable description.
79
+ description: string;
80
+ }
81
+
82
+ /// One host capability dependency.
83
+ table HostCapability {
84
+ capability: CapabilityKind;
85
+ scope: string;
86
+ required: bool = true;
87
+ description: string;
88
+ }
89
+
90
+ /// Timer entry declared by a plugin.
91
+ table TimerSpec {
92
+ timer_id: string (required);
93
+ method_id: string (required);
94
+ input_port_id: string;
95
+ default_interval_ms: uint64;
96
+ description: string;
97
+ }
98
+
99
+ /// Protocol handler declared by a plugin.
100
+ table ProtocolSpec {
101
+ protocol_id: string (required);
102
+ method_id: string (required);
103
+ input_port_id: string;
104
+ output_port_id: string;
105
+ description: string;
106
+ }
107
+
108
+ /// Build artifact emitted by the plugin toolchain.
109
+ table BuildArtifact {
110
+ artifact_id: string (required);
111
+ kind: string;
112
+ path: string (required);
113
+ target: string;
114
+ entry_symbol: string;
115
+ }
116
+
117
+ /// Canonical method declaration.
118
+ table MethodManifest {
119
+ method_id: string (required);
120
+ display_name: string;
121
+ input_ports: [PortManifest];
122
+ output_ports: [PortManifest];
123
+ max_batch: uint32 = 1;
124
+ drain_policy: DrainPolicy = DRAIN_UNTIL_YIELD;
125
+ description: string;
126
+ }
127
+
128
+ /// Canonical plugin manifest.
129
+ table PluginManifest {
130
+ plugin_id: string (required, key);
131
+ name: string;
132
+ version: string;
133
+ plugin_family: PluginFamily = ANALYSIS;
134
+ methods: [MethodManifest];
135
+ capabilities: [HostCapability];
136
+ timers: [TimerSpec];
137
+ protocols: [ProtocolSpec];
138
+ schemas_used: [orbpro.stream.FlatBufferTypeRef];
139
+ build_artifacts: [BuildArtifact];
140
+ abi_version: uint32 = 1;
141
+ }
142
+
143
+ root_type PluginManifest;
144
+ file_identifier "PMAN";
@@ -0,0 +1,78 @@
1
+ // OrbPro Plugin SDK - Typed Arena Buffer Schema
2
+ //
3
+ // Canonical descriptor for schema-tagged FlatBuffer frames moving through
4
+ // arena-backed plugin streams.
5
+
6
+ namespace orbpro.stream;
7
+
8
+ /// FlatBuffer schema identity for a stream frame or accepted port type.
9
+ table FlatBufferTypeRef {
10
+ /// Logical schema name, for example `OMM.fbs`.
11
+ schema_name: string;
12
+
13
+ /// Optional 4-byte FlatBuffer file identifier.
14
+ file_identifier: string;
15
+
16
+ /// Optional schema hash bytes for stronger compatibility checks.
17
+ schema_hash: [ubyte];
18
+
19
+ /// True when this port/type set accepts any FlatBuffer frame.
20
+ accepts_any_flatbuffer: bool = false;
21
+ }
22
+
23
+ /// Ownership mode for an arena-backed frame.
24
+ enum BufferOwnership : ubyte {
25
+ BORROWED = 0,
26
+ PRODUCER_OWNED = 1,
27
+ HOST_OWNED = 2,
28
+ SHARED = 3
29
+ }
30
+
31
+ /// Mutability contract for an arena-backed frame.
32
+ enum BufferMutability : ubyte {
33
+ IMMUTABLE = 0,
34
+ APPEND_ONLY = 1,
35
+ MUTABLE = 2
36
+ }
37
+
38
+ /// Runtime descriptor for one FlatBuffer frame stored in an arena.
39
+ table TypedArenaBuffer {
40
+ /// Runtime schema identity for this frame.
41
+ type_ref: FlatBufferTypeRef;
42
+
43
+ /// Port that produced or will consume this frame.
44
+ port_id: string;
45
+
46
+ /// Required alignment of the underlying frame bytes.
47
+ alignment: uint16 = 8;
48
+
49
+ /// Frame byte offset from the arena base.
50
+ offset: uint32;
51
+
52
+ /// Frame size in bytes.
53
+ size: uint32;
54
+
55
+ /// Ownership contract for the buffer.
56
+ ownership: BufferOwnership = BORROWED;
57
+
58
+ /// Generation counter for stale-reference detection.
59
+ generation: uint32;
60
+
61
+ /// Mutability contract for downstream consumers.
62
+ mutability: BufferMutability = IMMUTABLE;
63
+
64
+ /// Flow/runtime trace identifier.
65
+ trace_id: uint64;
66
+
67
+ /// Logical stream identifier.
68
+ stream_id: uint32;
69
+
70
+ /// Monotonic frame sequence number within a stream.
71
+ sequence: uint64;
72
+
73
+ /// True if this frame closes the stream.
74
+ end_of_stream: bool = false;
75
+ }
76
+
77
+ root_type TypedArenaBuffer;
78
+ file_identifier "TABF";
@@ -0,0 +1,72 @@
1
+ import { bytesToBase64 } from "../utils/encoding.js";
2
+ import { sha256Bytes } from "../utils/crypto.js";
3
+
4
+ function normalizeCanonicalValue(value) {
5
+ if (value === undefined) {
6
+ return undefined;
7
+ }
8
+ if (
9
+ value === null ||
10
+ typeof value === "boolean" ||
11
+ typeof value === "string"
12
+ ) {
13
+ return value;
14
+ }
15
+ if (typeof value === "number") {
16
+ if (!Number.isFinite(value)) {
17
+ throw new TypeError("Canonical payloads cannot contain non-finite numbers.");
18
+ }
19
+ return value;
20
+ }
21
+ if (typeof value === "bigint") {
22
+ return value.toString();
23
+ }
24
+ if (
25
+ value instanceof Uint8Array ||
26
+ value instanceof ArrayBuffer ||
27
+ ArrayBuffer.isView(value)
28
+ ) {
29
+ return {
30
+ __type: "bytes",
31
+ base64: bytesToBase64(
32
+ value instanceof Uint8Array
33
+ ? value
34
+ : new Uint8Array(
35
+ value.buffer ?? value,
36
+ value.byteOffset ?? 0,
37
+ value.byteLength ?? value.byteLength,
38
+ ),
39
+ ),
40
+ };
41
+ }
42
+ if (value instanceof Date) {
43
+ return value.toISOString();
44
+ }
45
+ if (Array.isArray(value)) {
46
+ return value.map((item) => {
47
+ const normalized = normalizeCanonicalValue(item);
48
+ return normalized === undefined ? null : normalized;
49
+ });
50
+ }
51
+ if (typeof value === "object") {
52
+ const entries = Object.entries(value)
53
+ .filter(([, nestedValue]) => nestedValue !== undefined)
54
+ .sort(([left], [right]) => left.localeCompare(right))
55
+ .map(([key, nestedValue]) => [key, normalizeCanonicalValue(nestedValue)]);
56
+ return Object.fromEntries(entries);
57
+ }
58
+ throw new TypeError(`Unsupported canonical value type: ${typeof value}`);
59
+ }
60
+
61
+ export function stableStringify(value) {
62
+ return JSON.stringify(normalizeCanonicalValue(value));
63
+ }
64
+
65
+ export function canonicalBytes(value) {
66
+ return new TextEncoder().encode(stableStringify(value));
67
+ }
68
+
69
+ export async function hashCanonicalValue(value) {
70
+ return sha256Bytes(canonicalBytes(value));
71
+ }
72
+