githolon 0.100.0 → 0.100.2

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/dist/cli.mjs CHANGED
@@ -3098,6 +3098,8 @@ __export(governance_exports, {
3098
3098
  certifyEra: () => certifyEra,
3099
3099
  consentRebirth: () => consentRebirth,
3100
3100
  discoverSigningDomain: () => discoverSigningDomain,
3101
+ ensureSovereignHome: () => ensureSovereignHome,
3102
+ exchangeOnboardingToken: () => exchangeOnboardingToken,
3101
3103
  executeRepairRebirth: () => executeRepairRebirth,
3102
3104
  governanceOffer: () => governanceOffer,
3103
3105
  governanceRelay: () => governanceRelay,
@@ -3120,6 +3122,8 @@ __export(governance_exports, {
3120
3122
  resolveGovernancePrincipal: () => resolveGovernancePrincipal,
3121
3123
  signAndPostGovernance: () => signAndPostGovernance,
3122
3124
  signAndPostOffer: () => signAndPostOffer,
3125
+ signAndPostWorkspaceBirth: () => signAndPostWorkspaceBirth,
3126
+ signAndPostWorkspaceRetirement: () => signAndPostWorkspaceRetirement,
3123
3127
  validatePlanForApply: () => validatePlanForApply,
3124
3128
  verifyWorkspaceVerdict: () => verifyWorkspaceVerdict
3125
3129
  });
@@ -3195,28 +3199,239 @@ function canonicalPrincipal(principal) {
3195
3199
  return s.includes(":") ? s : `user:${s}`;
3196
3200
  }
3197
3201
  async function signAndPostOffer(ws, domain, directiveId, payload, opts) {
3202
+ return signAndPostPreparedOffer(ws, domain, () => ({ directiveId, payload }), opts);
3203
+ }
3204
+ async function signAndPostPreparedOffer(ws, domain, prepare, opts, authorMode = "account", preferActor) {
3198
3205
  const cloud = cloudBase(opts.cloud);
3199
3206
  const principal = resolveGovernancePrincipal(opts);
3200
3207
  const holon = await connectGovernance(cloud, ws);
3201
- const resolvedDomain = domain ?? await discoverSigningDomain(holon, ws, directiveId, void 0);
3202
3208
  const device = await ensureDeviceKey(holon, principal);
3209
+ const accountActor = canonicalPrincipal(principal);
3210
+ const keyActor = `key:${device.keyHash}`;
3211
+ let actor = accountActor;
3212
+ if (preferActor !== void 0 && preferActor === keyActor) {
3213
+ actor = keyActor;
3214
+ } else if (preferActor !== void 0 && sameIdentity(preferActor, accountActor)) {
3215
+ actor = accountActor;
3216
+ } else if (authorMode === "self-if-unenrolled" && typeof holon.parentSignerEnrollment === "function") {
3217
+ try {
3218
+ const posture = await holon.parentSignerEnrollment({ parent: ws, principal: accountActor, keyHash: device.keyHash });
3219
+ if (!posture.enrolled) actor = keyActor;
3220
+ } catch {
3221
+ }
3222
+ }
3223
+ const prepared = prepare(actor);
3224
+ const resolvedDomain = domain ?? await discoverSigningDomain(holon, ws, prepared.directiveId, void 0);
3203
3225
  const sealed = await holon.signGovernanceOffer({
3204
3226
  domain: resolvedDomain,
3205
- directiveId,
3206
- payload,
3227
+ directiveId: prepared.directiveId,
3228
+ payload: prepared.payload,
3207
3229
  authorSecret: device.secret,
3208
- actor: canonicalPrincipal(principal)
3230
+ actor
3209
3231
  });
3210
3232
  if (sealed.intentBytes.length > STAGE_THRESHOLD_CHARS) {
3211
- const staged = await relaySealedOfferViaStage(cloud, ws, directiveId, sealed.intentBytes, fetch);
3233
+ const staged = await relaySealedOfferViaStage(cloud, ws, prepared.directiveId, sealed.intentBytes, fetch);
3212
3234
  if (staged !== null) {
3213
3235
  out2(` staged ${staged.size} sealed bytes content-addressed (sha256 ${staged.sha256.slice(0, 16)}\u2026) \u2014 the signed offer names the hash`);
3214
- return { ...staged.verdict, principal };
3236
+ return { ...staged.verdict, principal, actor, directiveId: prepared.directiveId };
3215
3237
  }
3216
3238
  err2(`this cloud has no /v2/stage content-addressed lane \u2014 relaying the ${sealed.intentBytes.length}-char sealed offer inline (a 413 here means the worker needs upgrading to accept law this large signed)`);
3217
3239
  }
3218
- const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws, domain: resolvedDomain });
3219
- return { ...verdict, principal };
3240
+ const verdict = await holon.postGovernanceOffer({ directiveId: prepared.directiveId, intentBytes: sealed.intentBytes, ws, domain: resolvedDomain });
3241
+ return { ...verdict, principal, actor, directiveId: prepared.directiveId };
3242
+ }
3243
+ async function signAndPostWorkspaceBirth(parent, name, opts) {
3244
+ return signAndPostPreparedOffer(
3245
+ parent,
3246
+ "workspaces",
3247
+ (actor) => {
3248
+ const identity = lineageIdentity(actor);
3249
+ return {
3250
+ directiveId: "createWorkspace",
3251
+ payload: {
3252
+ name,
3253
+ tier: "edge",
3254
+ parent: parent === "root" ? "" : parent,
3255
+ principal: identity,
3256
+ requestedBy: identity,
3257
+ requestedAt: Date.now()
3258
+ }
3259
+ };
3260
+ },
3261
+ opts,
3262
+ "self-if-unenrolled"
3263
+ );
3264
+ }
3265
+ async function exchangeOnboardingToken(session, keyHash, idp = process.env["NOMOS_ONBOARDING_IDP"] ?? DEFAULT_ONBOARDING_IDP, fetchImpl = fetch) {
3266
+ const r = await fetchImpl(`${idp.replace(/\/+$/, "")}/token`, {
3267
+ method: "POST",
3268
+ headers: { authorization: `Bearer ${session}`, "content-type": "application/json" },
3269
+ body: JSON.stringify({ nonce: keyHash })
3270
+ });
3271
+ const body = await r.json().catch(() => null);
3272
+ if (!r.ok || typeof body?.token !== "string" || typeof body.issuer !== "string" || typeof body.subject !== "string") {
3273
+ throw new Error(
3274
+ `Nomos could not establish the first device identity (${r.status}): ${String(body?.error ?? "invalid identity-provider response")}`
3275
+ );
3276
+ }
3277
+ return { token: body.token, issuer: body.issuer, subject: body.subject };
3278
+ }
3279
+ async function ensureSovereignHome(opts = {}) {
3280
+ const cloud = cloudBase(opts.cloud);
3281
+ const uid = opts.principal ?? currentPrincipal();
3282
+ if (!uid) throw new Error("a Nomos session is required \u2014 githolon login --agent");
3283
+ const actor = canonicalPrincipal(uid);
3284
+ if (!actor.startsWith("user:")) throw new Error("blank-session bootstrap requires an identity-provider principal");
3285
+ const bare = actor.slice("user:".length);
3286
+ const auth = await sessionToken();
3287
+ if (!auth) throw new Error("blank-session bootstrap needs the live login session \u2014 githolon login --agent");
3288
+ const client = await import("@githolon/client");
3289
+ const home = await client.homeNameOf(actor);
3290
+ const exists = await fetch(`${cloud}/v2/workspaces/${encodeURIComponent(home)}`).then((r) => r.ok).catch(() => false);
3291
+ const root = await connectGovernance(cloud, "root");
3292
+ const device = await ensureDeviceKey(root, bare);
3293
+ let signerReady = false;
3294
+ if (typeof root.parentSignerEnrollment === "function") {
3295
+ signerReady = await root.parentSignerEnrollment({ parent: "root", principal: actor, keyHash: device.keyHash, authToken: auth }).then((p) => p.enrolled && p.revoked !== true).catch(() => false);
3296
+ }
3297
+ const delegationReady = await fetch(
3298
+ `${cloud}/v2/workspaces/root/workspaces/signerDelegationByUser?user=${encodeURIComponent(actor)}`
3299
+ ).then(async (r) => {
3300
+ if (!r.ok) return false;
3301
+ const d = await r.json();
3302
+ return Array.isArray(d.rows) && d.rows.length > 0;
3303
+ }).catch(() => false);
3304
+ if (!signerReady || !delegationReady) {
3305
+ if (typeof root.enrollFirstDevice !== "function") {
3306
+ throw new Error("the installed @githolon/client predates automatic first-device enrollment");
3307
+ }
3308
+ const assertion = await exchangeOnboardingToken(auth, device.keyHash);
3309
+ if (assertion.subject !== bare) {
3310
+ throw new Error(`identity provider returned '${assertion.subject}' for logged-in principal '${bare}'`);
3311
+ }
3312
+ await root.enrollFirstDevice({
3313
+ parent: "root",
3314
+ domain: "workspaces",
3315
+ principal: actor,
3316
+ device,
3317
+ idToken: assertion.token,
3318
+ authToken: assertion.token,
3319
+ verifiedVia: assertion.issuer
3320
+ });
3321
+ }
3322
+ if (!exists) {
3323
+ const domains = await fetch(`${cloud}/v2/workspaces/root/domains`).then(async (r) => {
3324
+ if (!r.ok) throw new Error(`root law discovery failed (${r.status})`);
3325
+ return await r.json();
3326
+ });
3327
+ const frameworkHash = domains.currentLaw?.["nomos"];
3328
+ const lawHash = domains.currentLaw?.["workspaces"];
3329
+ if (!frameworkHash || !lawHash) throw new Error("root does not report the framework and governance laws needed for birth");
3330
+ const birthCert = await root.certSign({
3331
+ child: home,
3332
+ founder: actor,
3333
+ verified: true,
3334
+ parentKey: device.public,
3335
+ parentId: "root",
3336
+ epoch: 0,
3337
+ secret: device.secret
3338
+ });
3339
+ const now = Date.now();
3340
+ const birth = await fetch(`${cloud}/v2/workspaces/root/workspaces/birthChild`, {
3341
+ method: "POST",
3342
+ headers: { "content-type": "application/json" },
3343
+ body: JSON.stringify({
3344
+ name: home,
3345
+ parent: "",
3346
+ principal: bare,
3347
+ tier: "edge",
3348
+ requestedBy: bare,
3349
+ requestedAt: now,
3350
+ foundingAdminHash: device.keyHash,
3351
+ bornAt: new Date(now).toISOString(),
3352
+ frameworkHash,
3353
+ lawHash,
3354
+ platformCert: JSON.stringify(birthCert),
3355
+ actor,
3356
+ authorSecret: device.secret
3357
+ })
3358
+ });
3359
+ const body = await birth.json().catch(() => null);
3360
+ const failed = body?.birthOutcomes?.find((o) => o.status !== "born");
3361
+ if (!birth.ok || body?.ok === false || failed) {
3362
+ throw new Error(
3363
+ `Nomos could not birth the session home (${birth.status}): ${failed?.reason ?? String(body?.error ?? JSON.stringify(body))}`
3364
+ );
3365
+ }
3366
+ const deadline = Date.now() + 3e4;
3367
+ while (Date.now() < deadline) {
3368
+ if (await fetch(`${cloud}/v2/workspaces/${encodeURIComponent(home)}`).then((r) => r.ok).catch(() => false)) break;
3369
+ await new Promise((resolve6) => setTimeout(resolve6, 250));
3370
+ }
3371
+ if (!await fetch(`${cloud}/v2/workspaces/${encodeURIComponent(home)}`).then((r) => r.ok).catch(() => false)) {
3372
+ throw new Error(`Nomos admitted the birth of '${home}' but its custody did not become readable`);
3373
+ }
3374
+ const admin = await signAndPostOffer(
3375
+ home,
3376
+ "workspaces",
3377
+ "grantAdmin",
3378
+ { principal: bare, label: "founding admin", grantedBy: bare, grantedAt: now },
3379
+ { ...opts, principal: bare }
3380
+ );
3381
+ if (!admin.ok) throw new Error(`the born home refused its founding admin (${admin.status}): ${JSON.stringify(admin.body)}`);
3382
+ }
3383
+ const homeHolon = await connectGovernance(cloud, home);
3384
+ const homeSigner = typeof homeHolon.parentSignerEnrollment === "function" ? await homeHolon.parentSignerEnrollment({ parent: home, principal: actor, keyHash: device.keyHash, authToken: auth }).catch(() => null) : null;
3385
+ if (!homeSigner?.verifiedVia) {
3386
+ if (typeof homeHolon.enrollFirstDevice !== "function") {
3387
+ throw new Error("the installed @githolon/client predates verified session-home enrollment");
3388
+ }
3389
+ const assertion = await exchangeOnboardingToken(auth, device.keyHash);
3390
+ if (assertion.subject !== bare) {
3391
+ throw new Error(`identity provider returned '${assertion.subject}' for logged-in principal '${bare}'`);
3392
+ }
3393
+ const domain = await discoverSigningDomain(homeHolon, home, "enrollFirstDevice", "workspaces");
3394
+ await homeHolon.enrollFirstDevice({
3395
+ parent: home,
3396
+ domain,
3397
+ principal: actor,
3398
+ device,
3399
+ idToken: assertion.token,
3400
+ authToken: assertion.token,
3401
+ verifiedVia: assertion.issuer,
3402
+ includeDelegation: false
3403
+ });
3404
+ const object = `identity:${actor}`;
3405
+ const deadline = Date.now() + 3e4;
3406
+ let visible = false;
3407
+ while (Date.now() < deadline) {
3408
+ visible = await fetch(
3409
+ `${cloud}/v2/workspaces/${encodeURIComponent(home)}/workspaces/relationTuplesByObject?object=${encodeURIComponent(object)}`
3410
+ ).then(async (r) => {
3411
+ if (!r.ok) return false;
3412
+ const body = await r.json();
3413
+ const leaf = body.rows?.[0]?.data?.subjects?.[`key:${device.keyHash}`];
3414
+ return typeof leaf?.verifiedVia === "string" && leaf.verifiedVia.length > 0;
3415
+ }).catch(() => false);
3416
+ if (visible) break;
3417
+ await new Promise((resolve6) => setTimeout(resolve6, 250));
3418
+ }
3419
+ if (!visible) throw new Error(`the session home's verified device enrollment did not become readable`);
3420
+ }
3421
+ return { home, principal: actor, device, created: !exists };
3422
+ }
3423
+ async function signAndPostWorkspaceRetirement(parent, workspaceId, creator, opts) {
3424
+ return signAndPostPreparedOffer(
3425
+ parent,
3426
+ "workspaces",
3427
+ (actor) => ({
3428
+ directiveId: creator !== "" && sameIdentity(creator, actor) ? "retireOwnWorkspace" : "retireWorkspace",
3429
+ payload: { workspaceId, retiredBy: lineageIdentity(actor), retiredAt: Date.now() }
3430
+ }),
3431
+ opts,
3432
+ "self-if-unenrolled",
3433
+ creator
3434
+ );
3220
3435
  }
3221
3436
  async function relaySealedOfferViaStage(cloud, ws, directiveId, intentBytesB64, fetchImpl) {
3222
3437
  const raw = Buffer.from(intentBytesB64, "base64");
@@ -4517,13 +4732,16 @@ function readSealedIntentBytes(eng, ws, intentOut) {
4517
4732
  if (f?.data === void 0) return void 0;
4518
4733
  return Buffer.from(f.data).toString("base64");
4519
4734
  }
4520
- var out2, err2, STAGE_THRESHOLD_CHARS, HONEST_CEILING, ROOT_CUSTODIAN_SECRET, CLIENT_IMPACT;
4735
+ var out2, err2, lineageIdentity, sameIdentity, DEFAULT_ONBOARDING_IDP, STAGE_THRESHOLD_CHARS, HONEST_CEILING, ROOT_CUSTODIAN_SECRET, CLIENT_IMPACT;
4521
4736
  var init_governance = __esm({
4522
4737
  "src/governance.ts"() {
4523
4738
  "use strict";
4524
4739
  init_cloud();
4525
4740
  out2 = (s) => void process.stdout.write(s + "\n");
4526
4741
  err2 = (s) => void process.stderr.write("error: " + s + "\n");
4742
+ lineageIdentity = (actor) => actor.startsWith("user:") ? actor.slice("user:".length) : actor;
4743
+ sameIdentity = (a, b) => lineageIdentity(a) === lineageIdentity(b);
4744
+ DEFAULT_ONBOARDING_IDP = "https://id.nomos.captainapp.co.uk";
4527
4745
  STAGE_THRESHOLD_CHARS = 4 * 1024 * 1024;
4528
4746
  HONEST_CEILING = `The operator controls root's law and could deploy law removing the consent check \u2014 but that act is itself a signed, ordered, permanent fact in root's chain, visible to every tenant running \`replay root\`, and grounds for exit-with-everything. Not "we can't" \u2014 "we can't secretly, and you can always leave."`;
4529
4747
  ROOT_CUSTODIAN_SECRET = "nomos-root-custodian-key";
@@ -4948,6 +5166,7 @@ var init_lifecycle = __esm({
4948
5166
  var cloud_exports = {};
4949
5167
  __export(cloud_exports, {
4950
5168
  activePrincipal: () => activePrincipal,
5169
+ advertisedMainHead: () => advertisedMainHead,
4951
5170
  authUse: () => authUse,
4952
5171
  clearActAs: () => clearActAs,
4953
5172
  cloudBase: () => cloudBase,
@@ -4969,6 +5188,7 @@ __export(cloud_exports, {
4969
5188
  setDeviceKey: () => setDeviceKey,
4970
5189
  setPrincipal: () => setPrincipal,
4971
5190
  setSecret: () => setSecret,
5191
+ waitForDurableMainAdvance: () => waitForDurableMainAdvance,
4972
5192
  whoami: () => whoami,
4973
5193
  wsCreate: () => wsCreate,
4974
5194
  wsReclaim: () => wsReclaim,
@@ -5181,16 +5401,10 @@ async function logout() {
5181
5401
  async function wsCreate(name, opts) {
5182
5402
  const cloud = cloudBase(opts.cloud);
5183
5403
  if (opts.via !== void 0) {
5184
- const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5404
+ const { signAndPostWorkspaceBirth: signAndPostWorkspaceBirth2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5185
5405
  let v;
5186
5406
  try {
5187
- const principal2 = resolveGovernancePrincipal2(opts);
5188
- v = await signAndPostGovernance2(
5189
- opts.via,
5190
- "createWorkspace",
5191
- { name, tier: "edge", parent: opts.via === "root" ? "" : opts.via, principal: principal2, requestedBy: principal2, requestedAt: Date.now() },
5192
- opts
5193
- );
5407
+ v = await signAndPostWorkspaceBirth2(opts.via, name, opts);
5194
5408
  } catch (e) {
5195
5409
  err4(e.message);
5196
5410
  return 1;
@@ -5259,7 +5473,7 @@ async function wsStatus(name, opts) {
5259
5473
  }
5260
5474
  async function wsRetire(ws, opts) {
5261
5475
  const cloud = cloudBase(opts.cloud);
5262
- const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5476
+ const { signAndPostWorkspaceRetirement: signAndPostWorkspaceRetirement2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
5263
5477
  const parent = opts.parent ?? "root";
5264
5478
  let record;
5265
5479
  try {
@@ -5280,12 +5494,8 @@ async function wsRetire(ws, opts) {
5280
5494
  }
5281
5495
  let v;
5282
5496
  try {
5283
- const principal = resolveGovernancePrincipal2(opts);
5284
- const payload = { workspaceId: recordId, retiredBy: principal, retiredAt: Date.now() };
5285
- const bare = (s) => s.startsWith("user:") ? s.slice(5) : s;
5286
5497
  const creator = record?.data?.createdBy ?? record?.data?.principal ?? "";
5287
- const own = creator !== "" && bare(creator) === bare(principal);
5288
- v = await signAndPostGovernance2(parent, own ? "retireOwnWorkspace" : "retireWorkspace", payload, opts);
5498
+ v = await signAndPostWorkspaceRetirement2(parent, recordId, creator, opts);
5289
5499
  } catch (e) {
5290
5500
  err4(e.message);
5291
5501
  return 1;
@@ -5332,6 +5542,24 @@ async function wsReclaim(ws, opts) {
5332
5542
  out4(" the custodian drops the ledger bytes on seeing this fact folded; the reclaimed FACT is append-only");
5333
5543
  return 0;
5334
5544
  }
5545
+ function advertisedMainHead(text) {
5546
+ return /([0-9a-f]{40}) refs\/heads\/main\b/.exec(text)?.[1];
5547
+ }
5548
+ async function durableMainHead(cloud, ws, fetchImpl = fetch) {
5549
+ return fetchImpl(`${cloud}/v2/workspaces/${encodeURIComponent(ws)}/git/info/refs?service=git-upload-pack`).then(async (r) => r.ok ? advertisedMainHead(await r.text()) : void 0).catch(() => void 0);
5550
+ }
5551
+ async function waitForDurableMainAdvance(cloud, ws, previousHead, opts = {}) {
5552
+ const timeoutMs = Math.max(100, Number(opts.timeoutMs) || 3e4);
5553
+ const pollMs = Math.max(1, Number(opts.pollMs) || 250);
5554
+ const fetchImpl = opts.fetchImpl ?? fetch;
5555
+ const deadline = Date.now() + timeoutMs;
5556
+ while (Date.now() < deadline) {
5557
+ const head = await durableMainHead(cloud, ws, fetchImpl);
5558
+ if (head !== void 0 && head !== previousHead) return head;
5559
+ await new Promise((resolve6) => setTimeout(resolve6, pollMs));
5560
+ }
5561
+ return void 0;
5562
+ }
5335
5563
  async function deploy2(ws, opts) {
5336
5564
  const cloud = cloudBase(opts.cloud);
5337
5565
  let file;
@@ -5345,6 +5573,7 @@ async function deploy2(ws, opts) {
5345
5573
  const beforeLaw = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd : {}).catch(() => ({}));
5346
5574
  const before = beforeLaw.domains ?? [];
5347
5575
  const currentLaw2 = beforeLaw.currentLaw ?? {};
5576
+ const beforeDurableHead = await durableMainHead(cloud, ws);
5348
5577
  try {
5349
5578
  const compat = JSON.parse(readFileSync5(file, "utf8")).nomosCompat;
5350
5579
  const requires = compat?.requiresKernel ?? [];
@@ -5469,6 +5698,15 @@ async function deploy2(ws, opts) {
5469
5698
  }
5470
5699
  };
5471
5700
  if (deployedHash !== void 0 && isCurrent) {
5701
+ const wasCurrentBefore = Object.values(currentLaw2).includes(deployedHash);
5702
+ if (!wasCurrentBefore && beforeDurableHead !== void 0) {
5703
+ const durableHead = await waitForDurableMainAdvance(cloud, ws, beforeDurableHead);
5704
+ if (durableHead === void 0) {
5705
+ err4(`deploy '${ws}' was admitted, but canonical Git main did not advance within 30000ms`);
5706
+ err4(` Nomos will not start application code against a law that fresh peers cannot clone yet; retry the deploy.`);
5707
+ return 2;
5708
+ }
5709
+ }
5472
5710
  out4(`\u2713 deployed ${fileName} \u2192 ${ws}${phaseSuffix}`);
5473
5711
  out4(` law ${describeLawMove(before, deployedHash)}`);
5474
5712
  emitPlan();
@@ -8840,9 +9078,9 @@ var HELP = {
8840
9078
  examples: ["githolon compile", "githolon compile nomos.package.mjs --layered"]
8841
9079
  },
8842
9080
  proof: {
8843
- usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live] [--keep]",
9081
+ usage: "githolon proof [build/<name>.proof.mts] [--domain <key>] [--live]",
8844
9082
  what: "Run the proof GENERATED from your business law. By default it drives the real local engine:\nbirth, full-history verification, durable custody, eviction, restore, and read-back. --live runs the\nsame business proof against Nomos Cloud; identity, provisioning, persistence, and cleanup remain\ninside Nomos. --domain selects one domain in a multi-domain package. ALL GREEN or it names the jam.",
8845
- examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live", "githolon proof --live --keep"]
9083
+ examples: ["githolon proof", "githolon proof --domain co2_platform", "githolon proof --live"]
8846
9084
  },
8847
9085
  dev: {
8848
9086
  usage: "githolon dev [--port <n>] [--cloud <url>] [--once]",
@@ -9706,8 +9944,7 @@ async function runProof(args) {
9706
9944
  return 1;
9707
9945
  };
9708
9946
  const live = args.includes("--live");
9709
- const keep = args.includes("--keep");
9710
- if (keep && !live) return refuse("--keep only applies to --live (the offline proof creates no cloud workspace to keep)");
9947
+ if (args.includes("--keep")) return refuse("--keep no longer applies: proof --live uses your durable signed Nomos home and creates only the requested business aggregate");
9711
9948
  let domain;
9712
9949
  let domainValueIdx = -1;
9713
9950
  const di = args.findIndex((a) => a === "--domain" || a.startsWith("--domain="));
@@ -9774,54 +10011,40 @@ async function runProof(args) {
9774
10011
  const tsxPkg = JSON.parse(readFileSync16(join17(tsxDir, "package.json"), "utf8"));
9775
10012
  const tsxBinRel = typeof tsxPkg.bin === "string" ? tsxPkg.bin : tsxPkg.bin?.["tsx"];
9776
10013
  const tsxCli = join17(tsxDir, tsxBinRel ?? "dist/cli.mjs");
9777
- console.log("githolon proof --live \u2014 the generated business proof against Nomos Cloud" + (keep ? " (--keep preserves its temporary custody)" : ""));
9778
- const { wsCreate: wsCreate2, wsRetire: wsRetire2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
9779
- const { resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
9780
- const { login: login2, activePrincipal: activePrincipal2 } = await Promise.resolve().then(() => (init_cloud(), cloud_exports));
9781
- let principal = process.env.NOMOS_PRINCIPAL;
9782
- try {
9783
- principal ??= resolveGovernancePrincipal2({});
9784
- } catch {
9785
- const lr = await login2({ agent: true });
9786
- principal = lr === 0 ? activePrincipal2() : void 0;
10014
+ console.log("githolon proof --live \u2014 one generated app.birth against Nomos Cloud");
10015
+ const { ensureSovereignHome: ensureSovereignHome2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
10016
+ if (currentPrincipal() === void 0) {
10017
+ const lr = await login({ agent: true });
10018
+ if (lr !== 0) {
10019
+ return refuse("proof --live needs a login \u2014 githolon login --agent failed; try it directly");
10020
+ }
9787
10021
  }
9788
- if (principal === void 0) {
10022
+ if (currentPrincipal() === void 0) {
9789
10023
  return refuse("proof --live needs a principal \u2014 githolon login --agent (self-onboarding) failed; try it directly or pass NOMOS_PRINCIPAL");
9790
10024
  }
9791
- const proofSlug = proofPath.split("/").pop().replace(/\.proof\.mts$/, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
9792
- const ws = process.env.NOMOS_WS ?? `${proofSlug}-proof-${Math.random().toString(36).slice(2, 8)}`;
9793
- if (process.env.NOMOS_WS === void 0) {
9794
- const cr = await wsCreate2(ws, { via: "root", principal });
9795
- if (cr !== 0) {
9796
- return refuse(
9797
- `proof --live: Nomos could not establish safe custody for birth as ${principal}. This is a framework failure: no manual grant, key, parent, or provisioning step is part of the application contract. The local business proof remains available with: githolon proof`
9798
- );
9799
- }
10025
+ let home;
10026
+ try {
10027
+ home = await ensureSovereignHome2();
10028
+ } catch (e) {
10029
+ return refuse(
10030
+ `proof --live: Nomos could not establish the signed session home: ${e.message}. No certificate, signer, parent, or provisioning step is part of the application contract.`
10031
+ );
9800
10032
  }
10033
+ const barePrincipal = home.principal.startsWith("user:") ? home.principal.slice("user:".length) : home.principal;
10034
+ const deployed = await deploy2(home.home, { principal: barePrincipal });
10035
+ if (deployed !== 0) return refuse("proof --live: Nomos could not install the compiled business law into the signed session home");
9801
10036
  const r = spawnSync6(process.execPath, [tsxCli, proofPath], {
9802
10037
  stdio: "inherit",
9803
10038
  cwd: process.cwd(),
9804
10039
  env: {
9805
10040
  ...process.env,
9806
- NOMOS_WS: ws,
9807
- NOMOS_PRINCIPAL: principal,
9808
- NOMOS_PROOF_MANAGED: "1",
9809
- // the proof skips its own (dead) cleanup lane; the CLI retires below
9810
- ...keep ? { PROOF_KEEP: "1" } : {}
10041
+ NOMOS_WS: home.home,
10042
+ NOMOS_PRINCIPAL: home.principal,
10043
+ NOMOS_AUTHOR_SECRET: home.device.secret,
10044
+ NOMOS_PROOF_MANAGED: "1"
9811
10045
  }
9812
10046
  });
9813
- const proofStatus = r.status ?? 1;
9814
- if (keep) {
9815
- console.log(`workspace ${ws} KEPT (--keep) \u2014 retire it when done: githolon ws retire ${ws}`);
9816
- return proofStatus;
9817
- }
9818
- const rr = await wsRetire2(ws, { principal }).catch(() => 1);
9819
- if (rr !== 0) {
9820
- console.error(`\u26A0 THROWAWAY WORKSPACE NOT RETIRED: ${ws}`);
9821
- console.error(`\u26A0 retire it manually: githolon ws retire ${ws}`);
9822
- console.error(`\u26A0 (if it has no root registry row it was born unregistered and needs a custodian sweep)`);
9823
- }
9824
- return proofStatus;
10047
+ return r.status ?? 1;
9825
10048
  }
9826
10049
  function parseArgs(argv) {
9827
10050
  const [command, ...rest] = argv;
@@ -1851,7 +1851,21 @@ function resolveGatewayOperation(spec, opName, method) {
1851
1851
  const exactPath = `/${String(opName).replace(/^\/+/, "")}`;
1852
1852
  const exact = spec.paths[exactPath]?.[normalizedMethod];
1853
1853
  if (exact) return { op: exact, path: exactPath, ambiguous: null };
1854
- if (String(opName).includes("/")) return { op: null };
1854
+ const slash = String(opName).indexOf("/");
1855
+ if (slash > 0) {
1856
+ const requestedDomain = String(opName).slice(0, slash);
1857
+ const requestedAlias = String(opName).slice(slash + 1);
1858
+ const matches2 = [];
1859
+ for (const [path2, methods] of Object.entries(spec.paths)) {
1860
+ const op = methods?.[normalizedMethod];
1861
+ const x = op?.["x-nomos"];
1862
+ if (!op || !x || x.domain !== requestedDomain) continue;
1863
+ if (aliasIdsFor(x, path2).has(requestedAlias)) matches2.push({ op, path: path2 });
1864
+ }
1865
+ if (matches2.length === 1) return { ...matches2[0], ambiguous: null };
1866
+ if (matches2.length > 1) return { op: null, ambiguous: matches2.map((m) => m.path).sort() };
1867
+ return { op: null, ambiguous: null };
1868
+ }
1855
1869
  const matches = [];
1856
1870
  for (const [path2, methods] of Object.entries(spec.paths)) {
1857
1871
  const op = methods?.[normalizedMethod];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "githolon",
3
- "version": "0.100.0",
3
+ "version": "0.100.2",
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.100.0",
33
- "@githolon/dsl": "^0.100.0",
32
+ "@githolon/client": "^0.100.2",
33
+ "@githolon/dsl": "^0.100.2",
34
34
  "isomorphic-git": "^1.38.4"
35
35
  },
36
36
  "devDependencies": {