githolon 0.5.1 → 0.6.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 (2) hide show
  1. package/dist/cli.mjs +138 -26
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -460,13 +460,13 @@ async function sha256hex(t) {
460
460
  function osEntropyBuffer(n) {
461
461
  const bytes = new Uint8Array(n * 8);
462
462
  for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
463
- const out9 = new Array(n), T = 2 ** 53;
463
+ const out10 = new Array(n), T = 2 ** 53;
464
464
  for (let i = 0; i < n; i++) {
465
465
  let z = 0n;
466
466
  for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
467
- out9[i] = Number(z >> 11n) / T;
467
+ out10[i] = Number(z >> 11n) / T;
468
468
  }
469
- return out9;
469
+ return out10;
470
470
  }
471
471
  function stringifyBig(o) {
472
472
  return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
@@ -599,7 +599,7 @@ function applyIntentBytes(eng, ws, bytes) {
599
599
  function verifyChainLocal(eng, ws) {
600
600
  return JSON.parse(call(eng.ex, "verify_chain", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng.STDERR));
601
601
  }
602
- var enc2, dec2, BRANCH, repoArgOf, gitdirOf, unpack, installPayload, qById, query, count, sum;
602
+ var enc2, dec2, BRANCH, repoArgOf, gitdirOf, unpack, installPayload, qById, query, cryptoUnwrapKey, count, sum;
603
603
  var init_engine = __esm({
604
604
  "vendor/engine/engine.mjs"() {
605
605
  "use strict";
@@ -615,8 +615,9 @@ var init_engine = __esm({
615
615
  return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
616
616
  };
617
617
  installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, authorityScope: "workspace/root", dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
618
- qById = (eng, ws, id) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, branch: BRANCH }, eng.STDERR));
619
- query = (eng, ws, queryId, paramsJson) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, branch: BRANCH }, eng.STDERR));
618
+ qById = (eng, ws, id, principal = "", keys = []) => JSON.parse(call(eng.ex, "query_by_id", { repoArg: repoArgOf(ws), workspace: ws, aggregateId: id, principal, keys, branch: BRANCH }, eng.STDERR));
619
+ query = (eng, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryId, paramsJson, principal, keys, branch: BRANCH }, eng.STDERR));
620
+ cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "crypto_unwrap_key", { secret, hpkeEpk, ct }, eng.STDERR)).scopeKey;
620
621
  count = (eng, ws, countId, groupKey) => JSON.parse(call(eng.ex, "count", { repoArg: repoArgOf(ws), workspace: ws, countId, groupKey, branch: BRANCH }, eng.STDERR));
621
622
  sum = (eng, ws, sumId, groupKey) => JSON.parse(call(eng.ex, "sum", { repoArg: repoArgOf(ws), workspace: ws, sumId, groupKey, branch: BRANCH }, eng.STDERR));
622
623
  }
@@ -644,6 +645,13 @@ async function fetchRuntime(cloud) {
644
645
  const cache = join4(configDir(), "runtime");
645
646
  let wasmBytes;
646
647
  const wasmCache = join4(cache, "holon.wasm");
648
+ const localWasm = process.env["NOMOS_LOCAL_WASM"];
649
+ if (localWasm) {
650
+ const bytes = readFileSync2(localWasm);
651
+ const manifests2 = await fetchJsonCached(`${cloud}/v1/runtime/manifests`, join4(cache, "manifests.json"));
652
+ const packages2 = await fetchJsonCached(`${cloud}/v1/runtime/packages`, join4(cache, "packages.json"));
653
+ return { wasmBytes: bytes, manifests: manifests2, packages: packages2 };
654
+ }
647
655
  try {
648
656
  const r = await fetch(`${cloud}/v1/runtime/holon.wasm`);
649
657
  if (!r.ok) throw new Error(`runtime wasm \u2192 ${r.status}`);
@@ -1997,9 +2005,9 @@ function capJsonStrings(v, max = 2048) {
1997
2005
  if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
1998
2006
  if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
1999
2007
  if (typeof v === "object" && v !== null) {
2000
- const out9 = {};
2001
- for (const [k, x] of Object.entries(v)) out9[k] = capJsonStrings(x, max);
2002
- return out9;
2008
+ const out10 = {};
2009
+ for (const [k, x] of Object.entries(v)) out10[k] = capJsonStrings(x, max);
2010
+ return out10;
2003
2011
  }
2004
2012
  return v;
2005
2013
  }
@@ -2161,12 +2169,76 @@ stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below cover
2161
2169
  return printVerdict(verdict) ? 0 : 1;
2162
2170
  }
2163
2171
 
2172
+ // src/decrypt.ts
2173
+ init_local_holon();
2174
+ init_cloud();
2175
+ init_engine();
2176
+ var SOURCE2 = "source";
2177
+ var out8 = (s) => {
2178
+ process.stdout.write(s + "\n");
2179
+ };
2180
+ var err8 = (s) => {
2181
+ process.stderr.write(`error: ${s}
2182
+ `);
2183
+ };
2184
+ async function decrypt(ws, opts) {
2185
+ const cloud = cloudBase(opts.cloud);
2186
+ if (!opts.as) {
2187
+ err8("--as <principal> is required (who to decrypt as)");
2188
+ return 1;
2189
+ }
2190
+ if (!opts.secret) {
2191
+ err8("--secret <base64 X25519 device secret> is required");
2192
+ return 1;
2193
+ }
2194
+ if (!opts.query) {
2195
+ err8("--query <queryId> is required (the read to decrypt)");
2196
+ return 1;
2197
+ }
2198
+ let eng;
2199
+ try {
2200
+ ({ eng } = await holonEngine(cloud, opts.overlay, "githolon-decrypt"));
2201
+ } catch (e) {
2202
+ err8(`cannot boot the local holon: ${String(e.message ?? e)}`);
2203
+ return 1;
2204
+ }
2205
+ const m = await mountWorkspace(eng, SOURCE2, {
2206
+ remote: `${cloud}/v1/workspaces/${ws}/git`,
2207
+ headers: { "x-nomos-principal": opts.as }
2208
+ });
2209
+ if (m.restored !== true) {
2210
+ err8(`workspace '${ws}' has no main on ${cloud}, or its clone was refused for '${opts.as}'` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : ""));
2211
+ return 1;
2212
+ }
2213
+ const wraps = query(eng, SOURCE2, "wrappedKeysByRecipient", JSON.stringify({ recipientPrincipal: opts.as }), opts.as);
2214
+ const keys = [];
2215
+ if (Array.isArray(wraps)) {
2216
+ for (const w of wraps) {
2217
+ const d = w.data ?? {};
2218
+ try {
2219
+ const key = cryptoUnwrapKey(eng, { secret: opts.secret, hpkeEpk: d.hpkeEpk, ct: d.ct });
2220
+ keys.push({ scope: d.scope, epoch: Number(d.epoch), key });
2221
+ } catch {
2222
+ }
2223
+ }
2224
+ }
2225
+ if (opts.json !== true) out8(`# resolved ${keys.length} scope key(s) for '${opts.as}': ${keys.map((k) => `${k.scope}@${k.epoch}`).join(", ") || "(none)"}`);
2226
+ const rows = query(eng, SOURCE2, opts.query, JSON.stringify(opts.params ?? {}), opts.as, keys);
2227
+ if (rows && !Array.isArray(rows) && rows.ok === false) {
2228
+ err8(`the read was refused: ${rows.error}` + (String(rows.error).startsWith("read-forbidden") ? `
2229
+ '${opts.as}' is not granted this scope's read capability` : ""));
2230
+ return 1;
2231
+ }
2232
+ out8(JSON.stringify(rows, null, opts.json === true ? 0 : 2));
2233
+ return 0;
2234
+ }
2235
+
2164
2236
  // src/status.ts
2165
2237
  init_engine();
2166
2238
  init_cloud();
2167
2239
  import { readFileSync as readFileSync8 } from "node:fs";
2168
- var out8 = (s) => void process.stdout.write(s + "\n");
2169
- var err8 = (s) => void process.stderr.write("error: " + s + "\n");
2240
+ var out9 = (s) => void process.stdout.write(s + "\n");
2241
+ var err9 = (s) => void process.stderr.write("error: " + s + "\n");
2170
2242
  function lawDiffVerdict(localHash, installed, ws) {
2171
2243
  const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
2172
2244
  const currents = active.filter((d) => d.current === true);
@@ -2204,7 +2276,7 @@ async function status(wsArg, opts) {
2204
2276
  try {
2205
2277
  ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
2206
2278
  } catch (e) {
2207
- err8(e.message);
2279
+ err9(e.message);
2208
2280
  return 1;
2209
2281
  }
2210
2282
  let localHash;
@@ -2216,35 +2288,35 @@ async function status(wsArg, opts) {
2216
2288
  }
2217
2289
  const r = await fetch(`${cloud}/v1/workspaces/${ws}/domains`).catch(() => void 0);
2218
2290
  if (r === void 0) {
2219
- err8(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
2291
+ err9(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
2220
2292
  return 1;
2221
2293
  }
2222
2294
  const d = await r.json().catch(() => void 0);
2223
2295
  if (!r.ok || d?.ok !== true) {
2224
- err8(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
2296
+ err9(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
2225
2297
  is the workspace born? githolon ws create ${ws}`);
2226
2298
  return 1;
2227
2299
  }
2228
- out8(`workspace ${ws} on ${cloud}`);
2300
+ out9(`workspace ${ws} on ${cloud}`);
2229
2301
  const allDomains = d.domains ?? [];
2230
2302
  const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
2231
2303
  const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
2232
2304
  const controllerCount = allDomains.length - domains.length;
2233
- if (allDomains.length === 0) out8(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
2305
+ if (allDomains.length === 0) out9(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
2234
2306
  for (const dom of domains) {
2235
2307
  const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
2236
- out8(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
2308
+ out9(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
2237
2309
  }
2238
- if (controllerCount > 0) out8(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
2310
+ if (controllerCount > 0) out9(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
2239
2311
  const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
2240
- if (tenantActive > 1) out8(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
2312
+ if (tenantActive > 1) out9(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
2241
2313
  if (localHash === void 0) {
2242
- out8(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
2314
+ out9(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
2243
2315
  return 0;
2244
2316
  }
2245
- out8(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
2317
+ out9(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
2246
2318
  const verdict = lawDiffVerdict(localHash, domains, ws);
2247
- for (const line of verdict.lines) out8(line);
2319
+ for (const line of verdict.lines) out9(line);
2248
2320
  return verdict.inSync ? 0 : 2;
2249
2321
  }
2250
2322
 
@@ -2260,6 +2332,8 @@ The inner loop:
2260
2332
  githolon status [<ws>] local law hash vs the DEPLOYED law, remedy named
2261
2333
  githolon replay <ws|dir> the ledger as film: step the chain intent-by-
2262
2334
  intent; finale = the verify_chain verdict
2335
+ githolon decrypt <ws> --as <p> --secret <b64> clone + decrypt as a principal: unwrap their scope
2336
+ --query <id> [--param k=v] keys from the chain, read private fields in cleartext
2263
2337
 
2264
2338
  Authoring:
2265
2339
  githolon compile [compiler args] compile the package by ./nomos.package.mjs
@@ -2610,6 +2684,44 @@ ${commandHelp("replay")}`);
2610
2684
  }
2611
2685
  return replay(target, { ...opts, ...step !== void 0 ? { step } : {}, ...json ? { json: true } : {} });
2612
2686
  }
2687
+ if (argv[0] === "decrypt") {
2688
+ const rest = argv.slice(1);
2689
+ const pull = (flag) => {
2690
+ const at = rest.indexOf(flag);
2691
+ return at >= 0 ? rest[at + 1] : void 0;
2692
+ };
2693
+ const as = pull("--as"), secret = pull("--secret"), q = pull("--query");
2694
+ const json = rest.includes("--json");
2695
+ const params = {};
2696
+ rest.forEach((a, i) => {
2697
+ if (a === "--param" && rest[i + 1]) {
2698
+ const [k, ...v] = rest[i + 1].split("=");
2699
+ params[k] = v.join("=");
2700
+ }
2701
+ });
2702
+ const consumed = /* @__PURE__ */ new Set();
2703
+ rest.forEach((a, i) => {
2704
+ if (["--as", "--secret", "--query", "--param"].includes(a)) {
2705
+ consumed.add(i);
2706
+ consumed.add(i + 1);
2707
+ } else if (a === "--json") consumed.add(i);
2708
+ });
2709
+ const cleaned = rest.filter((_, i) => !consumed.has(i));
2710
+ const { pos, opts, bad } = cloudArgs(cleaned);
2711
+ if (bad !== void 0) {
2712
+ process.stderr.write(`error: ${bad}
2713
+
2714
+ ${commandHelp("decrypt") ?? ""}`);
2715
+ return 1;
2716
+ }
2717
+ const ws = pos[0];
2718
+ if (ws === void 0 || as === void 0 || secret === void 0 || q === void 0) {
2719
+ process.stderr.write(`error: usage: githolon decrypt <ws> --as <principal> --secret <b64> --query <id> [--param k=v ...]
2720
+ `);
2721
+ return 1;
2722
+ }
2723
+ return decrypt(ws, { ...opts, as, secret, query: q, params, ...json ? { json: true } : {} });
2724
+ }
2613
2725
  if (argv[0] === "status") {
2614
2726
  const { pos, opts, bad } = cloudArgs(argv.slice(1));
2615
2727
  if (bad !== void 0) {
@@ -2682,8 +2794,8 @@ ${USAGE}`);
2682
2794
  let parsed;
2683
2795
  try {
2684
2796
  parsed = parseArgs(argv);
2685
- } catch (err9) {
2686
- process.stderr.write(`error: ${err9.message}
2797
+ } catch (err10) {
2798
+ process.stderr.write(`error: ${err10.message}
2687
2799
 
2688
2800
  ${USAGE}`);
2689
2801
  return 1;
@@ -2702,8 +2814,8 @@ ${USAGE}`);
2702
2814
  process.stdout.write(`created ${path}
2703
2815
  `);
2704
2816
  return 0;
2705
- } catch (err9) {
2706
- process.stderr.write(`error: ${err9.message}
2817
+ } catch (err10) {
2818
+ process.stderr.write(`error: ${err10.message}
2707
2819
  `);
2708
2820
  return 1;
2709
2821
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "githolon — the Nomos developer CLI: Rails-style generators for @githolon/dsl domains + the package compiler. Kernel-independent.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@bjorn3/browser_wasi_shim": "0.4.2",
30
- "@githolon/dsl": "^0.5.1",
30
+ "@githolon/dsl": "^0.6.0",
31
31
  "isomorphic-git": "^1.38.4"
32
32
  },
33
33
  "devDependencies": {