blindseal 0.5.0 → 0.5.1

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 (34) hide show
  1. package/dashboard/assets/blindseal_verifier_bg-BYF7rQG1.wasm +0 -0
  2. package/dashboard/assets/geist-400-gapTbOY8.woff2 +0 -0
  3. package/dashboard/assets/geist-500-uokXdC-Q.woff2 +0 -0
  4. package/dashboard/assets/geist-600-DFOURf8L.woff2 +0 -0
  5. package/dashboard/assets/geist-mono-400-DTRLJnHl.woff2 +0 -0
  6. package/dashboard/assets/geist-mono-600-V-KvD_pi.woff2 +0 -0
  7. package/dashboard/assets/index-ErSVkiAF.css +1 -0
  8. package/dashboard/assets/index-cOSTL8eT.js +1 -0
  9. package/dashboard/assets/index-p0eB3k2y.js +70 -0
  10. package/dashboard/index.html +76 -0
  11. package/dist/attest-VWYZAOLY.js +346 -0
  12. package/dist/{audit-C756XFXR.js → audit-G4DDMAXH.js} +1 -1
  13. package/dist/chunk-6EU4GH4P.js +513 -0
  14. package/dist/chunk-B44GZXDX.js +136 -0
  15. package/dist/chunk-CIUQ6WHJ.js +88 -0
  16. package/dist/{chunk-BTIXDNTV.js → chunk-DJL2E6RF.js} +69 -6
  17. package/dist/{chunk-PQ457W6T.js → chunk-VSOUJM6X.js} +1 -0
  18. package/dist/{chunk-JJY62YXJ.js → chunk-ZZTWGFH2.js} +0 -20
  19. package/dist/dashboard-J6BA7OBL.js +105 -0
  20. package/dist/decide.js +1 -1
  21. package/dist/index.js +122 -1307
  22. package/dist/{init.js → init-ZP3SLHM3.js} +122 -36
  23. package/dist/run-YQBNEPSD.js +17 -0
  24. package/dist/{submit-2UALCRX2.js → submit-D6MQTAXT.js} +1 -1
  25. package/dist/verify-RISAGKXC.js +98 -0
  26. package/fixtures/attestation-sample.json +25 -0
  27. package/fixtures/attestation-tampered.json +29 -0
  28. package/fixtures/attestations.json +174 -0
  29. package/fixtures/audit.json +1810 -0
  30. package/fixtures/decisions.json +9899 -0
  31. package/fixtures/policy.json +216 -0
  32. package/package.json +8 -8
  33. package/dist/attest-QLIE2OSQ.js +0 -195
  34. package/dist/verify-HO4IU7XV.js +0 -45
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/paths.ts
4
+ import { randomBytes } from "crypto";
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6
+ import os from "os";
7
+ import path from "path";
8
+ import { fileURLToPath } from "url";
9
+ var cachedRoot;
10
+ function packageRoot() {
11
+ if (cachedRoot) return cachedRoot;
12
+ let dir = path.dirname(fileURLToPath(import.meta.url));
13
+ for (let i = 0; i < 6; i++) {
14
+ const pkg = path.join(dir, "package.json");
15
+ if (existsSync(pkg)) {
16
+ try {
17
+ const name = JSON.parse(readFileSync(pkg, "utf8")).name;
18
+ if (name === "blindseal") {
19
+ cachedRoot = dir;
20
+ return dir;
21
+ }
22
+ } catch {
23
+ }
24
+ }
25
+ const parent = path.dirname(dir);
26
+ if (parent === dir) break;
27
+ dir = parent;
28
+ }
29
+ cachedRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
30
+ return cachedRoot;
31
+ }
32
+ function packageVersion() {
33
+ try {
34
+ const pkg = JSON.parse(readFileSync(path.join(packageRoot(), "package.json"), "utf8"));
35
+ return pkg.version ? String(pkg.version) : "0.0.0";
36
+ } catch {
37
+ return "0.0.0";
38
+ }
39
+ }
40
+ function blindsealHome() {
41
+ return path.join(os.homedir(), ".blindseal");
42
+ }
43
+ function shippedDashboardDir() {
44
+ const dir = path.join(packageRoot(), "dashboard");
45
+ return existsSync(path.join(dir, "index.html")) ? dir : void 0;
46
+ }
47
+ function shippedFixturesDir() {
48
+ const dir = path.join(packageRoot(), "fixtures");
49
+ return existsSync(path.join(dir, "decisions.json")) ? dir : void 0;
50
+ }
51
+ function nearestFixturesDir(from = process.cwd()) {
52
+ let dir = from;
53
+ for (let i = 0; i < 7; i++) {
54
+ const candidate = path.join(dir, "fixtures");
55
+ if (existsSync(path.join(candidate, "decisions.json"))) return candidate;
56
+ const parent = path.dirname(dir);
57
+ if (parent === dir) break;
58
+ dir = parent;
59
+ }
60
+ return void 0;
61
+ }
62
+ function dashboardTokenPath() {
63
+ return path.join(blindsealHome(), "dashboard.token");
64
+ }
65
+ function resolveDashboardToken(explicit) {
66
+ if (explicit) return explicit;
67
+ const file = dashboardTokenPath();
68
+ try {
69
+ if (existsSync(file)) return readFileSync(file, "utf8").trim();
70
+ mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
71
+ const tok = randomBytes(24).toString("base64url");
72
+ writeFileSync(file, tok + "\n", { mode: 384 });
73
+ return tok;
74
+ } catch {
75
+ return randomBytes(24).toString("base64url");
76
+ }
77
+ }
78
+
79
+ export {
80
+ packageRoot,
81
+ packageVersion,
82
+ blindsealHome,
83
+ shippedDashboardDir,
84
+ shippedFixturesDir,
85
+ nearestFixturesDir,
86
+ dashboardTokenPath,
87
+ resolveDashboardToken
88
+ };
@@ -36,9 +36,28 @@ var u64le = (n) => {
36
36
  b.writeBigUInt64LE(n);
37
37
  return b;
38
38
  };
39
+ var PUBLIC_INPUTS_LEN = 168;
39
40
  function decodePublicInputs(blob) {
40
- if (blob.length !== 168)
41
- throw new Error(`publicInputs must be 168 bytes, got ${blob.length}`);
41
+ if (typeof blob === "string") {
42
+ const h = blob.trim().replace(/^0x/i, "").toLowerCase();
43
+ if (!/^[0-9a-f]*$/.test(h) || h.length % 2 !== 0)
44
+ throw new Error("publicInputs is not a hex string");
45
+ const b = decodeBytes(Buffer.from(h, "hex"));
46
+ const hx = (x) => "0x" + x.toString("hex");
47
+ return {
48
+ payloadHash: hx(b.payloadHash),
49
+ policyRoot: hx(b.policyRoot),
50
+ detectorVersion: hx(b.detectorVersion),
51
+ actionHash: hx(b.actionHash),
52
+ nullifier: hx(b.nullifier),
53
+ expirySlot: b.expirySlot.toString()
54
+ };
55
+ }
56
+ return decodeBytes(blob);
57
+ }
58
+ function decodeBytes(blob) {
59
+ if (blob.length !== PUBLIC_INPUTS_LEN)
60
+ throw new Error(`publicInputs must be ${PUBLIC_INPUTS_LEN} bytes, got ${blob.length}`);
42
61
  const b = Buffer.from(blob);
43
62
  return {
44
63
  payloadHash: b.subarray(0, 32),
@@ -122,17 +141,61 @@ async function isConsumed(att, conn, programId = PROGRAM_ID) {
122
141
  const info = await conn.getAccountInfo(nullifierPda(nullifier, programId), "confirmed");
123
142
  return info !== null;
124
143
  }
125
- async function verifyOffchain(att) {
144
+ var LABELED_FIELDS = ["payloadHash", "policyRoot", "detectorVersion", "actionHash", "nullifier", "expirySlot"];
145
+ var normHex = (v) => "0x" + String(v ?? "").trim().replace(/^0x/i, "").toLowerCase();
146
+ var normSlot = (v) => {
147
+ if (typeof v === "bigint")
148
+ return v.toString();
149
+ if (typeof v === "number")
150
+ return Number.isInteger(v) && v >= 0 ? String(v) : null;
151
+ if (typeof v === "string" && /^\d+$/.test(v.trim()))
152
+ return BigInt(v.trim()).toString();
153
+ return null;
154
+ };
155
+ function labelMismatches(att, decoded) {
156
+ const out = [];
157
+ for (const k of LABELED_FIELDS) {
158
+ const same = k === "expirySlot" ? normSlot(att.expirySlot) === decoded.expirySlot : normHex(att[k]) === decoded[k];
159
+ if (!same)
160
+ out.push(k);
161
+ }
162
+ return out;
163
+ }
164
+ var wasmReady = null;
165
+ var loadWasm = () => wasmReady ??= (async () => {
126
166
  const wasm = await import("blindseal-verifier-wasm");
127
167
  const { readFileSync } = await import("fs");
128
168
  const req = createRequire(import.meta.url);
129
- const bytes = readFileSync(req.resolve("blindseal-verifier-wasm/bg.wasm"));
130
- wasm.initSync({ module: bytes });
131
- return JSON.parse(wasm.verify_attestation_json(JSON.stringify(att)));
169
+ wasm.initSync({ module: readFileSync(req.resolve("blindseal-verifier-wasm/bg.wasm")) });
170
+ return wasm;
171
+ })().catch((e) => {
172
+ wasmReady = null;
173
+ throw e;
174
+ });
175
+ async function verifyOffchain(att) {
176
+ const wasm = await loadWasm();
177
+ let decoded;
178
+ let decodeError;
179
+ try {
180
+ decoded = decodePublicInputs(String(att.publicInputs?.[0] ?? ""));
181
+ } catch (e) {
182
+ decodeError = e instanceof Error ? e.message : String(e);
183
+ }
184
+ const forWasm = decoded ? { ...att, detectorVersion: decoded.detectorVersion } : att;
185
+ const proof = JSON.parse(wasm.verify_attestation_json(JSON.stringify(forWasm)));
186
+ const mismatches = decoded ? labelMismatches(att, decoded) : [];
187
+ if (!proof.ok)
188
+ return { ok: false, reason: proof.reason ?? "proof did not verify", decoded, mismatches };
189
+ if (!decoded)
190
+ return { ok: false, reason: decodeError ?? "publicInputs did not decode", mismatches };
191
+ if (mismatches.length)
192
+ return { ok: false, reason: `label mismatch: ${mismatches.join(", ")}`, decoded, mismatches };
193
+ return { ok: true, decoded, mismatches };
132
194
  }
133
195
 
134
196
  export {
135
197
  DEFAULT_RPC_URL,
198
+ decodePublicInputs,
136
199
  WrongClusterError,
137
200
  submit,
138
201
  isConsumed,
@@ -255,6 +255,7 @@ function openAudit(baseDir = path4.join(os.homedir(), ".blindseal")) {
255
255
 
256
256
  export {
257
257
  hashEntry,
258
+ loadIdentity,
258
259
  discloseBundle,
259
260
  verifyDisclosure,
260
261
  isCleanWindow,
@@ -275,7 +275,6 @@ function loadConfig() {
275
275
  capability: loadCapabilityConfig(),
276
276
  tradePolicy: loadTradePolicyConfig(),
277
277
  manifest: loadManifestConfig(),
278
- telemetry: loadTelemetryConfig(),
279
278
  audit: loadAuditConfig(),
280
279
  persistence: loadScanStoreConfig()
281
280
  };
@@ -284,25 +283,6 @@ function loadAuditConfig() {
284
283
  const filepath = process.env.BLINDSEAL_AUDIT_LOG;
285
284
  return { enabled: !!filepath, filepath };
286
285
  }
287
- function loadTelemetryConfig() {
288
- const flag = process.env.BLINDSEAL_TELEMETRY?.toLowerCase();
289
- const explicitlyOff = flag === "0" || flag === "false" || flag === "off" || flag === "no";
290
- const explicitlyOn = flag === "1" || flag === "true" || flag === "on";
291
- const url = process.env.BLINDSEAL_TELEMETRY_URL;
292
- const enabled = !explicitlyOff && !!url;
293
- const secret = process.env.BLINDSEAL_TELEMETRY_SECRET;
294
- const batchSize = positiveInt(process.env.BLINDSEAL_TELEMETRY_BATCH, 100);
295
- const flushIntervalMs = positiveInt(process.env.BLINDSEAL_TELEMETRY_FLUSH_MS, 5e3);
296
- return {
297
- enabled,
298
- url,
299
- secret,
300
- batchSize,
301
- flushIntervalMs,
302
- bannerOnStart: enabled && !explicitlyOn
303
- // implicit-on path
304
- };
305
- }
306
286
  function loadManifestConfig() {
307
287
  const raw = process.env.BLINDSEAL_MANIFEST_CHECK?.toLowerCase();
308
288
  let mode;
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runRun
4
+ } from "./chunk-6EU4GH4P.js";
5
+ import "./chunk-B44GZXDX.js";
6
+ import "./chunk-VSOUJM6X.js";
7
+ import {
8
+ dashboardTokenPath,
9
+ resolveDashboardToken
10
+ } from "./chunk-CIUQ6WHJ.js";
11
+ import "./chunk-PDX44BCA.js";
12
+
13
+ // src/cli/dashboard.ts
14
+ import { spawn } from "child_process";
15
+ var HELP = `blindseal dashboard \u2014 open the local dashboard in your browser
16
+
17
+ Usage:
18
+ blindseal dashboard [flags]
19
+
20
+ Flags
21
+ --port <n> local API port (default 7777)
22
+ --host <addr> local API host (default 127.0.0.1)
23
+ --no-open print the URL only, do not launch a browser
24
+ --help, -h this help
25
+
26
+ If nothing is listening, \`blindseal run --mock\` is started for you (Ctrl-C to stop it).
27
+ The URL carries the token from ${"~/.blindseal/dashboard.token"} in its fragment; it never leaves this machine.
28
+ `;
29
+ function parseDashboardArgs(argv) {
30
+ const out = { port: 7777, host: "127.0.0.1", open: true };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const a = argv[i];
33
+ if (a === "--help" || a === "-h") out.help = true;
34
+ else if (a === "--port") out.port = Number.parseInt(argv[++i] ?? "", 10);
35
+ else if (a === "--host") out.host = argv[++i] ?? out.host;
36
+ else if (a === "--no-open") out.open = false;
37
+ else if (a && a.startsWith("--")) throw new Error(`unknown flag: ${a}`);
38
+ }
39
+ if (!Number.isFinite(out.port) || out.port <= 0) throw new Error("--port must be a positive integer");
40
+ return out;
41
+ }
42
+ function dashboardUrl(host, port, token) {
43
+ return `http://${host}:${port}/#token=${token}`;
44
+ }
45
+ async function isListening(host, port, timeoutMs = 1500) {
46
+ try {
47
+ await fetch(`http://${host}:${port}/api/status`, { signal: AbortSignal.timeout(timeoutMs) });
48
+ return true;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+ function openBrowser(url) {
54
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
55
+ try {
56
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
57
+ child.on("error", () => {
58
+ });
59
+ child.unref();
60
+ } catch {
61
+ }
62
+ }
63
+ async function runDashboardCli(argv) {
64
+ let o;
65
+ try {
66
+ o = parseDashboardArgs(argv);
67
+ } catch (e) {
68
+ process.stderr.write(`blindseal dashboard: ${e.message}
69
+ ${HELP}`);
70
+ return 2;
71
+ }
72
+ if (o.help) {
73
+ process.stdout.write(HELP);
74
+ return 0;
75
+ }
76
+ const token = resolveDashboardToken();
77
+ const url = dashboardUrl(o.host, o.port, token);
78
+ if (await isListening(o.host, o.port)) {
79
+ process.stdout.write(`blindseal dashboard: ${url}
80
+ (token from ${dashboardTokenPath()})
81
+ `);
82
+ if (o.open) openBrowser(url);
83
+ return 0;
84
+ }
85
+ process.stdout.write(`blindseal dashboard: nothing listening on ${o.host}:${o.port} \u2014 starting \`blindseal run --mock\`
86
+ `);
87
+ const running = runRun(["--mock", "--port", String(o.port), "--host", o.host]);
88
+ for (let i = 0; i < 20; i++) {
89
+ if (await isListening(o.host, o.port, 500)) {
90
+ process.stdout.write(`blindseal dashboard: ${url}
91
+ `);
92
+ if (o.open) openBrowser(url);
93
+ break;
94
+ }
95
+ await new Promise((r) => setTimeout(r, 250));
96
+ }
97
+ return running;
98
+ }
99
+ export {
100
+ dashboardUrl,
101
+ isListening,
102
+ openBrowser,
103
+ parseDashboardArgs,
104
+ runDashboardCli
105
+ };
package/dist/decide.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  decideTradePolicy,
7
7
  loadConfig,
8
8
  mergeDecisions
9
- } from "./chunk-JJY62YXJ.js";
9
+ } from "./chunk-ZZTWGFH2.js";
10
10
  import "./chunk-PDX44BCA.js";
11
11
 
12
12
  // src/cli/decide.ts