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.
- package/README.md +139 -0
- package/bin/space-data-module.js +230 -0
- package/package.json +40 -0
- package/schemas/PluginManifest.fbs +144 -0
- package/schemas/TypedArenaBuffer.fbs +78 -0
- package/src/auth/canonicalize.js +72 -0
- package/src/auth/index.js +10 -0
- package/src/auth/permissions.js +190 -0
- package/src/compiler/compileModule.js +237 -0
- package/src/compiler/index.js +7 -0
- package/src/compliance/index.js +56 -0
- package/src/compliance/pluginCompliance.js +574 -0
- package/src/embeddedManifest.js +124 -0
- package/src/generated/orbpro/manifest/accepted-type-set.d.ts +45 -0
- package/src/generated/orbpro/manifest/accepted-type-set.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/accepted-type-set.js +100 -0
- package/src/generated/orbpro/manifest/accepted-type-set.js.map +1 -0
- package/src/generated/orbpro/manifest/accepted-type-set.ts +200 -0
- package/src/generated/orbpro/manifest/build-artifact.d.ts +41 -0
- package/src/generated/orbpro/manifest/build-artifact.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/build-artifact.js +105 -0
- package/src/generated/orbpro/manifest/build-artifact.js.map +1 -0
- package/src/generated/orbpro/manifest/build-artifact.ts +193 -0
- package/src/generated/orbpro/manifest/capability-kind.d.ts +17 -0
- package/src/generated/orbpro/manifest/capability-kind.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/capability-kind.js +20 -0
- package/src/generated/orbpro/manifest/capability-kind.js.map +1 -0
- package/src/generated/orbpro/manifest/capability-kind.ts +20 -0
- package/src/generated/orbpro/manifest/drain-policy.d.ts +9 -0
- package/src/generated/orbpro/manifest/drain-policy.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/drain-policy.js +12 -0
- package/src/generated/orbpro/manifest/drain-policy.js.map +1 -0
- package/src/generated/orbpro/manifest/drain-policy.ts +12 -0
- package/src/generated/orbpro/manifest/host-capability.d.ts +36 -0
- package/src/generated/orbpro/manifest/host-capability.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/host-capability.js +91 -0
- package/src/generated/orbpro/manifest/host-capability.js.map +1 -0
- package/src/generated/orbpro/manifest/host-capability.ts +161 -0
- package/src/generated/orbpro/manifest/method-manifest.d.ts +53 -0
- package/src/generated/orbpro/manifest/method-manifest.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/method-manifest.js +154 -0
- package/src/generated/orbpro/manifest/method-manifest.js.map +1 -0
- package/src/generated/orbpro/manifest/method-manifest.ts +306 -0
- package/src/generated/orbpro/manifest/plugin-family.d.ts +17 -0
- package/src/generated/orbpro/manifest/plugin-family.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/plugin-family.js +20 -0
- package/src/generated/orbpro/manifest/plugin-family.js.map +1 -0
- package/src/generated/orbpro/manifest/plugin-family.ts +20 -0
- package/src/generated/orbpro/manifest/plugin-manifest.d.ts +85 -0
- package/src/generated/orbpro/manifest/plugin-manifest.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/plugin-manifest.js +268 -0
- package/src/generated/orbpro/manifest/plugin-manifest.js.map +1 -0
- package/src/generated/orbpro/manifest/plugin-manifest.ts +562 -0
- package/src/generated/orbpro/manifest/port-manifest.d.ts +70 -0
- package/src/generated/orbpro/manifest/port-manifest.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/port-manifest.js +150 -0
- package/src/generated/orbpro/manifest/port-manifest.js.map +1 -0
- package/src/generated/orbpro/manifest/port-manifest.ts +284 -0
- package/src/generated/orbpro/manifest/protocol-spec.d.ts +41 -0
- package/src/generated/orbpro/manifest/protocol-spec.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/protocol-spec.js +105 -0
- package/src/generated/orbpro/manifest/protocol-spec.js.map +1 -0
- package/src/generated/orbpro/manifest/protocol-spec.ts +205 -0
- package/src/generated/orbpro/manifest/timer-spec.d.ts +40 -0
- package/src/generated/orbpro/manifest/timer-spec.d.ts.map +1 -0
- package/src/generated/orbpro/manifest/timer-spec.js +104 -0
- package/src/generated/orbpro/manifest/timer-spec.js.map +1 -0
- package/src/generated/orbpro/manifest/timer-spec.ts +195 -0
- package/src/generated/orbpro/manifest.js +14 -0
- package/src/generated/orbpro/stream/buffer-mutability.d.ts +9 -0
- package/src/generated/orbpro/stream/buffer-mutability.d.ts.map +1 -0
- package/src/generated/orbpro/stream/buffer-mutability.js +12 -0
- package/src/generated/orbpro/stream/buffer-mutability.js.map +1 -0
- package/src/generated/orbpro/stream/buffer-mutability.ts +12 -0
- package/src/generated/orbpro/stream/buffer-ownership.d.ts +10 -0
- package/src/generated/orbpro/stream/buffer-ownership.d.ts.map +1 -0
- package/src/generated/orbpro/stream/buffer-ownership.js +13 -0
- package/src/generated/orbpro/stream/buffer-ownership.js.map +1 -0
- package/src/generated/orbpro/stream/buffer-ownership.ts +13 -0
- package/src/generated/orbpro/stream/flat-buffer-type-ref.d.ts +51 -0
- package/src/generated/orbpro/stream/flat-buffer-type-ref.d.ts.map +1 -0
- package/src/generated/orbpro/stream/flat-buffer-type-ref.js +115 -0
- package/src/generated/orbpro/stream/flat-buffer-type-ref.js.map +1 -0
- package/src/generated/orbpro/stream/flat-buffer-type-ref.ts +222 -0
- package/src/generated/orbpro/stream/typed-arena-buffer.d.ts +100 -0
- package/src/generated/orbpro/stream/typed-arena-buffer.d.ts.map +1 -0
- package/src/generated/orbpro/stream/typed-arena-buffer.js +215 -0
- package/src/generated/orbpro/stream/typed-arena-buffer.js.map +1 -0
- package/src/generated/orbpro/stream/typed-arena-buffer.ts +344 -0
- package/src/index.js +8 -0
- package/src/manifest/codec.js +40 -0
- package/src/manifest/index.js +9 -0
- package/src/manifest/normalize.js +275 -0
- package/src/runtime/bufferLike.js +28 -0
- package/src/runtime/constants.js +34 -0
- package/src/standards/index.js +153 -0
- package/src/standards/sharedCatalog.js +196 -0
- package/src/transport/index.js +8 -0
- package/src/transport/pki.js +140 -0
- package/src/utils/crypto.js +8 -0
- package/src/utils/encoding.js +54 -0
- package/src/utils/wasmCrypto.js +70 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { canonicalBytes, hashCanonicalValue, stableStringify } from "./canonicalize.js";
|
|
2
|
+
export {
|
|
3
|
+
assertDeploymentAuthorization,
|
|
4
|
+
createDeploymentAuthorization,
|
|
5
|
+
createHdWalletSigner,
|
|
6
|
+
createHdWalletVerifier,
|
|
7
|
+
signAuthorization,
|
|
8
|
+
verifyAuthorization,
|
|
9
|
+
} from "./permissions.js";
|
|
10
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { canonicalBytes } from "./canonicalize.js";
|
|
2
|
+
import { bytesToHex, hexToBytes, toUint8Array } from "../utils/encoding.js";
|
|
3
|
+
import { randomBytes, sha256Bytes } from "../utils/crypto.js";
|
|
4
|
+
|
|
5
|
+
function normalizeCapabilityList(capabilities) {
|
|
6
|
+
if (!Array.isArray(capabilities)) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
return capabilities
|
|
10
|
+
.map((capability) => String(capability ?? "").trim())
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeTarget(target = null) {
|
|
15
|
+
if (typeof target === "string") {
|
|
16
|
+
return {
|
|
17
|
+
kind: "remote",
|
|
18
|
+
id: null,
|
|
19
|
+
audience: null,
|
|
20
|
+
url: target,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
kind: target?.kind ?? "remote",
|
|
25
|
+
id: target?.id ?? target?.targetId ?? null,
|
|
26
|
+
audience: target?.audience ?? null,
|
|
27
|
+
url: target?.url ?? null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function createDeploymentAuthorization(options = {}) {
|
|
32
|
+
const issuedAt = Number(options.issuedAt ?? Date.now());
|
|
33
|
+
const ttlMs = Number(options.ttlMs ?? 5 * 60 * 1000);
|
|
34
|
+
const artifact = options.artifact ?? {};
|
|
35
|
+
const target = normalizeTarget(options.target);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
version: 1,
|
|
39
|
+
action: "deploy-flow",
|
|
40
|
+
artifactId: String(artifact.artifactId ?? options.artifactId ?? "").trim(),
|
|
41
|
+
programId: String(artifact.programId ?? options.programId ?? "").trim(),
|
|
42
|
+
graphHash: artifact.graphHash ?? options.graphHash ?? null,
|
|
43
|
+
manifestHash: artifact.manifestHash ?? options.manifestHash ?? null,
|
|
44
|
+
target,
|
|
45
|
+
capabilities: normalizeCapabilityList(
|
|
46
|
+
options.capabilities ?? artifact.requiredCapabilities ?? [],
|
|
47
|
+
),
|
|
48
|
+
issuedAt,
|
|
49
|
+
expiresAt: issuedAt + ttlMs,
|
|
50
|
+
nonce: options.nonce ?? bytesToHex(await randomBytes(16)),
|
|
51
|
+
constraints: options.constraints ?? null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createHdWalletSigner(options = {}) {
|
|
56
|
+
if (typeof options.signDigest !== "function") {
|
|
57
|
+
throw new Error("createHdWalletSigner requires signDigest.");
|
|
58
|
+
}
|
|
59
|
+
const publicKeyHex = String(options.publicKeyHex ?? "").trim();
|
|
60
|
+
if (!publicKeyHex) {
|
|
61
|
+
throw new Error("createHdWalletSigner requires publicKeyHex.");
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
algorithm: options.algorithm ?? "secp256k1-sha256",
|
|
65
|
+
curve: options.curve ?? "secp256k1",
|
|
66
|
+
publicKeyHex,
|
|
67
|
+
derivationPath: options.derivationPath ?? null,
|
|
68
|
+
keyId: options.keyId ?? null,
|
|
69
|
+
async sign(bytes) {
|
|
70
|
+
const digest = await sha256Bytes(bytes);
|
|
71
|
+
return toUint8Array(await options.signDigest(digest));
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createHdWalletVerifier(options = {}) {
|
|
77
|
+
if (typeof options.verifyDigest !== "function") {
|
|
78
|
+
throw new Error("createHdWalletVerifier requires verifyDigest.");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
async verify(bytes, signature, header, payload) {
|
|
82
|
+
const digest = await sha256Bytes(bytes);
|
|
83
|
+
return options.verifyDigest(digest, signature, header, payload);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function signAuthorization({ authorization, signer }) {
|
|
89
|
+
if (!signer || typeof signer.sign !== "function") {
|
|
90
|
+
throw new Error("signAuthorization requires a signer.");
|
|
91
|
+
}
|
|
92
|
+
const payload = authorization ?? {};
|
|
93
|
+
const payloadBytes = canonicalBytes(payload);
|
|
94
|
+
const signature = await signer.sign(payloadBytes);
|
|
95
|
+
return {
|
|
96
|
+
protected: {
|
|
97
|
+
algorithm: signer.algorithm ?? "unknown",
|
|
98
|
+
curve: signer.curve ?? null,
|
|
99
|
+
publicKeyHex: signer.publicKeyHex ?? null,
|
|
100
|
+
derivationPath: signer.derivationPath ?? null,
|
|
101
|
+
keyId: signer.keyId ?? null,
|
|
102
|
+
},
|
|
103
|
+
payload,
|
|
104
|
+
signatureHex: bytesToHex(signature),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function verifyAuthorization({
|
|
109
|
+
envelope,
|
|
110
|
+
verifier,
|
|
111
|
+
now = Date.now(),
|
|
112
|
+
}) {
|
|
113
|
+
if (!verifier || typeof verifier.verify !== "function") {
|
|
114
|
+
throw new Error("verifyAuthorization requires a verifier.");
|
|
115
|
+
}
|
|
116
|
+
if (!envelope?.payload || !envelope?.signatureHex) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
typeof envelope.payload.expiresAt === "number" &&
|
|
121
|
+
envelope.payload.expiresAt < now
|
|
122
|
+
) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const payloadBytes = canonicalBytes(envelope.payload);
|
|
126
|
+
return verifier.verify(
|
|
127
|
+
payloadBytes,
|
|
128
|
+
hexToBytes(envelope.signatureHex),
|
|
129
|
+
envelope.protected ?? {},
|
|
130
|
+
envelope.payload,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function assertDeploymentAuthorization({
|
|
135
|
+
envelope,
|
|
136
|
+
artifact,
|
|
137
|
+
target,
|
|
138
|
+
requiredCapabilities = [],
|
|
139
|
+
now = Date.now(),
|
|
140
|
+
}) {
|
|
141
|
+
const payload = envelope?.payload;
|
|
142
|
+
if (!payload) {
|
|
143
|
+
throw new Error("Deployment authorization envelope is missing payload.");
|
|
144
|
+
}
|
|
145
|
+
if (payload.action !== "deploy-flow") {
|
|
146
|
+
throw new Error(`Unexpected authorization action "${payload.action}".`);
|
|
147
|
+
}
|
|
148
|
+
if (typeof payload.expiresAt === "number" && payload.expiresAt < now) {
|
|
149
|
+
throw new Error("Deployment authorization has expired.");
|
|
150
|
+
}
|
|
151
|
+
if (artifact?.artifactId && payload.artifactId !== artifact.artifactId) {
|
|
152
|
+
throw new Error("Deployment authorization artifactId mismatch.");
|
|
153
|
+
}
|
|
154
|
+
if (artifact?.programId && payload.programId !== artifact.programId) {
|
|
155
|
+
throw new Error("Deployment authorization programId mismatch.");
|
|
156
|
+
}
|
|
157
|
+
if (artifact?.graphHash && payload.graphHash !== artifact.graphHash) {
|
|
158
|
+
throw new Error("Deployment authorization graphHash mismatch.");
|
|
159
|
+
}
|
|
160
|
+
if (artifact?.manifestHash && payload.manifestHash !== artifact.manifestHash) {
|
|
161
|
+
throw new Error("Deployment authorization manifestHash mismatch.");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const normalizedTarget = normalizeTarget(target);
|
|
165
|
+
if (
|
|
166
|
+
normalizedTarget.id &&
|
|
167
|
+
payload.target?.id &&
|
|
168
|
+
normalizedTarget.id !== payload.target.id
|
|
169
|
+
) {
|
|
170
|
+
throw new Error("Deployment authorization target id mismatch.");
|
|
171
|
+
}
|
|
172
|
+
if (
|
|
173
|
+
normalizedTarget.audience &&
|
|
174
|
+
payload.target?.audience &&
|
|
175
|
+
normalizedTarget.audience !== payload.target.audience
|
|
176
|
+
) {
|
|
177
|
+
throw new Error("Deployment authorization target audience mismatch.");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const granted = new Set(normalizeCapabilityList(payload.capabilities));
|
|
181
|
+
for (const capability of normalizeCapabilityList(requiredCapabilities)) {
|
|
182
|
+
if (!granted.has(capability)) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Deployment authorization missing capability "${capability}".`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createDeploymentAuthorization,
|
|
9
|
+
createHdWalletSigner,
|
|
10
|
+
signAuthorization,
|
|
11
|
+
} from "../auth/index.js";
|
|
12
|
+
import { validateArtifactWithStandards } from "../compliance/index.js";
|
|
13
|
+
import { generateEmbeddedManifestSource } from "../embeddedManifest.js";
|
|
14
|
+
import { encodePluginManifest, toEmbeddedPluginManifest } from "../manifest/index.js";
|
|
15
|
+
import {
|
|
16
|
+
encryptJsonForRecipient,
|
|
17
|
+
generateX25519Keypair,
|
|
18
|
+
} from "../transport/index.js";
|
|
19
|
+
import {
|
|
20
|
+
base64ToBytes,
|
|
21
|
+
bytesToBase64,
|
|
22
|
+
bytesToHex,
|
|
23
|
+
hexToBytes,
|
|
24
|
+
} from "../utils/encoding.js";
|
|
25
|
+
import { sha256Bytes } from "../utils/crypto.js";
|
|
26
|
+
import { getWasmWallet } from "../utils/wasmCrypto.js";
|
|
27
|
+
|
|
28
|
+
const execFile = promisify(execFileCallback);
|
|
29
|
+
const C_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
30
|
+
|
|
31
|
+
function selectCompiler(language) {
|
|
32
|
+
const normalized = String(language ?? "c").trim().toLowerCase();
|
|
33
|
+
if (normalized === "c++" || normalized === "cpp" || normalized === "cxx") {
|
|
34
|
+
return { command: "em++", extension: "cpp", language: "c++" };
|
|
35
|
+
}
|
|
36
|
+
return { command: "emcc", extension: "c", language: "c" };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ensureExportableMethodIds(manifest) {
|
|
40
|
+
const invalidMethod = (Array.isArray(manifest?.methods) ? manifest.methods : []).find(
|
|
41
|
+
(method) => !C_IDENTIFIER.test(String(method?.methodId ?? "")),
|
|
42
|
+
);
|
|
43
|
+
if (invalidMethod) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Method id "${invalidMethod.methodId}" is not a valid C export name. ` +
|
|
46
|
+
"Source compilation requires methodId values to be valid C identifiers.",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function compileModuleFromSource(options = {}) {
|
|
52
|
+
const manifest = options.manifest ?? {};
|
|
53
|
+
const sourceCode = String(options.sourceCode ?? "");
|
|
54
|
+
if (!sourceCode.trim()) {
|
|
55
|
+
throw new Error("compileModuleFromSource requires sourceCode.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ensureExportableMethodIds(manifest);
|
|
59
|
+
|
|
60
|
+
const validation = await validateArtifactWithStandards({ manifest });
|
|
61
|
+
if (!validation.ok) {
|
|
62
|
+
const error = new Error("Manifest validation failed.");
|
|
63
|
+
error.report = validation;
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const compiler = selectCompiler(options.language);
|
|
68
|
+
const { manifest: embeddedManifest, warnings } = toEmbeddedPluginManifest(
|
|
69
|
+
manifest,
|
|
70
|
+
);
|
|
71
|
+
const tempDir = await mkdtemp(
|
|
72
|
+
path.join(os.tmpdir(), "space-data-module-sdk-compile-"),
|
|
73
|
+
);
|
|
74
|
+
const sourcePath = path.join(tempDir, `module.${compiler.extension}`);
|
|
75
|
+
const manifestSourcePath = path.join(tempDir, "plugin-manifest-exports.c");
|
|
76
|
+
const outputPath = path.resolve(options.outputPath ?? path.join(tempDir, "module.wasm"));
|
|
77
|
+
|
|
78
|
+
await writeFile(sourcePath, sourceCode, "utf8");
|
|
79
|
+
await writeFile(
|
|
80
|
+
manifestSourcePath,
|
|
81
|
+
generateEmbeddedManifestSource({ manifest: embeddedManifest }),
|
|
82
|
+
"utf8",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const exportedSymbols = [
|
|
86
|
+
"plugin_get_manifest_flatbuffer",
|
|
87
|
+
"plugin_get_manifest_flatbuffer_size",
|
|
88
|
+
...new Set(
|
|
89
|
+
(Array.isArray(manifest.methods) ? manifest.methods : [])
|
|
90
|
+
.map((method) => String(method?.methodId ?? "").trim())
|
|
91
|
+
.filter(Boolean),
|
|
92
|
+
),
|
|
93
|
+
];
|
|
94
|
+
const linkerExports = exportedSymbols.flatMap((symbol) => [
|
|
95
|
+
"-Wl,--export=" + symbol,
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
await execFile(compiler.command, [
|
|
100
|
+
sourcePath,
|
|
101
|
+
manifestSourcePath,
|
|
102
|
+
"-O2",
|
|
103
|
+
"--no-entry",
|
|
104
|
+
"-s",
|
|
105
|
+
"STANDALONE_WASM=1",
|
|
106
|
+
...linkerExports,
|
|
107
|
+
"-o",
|
|
108
|
+
outputPath,
|
|
109
|
+
]);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
error.message =
|
|
112
|
+
`Compilation failed with ${compiler.command}: ` +
|
|
113
|
+
(error.stderr || error.message);
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const wasmBytes = await readFile(outputPath);
|
|
118
|
+
const report = await validateArtifactWithStandards({
|
|
119
|
+
manifest,
|
|
120
|
+
wasmPath: outputPath,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
compiler: compiler.command,
|
|
125
|
+
language: compiler.language,
|
|
126
|
+
outputPath,
|
|
127
|
+
tempDir,
|
|
128
|
+
wasmBytes,
|
|
129
|
+
manifestWarnings: warnings,
|
|
130
|
+
report,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function cleanupCompilation(result) {
|
|
135
|
+
if (result?.tempDir) {
|
|
136
|
+
await rm(result.tempDir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function deriveSigningIdentity(mnemonic) {
|
|
141
|
+
const wallet = await getWasmWallet();
|
|
142
|
+
const resolvedMnemonic =
|
|
143
|
+
mnemonic && wallet.mnemonic.validate(mnemonic)
|
|
144
|
+
? mnemonic
|
|
145
|
+
: wallet.mnemonic.generate(12);
|
|
146
|
+
const seed = wallet.mnemonic.toSeed(resolvedMnemonic);
|
|
147
|
+
const root = wallet.hdkey.fromSeed(seed);
|
|
148
|
+
const signingKey = wallet.getSigningKey(root, 0, 0, 0);
|
|
149
|
+
return {
|
|
150
|
+
wallet,
|
|
151
|
+
mnemonic: resolvedMnemonic,
|
|
152
|
+
signingKey,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function protectModuleArtifact(options = {}) {
|
|
157
|
+
const manifest = options.manifest ?? {};
|
|
158
|
+
const wasmBytes =
|
|
159
|
+
options.wasmBytes instanceof Uint8Array
|
|
160
|
+
? options.wasmBytes
|
|
161
|
+
: base64ToBytes(options.wasmBase64 ?? "");
|
|
162
|
+
if (wasmBytes.length === 0) {
|
|
163
|
+
throw new Error("protectModuleArtifact requires wasmBytes or wasmBase64.");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const manifestBytes = encodePluginManifest(manifest);
|
|
167
|
+
const wasmHashHex = bytesToHex(await sha256Bytes(wasmBytes));
|
|
168
|
+
const manifestHashHex = bytesToHex(await sha256Bytes(manifestBytes));
|
|
169
|
+
const artifactId = options.artifactId ?? `module-${wasmHashHex.slice(0, 16)}`;
|
|
170
|
+
const programId = manifest.pluginId ?? artifactId;
|
|
171
|
+
|
|
172
|
+
const identity = await deriveSigningIdentity(options.mnemonic ?? null);
|
|
173
|
+
const signer = createHdWalletSigner({
|
|
174
|
+
publicKeyHex: bytesToHex(identity.signingKey.publicKey),
|
|
175
|
+
derivationPath: identity.signingKey.path,
|
|
176
|
+
keyId: artifactId,
|
|
177
|
+
async signDigest(digest) {
|
|
178
|
+
return identity.wallet.curves.secp256k1.sign(
|
|
179
|
+
digest,
|
|
180
|
+
identity.signingKey.privateKey,
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const authorization = await createDeploymentAuthorization({
|
|
186
|
+
artifactId,
|
|
187
|
+
programId,
|
|
188
|
+
manifestHash: manifestHashHex,
|
|
189
|
+
graphHash: wasmHashHex,
|
|
190
|
+
target: options.targetUrl ?? options.target ?? null,
|
|
191
|
+
capabilities: options.capabilities ?? [],
|
|
192
|
+
});
|
|
193
|
+
const signedAuthorization = await signAuthorization({
|
|
194
|
+
authorization,
|
|
195
|
+
signer,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const payload = {
|
|
199
|
+
version: 1,
|
|
200
|
+
format: "space-data-module-package",
|
|
201
|
+
artifactId,
|
|
202
|
+
programId,
|
|
203
|
+
manifest,
|
|
204
|
+
manifestBase64: bytesToBase64(manifestBytes),
|
|
205
|
+
wasmBase64: bytesToBase64(wasmBytes),
|
|
206
|
+
wasmHashHex,
|
|
207
|
+
manifestHashHex,
|
|
208
|
+
authorization: signedAuthorization,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
let encryptedEnvelope = null;
|
|
212
|
+
if (options.recipientPublicKeyHex) {
|
|
213
|
+
encryptedEnvelope = await encryptJsonForRecipient({
|
|
214
|
+
payload,
|
|
215
|
+
recipientPublicKey: hexToBytes(options.recipientPublicKeyHex),
|
|
216
|
+
context: "space-data-module-sdk/package",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
mnemonic: identity.mnemonic,
|
|
222
|
+
signingPublicKeyHex: bytesToHex(identity.signingKey.publicKey),
|
|
223
|
+
signingPath: identity.signingKey.path,
|
|
224
|
+
payload,
|
|
225
|
+
encrypted: Boolean(encryptedEnvelope),
|
|
226
|
+
encryptedEnvelope,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function createRecipientKeypairHex() {
|
|
231
|
+
const keypair = await generateX25519Keypair();
|
|
232
|
+
return {
|
|
233
|
+
publicKeyHex: bytesToHex(keypair.publicKey),
|
|
234
|
+
privateKeyHex: bytesToHex(keypair.privateKey),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findManifestFiles,
|
|
3
|
+
getWasmExportNames,
|
|
4
|
+
getWasmExportNamesFromFile,
|
|
5
|
+
loadManifestFromFile,
|
|
6
|
+
loadComplianceConfig,
|
|
7
|
+
RecommendedCapabilityIds,
|
|
8
|
+
resolveManifestFiles,
|
|
9
|
+
validatePluginArtifact,
|
|
10
|
+
validatePluginManifest,
|
|
11
|
+
} from "./pluginCompliance.js";
|
|
12
|
+
import { validateManifestAgainstStandardsCatalog } from "../standards/index.js";
|
|
13
|
+
|
|
14
|
+
function mergeReport(baseReport, issues) {
|
|
15
|
+
const mergedIssues = [...baseReport.issues, ...issues];
|
|
16
|
+
const errors = mergedIssues.filter((issue) => issue.severity === "error");
|
|
17
|
+
const warnings = mergedIssues.filter((issue) => issue.severity === "warning");
|
|
18
|
+
return {
|
|
19
|
+
...baseReport,
|
|
20
|
+
ok: errors.length === 0,
|
|
21
|
+
issues: mergedIssues,
|
|
22
|
+
errors,
|
|
23
|
+
warnings,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function validateManifestWithStandards(manifest, options = {}) {
|
|
28
|
+
const baseReport = validatePluginManifest(manifest, options);
|
|
29
|
+
const standards = await validateManifestAgainstStandardsCatalog(
|
|
30
|
+
manifest,
|
|
31
|
+
options,
|
|
32
|
+
);
|
|
33
|
+
return mergeReport(baseReport, standards.issues);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function validateArtifactWithStandards(options = {}) {
|
|
37
|
+
const baseReport = await validatePluginArtifact(options);
|
|
38
|
+
const standards = await validateManifestAgainstStandardsCatalog(
|
|
39
|
+
options.manifest,
|
|
40
|
+
{ sourceName: baseReport.sourceName },
|
|
41
|
+
);
|
|
42
|
+
return mergeReport(baseReport, standards.issues);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
findManifestFiles,
|
|
47
|
+
getWasmExportNames,
|
|
48
|
+
getWasmExportNamesFromFile,
|
|
49
|
+
loadManifestFromFile,
|
|
50
|
+
loadComplianceConfig,
|
|
51
|
+
RecommendedCapabilityIds,
|
|
52
|
+
resolveManifestFiles,
|
|
53
|
+
validatePluginArtifact,
|
|
54
|
+
validatePluginManifest,
|
|
55
|
+
};
|
|
56
|
+
|