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 +287 -64
- package/dist/dev-server.mjs +15 -1
- package/package.json +3 -3
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
|
|
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 {
|
|
5404
|
+
const { signAndPostWorkspaceBirth: signAndPostWorkspaceBirth2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
5185
5405
|
let v;
|
|
5186
5406
|
try {
|
|
5187
|
-
|
|
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 {
|
|
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
|
-
|
|
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]
|
|
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"
|
|
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
|
-
|
|
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
|
|
9778
|
-
const {
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
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 (
|
|
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
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
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:
|
|
9807
|
-
NOMOS_PRINCIPAL: principal,
|
|
9808
|
-
|
|
9809
|
-
|
|
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
|
-
|
|
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;
|
package/dist/dev-server.mjs
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
33
|
-
"@githolon/dsl": "^0.100.
|
|
32
|
+
"@githolon/client": "^0.100.2",
|
|
33
|
+
"@githolon/dsl": "^0.100.2",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|