space-data-module-sdk 0.5.13 → 0.5.15
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.
- package/README.md +45 -1
- package/package.json +1 -4
- package/src/compiler/flatcSupport.js +3 -47
- package/src/compliance/pluginCompliance.js +1 -1
- package/src/index.d.ts +4 -0
- package/src/manifest/typeRefs.js +12 -49
- package/src/testing/index.d.ts +81 -0
- package/src/testing/index.js +4 -0
- package/src/testing/publicationProtectionDemo.js +271 -0
- package/src/transport/records.js +1 -7
package/README.md
CHANGED
|
@@ -321,6 +321,46 @@ The reference path lives in
|
|
|
321
321
|
Standard bundle payloads now include the optional `deployment-plan` JSON entry
|
|
322
322
|
for resolved protocol installations and producer input bindings.
|
|
323
323
|
|
|
324
|
+
## Publication Protection Extensions
|
|
325
|
+
|
|
326
|
+
Digital signature and encrypted-delivery metadata are publication-layer
|
|
327
|
+
extensions, not a second module format. The canonical module is still:
|
|
328
|
+
|
|
329
|
+
- valid `.wasm`
|
|
330
|
+
- optionally carrying `sds.bundle` as a wasm custom section
|
|
331
|
+
- optionally carrying one appended SDS `REC` trailer after the wasm bytes
|
|
332
|
+
|
|
333
|
+
That trailing `REC` FlatBuffer is the standards-backed container for
|
|
334
|
+
publication records:
|
|
335
|
+
|
|
336
|
+
- `PNM` is the publication notice / digital-signature record
|
|
337
|
+
- identifies the artifact
|
|
338
|
+
- carries the CID and publish timestamp
|
|
339
|
+
- carries signature metadata so a loader can verify who published the module
|
|
340
|
+
- `ENC` is the encrypted-delivery record
|
|
341
|
+
- carries the X25519 / AES-CTR / HKDF parameters needed to decrypt the
|
|
342
|
+
protected transport payload
|
|
343
|
+
- does not change the canonical module ABI or manifest shape
|
|
344
|
+
|
|
345
|
+
The loader contract is always:
|
|
346
|
+
|
|
347
|
+
1. Start with the protected blob.
|
|
348
|
+
2. Scan backward for the trailing `REC` footer.
|
|
349
|
+
3. Parse `REC` using the standards-generated `REC`, `PNM`, and `ENC`
|
|
350
|
+
FlatBuffers from `spacedatastandards.org`.
|
|
351
|
+
4. Resolve `PNM` for signature/publication metadata.
|
|
352
|
+
5. Resolve `ENC` if the delivery was encrypted.
|
|
353
|
+
6. Strip the trailer or decrypt the protected payload.
|
|
354
|
+
7. Hand the remaining raw wasm bytes to the runtime.
|
|
355
|
+
|
|
356
|
+
Aligned-binary payloads are a separate invoke-ABI optimization. They do not
|
|
357
|
+
replace the canonical FlatBuffer schema. If a port advertises
|
|
358
|
+
`wireFormat: "aligned-binary"`, it must also advertise the regular
|
|
359
|
+
`wireFormat: "flatbuffer"` fallback for the same schema and file identifier in
|
|
360
|
+
the same accepted type set. The publication demo in the local lab uses a
|
|
361
|
+
regular `OMM.fbs` request and a `StateVector.fbs` response that advertises both
|
|
362
|
+
the canonical FlatBuffer fallback and the aligned-binary layout metadata.
|
|
363
|
+
|
|
324
364
|
## Module Publication
|
|
325
365
|
|
|
326
366
|
Packages that publish Space Data modules use the canonical `sdn-module`
|
|
@@ -424,10 +464,14 @@ npm run start:lab
|
|
|
424
464
|
|
|
425
465
|
Then open `http://localhost:4318`.
|
|
426
466
|
|
|
467
|
+
Use the `Run Publication Demo` button to generate a protected demo artifact and
|
|
468
|
+
inspect the parsed `REC`, `PNM`, and `ENC` records produced by the real
|
|
469
|
+
`spacedatastandards.org` generated message classes.
|
|
470
|
+
|
|
427
471
|
## Related Projects
|
|
428
472
|
|
|
429
473
|
- [`spacedatastandards.org`](https://spacedatastandards.org)
|
|
430
|
-
- [
|
|
474
|
+
- [`sdn-flow`](https://github.com/DigitalArsenal/sdn-flow)
|
|
431
475
|
- [`hd-wallet-wasm`](https://github.com/nicktj-dev/hd-wallet-wasm)
|
|
432
476
|
|
|
433
477
|
## Development
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "space-data-module-sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.15",
|
|
4
4
|
"description": "Module SDK for building, validating, signing, and deploying WebAssembly modules on the Space Data Network.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -40,9 +40,6 @@
|
|
|
40
40
|
"schemas/",
|
|
41
41
|
"src/"
|
|
42
42
|
],
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"access": "public"
|
|
45
|
-
},
|
|
46
43
|
"scripts": {
|
|
47
44
|
"test": "node --test",
|
|
48
45
|
"test:host-surfaces": "node --test test/node-host.test.js test/host-abi.test.js",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { readFile
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
5
5
|
import { FlatcRunner } from "flatc-wasm";
|
|
@@ -18,48 +18,6 @@ function loadFlatcRunner() {
|
|
|
18
18
|
return flatcRunnerPromise;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const FLATBUFFERS_INCLUDE_ROOT_CANDIDATES = [
|
|
22
|
-
process.env.FLATBUFFERS_INCLUDE_DIR,
|
|
23
|
-
"/opt/homebrew/include",
|
|
24
|
-
"/usr/local/include",
|
|
25
|
-
"/usr/include",
|
|
26
|
-
].filter(Boolean);
|
|
27
|
-
|
|
28
|
-
async function findFlatbuffersIncludeRoot() {
|
|
29
|
-
for (const candidate of FLATBUFFERS_INCLUDE_ROOT_CANDIDATES) {
|
|
30
|
-
const headerPath = path.join(candidate, "flatbuffers", "flatbuffers.h");
|
|
31
|
-
try {
|
|
32
|
-
const headerStat = await stat(headerPath);
|
|
33
|
-
if (headerStat.isFile()) {
|
|
34
|
-
return candidate;
|
|
35
|
-
}
|
|
36
|
-
} catch {
|
|
37
|
-
// Try the next candidate.
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
throw new Error(
|
|
41
|
-
"Unable to locate the installed flatbuffers C++ headers. Set FLATBUFFERS_INCLUDE_DIR to the directory containing flatbuffers/flatbuffers.h.",
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function readDirectoryTree(rootDir, currentDir = rootDir) {
|
|
46
|
-
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
47
|
-
const files = [];
|
|
48
|
-
for (const entry of entries) {
|
|
49
|
-
const fullPath = path.join(currentDir, entry.name);
|
|
50
|
-
if (entry.isDirectory()) {
|
|
51
|
-
files.push(...(await readDirectoryTree(rootDir, fullPath)));
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (!entry.isFile()) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const relativePath = path.relative(rootDir, fullPath).split(path.sep).join("/");
|
|
58
|
-
files.push([`/${relativePath}`, await readFile(fullPath, "utf8")]);
|
|
59
|
-
}
|
|
60
|
-
return files;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
21
|
async function loadInvokeSchemaFiles() {
|
|
64
22
|
const filenames = [
|
|
65
23
|
"TypedArenaBuffer.fbs",
|
|
@@ -78,10 +36,8 @@ async function loadInvokeSchemaFiles() {
|
|
|
78
36
|
export async function getFlatbuffersCppRuntimeHeaders() {
|
|
79
37
|
if (!flatbuffersCppRuntimeHeadersPromise) {
|
|
80
38
|
flatbuffersCppRuntimeHeadersPromise = (async () => {
|
|
81
|
-
const
|
|
82
|
-
return
|
|
83
|
-
await readDirectoryTree(includeRoot, path.join(includeRoot, "flatbuffers")),
|
|
84
|
-
);
|
|
39
|
+
const flatc = await loadFlatcRunner();
|
|
40
|
+
return flatc.getEmbeddedRuntime("cpp");
|
|
85
41
|
})();
|
|
86
42
|
}
|
|
87
43
|
return flatbuffersCppRuntimeHeadersPromise;
|
package/src/index.d.ts
CHANGED
|
@@ -664,6 +664,8 @@ export type {
|
|
|
664
664
|
ManifestHarnessPlan,
|
|
665
665
|
ModuleHarness,
|
|
666
666
|
ModuleHarnessRuntimeDescriptor,
|
|
667
|
+
PublicationProtectionDemoAlignedType,
|
|
668
|
+
PublicationProtectionDemoSummary,
|
|
667
669
|
PluginInvokeProcessClient,
|
|
668
670
|
PluginInvokeProcessLaunchPlan,
|
|
669
671
|
WasmEdgeRunnerBuildPlan,
|
|
@@ -672,6 +674,8 @@ export type {
|
|
|
672
674
|
export {
|
|
673
675
|
buildWasmEdgeEmscriptenPthreadRunner,
|
|
674
676
|
buildWasmEdgeSpawnEnv,
|
|
677
|
+
createPublicationProtectionDemoManifest,
|
|
678
|
+
createPublicationProtectionDemoSummary,
|
|
675
679
|
createModuleHarness,
|
|
676
680
|
createPluginInvokeProcessClient,
|
|
677
681
|
describeCapabilityRuntimeSurface,
|
package/src/manifest/typeRefs.js
CHANGED
|
@@ -1,62 +1,31 @@
|
|
|
1
1
|
function cloneSchemaHash(value) {
|
|
2
2
|
if (value instanceof Uint8Array) {
|
|
3
|
-
return
|
|
3
|
+
return new Uint8Array(value);
|
|
4
4
|
}
|
|
5
5
|
if (Array.isArray(value)) {
|
|
6
|
-
return
|
|
6
|
+
return [...value];
|
|
7
7
|
}
|
|
8
8
|
return value ?? undefined;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function normalizeNullableString(value) {
|
|
12
|
-
if (value === undefined || value === null) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
const normalized = String(value).trim();
|
|
16
|
-
return normalized.length > 0 ? normalized : null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeAlignedMetadataScalar(value) {
|
|
20
|
-
if (value === undefined || value === null || value === "") {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
const numeric = Number(value);
|
|
24
|
-
if (Number.isFinite(numeric)) {
|
|
25
|
-
return numeric === 0 ? undefined : numeric;
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
11
|
export function clonePayloadTypeRef(value = null) {
|
|
31
12
|
if (!value || typeof value !== "object") {
|
|
32
|
-
return { acceptsAnyFlatbuffer: true
|
|
13
|
+
return { acceptsAnyFlatbuffer: true };
|
|
33
14
|
}
|
|
34
15
|
return {
|
|
35
|
-
schemaName:
|
|
36
|
-
|
|
37
|
-
),
|
|
38
|
-
fileIdentifier: normalizeNullableString(
|
|
39
|
-
value.fileIdentifier ?? value.file_identifier,
|
|
40
|
-
),
|
|
16
|
+
schemaName: value.schemaName ?? value.schema_name ?? undefined,
|
|
17
|
+
fileIdentifier: value.fileIdentifier ?? value.file_identifier ?? undefined,
|
|
41
18
|
schemaHash: cloneSchemaHash(value.schemaHash ?? value.schema_hash),
|
|
42
19
|
acceptsAnyFlatbuffer: Boolean(
|
|
43
20
|
value.acceptsAnyFlatbuffer ?? value.accepts_any_flatbuffer ?? false,
|
|
44
21
|
),
|
|
45
|
-
wireFormat:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
value.fixedStringLength ?? value.fixed_string_length,
|
|
53
|
-
),
|
|
54
|
-
byteLength: normalizeAlignedMetadataScalar(
|
|
55
|
-
value.byteLength ?? value.byte_length,
|
|
56
|
-
),
|
|
57
|
-
requiredAlignment: normalizeAlignedMetadataScalar(
|
|
58
|
-
value.requiredAlignment ?? value.required_alignment,
|
|
59
|
-
),
|
|
22
|
+
wireFormat: value.wireFormat ?? value.wire_format ?? undefined,
|
|
23
|
+
rootTypeName: value.rootTypeName ?? value.root_type_name ?? undefined,
|
|
24
|
+
fixedStringLength:
|
|
25
|
+
value.fixedStringLength ?? value.fixed_string_length ?? undefined,
|
|
26
|
+
byteLength: value.byteLength ?? value.byte_length ?? undefined,
|
|
27
|
+
requiredAlignment:
|
|
28
|
+
value.requiredAlignment ?? value.required_alignment ?? undefined,
|
|
60
29
|
};
|
|
61
30
|
}
|
|
62
31
|
|
|
@@ -64,12 +33,6 @@ export function normalizePayloadWireFormatName(value) {
|
|
|
64
33
|
if (value === undefined || value === null || value === "") {
|
|
65
34
|
return null;
|
|
66
35
|
}
|
|
67
|
-
if (value === 1 || value === "1") {
|
|
68
|
-
return "aligned-binary";
|
|
69
|
-
}
|
|
70
|
-
if (value === 0 || value === "0") {
|
|
71
|
-
return "flatbuffer";
|
|
72
|
-
}
|
|
73
36
|
const normalized = String(value).trim().toLowerCase().replace(/_/g, "-");
|
|
74
37
|
if (normalized === "aligned-binary") {
|
|
75
38
|
return "aligned-binary";
|
package/src/testing/index.d.ts
CHANGED
|
@@ -57,6 +57,75 @@ export interface ManifestHarnessPlan {
|
|
|
57
57
|
scenarios: Array<HarnessInvokeScenario | HarnessRawScenario>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export interface PublicationProtectionDemoAlignedType {
|
|
61
|
+
methodId: string | null;
|
|
62
|
+
portId: string | null;
|
|
63
|
+
setId: string | null;
|
|
64
|
+
schemaName: string | null;
|
|
65
|
+
fileIdentifier: string | null;
|
|
66
|
+
rootTypeName: string | null;
|
|
67
|
+
byteLength: number | null;
|
|
68
|
+
requiredAlignment: number | null;
|
|
69
|
+
hasFlatbufferFallback: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PublicationProtectionDemoSummary {
|
|
73
|
+
manifest: PluginManifest;
|
|
74
|
+
recTrailer: {
|
|
75
|
+
fileIdentifier: string;
|
|
76
|
+
version: string | null;
|
|
77
|
+
recordCount: number;
|
|
78
|
+
recordStandards: Array<string | null>;
|
|
79
|
+
usesStandardsFlatbuffers: boolean;
|
|
80
|
+
records: Array<Record<string, unknown>>;
|
|
81
|
+
};
|
|
82
|
+
alignedBinaryContract: PublicationProtectionDemoAlignedType[];
|
|
83
|
+
signedOnly: {
|
|
84
|
+
artifactId: string;
|
|
85
|
+
encrypted: boolean;
|
|
86
|
+
trailer: PublicationProtectionDemoSummary["recTrailer"];
|
|
87
|
+
recordStandards: Array<string | null>;
|
|
88
|
+
pnm: {
|
|
89
|
+
fileName: string | null;
|
|
90
|
+
fileId: string | null;
|
|
91
|
+
cid: string | null;
|
|
92
|
+
hasSignature: boolean;
|
|
93
|
+
signatureType: string | null;
|
|
94
|
+
publishTimestamp: string | null;
|
|
95
|
+
} | null;
|
|
96
|
+
enc: null;
|
|
97
|
+
envelope: null;
|
|
98
|
+
};
|
|
99
|
+
encryptedDelivery: {
|
|
100
|
+
artifactId: string;
|
|
101
|
+
encrypted: boolean;
|
|
102
|
+
trailer: PublicationProtectionDemoSummary["recTrailer"];
|
|
103
|
+
recordStandards: Array<string | null>;
|
|
104
|
+
pnm: {
|
|
105
|
+
fileName: string | null;
|
|
106
|
+
fileId: string | null;
|
|
107
|
+
cid: string | null;
|
|
108
|
+
hasSignature: boolean;
|
|
109
|
+
signatureType: string | null;
|
|
110
|
+
publishTimestamp: string | null;
|
|
111
|
+
} | null;
|
|
112
|
+
enc: {
|
|
113
|
+
context: string | null;
|
|
114
|
+
rootType: string | null;
|
|
115
|
+
keyExchange: string | null;
|
|
116
|
+
symmetric: string | null;
|
|
117
|
+
keyDerivation: string | null;
|
|
118
|
+
nonceLength: number;
|
|
119
|
+
ephemeralPublicKeyLength: number;
|
|
120
|
+
} | null;
|
|
121
|
+
envelope: {
|
|
122
|
+
scheme: string | null;
|
|
123
|
+
hasEncRecord: boolean;
|
|
124
|
+
hasPnmRecord: boolean;
|
|
125
|
+
} | null;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
60
129
|
export interface PluginInvokeProcessLaunchPlan {
|
|
61
130
|
command: string;
|
|
62
131
|
args: string[];
|
|
@@ -124,6 +193,18 @@ export function describeCapabilityRuntimeSurface(
|
|
|
124
193
|
capability: string,
|
|
125
194
|
): CapabilityRuntimeSurface;
|
|
126
195
|
|
|
196
|
+
export function createPublicationProtectionDemoManifest(): PluginManifest;
|
|
197
|
+
|
|
198
|
+
export function createPublicationProtectionDemoSummary(options?: {
|
|
199
|
+
manifest?: PluginManifest;
|
|
200
|
+
wasmBytes?: Uint8Array;
|
|
201
|
+
mnemonic?: string | null;
|
|
202
|
+
recipient?: {
|
|
203
|
+
publicKeyHex: string;
|
|
204
|
+
privateKeyHex: string;
|
|
205
|
+
};
|
|
206
|
+
}): Promise<PublicationProtectionDemoSummary>;
|
|
207
|
+
|
|
127
208
|
export function generateManifestHarnessPlan(options: {
|
|
128
209
|
manifest: PluginManifest;
|
|
129
210
|
includeOptionalInputs?: boolean;
|
package/src/testing/index.js
CHANGED
|
@@ -3,6 +3,10 @@ import { Buffer } from "node:buffer";
|
|
|
3
3
|
import { encodePluginInvokeRequest } from "../invoke/codec.js";
|
|
4
4
|
import { normalizeInvokeSurfaces } from "../invoke/index.js";
|
|
5
5
|
import { selectPreferredPayloadTypeRef } from "../manifest/typeRefs.js";
|
|
6
|
+
export {
|
|
7
|
+
createPublicationProtectionDemoManifest,
|
|
8
|
+
createPublicationProtectionDemoSummary,
|
|
9
|
+
} from "./publicationProtectionDemo.js";
|
|
6
10
|
export {
|
|
7
11
|
buildWasmEdgeSpawnEnv,
|
|
8
12
|
createPluginInvokeProcessClient,
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import * as flatbuffers from "flatbuffers";
|
|
2
|
+
|
|
3
|
+
import { createRecipientKeypairHex, protectModuleArtifact } from "../compiler/compileModule.js";
|
|
4
|
+
import { extractPublicationRecordCollection } from "../transport/records.js";
|
|
5
|
+
import { REC } from "spacedatastandards.org/lib/js/REC/REC.js";
|
|
6
|
+
import { Record } from "spacedatastandards.org/lib/js/REC/Record.js";
|
|
7
|
+
import { PNM } from "spacedatastandards.org/lib/js/PNM/main.js";
|
|
8
|
+
import { ENC } from "spacedatastandards.org/lib/js/ENC/main.js";
|
|
9
|
+
|
|
10
|
+
const MINIMAL_WASM_BYTES = Uint8Array.of(
|
|
11
|
+
0x00,
|
|
12
|
+
0x61,
|
|
13
|
+
0x73,
|
|
14
|
+
0x6d,
|
|
15
|
+
0x01,
|
|
16
|
+
0x00,
|
|
17
|
+
0x00,
|
|
18
|
+
0x00,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function createPublicationProtectionDemoManifest() {
|
|
22
|
+
return {
|
|
23
|
+
pluginId: "com.digitalarsenal.examples.publication-protection-demo",
|
|
24
|
+
name: "Publication Protection Demo",
|
|
25
|
+
version: "0.1.0",
|
|
26
|
+
pluginFamily: "propagator",
|
|
27
|
+
capabilities: ["clock", "crypto_sign", "crypto_encrypt"],
|
|
28
|
+
externalInterfaces: [],
|
|
29
|
+
methods: [
|
|
30
|
+
{
|
|
31
|
+
methodId: "propagate",
|
|
32
|
+
displayName: "Propagate",
|
|
33
|
+
inputPorts: [
|
|
34
|
+
{
|
|
35
|
+
portId: "request",
|
|
36
|
+
acceptedTypeSets: [
|
|
37
|
+
{
|
|
38
|
+
setId: "omm-request",
|
|
39
|
+
allowedTypes: [
|
|
40
|
+
{
|
|
41
|
+
schemaName: "OMM.fbs",
|
|
42
|
+
fileIdentifier: "$OMM",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
minStreams: 1,
|
|
48
|
+
maxStreams: 1,
|
|
49
|
+
required: true,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
outputPorts: [
|
|
53
|
+
{
|
|
54
|
+
portId: "state",
|
|
55
|
+
acceptedTypeSets: [
|
|
56
|
+
{
|
|
57
|
+
setId: "state-vector",
|
|
58
|
+
allowedTypes: [
|
|
59
|
+
{
|
|
60
|
+
schemaName: "StateVector.fbs",
|
|
61
|
+
fileIdentifier: "STVC",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
schemaName: "StateVector.fbs",
|
|
65
|
+
fileIdentifier: "STVC",
|
|
66
|
+
wireFormat: "aligned-binary",
|
|
67
|
+
rootTypeName: "StateVector",
|
|
68
|
+
byteLength: 72,
|
|
69
|
+
requiredAlignment: 8,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
minStreams: 1,
|
|
75
|
+
maxStreams: 1,
|
|
76
|
+
required: true,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
maxBatch: 32,
|
|
80
|
+
drainPolicy: "drain-to-empty",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
schemasUsed: [
|
|
84
|
+
{
|
|
85
|
+
schemaName: "OMM.fbs",
|
|
86
|
+
fileIdentifier: "$OMM",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
schemaName: "StateVector.fbs",
|
|
90
|
+
fileIdentifier: "STVC",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
schemaName: "StateVector.fbs",
|
|
94
|
+
fileIdentifier: "STVC",
|
|
95
|
+
wireFormat: "aligned-binary",
|
|
96
|
+
rootTypeName: "StateVector",
|
|
97
|
+
byteLength: 72,
|
|
98
|
+
requiredAlignment: 8,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function summarizeAlignedBinaryContract(manifest = {}) {
|
|
105
|
+
const summaries = [];
|
|
106
|
+
for (const method of Array.isArray(manifest.methods) ? manifest.methods : []) {
|
|
107
|
+
const ports = [
|
|
108
|
+
...(Array.isArray(method.inputPorts) ? method.inputPorts : []),
|
|
109
|
+
...(Array.isArray(method.outputPorts) ? method.outputPorts : []),
|
|
110
|
+
];
|
|
111
|
+
for (const port of ports) {
|
|
112
|
+
for (const typeSet of Array.isArray(port.acceptedTypeSets)
|
|
113
|
+
? port.acceptedTypeSets
|
|
114
|
+
: []) {
|
|
115
|
+
const allowedTypes = Array.isArray(typeSet.allowedTypes)
|
|
116
|
+
? typeSet.allowedTypes
|
|
117
|
+
: [];
|
|
118
|
+
for (const allowedType of allowedTypes) {
|
|
119
|
+
if (allowedType?.wireFormat !== "aligned-binary") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const hasFlatbufferFallback = allowedTypes.some(
|
|
123
|
+
(candidate) =>
|
|
124
|
+
(candidate?.wireFormat ?? "flatbuffer") === "flatbuffer" &&
|
|
125
|
+
candidate?.schemaName === allowedType.schemaName &&
|
|
126
|
+
candidate?.fileIdentifier === allowedType.fileIdentifier,
|
|
127
|
+
);
|
|
128
|
+
summaries.push({
|
|
129
|
+
methodId: method.methodId ?? null,
|
|
130
|
+
portId: port.portId ?? null,
|
|
131
|
+
setId: typeSet.setId ?? null,
|
|
132
|
+
schemaName: allowedType.schemaName ?? null,
|
|
133
|
+
fileIdentifier: allowedType.fileIdentifier ?? null,
|
|
134
|
+
rootTypeName: allowedType.rootTypeName ?? null,
|
|
135
|
+
byteLength: allowedType.byteLength ?? null,
|
|
136
|
+
requiredAlignment: allowedType.requiredAlignment ?? null,
|
|
137
|
+
hasFlatbufferFallback,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return summaries;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function parseStandardsRec(recordCollectionBytes) {
|
|
147
|
+
const recBuffer = new flatbuffers.ByteBuffer(recordCollectionBytes);
|
|
148
|
+
const rec = REC.getRootAsREC(recBuffer);
|
|
149
|
+
const records = [];
|
|
150
|
+
for (let index = 0; index < rec.recordsLength(); index += 1) {
|
|
151
|
+
const record = rec.RECORDS(index, new Record());
|
|
152
|
+
if (!record) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const standard = record.standard() ?? null;
|
|
156
|
+
if (standard === "PNM") {
|
|
157
|
+
const pnm = record.value(new PNM());
|
|
158
|
+
records.push({
|
|
159
|
+
standard,
|
|
160
|
+
fileIdentifier: "$PNM",
|
|
161
|
+
fileName: pnm?.FILE_NAME() ?? null,
|
|
162
|
+
fileId: pnm?.FILE_ID() ?? null,
|
|
163
|
+
cid: pnm?.CID() ?? null,
|
|
164
|
+
hasSignature: Boolean(pnm?.SIGNATURE()),
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (standard === "ENC") {
|
|
169
|
+
const enc = record.value(new ENC());
|
|
170
|
+
records.push({
|
|
171
|
+
standard,
|
|
172
|
+
fileIdentifier: "$ENC",
|
|
173
|
+
context: enc?.CONTEXT() ?? null,
|
|
174
|
+
rootType: enc?.ROOT_TYPE() ?? null,
|
|
175
|
+
nonceLength: enc?.nonceStartLength() ?? 0,
|
|
176
|
+
ephemeralPublicKeyLength: enc?.ephemeralPublicKeyLength() ?? 0,
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
records.push({
|
|
181
|
+
standard,
|
|
182
|
+
fileIdentifier: null,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
fileIdentifier: "$REC",
|
|
187
|
+
version: rec.version() ?? null,
|
|
188
|
+
recordCount: records.length,
|
|
189
|
+
recordStandards: records.map((record) => record.standard),
|
|
190
|
+
usesStandardsFlatbuffers: REC.bufferHasIdentifier(
|
|
191
|
+
new flatbuffers.ByteBuffer(recordCollectionBytes),
|
|
192
|
+
),
|
|
193
|
+
records,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function summarizeProtectedArtifact(protectedArtifact) {
|
|
198
|
+
const parsed = extractPublicationRecordCollection(
|
|
199
|
+
protectedArtifact.protectedArtifactBytes,
|
|
200
|
+
);
|
|
201
|
+
if (!parsed) {
|
|
202
|
+
throw new Error("Protected artifact is missing the REC publication trailer.");
|
|
203
|
+
}
|
|
204
|
+
const trailer = parseStandardsRec(parsed.recordCollectionBytes);
|
|
205
|
+
return {
|
|
206
|
+
artifactId: protectedArtifact.payload.artifactId,
|
|
207
|
+
encrypted: protectedArtifact.encrypted,
|
|
208
|
+
trailer,
|
|
209
|
+
recordStandards: trailer.recordStandards,
|
|
210
|
+
pnm: parsed.pnm
|
|
211
|
+
? {
|
|
212
|
+
fileName: parsed.pnm.fileName ?? null,
|
|
213
|
+
fileId: parsed.pnm.fileId ?? null,
|
|
214
|
+
cid: parsed.pnm.cid ?? null,
|
|
215
|
+
hasSignature: Boolean(parsed.pnm.signature),
|
|
216
|
+
signatureType: parsed.pnm.signatureType ?? null,
|
|
217
|
+
publishTimestamp: parsed.pnm.publishTimestamp ?? null,
|
|
218
|
+
}
|
|
219
|
+
: null,
|
|
220
|
+
enc: parsed.enc
|
|
221
|
+
? {
|
|
222
|
+
context: parsed.enc.context ?? null,
|
|
223
|
+
rootType: parsed.enc.rootType ?? null,
|
|
224
|
+
keyExchange: parsed.enc.keyExchange ?? null,
|
|
225
|
+
symmetric: parsed.enc.symmetric ?? null,
|
|
226
|
+
keyDerivation: parsed.enc.keyDerivation ?? null,
|
|
227
|
+
nonceLength: parsed.enc.nonceStart?.length ?? 0,
|
|
228
|
+
ephemeralPublicKeyLength: parsed.enc.ephemeralPublicKey?.length ?? 0,
|
|
229
|
+
}
|
|
230
|
+
: null,
|
|
231
|
+
envelope: protectedArtifact.encryptedEnvelope
|
|
232
|
+
? {
|
|
233
|
+
scheme: protectedArtifact.encryptedEnvelope.scheme ?? null,
|
|
234
|
+
hasEncRecord: Boolean(
|
|
235
|
+
protectedArtifact.encryptedEnvelope.encRecordBase64,
|
|
236
|
+
),
|
|
237
|
+
hasPnmRecord: Boolean(
|
|
238
|
+
protectedArtifact.encryptedEnvelope.pnmRecordBase64,
|
|
239
|
+
),
|
|
240
|
+
}
|
|
241
|
+
: null,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function createPublicationProtectionDemoSummary(options = {}) {
|
|
246
|
+
const manifest = options.manifest ?? createPublicationProtectionDemoManifest();
|
|
247
|
+
const wasmBytes =
|
|
248
|
+
options.wasmBytes instanceof Uint8Array
|
|
249
|
+
? options.wasmBytes
|
|
250
|
+
: MINIMAL_WASM_BYTES;
|
|
251
|
+
const recipient = options.recipient ?? (await createRecipientKeypairHex());
|
|
252
|
+
const signedOnly = await protectModuleArtifact({
|
|
253
|
+
manifest,
|
|
254
|
+
wasmBytes,
|
|
255
|
+
mnemonic: options.mnemonic ?? null,
|
|
256
|
+
});
|
|
257
|
+
const encryptedDelivery = await protectModuleArtifact({
|
|
258
|
+
manifest,
|
|
259
|
+
wasmBytes,
|
|
260
|
+
mnemonic: options.mnemonic ?? null,
|
|
261
|
+
recipientPublicKeyHex: recipient.publicKeyHex,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
manifest,
|
|
266
|
+
recTrailer: parseStandardsRec(signedOnly.publicationRecordsBytes),
|
|
267
|
+
alignedBinaryContract: summarizeAlignedBinaryContract(manifest),
|
|
268
|
+
signedOnly: summarizeProtectedArtifact(signedOnly),
|
|
269
|
+
encryptedDelivery: summarizeProtectedArtifact(encryptedDelivery),
|
|
270
|
+
};
|
|
271
|
+
}
|
package/src/transport/records.js
CHANGED
|
@@ -612,13 +612,7 @@ export function decodePublicationRecordCollection(bytes) {
|
|
|
612
612
|
const recordType =
|
|
613
613
|
typeof recordTable.valueType === "function"
|
|
614
614
|
? recordTable.valueType()
|
|
615
|
-
:
|
|
616
|
-
? recordTable.value_type()
|
|
617
|
-
: (() => {
|
|
618
|
-
throw new TypeError(
|
|
619
|
-
"REC Record wrapper is missing both valueType() and value_type() accessors.",
|
|
620
|
-
);
|
|
621
|
-
})();
|
|
615
|
+
: recordTable.value_type();
|
|
622
616
|
const standard =
|
|
623
617
|
normalizeStringField(recordTable.standard()) ??
|
|
624
618
|
STANDARD_BY_RECORD_TYPE[recordType] ??
|