githolon 0.77.1 → 0.78.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 +92 -6
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -3012,6 +3012,7 @@ __export(governance_exports, {
|
|
|
3012
3012
|
buildRebirthPlan: () => buildRebirthPlan,
|
|
3013
3013
|
certifyEra: () => certifyEra,
|
|
3014
3014
|
consentRebirth: () => consentRebirth,
|
|
3015
|
+
discoverSigningDomain: () => discoverSigningDomain,
|
|
3015
3016
|
executeRepairRebirth: () => executeRepairRebirth,
|
|
3016
3017
|
governanceOffer: () => governanceOffer,
|
|
3017
3018
|
governanceRelay: () => governanceRelay,
|
|
@@ -3044,6 +3045,33 @@ async function clientConnect() {
|
|
|
3044
3045
|
const mod = await import("@githolon/client");
|
|
3045
3046
|
return mod.connect;
|
|
3046
3047
|
}
|
|
3048
|
+
async function discoverSigningDomain(holon, parent, directiveId, explicit) {
|
|
3049
|
+
if (explicit !== void 0 && explicit !== "") return explicit;
|
|
3050
|
+
try {
|
|
3051
|
+
const offer = typeof holon.interfaceOffer === "function" ? await holon.interfaceOffer() : null;
|
|
3052
|
+
const declaring = [];
|
|
3053
|
+
for (const [domain, iface] of Object.entries(offer ?? {})) {
|
|
3054
|
+
if (iface?.directives !== void 0 && Object.prototype.hasOwnProperty.call(iface.directives, directiveId)) declaring.push(domain);
|
|
3055
|
+
}
|
|
3056
|
+
if (declaring.length === 1) return declaring[0];
|
|
3057
|
+
if (declaring.length > 1) {
|
|
3058
|
+
throw new Error(
|
|
3059
|
+
`'${directiveId}' is declared by ${declaring.length} installed domains on ${parent} (${declaring.join(", ")}) \u2014 pass --domain <one of them>`
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
} catch (e) {
|
|
3063
|
+
if (/pass --domain/.test(e.message)) throw e;
|
|
3064
|
+
}
|
|
3065
|
+
if (typeof holon.discoverDirectiveDomain === "function") {
|
|
3066
|
+
try {
|
|
3067
|
+
const r = await holon.discoverDirectiveDomain({ ws: parent, directiveId });
|
|
3068
|
+
return r.domain;
|
|
3069
|
+
} catch (e) {
|
|
3070
|
+
throw new Error(`${e.message} (override with --domain <d> [--directive <id>])`);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
return "workspaces";
|
|
3074
|
+
}
|
|
3047
3075
|
async function connectGovernance(cloud, parent) {
|
|
3048
3076
|
const authToken = await sessionToken().catch(() => void 0);
|
|
3049
3077
|
const clientId = `githolon-cli-${Date.now().toString(16)}`;
|
|
@@ -3307,20 +3335,29 @@ async function keyEnroll(opts) {
|
|
|
3307
3335
|
err2(`could not connect to ${parent} on ${cloud} to enroll: ${e.message}`);
|
|
3308
3336
|
return 1;
|
|
3309
3337
|
}
|
|
3338
|
+
const directiveId = opts.directive ?? "enrollSigner";
|
|
3339
|
+
let domain;
|
|
3340
|
+
try {
|
|
3341
|
+
domain = await discoverSigningDomain(holon, parent, directiveId, opts.domain);
|
|
3342
|
+
} catch (e) {
|
|
3343
|
+
err2(`could not resolve the signing directive on ${parent}: ${e.message}`);
|
|
3344
|
+
return 1;
|
|
3345
|
+
}
|
|
3310
3346
|
const device = await ensureDeviceKey(holon, principal);
|
|
3311
3347
|
const enrollPayload = await holon.enrollSignerPayload({ principal, publicKey: device.public });
|
|
3312
3348
|
const sealed = await holon.signGovernanceOffer({
|
|
3313
|
-
|
|
3349
|
+
domain,
|
|
3350
|
+
directiveId,
|
|
3314
3351
|
payload: enrollPayload,
|
|
3315
3352
|
authorSecret: device.secret,
|
|
3316
3353
|
actor: `user:${principal}`
|
|
3317
3354
|
});
|
|
3318
|
-
const v = await holon.postGovernanceOffer({ directiveId
|
|
3355
|
+
const v = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws: parent });
|
|
3319
3356
|
if (!v.ok) {
|
|
3320
|
-
err2(
|
|
3357
|
+
err2(`${domain}/${directiveId} refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
3321
3358
|
return 1;
|
|
3322
3359
|
}
|
|
3323
|
-
out2(`\u2713 device key enrolled for ${principal} at ${parent} on ${cloud}`);
|
|
3360
|
+
out2(`\u2713 device key enrolled for ${principal} at ${parent} on ${cloud} (via ${domain}/${directiveId})`);
|
|
3324
3361
|
out2(` keyHash ${device.keyHash.slice(0, 16)}\u2026 (secret stays local; signed governance is now warranted)`);
|
|
3325
3362
|
return 0;
|
|
3326
3363
|
}
|
|
@@ -3727,6 +3764,28 @@ async function buildRebirthPlan(cloud, ws, reason) {
|
|
|
3727
3764
|
}
|
|
3728
3765
|
} catch {
|
|
3729
3766
|
}
|
|
3767
|
+
const signerTuplesRaw = [];
|
|
3768
|
+
try {
|
|
3769
|
+
const rows2 = query2(eng, ws, "aggregatesByType", JSON.stringify({ __type: "RelationTuple" }));
|
|
3770
|
+
for (const r of rows2) {
|
|
3771
|
+
const data = r?.data ?? {};
|
|
3772
|
+
if (data.relation !== "signer" || typeof data.object !== "string" || !data.object.startsWith("identity:")) continue;
|
|
3773
|
+
const principal = data.object.slice("identity:".length);
|
|
3774
|
+
for (const [subject, leafRaw] of Object.entries(data.subjects ?? {})) {
|
|
3775
|
+
if (!subject.startsWith("key:")) continue;
|
|
3776
|
+
let status2 = leafRaw;
|
|
3777
|
+
if (typeof leafRaw === "string") {
|
|
3778
|
+
try {
|
|
3779
|
+
status2 = JSON.parse(leafRaw).status;
|
|
3780
|
+
} catch {
|
|
3781
|
+
status2 = leafRaw;
|
|
3782
|
+
}
|
|
3783
|
+
} else if (leafRaw !== null && typeof leafRaw === "object") status2 = leafRaw.status;
|
|
3784
|
+
signerTuplesRaw.push({ principal, keyHash: subject.slice(4), status: status2 === "revoked" ? "revoked" : "active" });
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
} catch {
|
|
3788
|
+
}
|
|
3730
3789
|
let compaction = { carried: null, parked: null, scarShape: "unknown" };
|
|
3731
3790
|
let parkedAllLawInstalls = false;
|
|
3732
3791
|
try {
|
|
@@ -3793,6 +3852,12 @@ async function buildRebirthPlan(cloud, ws, reason) {
|
|
|
3793
3852
|
compaction,
|
|
3794
3853
|
repairs,
|
|
3795
3854
|
posture: { posture, consent: consentState },
|
|
3855
|
+
...signerTuplesRaw.length > 0 ? {
|
|
3856
|
+
signerEnrollments: signerTuplesRaw.map((t) => ({
|
|
3857
|
+
...t,
|
|
3858
|
+
recovery: mode === "state-seed" ? "re-enroll-required" : "carried-by-replay"
|
|
3859
|
+
}))
|
|
3860
|
+
} : {},
|
|
3796
3861
|
clientImpact: CLIENT_IMPACT,
|
|
3797
3862
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3798
3863
|
};
|
|
@@ -3849,6 +3914,23 @@ function renderRebirthImpact(plan, ledgerCommits) {
|
|
|
3849
3914
|
L.push(" \u2192 preserved by lineage continuity; their birth-cert chains stay valid (same founding key + self-id).");
|
|
3850
3915
|
}
|
|
3851
3916
|
L.push("");
|
|
3917
|
+
const se = plan.signerEnrollments ?? [];
|
|
3918
|
+
L.push(`SIGNER ENROLLMENTS (${se.length})`);
|
|
3919
|
+
if (se.length === 0) L.push(" (none on this chain \u2014 no device\u2192principal bindings to recover)");
|
|
3920
|
+
else {
|
|
3921
|
+
for (const s of se) {
|
|
3922
|
+
L.push(` \u2022 identity:${s.principal}#signer @ key:${s.keyHash.slice(0, 16)}\u2026 [${s.status}] \u2192 ${s.recovery}`);
|
|
3923
|
+
}
|
|
3924
|
+
if (se.some((s) => s.recovery === "re-enroll-required")) {
|
|
3925
|
+
L.push(" \u2192 a state-seed re-genesis does NOT carry these tuples: every device that authors against this");
|
|
3926
|
+
L.push(" workspace (e.g. for sibling births) must be lawfully re-enrolled AFTER the rebirth \u2014 client");
|
|
3927
|
+
L.push(" ensureParentSignerEnrollment / NomosScope.home's reopen repair / `githolon key enroll --parent <ws>`.");
|
|
3928
|
+
} else {
|
|
3929
|
+
L.push(" \u2192 replay-compact re-folds the enrolling intents; a PARKED enrolling intent would still strand its");
|
|
3930
|
+
L.push(" binding until re-admitted \u2014 verify with the parked list, or re-enroll idempotently (a no-op when live).");
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
L.push("");
|
|
3852
3934
|
L.push(`REPAIRS (${plan.repairs.length})`);
|
|
3853
3935
|
if (plan.repairs.length === 0) L.push(" (none matched \u2014 a plain compaction; no corrective verb declared for this refusal)");
|
|
3854
3936
|
else {
|
|
@@ -8283,6 +8365,8 @@ var HELP = {
|
|
|
8283
8365
|
["--sha256", "import: derive the seed as sha256(--secret) (a passphrase/preimage)"],
|
|
8284
8366
|
["--format <f>", "import: force hex|base64 interpretation (default: auto-detect)"],
|
|
8285
8367
|
["--parent <ws>", "enroll: the governance workspace to enroll at (default root)"],
|
|
8368
|
+
["--domain <d>", "enroll: the domain declaring the signing directive (default: DISCOVERED from the parent's installed law \u2014 never assume 'workspaces')"],
|
|
8369
|
+
["--directive <id>", "enroll: the signing directive id (default enrollSigner)"],
|
|
8286
8370
|
["--yes", "export: confirm printing the secret to stdout"]
|
|
8287
8371
|
],
|
|
8288
8372
|
examples: [
|
|
@@ -8930,7 +9014,7 @@ Identity (~/.holon/credentials.json \u2014 sessions auto-refresh):
|
|
|
8930
9014
|
githolon use [<uid>] [--clear] set/show/clear the ACTIVE signing principal: the one
|
|
8931
9015
|
deploy/gov/grant/key author as (--as overrides per-cmd)
|
|
8932
9016
|
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
|
|
9017
|
+
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
9018
|
(secret stays local; warrants SIGNED governance)
|
|
8935
9019
|
githolon key import --principal <uid> --secret <key> bring an EXISTING admin key into ~/.holon (accepts
|
|
8936
9020
|
hex / base64 seed, or --sha256 <passphrase>)
|
|
@@ -8997,7 +9081,7 @@ Options:
|
|
|
8997
9081
|
function cloudArgs(rest) {
|
|
8998
9082
|
const pos = [];
|
|
8999
9083
|
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"];
|
|
9084
|
+
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
9085
|
const BOOL_FLAGS = ["--sha256", "--stamp", "--yes", "--clear", "--keep-beside", "--retire-replaced", "--explain-current-interface", "--dry-run", "--json", "--full"];
|
|
9002
9086
|
for (let i = 0; i < rest.length; i++) {
|
|
9003
9087
|
const a = rest[i];
|
|
@@ -9027,6 +9111,8 @@ function cloudArgs(rest) {
|
|
|
9027
9111
|
else if (a === "--save") opts.save = v;
|
|
9028
9112
|
else if (a === "--format") opts.format = v;
|
|
9029
9113
|
else if (a === "--with") opts.with = v;
|
|
9114
|
+
else if (a === "--domain") opts.domain = v;
|
|
9115
|
+
else if (a === "--directive") opts.directive = v;
|
|
9030
9116
|
else if (a === "--out") opts.out = v;
|
|
9031
9117
|
else if (a === "--max") {
|
|
9032
9118
|
const n = Number(v);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.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.78.0",
|
|
33
|
+
"@githolon/dsl": "^0.78.0",
|
|
34
34
|
"isomorphic-git": "^1.38.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|