githolon 0.34.0 → 0.35.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 +124 -11
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -924,20 +924,45 @@ async function wsRetire(ws, opts) {
924
924
  const cloud = cloudBase(opts.cloud);
925
925
  const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
926
926
  const parent = opts.parent ?? "root";
927
+ let record;
928
+ try {
929
+ const q = await fetch(`${cloud}/v2/workspaces/${parent}/workspaceByName?name=${encodeURIComponent(ws)}`);
930
+ const qd = await q.json().catch(() => void 0);
931
+ const rows = qd?.ok === true ? qd.rows ?? [] : [];
932
+ record = rows.find((r) => r.data?.status !== "retired" && r.data?.status !== "reclaimed") ?? rows[0];
933
+ } catch {
934
+ }
935
+ const recordId = record?.id;
936
+ if (recordId === void 0) {
937
+ err3(
938
+ `retire '${ws}': no CloudWorkspace record named '${ws}' in ${parent}'s registry \u2014 nothing lawful to retire.
939
+ (a workspace born via the OPEN lane \u2014 bare POST /v2/workspaces/${ws} \u2014 has no registry row;
940
+ registered births ride \`githolon ws create ${ws} --via ${parent}\`. An unregistered leak needs a custodian sweep.)`
941
+ );
942
+ return 1;
943
+ }
927
944
  let v;
928
945
  try {
929
946
  const principal = resolveGovernancePrincipal2(opts);
930
- const payload = { workspaceId: ws, retiredBy: principal, retiredAt: Date.now() };
931
- v = await signAndPostGovernance2(parent, "retireOwnWorkspace", payload, opts);
932
- if (!v.ok && v.status === 422) {
933
- v = await signAndPostGovernance2(parent, "retireWorkspace", payload, opts);
934
- }
947
+ const payload = { workspaceId: recordId, retiredBy: principal, retiredAt: Date.now() };
948
+ const bare = (s) => s.startsWith("user:") ? s.slice(5) : s;
949
+ const creator = record?.data?.createdBy ?? record?.data?.principal ?? "";
950
+ const own = creator !== "" && bare(creator) === bare(principal);
951
+ v = await signAndPostGovernance2(parent, own ? "retireOwnWorkspace" : "retireWorkspace", payload, opts);
935
952
  } catch (e) {
936
953
  err3(e.message);
937
954
  return 1;
938
955
  }
939
956
  if (!v.ok) {
940
957
  err3(`retire '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
958
+ if (JSON.stringify(v.body ?? "").includes("retire-own-only")) {
959
+ err3(
960
+ `note: you appear to BE '${ws}'s creator \u2014 the parent's deployed governance law may predate the
961
+ 0.34.1 retireOwnWorkspace creator-normalization fix (the 'user:' subject prefix vs the bare uid).
962
+ A governance re-deploy to ${parent} fixes the self-service lane; meanwhile an ADMIN can retire it:
963
+ githolon ws retire ${ws} --as <admin-uid>`
964
+ );
965
+ }
941
966
  return 1;
942
967
  }
943
968
  out3(`\u2713 workspace ${ws} retired via signed governance offer to ${parent} on ${cloud}`);
@@ -1515,11 +1540,35 @@ function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}
1515
1540
  capPorts.principal_verified = !!opts.principalVerified;
1516
1541
  }
1517
1542
  const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
1543
+ if (opts.attestations !== void 0) envelope.attestations = opts.attestations;
1518
1544
  writeWork(eng, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
1519
1545
  writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
1520
1546
  const genesis = !controllerHash;
1547
+ const attestorSecretFor = (from) => {
1548
+ const s = opts.attestorSecrets;
1549
+ if (!s) return null;
1550
+ if (typeof s === "string") return s;
1551
+ return typeof s[from] === "string" ? s[from] : null;
1552
+ };
1521
1553
  const defer = opts.deferProjection !== false;
1522
- const v = JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...opts.authToken ? { authToken: opts.authToken } : {}, domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}, ...opts.dryRun ? { dryRun: true } : defer ? { deferProjection: true } : {} }, eng.STDERR));
1554
+ const offerOnce = () => JSON.parse(call(eng.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...opts.authToken ? { authToken: opts.authToken } : {}, domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}, ...opts.dryRun ? { dryRun: true } : defer ? { deferProjection: true } : {} }, eng.STDERR));
1555
+ let v = offerOnce();
1556
+ for (let pass = 0; pass < 8 && v.outcome === "refused"; pass++) {
1557
+ const need = parseAttestedReadNeed(v.verdict?.reason ?? v.error);
1558
+ if (!need || !eng.mounted.has(need.from)) break;
1559
+ const secret = attestorSecretFor(need.from);
1560
+ if (!secret) break;
1561
+ const served = attestedRead(eng, need.from, {
1562
+ queryId: need.queryId,
1563
+ paramsJson: JSON.stringify(need.args),
1564
+ attestorSecret: secret,
1565
+ nowMs: opts.attestNowMs ?? Date.now()
1566
+ });
1567
+ if (!served || served.ok !== true) break;
1568
+ envelope.attestations = [...envelope.attestations ?? [], { queryId: need.queryId, result: served.rows, attestation: served.attestation }];
1569
+ writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
1570
+ v = offerOnce();
1571
+ }
1523
1572
  const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, ...v.born ? { born: v.born } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
1524
1573
  if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
1525
1574
  return res;
@@ -1552,6 +1601,19 @@ async function custodyFetch(eng, ws, ledger, { remoteRef = "refs/heads/main", lo
1552
1601
  kgit.writeRef(eng, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
1553
1602
  return tip;
1554
1603
  }
1604
+ function parseAttestedReadNeed(error) {
1605
+ const m = /attested-read-unreachable: read\('([^']+)', \{from: '([^']+)'\}\) args=(.*?) has no injected attestation/.exec(String(error || ""));
1606
+ if (!m) return null;
1607
+ let blob = m[3];
1608
+ for (let i = 0; i < 4; i++) {
1609
+ try {
1610
+ return { queryId: m[1], from: m[2], args: JSON.parse(blob) };
1611
+ } catch {
1612
+ blob = blob.replace(/\\(["\\])/g, "$1");
1613
+ }
1614
+ }
1615
+ return null;
1616
+ }
1555
1617
  function offerIntent(eng, ws, bytes, opts) {
1556
1618
  const name = `offer-in-${eng.seq++}.json`;
1557
1619
  writeWork(eng, name, bytes);
@@ -1569,7 +1631,7 @@ function applyIntentBytes(eng, ws, bytes, opts) {
1569
1631
  function verifyChainLocal(eng, ws) {
1570
1632
  return JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng.STDERR));
1571
1633
  }
1572
- var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, cryptoUnwrapKey, count, sum;
1634
+ var enc3, dec3, BRANCH, RESULT_PACK_INLINE_MAX, repoArgOf, gitdirOf, unpack, repoArgOfGitdir, bytesFromB64, kgit, installPayload, fullRef, applyPackInto, custodyLsRefs, qById, query, attestedRead, cryptoUnwrapKey, count, sum;
1573
1635
  var init_engine = __esm({
1574
1636
  "vendor/engine/engine.mjs"() {
1575
1637
  "use strict";
@@ -1626,6 +1688,12 @@ var init_engine = __esm({
1626
1688
  custodyLsRefs = (ledger, prefix = null) => lsRefs(ledger.remote, ledger.headers || {}, { service: "git-upload-pack", prefix });
1627
1689
  qById = (eng, ws, id, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "byId", aggregateId: id }), principal, keys, branch: BRANCH }, eng.STDERR));
1628
1690
  query = (eng, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ kind: "query", queryId, paramsJson }), principal, keys, branch: BRANCH }, eng.STDERR));
1691
+ attestedRead = (eng, ws, { queryId, paramsJson, attestorSecret, nowMs }) => JSON.parse(call(eng.ex, "query", {
1692
+ repoArg: repoArgOf(ws),
1693
+ workspace: ws,
1694
+ branch: BRANCH,
1695
+ queryBytes: b64Json({ op: "attestedRead", queryId, paramsJson: paramsJson || "{}", attestorSecret, nowMs: nowMs ?? Date.now(), sourceWorkspace: ws })
1696
+ }, eng.STDERR));
1629
1697
  cryptoUnwrapKey = (eng, { secret, hpkeEpk, ct }) => JSON.parse(call(eng.ex, "query", { queryBytes: b64Json({ op: "cryptoUnwrapKey", secret, hpkeEpk, ct }) }, eng.STDERR)).scopeKey;
1630
1698
  count = (eng, ws, countId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
1631
1699
  sum = (eng, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng.STDERR));
@@ -4014,7 +4082,7 @@ var HELP = {
4014
4082
  },
4015
4083
  proof: {
4016
4084
  usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live] [--keep]",
4017
- what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence), RETIRED on every\nexit; --keep keeps it and prints the secret once. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
4085
+ what: "Run the proof GENERATED from your law. DEFAULT: the OFFLINE legs on a LOCAL holon \u2014 the\nsame engine plane the cloud edge runs; no cloud workspace is touched (develop/play/understand\nhere, deploy with confidence after). --domain <key> proves a SPECIFIC domain of a multi-domain\npackage (offline; the default is the first). --live runs the full cloud loop (throwaway\nworkspace \u2192 deploy \u2192 offline write \u2192 admission \u2192 cloud reads \u2192 convergence). The throwaway is\ncreated REGISTERED (a signed createWorkspace at root \u2014 your principal needs creation authority\nthere) and RETIRED on every exit via the lawful retireOwnWorkspace; --keep keeps it. ALL GREEN or it names the jam. The offline\nlegs also rerun on every save in `githolon dev`.",
4018
4086
  examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live", "githolon proof --live --keep"]
4019
4087
  },
4020
4088
  dev: {
@@ -4899,13 +4967,58 @@ async function runProof(args) {
4899
4967
  const tsxPkg = JSON.parse(readFileSync11(join11(tsxDir, "package.json"), "utf8"));
4900
4968
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
4901
4969
  const tsxCli = join11(tsxDir, tsxBinRel ?? "dist/cli.mjs");
4902
- console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept, secret printed once" : "") + ")");
4970
+ console.log("githolon proof --live \u2014 the full cloud loop (throwaway workspace, retired on exit" + (keep ? "; --keep: kept" : "") + ")");
4971
+ const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
4972
+ const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
4973
+ const { login: login2, activePrincipal: activePrincipal2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
4974
+ let principal = process.env.NOMOS_PRINCIPAL;
4975
+ try {
4976
+ principal ??= resolveGovernancePrincipal2({});
4977
+ } catch {
4978
+ const lr = await login2({ agent: true });
4979
+ principal = lr === 0 ? activePrincipal2() : void 0;
4980
+ }
4981
+ if (principal === void 0) {
4982
+ return refuse("proof --live needs a principal \u2014 githolon login --agent (self-onboarding) failed; try it directly or pass NOMOS_PRINCIPAL");
4983
+ }
4984
+ const proofSlug = proofPath.split("/").pop().replace(/\.proof\.mts$/, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
4985
+ const ws = process.env.NOMOS_WS ?? `${proofSlug}-proof-${Math.random().toString(36).slice(2, 8)}`;
4986
+ if (process.env.NOMOS_WS === void 0) {
4987
+ const cr = await wsCreate2(ws, { via: "root", principal });
4988
+ if (cr !== 0) {
4989
+ return refuse(
4990
+ `proof --live: registered create refused at root \u2014 principal ${principal} lacks creation authority, and the open birth lane would leak an unretirable workspace.
4991
+ remedies:
4992
+ \u2022 ask an operator: githolon grant creation ${principal} (then rerun)
4993
+ \u2022 or pre-create a REGISTERED workspace yourself and hand it over: NOMOS_WS=<ws> githolon proof --live
4994
+ \u2022 or develop offline (no cloud workspace at all): githolon proof`
4995
+ );
4996
+ }
4997
+ }
4903
4998
  const r = spawnSync4(process.execPath, [tsxCli, proofPath], {
4904
4999
  stdio: "inherit",
4905
5000
  cwd: process.cwd(),
4906
- env: { ...process.env, ...keep ? { PROOF_KEEP: "1" } : {} }
5001
+ env: {
5002
+ ...process.env,
5003
+ NOMOS_WS: ws,
5004
+ NOMOS_PRINCIPAL: principal,
5005
+ NOMOS_PROOF_MANAGED: "1",
5006
+ // the proof skips its own (dead) cleanup lane; the CLI retires below
5007
+ ...keep ? { PROOF_KEEP: "1" } : {}
5008
+ }
4907
5009
  });
4908
- return r.status ?? 1;
5010
+ const proofStatus = r.status ?? 1;
5011
+ if (keep) {
5012
+ console.log(`workspace ${ws} KEPT (--keep) \u2014 retire it when done: githolon ws retire ${ws}`);
5013
+ return proofStatus;
5014
+ }
5015
+ const rr = await wsRetire2(ws, { principal }).catch(() => 1);
5016
+ if (rr !== 0) {
5017
+ console.error(`\u26A0 THROWAWAY WORKSPACE NOT RETIRED: ${ws}`);
5018
+ console.error(`\u26A0 retire it manually: githolon ws retire ${ws}`);
5019
+ console.error(`\u26A0 (if it has no root registry row it was born unregistered and needs a custodian sweep)`);
5020
+ }
5021
+ return proofStatus;
4909
5022
  }
4910
5023
  function parseArgs(argv) {
4911
5024
  const [command, ...rest] = argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.34.0",
3
+ "version": "0.35.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",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@bjorn3/browser_wasi_shim": "0.4.2",
32
- "@githolon/client": "^0.34.0",
33
- "@githolon/dsl": "^0.34.0",
32
+ "@githolon/client": "^0.35.0",
33
+ "@githolon/dsl": "^0.35.0",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {