githolon 0.77.1 → 0.79.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.
- package/dist/cli.mjs +111 -16
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -3010,8 +3010,10 @@ __export(governance_exports, {
|
|
|
3010
3010
|
STAGE_THRESHOLD_CHARS: () => STAGE_THRESHOLD_CHARS,
|
|
3011
3011
|
buildCertificate: () => buildCertificate,
|
|
3012
3012
|
buildRebirthPlan: () => buildRebirthPlan,
|
|
3013
|
+
canonicalPrincipal: () => canonicalPrincipal,
|
|
3013
3014
|
certifyEra: () => certifyEra,
|
|
3014
3015
|
consentRebirth: () => consentRebirth,
|
|
3016
|
+
discoverSigningDomain: () => discoverSigningDomain,
|
|
3015
3017
|
executeRepairRebirth: () => executeRepairRebirth,
|
|
3016
3018
|
governanceOffer: () => governanceOffer,
|
|
3017
3019
|
governanceRelay: () => governanceRelay,
|
|
@@ -3044,6 +3046,33 @@ async function clientConnect() {
|
|
|
3044
3046
|
const mod = await import("@githolon/client");
|
|
3045
3047
|
return mod.connect;
|
|
3046
3048
|
}
|
|
3049
|
+
async function discoverSigningDomain(holon, parent, directiveId, explicit) {
|
|
3050
|
+
if (explicit !== void 0 && explicit !== "") return explicit;
|
|
3051
|
+
try {
|
|
3052
|
+
const offer = typeof holon.interfaceOffer === "function" ? await holon.interfaceOffer() : null;
|
|
3053
|
+
const declaring = [];
|
|
3054
|
+
for (const [domain, iface] of Object.entries(offer ?? {})) {
|
|
3055
|
+
if (iface?.directives !== void 0 && Object.prototype.hasOwnProperty.call(iface.directives, directiveId)) declaring.push(domain);
|
|
3056
|
+
}
|
|
3057
|
+
if (declaring.length === 1) return declaring[0];
|
|
3058
|
+
if (declaring.length > 1) {
|
|
3059
|
+
throw new Error(
|
|
3060
|
+
`'${directiveId}' is declared by ${declaring.length} installed domains on ${parent} (${declaring.join(", ")}) \u2014 pass --domain <one of them>`
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
} catch (e) {
|
|
3064
|
+
if (/pass --domain/.test(e.message)) throw e;
|
|
3065
|
+
}
|
|
3066
|
+
if (typeof holon.discoverDirectiveDomain === "function") {
|
|
3067
|
+
try {
|
|
3068
|
+
const r = await holon.discoverDirectiveDomain({ ws: parent, directiveId });
|
|
3069
|
+
return r.domain;
|
|
3070
|
+
} catch (e) {
|
|
3071
|
+
throw new Error(`${e.message} (override with --domain <d> [--directive <id>])`);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
return "workspaces";
|
|
3075
|
+
}
|
|
3047
3076
|
async function connectGovernance(cloud, parent) {
|
|
3048
3077
|
const authToken = await sessionToken().catch(() => void 0);
|
|
3049
3078
|
const clientId = `githolon-cli-${Date.now().toString(16)}`;
|
|
@@ -3076,17 +3105,23 @@ function resolveGovernancePrincipal(opts) {
|
|
|
3076
3105
|
}
|
|
3077
3106
|
return principal;
|
|
3078
3107
|
}
|
|
3108
|
+
function canonicalPrincipal(principal) {
|
|
3109
|
+
const s = String(principal ?? "").trim();
|
|
3110
|
+
if (s === "") return "";
|
|
3111
|
+
return s.includes(":") ? s : `user:${s}`;
|
|
3112
|
+
}
|
|
3079
3113
|
async function signAndPostOffer(ws, domain, directiveId, payload, opts) {
|
|
3080
3114
|
const cloud = cloudBase(opts.cloud);
|
|
3081
3115
|
const principal = resolveGovernancePrincipal(opts);
|
|
3082
3116
|
const holon = await connectGovernance(cloud, ws);
|
|
3117
|
+
const resolvedDomain = domain ?? await discoverSigningDomain(holon, ws, directiveId, void 0);
|
|
3083
3118
|
const device = await ensureDeviceKey(holon, principal);
|
|
3084
3119
|
const sealed = await holon.signGovernanceOffer({
|
|
3085
|
-
domain,
|
|
3120
|
+
domain: resolvedDomain,
|
|
3086
3121
|
directiveId,
|
|
3087
3122
|
payload,
|
|
3088
3123
|
authorSecret: device.secret,
|
|
3089
|
-
actor:
|
|
3124
|
+
actor: canonicalPrincipal(principal)
|
|
3090
3125
|
});
|
|
3091
3126
|
if (sealed.intentBytes.length > STAGE_THRESHOLD_CHARS) {
|
|
3092
3127
|
const staged = await relaySealedOfferViaStage(cloud, ws, directiveId, sealed.intentBytes, fetch);
|
|
@@ -3124,7 +3159,7 @@ async function relaySealedOfferViaStage(cloud, ws, directiveId, intentBytesB64,
|
|
|
3124
3159
|
return { sha256: sha2562, size: raw.length, verdict: { ok: r.ok, status: r.status, body } };
|
|
3125
3160
|
}
|
|
3126
3161
|
async function signAndPostGovernance(parent, directiveId, payload, opts) {
|
|
3127
|
-
return signAndPostOffer(parent,
|
|
3162
|
+
return signAndPostOffer(parent, opts.domain, directiveId, payload, opts);
|
|
3128
3163
|
}
|
|
3129
3164
|
async function grantOrRevoke(verb, kind, subject, opts) {
|
|
3130
3165
|
const parent = opts.parent ?? "root";
|
|
@@ -3307,20 +3342,29 @@ async function keyEnroll(opts) {
|
|
|
3307
3342
|
err2(`could not connect to ${parent} on ${cloud} to enroll: ${e.message}`);
|
|
3308
3343
|
return 1;
|
|
3309
3344
|
}
|
|
3345
|
+
const directiveId = opts.directive ?? "enrollSigner";
|
|
3346
|
+
let domain;
|
|
3347
|
+
try {
|
|
3348
|
+
domain = await discoverSigningDomain(holon, parent, directiveId, opts.domain);
|
|
3349
|
+
} catch (e) {
|
|
3350
|
+
err2(`could not resolve the signing directive on ${parent}: ${e.message}`);
|
|
3351
|
+
return 1;
|
|
3352
|
+
}
|
|
3310
3353
|
const device = await ensureDeviceKey(holon, principal);
|
|
3311
3354
|
const enrollPayload = await holon.enrollSignerPayload({ principal, publicKey: device.public });
|
|
3312
3355
|
const sealed = await holon.signGovernanceOffer({
|
|
3313
|
-
|
|
3356
|
+
domain,
|
|
3357
|
+
directiveId,
|
|
3314
3358
|
payload: enrollPayload,
|
|
3315
3359
|
authorSecret: device.secret,
|
|
3316
|
-
actor:
|
|
3360
|
+
actor: canonicalPrincipal(principal)
|
|
3317
3361
|
});
|
|
3318
|
-
const v = await holon.postGovernanceOffer({ directiveId
|
|
3362
|
+
const v = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws: parent });
|
|
3319
3363
|
if (!v.ok) {
|
|
3320
|
-
err2(
|
|
3364
|
+
err2(`${domain}/${directiveId} refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
3321
3365
|
return 1;
|
|
3322
3366
|
}
|
|
3323
|
-
out2(`\u2713 device key enrolled for ${principal} at ${parent} on ${cloud}`);
|
|
3367
|
+
out2(`\u2713 device key enrolled for ${principal} at ${parent} on ${cloud} (via ${domain}/${directiveId})`);
|
|
3324
3368
|
out2(` keyHash ${device.keyHash.slice(0, 16)}\u2026 (secret stays local; signed governance is now warranted)`);
|
|
3325
3369
|
return 0;
|
|
3326
3370
|
}
|
|
@@ -3444,8 +3488,9 @@ async function governanceOffer(ws, directiveId, opts) {
|
|
|
3444
3488
|
if (opts.save !== void 0) {
|
|
3445
3489
|
try {
|
|
3446
3490
|
const holon = await connectGovernance(cloudBase(opts.cloud), ws);
|
|
3491
|
+
const domain = await discoverSigningDomain(holon, ws, directiveId, opts.domain);
|
|
3447
3492
|
const device = await ensureDeviceKey(holon, principal);
|
|
3448
|
-
const sealed = await holon.signGovernanceOffer({ directiveId, payload, authorSecret: device.secret, actor:
|
|
3493
|
+
const sealed = await holon.signGovernanceOffer({ domain, directiveId, payload, authorSecret: device.secret, actor: canonicalPrincipal(principal) });
|
|
3449
3494
|
writeFileSync4(opts.save, JSON.stringify({ ws, directiveId, principal, intentBytes: sealed.intentBytes }, null, 2) + "\n");
|
|
3450
3495
|
} catch (e) {
|
|
3451
3496
|
err2(e.message);
|
|
@@ -3727,6 +3772,28 @@ async function buildRebirthPlan(cloud, ws, reason) {
|
|
|
3727
3772
|
}
|
|
3728
3773
|
} catch {
|
|
3729
3774
|
}
|
|
3775
|
+
const signerTuplesRaw = [];
|
|
3776
|
+
try {
|
|
3777
|
+
const rows2 = query2(eng, ws, "aggregatesByType", JSON.stringify({ __type: "RelationTuple" }));
|
|
3778
|
+
for (const r of rows2) {
|
|
3779
|
+
const data = r?.data ?? {};
|
|
3780
|
+
if (data.relation !== "signer" || typeof data.object !== "string" || !data.object.startsWith("identity:")) continue;
|
|
3781
|
+
const principal = data.object.slice("identity:".length);
|
|
3782
|
+
for (const [subject, leafRaw] of Object.entries(data.subjects ?? {})) {
|
|
3783
|
+
if (!subject.startsWith("key:")) continue;
|
|
3784
|
+
let status2 = leafRaw;
|
|
3785
|
+
if (typeof leafRaw === "string") {
|
|
3786
|
+
try {
|
|
3787
|
+
status2 = JSON.parse(leafRaw).status;
|
|
3788
|
+
} catch {
|
|
3789
|
+
status2 = leafRaw;
|
|
3790
|
+
}
|
|
3791
|
+
} else if (leafRaw !== null && typeof leafRaw === "object") status2 = leafRaw.status;
|
|
3792
|
+
signerTuplesRaw.push({ principal, keyHash: subject.slice(4), status: status2 === "revoked" ? "revoked" : "active" });
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
} catch {
|
|
3796
|
+
}
|
|
3730
3797
|
let compaction = { carried: null, parked: null, scarShape: "unknown" };
|
|
3731
3798
|
let parkedAllLawInstalls = false;
|
|
3732
3799
|
try {
|
|
@@ -3793,6 +3860,12 @@ async function buildRebirthPlan(cloud, ws, reason) {
|
|
|
3793
3860
|
compaction,
|
|
3794
3861
|
repairs,
|
|
3795
3862
|
posture: { posture, consent: consentState },
|
|
3863
|
+
...signerTuplesRaw.length > 0 ? {
|
|
3864
|
+
signerEnrollments: signerTuplesRaw.map((t) => ({
|
|
3865
|
+
...t,
|
|
3866
|
+
recovery: mode === "state-seed" ? "re-enroll-required" : "carried-by-replay"
|
|
3867
|
+
}))
|
|
3868
|
+
} : {},
|
|
3796
3869
|
clientImpact: CLIENT_IMPACT,
|
|
3797
3870
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3798
3871
|
};
|
|
@@ -3849,6 +3922,23 @@ function renderRebirthImpact(plan, ledgerCommits) {
|
|
|
3849
3922
|
L.push(" \u2192 preserved by lineage continuity; their birth-cert chains stay valid (same founding key + self-id).");
|
|
3850
3923
|
}
|
|
3851
3924
|
L.push("");
|
|
3925
|
+
const se = plan.signerEnrollments ?? [];
|
|
3926
|
+
L.push(`SIGNER ENROLLMENTS (${se.length})`);
|
|
3927
|
+
if (se.length === 0) L.push(" (none on this chain \u2014 no device\u2192principal bindings to recover)");
|
|
3928
|
+
else {
|
|
3929
|
+
for (const s of se) {
|
|
3930
|
+
L.push(` \u2022 identity:${s.principal}#signer @ key:${s.keyHash.slice(0, 16)}\u2026 [${s.status}] \u2192 ${s.recovery}`);
|
|
3931
|
+
}
|
|
3932
|
+
if (se.some((s) => s.recovery === "re-enroll-required")) {
|
|
3933
|
+
L.push(" \u2192 a state-seed re-genesis does NOT carry these tuples: every device that authors against this");
|
|
3934
|
+
L.push(" workspace (e.g. for sibling births) must be lawfully re-enrolled AFTER the rebirth \u2014 client");
|
|
3935
|
+
L.push(" ensureParentSignerEnrollment / NomosScope.home's reopen repair / `githolon key enroll --parent <ws>`.");
|
|
3936
|
+
} else {
|
|
3937
|
+
L.push(" \u2192 replay-compact re-folds the enrolling intents; a PARKED enrolling intent would still strand its");
|
|
3938
|
+
L.push(" binding until re-admitted \u2014 verify with the parked list, or re-enroll idempotently (a no-op when live).");
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
L.push("");
|
|
3852
3942
|
L.push(`REPAIRS (${plan.repairs.length})`);
|
|
3853
3943
|
if (plan.repairs.length === 0) L.push(" (none matched \u2014 a plain compaction; no corrective verb declared for this refusal)");
|
|
3854
3944
|
else {
|
|
@@ -4051,7 +4141,7 @@ gov rebirth ${ws}:`);
|
|
|
4051
4141
|
return 1;
|
|
4052
4142
|
}
|
|
4053
4143
|
signSecret = dk.secret;
|
|
4054
|
-
actor =
|
|
4144
|
+
actor = canonicalPrincipal(actorUid);
|
|
4055
4145
|
}
|
|
4056
4146
|
const res = author2(eng, "root", "workspaces", "rebirthWorkspace", { workspaceId, reason, rebornAt }, controllerHash, { authorSecret: signSecret, actor });
|
|
4057
4147
|
if (res.ok === false) {
|
|
@@ -8283,6 +8373,8 @@ var HELP = {
|
|
|
8283
8373
|
["--sha256", "import: derive the seed as sha256(--secret) (a passphrase/preimage)"],
|
|
8284
8374
|
["--format <f>", "import: force hex|base64 interpretation (default: auto-detect)"],
|
|
8285
8375
|
["--parent <ws>", "enroll: the governance workspace to enroll at (default root)"],
|
|
8376
|
+
["--domain <d>", "enroll: the domain declaring the signing directive (default: DISCOVERED from the parent's installed law \u2014 never assume 'workspaces')"],
|
|
8377
|
+
["--directive <id>", "enroll: the signing directive id (default enrollSigner)"],
|
|
8286
8378
|
["--yes", "export: confirm printing the secret to stdout"]
|
|
8287
8379
|
],
|
|
8288
8380
|
examples: [
|
|
@@ -8294,8 +8386,8 @@ var HELP = {
|
|
|
8294
8386
|
]
|
|
8295
8387
|
},
|
|
8296
8388
|
gov: {
|
|
8297
|
-
usage: "githolon gov <ws> <directiveId> [--payload <json> | --payload-file <f>] [--as <uid>] [--stamp] [--save <f>] [--cloud <url>]\n githolon gov relay <ws> <offer-file> [--cloud <url>]",
|
|
8298
|
-
what: "Author ANY
|
|
8389
|
+
usage: "githolon gov <ws> <directiveId> [--domain <d>] [--payload <json> | --payload-file <f>] [--as <uid>] [--stamp] [--save <f>] [--cloud <url>]\n githolon gov relay <ws> <offer-file> [--cloud <url>]",
|
|
8390
|
+
what: "Author ANY governance directive as a SIGNED offer to <ws> and relay it \u2014 the generic lane. The\ndirective's DOMAIN is discovered from <ws>'s installed law (a tenant-composed domain like\n`co2_platform` routes correctly; --domain overrides; ambiguity names the candidates) \u2014 the generic\nlane the grant/ws/key verbs are sugar over. The directive is planned against <ws>'s OWN folded law\n(we connect + sign locally with your enrolled key; the host relays opaque {intentBytes} and the\nkernel re-verifies the signature). SIGNING IS LOCAL: with --save it writes the sealed offer WITHOUT\nrelaying \u2014 sign now, relay later/elsewhere with `gov relay` (the only step that touches the cloud).\nThe acting principal needs an enrolled signing key (githolon key enroll | key import).",
|
|
8299
8391
|
flags: [
|
|
8300
8392
|
["--payload <json>", `the directive payload as a JSON object string ('{"name":"\u2026"}')`],
|
|
8301
8393
|
["--payload-file <f>", "read the payload JSON from a file instead"],
|
|
@@ -8930,14 +9022,15 @@ Identity (~/.holon/credentials.json \u2014 sessions auto-refresh):
|
|
|
8930
9022
|
githolon use [<uid>] [--clear] set/show/clear the ACTIVE signing principal: the one
|
|
8931
9023
|
deploy/gov/grant/key author as (--as overrides per-cmd)
|
|
8932
9024
|
githolon logout clear the session (workspace secrets kept)
|
|
8933
|
-
githolon key enroll [--principal <uid>] [--parent <ws>] mint a signing device key + enroll it on-chain
|
|
9025
|
+
githolon key enroll [--principal <uid>] [--parent <ws>] [--domain <d>] [--directive <id>] mint a signing device key + enroll it on-chain (the signing directive is DISCOVERED from the parent's installed law; the flags override)
|
|
8934
9026
|
(secret stays local; warrants SIGNED governance)
|
|
8935
9027
|
githolon key import --principal <uid> --secret <key> bring an EXISTING admin key into ~/.holon (accepts
|
|
8936
9028
|
hex / base64 seed, or --sha256 <passphrase>)
|
|
8937
9029
|
githolon key show | export [--principal <uid>] the device key's public identity (export --yes: secret)
|
|
8938
9030
|
|
|
8939
9031
|
Governance (SIGNED client-side, relayed as {intentBytes} \u2014 never a host-author endpoint):
|
|
8940
|
-
githolon gov <ws> <directive> [--
|
|
9032
|
+
githolon gov <ws> <directive> [--domain <d>] author ANY governance directive (domain discovered
|
|
9033
|
+
[--payload <json>] from installed law; --domain overrides) as a signed offer
|
|
8941
9034
|
[--as <uid>] [--stamp] [--save <file>] to <ws> (--save signs without relaying; relay later)
|
|
8942
9035
|
githolon gov relay <ws> <offer-file> relay a saved offer (the only step that hits the cloud)
|
|
8943
9036
|
githolon gov rebirth <ws> --reason <text> LAWFUL SUPERSESSION (recovery verb 3): supersede a
|
|
@@ -8997,7 +9090,7 @@ Options:
|
|
|
8997
9090
|
function cloudArgs(rest) {
|
|
8998
9091
|
const pos = [];
|
|
8999
9092
|
const opts = {};
|
|
9000
|
-
const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out", "--reason", "--oid", "--seed-secret", "--gcp-secret", "--controller-hash", "--plan", "--depth"];
|
|
9093
|
+
const VALUE_FLAGS = ["--cloud", "--principal", "--as", "--file", "--target", "--parent", "--via", "--max", "--label", "--secret", "--payload", "--payload-file", "--save", "--format", "--with", "--out", "--reason", "--oid", "--seed-secret", "--gcp-secret", "--controller-hash", "--plan", "--depth", "--domain", "--directive"];
|
|
9001
9094
|
const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface", "--dry-run", "--json", "--full"];
|
|
9002
9095
|
for (let i = 0; i < rest.length; i++) {
|
|
9003
9096
|
const a = rest[i];
|
|
@@ -9027,6 +9120,8 @@ function cloudArgs(rest) {
|
|
|
9027
9120
|
else if (a === "--save") opts.save = v;
|
|
9028
9121
|
else if (a === "--format") opts.format = v;
|
|
9029
9122
|
else if (a === "--with") opts.with = v;
|
|
9123
|
+
else if (a === "--domain") opts.domain = v;
|
|
9124
|
+
else if (a === "--directive") opts.directive = v;
|
|
9030
9125
|
else if (a === "--out") opts.out = v;
|
|
9031
9126
|
else if (a === "--max") {
|
|
9032
9127
|
const n = Number(v);
|
|
@@ -9696,7 +9791,7 @@ ${commandHelp("gov") ?? USAGE}`);
|
|
|
9696
9791
|
}
|
|
9697
9792
|
const [ws, directiveId] = pos;
|
|
9698
9793
|
if (ws === void 0 || directiveId === void 0) {
|
|
9699
|
-
process.stderr.write(`error: expected: githolon gov <ws> <directiveId> [--payload <json>] [--as <uid>]
|
|
9794
|
+
process.stderr.write(`error: expected: githolon gov <ws> <directiveId> [--domain <d>] [--payload <json>] [--as <uid>]
|
|
9700
9795
|
|
|
9701
9796
|
${commandHelp("gov") ?? USAGE}`);
|
|
9702
9797
|
return 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.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.
|
|
33
|
-
"@githolon/dsl": "^0.
|
|
32
|
+
"@githolon/client": "^0.79.0",
|
|
33
|
+
"@githolon/dsl": "^0.79.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|