githolon 0.17.0 → 0.19.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 +597 -233
- package/package.json +3 -2
package/dist/cli.mjs
CHANGED
|
@@ -9,14 +9,172 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/governance.ts
|
|
13
|
+
var governance_exports = {};
|
|
14
|
+
__export(governance_exports, {
|
|
15
|
+
grantOrRevoke: () => grantOrRevoke,
|
|
16
|
+
keyEnroll: () => keyEnroll,
|
|
17
|
+
keyShow: () => keyShow,
|
|
18
|
+
resolveGovernancePrincipal: () => resolveGovernancePrincipal,
|
|
19
|
+
signAndPostGovernance: () => signAndPostGovernance
|
|
20
|
+
});
|
|
21
|
+
import { connect } from "@githolon/client";
|
|
22
|
+
async function connectGovernance(cloud, parent) {
|
|
23
|
+
const authToken = await sessionToken().catch(() => void 0);
|
|
24
|
+
const clientId = `githolon-cli-${Date.now().toString(16)}`;
|
|
25
|
+
return await connect({
|
|
26
|
+
cloud,
|
|
27
|
+
workspace: parent,
|
|
28
|
+
clientId,
|
|
29
|
+
...authToken !== void 0 ? { authToken } : {}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async function ensureDeviceKey(holon, principal) {
|
|
33
|
+
const existing = getDeviceKey(principal);
|
|
34
|
+
if (existing !== void 0) return existing;
|
|
35
|
+
const minted = await holon.authorKeygen();
|
|
36
|
+
if (typeof minted.secret !== "string" || typeof minted.public !== "string") {
|
|
37
|
+
throw new Error("authorKeygen did not return a {secret, public} device key");
|
|
38
|
+
}
|
|
39
|
+
const payload = await holon.enrollSignerPayload({ principal, publicKey: minted.public });
|
|
40
|
+
const key = { secret: minted.secret, public: minted.public, keyHash: payload.keyHash };
|
|
41
|
+
setDeviceKey(principal, key);
|
|
42
|
+
return key;
|
|
43
|
+
}
|
|
44
|
+
function resolveGovernancePrincipal(opts) {
|
|
45
|
+
const principal = opts.principal ?? currentPrincipal();
|
|
46
|
+
if (principal === void 0) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"signed governance needs a principal \u2014 githolon login --agent (self-onboarding), or pass --principal <your-auth-uid>"
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return principal;
|
|
52
|
+
}
|
|
53
|
+
async function signAndPostGovernance(parent, directiveId, payload, opts) {
|
|
54
|
+
const cloud = cloudBase(opts.cloud);
|
|
55
|
+
const principal = resolveGovernancePrincipal(opts);
|
|
56
|
+
const holon = await connectGovernance(cloud, parent);
|
|
57
|
+
const device = await ensureDeviceKey(holon, principal);
|
|
58
|
+
const sealed = await holon.signGovernanceOffer({
|
|
59
|
+
directiveId,
|
|
60
|
+
payload,
|
|
61
|
+
authorSecret: device.secret,
|
|
62
|
+
actor: `user:${principal}`
|
|
63
|
+
});
|
|
64
|
+
const verdict = await holon.postGovernanceOffer({ directiveId, intentBytes: sealed.intentBytes, ws: parent });
|
|
65
|
+
return { ...verdict, principal };
|
|
66
|
+
}
|
|
67
|
+
async function grantOrRevoke(verb, kind, subject, opts) {
|
|
68
|
+
const parent = opts.parent ?? "root";
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
let directiveId;
|
|
71
|
+
let payload;
|
|
72
|
+
const granter = (() => {
|
|
73
|
+
try {
|
|
74
|
+
return resolveGovernancePrincipal(opts);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
err(e.message);
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
if (granter === void 0) return 1;
|
|
81
|
+
const attribution = verb === "grant" ? { grantedBy: granter, grantedAt: now } : { revokedBy: granter, revokedAt: now };
|
|
82
|
+
if (kind === "admin" || kind === "creation") {
|
|
83
|
+
directiveId = (verb === "grant" ? "grant" : "revoke") + (kind === "admin" ? "Admin" : "Creation");
|
|
84
|
+
payload = { principal: subject, ...verb === "grant" ? { label: opts.label ?? `${kind} via githolon` } : {}, ...attribution };
|
|
85
|
+
} else if (kind === "quota" && verb === "grant") {
|
|
86
|
+
if (typeof opts.max !== "number" || !Number.isInteger(opts.max) || opts.max <= 0) {
|
|
87
|
+
err("grant quota needs --max <positive int> (the principal's workspace allowance)");
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
directiveId = "grantQuota";
|
|
91
|
+
payload = { principal: subject, maxWorkspaces: opts.max, ...attribution };
|
|
92
|
+
} else {
|
|
93
|
+
err(`unsupported: githolon ${verb} ${kind} \u2014 expected ${verb} <admin|creation${verb === "grant" ? "|quota" : ""}> <principal>`);
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
let v;
|
|
97
|
+
try {
|
|
98
|
+
v = await signAndPostGovernance(parent, directiveId, payload, opts);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
err(e.message);
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
if (!v.ok) {
|
|
104
|
+
err(`${directiveId} refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
out(`\u2713 ${directiveId} ${subject} via signed governance offer to ${parent}`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
async function keyEnroll(opts) {
|
|
111
|
+
const cloud = cloudBase(opts.cloud);
|
|
112
|
+
const principal = opts.principal ?? currentPrincipal();
|
|
113
|
+
if (principal === void 0) {
|
|
114
|
+
err("enroll needs a principal \u2014 githolon login --agent first (or --principal <uid>)");
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
const parent = opts.parent ?? "root";
|
|
118
|
+
let holon;
|
|
119
|
+
try {
|
|
120
|
+
holon = await connectGovernance(cloud, parent);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
err(`could not connect to ${parent} on ${cloud} to enroll: ${e.message}`);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
const device = await ensureDeviceKey(holon, principal);
|
|
126
|
+
const enrollPayload = await holon.enrollSignerPayload({ principal, publicKey: device.public });
|
|
127
|
+
const sealed = await holon.signGovernanceOffer({
|
|
128
|
+
directiveId: "enrollSigner",
|
|
129
|
+
payload: enrollPayload,
|
|
130
|
+
authorSecret: device.secret,
|
|
131
|
+
actor: `user:${principal}`
|
|
132
|
+
});
|
|
133
|
+
const v = await holon.postGovernanceOffer({ directiveId: "enrollSigner", intentBytes: sealed.intentBytes, ws: parent });
|
|
134
|
+
if (!v.ok) {
|
|
135
|
+
err(`enrollSigner refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
out(`\u2713 device key enrolled for ${principal} at ${parent} on ${cloud}`);
|
|
139
|
+
out(` keyHash ${device.keyHash.slice(0, 16)}\u2026 (secret stays local; signed governance is now warranted)`);
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
async function keyShow(opts) {
|
|
143
|
+
const principal = opts.principal ?? currentPrincipal();
|
|
144
|
+
if (principal === void 0) {
|
|
145
|
+
out("no principal on file \u2014 githolon login --agent, or pass --principal <uid>");
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
const device = getDeviceKey(principal);
|
|
149
|
+
if (device === void 0) {
|
|
150
|
+
out(`no signing device key for ${principal} yet \u2014 githolon key enroll mints + enrolls one`);
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
out(`device key for ${principal}:`);
|
|
154
|
+
out(` public ${device.public}`);
|
|
155
|
+
out(` keyHash ${device.keyHash} (the on-chain key:<hash> leaf; secret stays local)`);
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
var out, err;
|
|
159
|
+
var init_governance = __esm({
|
|
160
|
+
"src/governance.ts"() {
|
|
161
|
+
"use strict";
|
|
162
|
+
init_cloud();
|
|
163
|
+
out = (s) => void process.stdout.write(s + "\n");
|
|
164
|
+
err = (s) => void process.stderr.write("error: " + s + "\n");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
12
168
|
// src/cloud.ts
|
|
13
169
|
var cloud_exports = {};
|
|
14
170
|
__export(cloud_exports, {
|
|
15
171
|
cloudBase: () => cloudBase,
|
|
16
172
|
configDir: () => configDir,
|
|
173
|
+
currentPrincipal: () => currentPrincipal,
|
|
17
174
|
deploy: () => deploy,
|
|
18
175
|
describeLawMove: () => describeLawMove,
|
|
19
176
|
discoverDeployJson: () => discoverDeployJson,
|
|
177
|
+
getDeviceKey: () => getDeviceKey,
|
|
20
178
|
getSecret: () => getSecret,
|
|
21
179
|
loadCreds: () => loadCreds,
|
|
22
180
|
login: () => login,
|
|
@@ -25,10 +183,12 @@ __export(cloud_exports, {
|
|
|
25
183
|
secretRotate: () => secretRotate,
|
|
26
184
|
secretSet: () => secretSet,
|
|
27
185
|
sessionToken: () => sessionToken,
|
|
186
|
+
setDeviceKey: () => setDeviceKey,
|
|
28
187
|
setPrincipal: () => setPrincipal,
|
|
29
188
|
setSecret: () => setSecret,
|
|
30
189
|
whoami: () => whoami,
|
|
31
190
|
wsCreate: () => wsCreate,
|
|
191
|
+
wsReclaim: () => wsReclaim,
|
|
32
192
|
wsRetire: () => wsRetire,
|
|
33
193
|
wsStatus: () => wsStatus
|
|
34
194
|
});
|
|
@@ -72,6 +232,18 @@ function setPrincipal(principal) {
|
|
|
72
232
|
c.principal = principal;
|
|
73
233
|
saveCreds(c);
|
|
74
234
|
}
|
|
235
|
+
function currentPrincipal() {
|
|
236
|
+
const c = loadCreds();
|
|
237
|
+
return c.session?.uid ?? c.principal;
|
|
238
|
+
}
|
|
239
|
+
function getDeviceKey(principal) {
|
|
240
|
+
return loadCreds().devices?.[principal];
|
|
241
|
+
}
|
|
242
|
+
function setDeviceKey(principal, key) {
|
|
243
|
+
const c = loadCreds();
|
|
244
|
+
(c.devices ??= {})[principal] = key;
|
|
245
|
+
saveCreds(c);
|
|
246
|
+
}
|
|
75
247
|
function principalHeaders(principal) {
|
|
76
248
|
return /^[\w-]+\.[\w-]+\.[\w-]+$/.test(principal) ? { "x-nomos-auth": principal } : { "x-nomos-principal": principal };
|
|
77
249
|
}
|
|
@@ -133,7 +305,7 @@ function discoverDeployJson(cwd, explicit) {
|
|
|
133
305
|
}
|
|
134
306
|
async function login(opts) {
|
|
135
307
|
if (opts.agent !== true && opts.token === void 0) {
|
|
136
|
-
|
|
308
|
+
err2(
|
|
137
309
|
"browser login is not wired up yet \u2014 use one of:\n githolon login --agent anonymous sign-in (verified identity, linkable later)\n githolon login --token <refresh> adopt an existing captain app session"
|
|
138
310
|
);
|
|
139
311
|
return 1;
|
|
@@ -141,43 +313,67 @@ async function login(opts) {
|
|
|
141
313
|
const d = opts.agent === true ? await gotrue("/auth/v1/signup", "{}") : await gotrue("/auth/v1/token?grant_type=refresh_token", JSON.stringify({ refresh_token: opts.token }));
|
|
142
314
|
const s = sessionFromGotrue(d);
|
|
143
315
|
if (s === void 0) {
|
|
144
|
-
|
|
316
|
+
err2(`login failed: ${d.error_description ?? d.error ?? d.msg ?? JSON.stringify(d)}`);
|
|
145
317
|
return 1;
|
|
146
318
|
}
|
|
147
319
|
saveSession(s);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
320
|
+
out2(`\u2713 logged in${s.anonymous ? " (anonymous agent identity \u2014 linkable to a full account later)" : ""}`);
|
|
321
|
+
out2(` principal uid ${s.uid}`);
|
|
322
|
+
out2(` session saved \u2192 ${credsPath()} (auto-refreshed; births now ride x-nomos-auth)`);
|
|
151
323
|
return 0;
|
|
152
324
|
}
|
|
153
325
|
async function whoami() {
|
|
154
326
|
const c = loadCreds();
|
|
155
327
|
if (c.session !== void 0) {
|
|
156
|
-
|
|
328
|
+
out2(`session: ${c.session.anonymous ? "anonymous agent" : "account"} uid ${c.session.uid} (auth ${c.session.url})`);
|
|
157
329
|
return 0;
|
|
158
330
|
}
|
|
159
331
|
if (c.principal !== void 0) {
|
|
160
|
-
|
|
332
|
+
out2(`bare principal (transitional, unverified): ${c.principal}`);
|
|
161
333
|
return 0;
|
|
162
334
|
}
|
|
163
|
-
|
|
335
|
+
out2("not logged in \u2014 githolon login --agent, or pass --principal <uid> per birth");
|
|
164
336
|
return 0;
|
|
165
337
|
}
|
|
166
338
|
async function logout() {
|
|
167
339
|
const c = loadCreds();
|
|
168
340
|
delete c.session;
|
|
169
341
|
saveCreds(c);
|
|
170
|
-
|
|
342
|
+
out2("\u2713 session cleared (stored workspace secrets kept)");
|
|
171
343
|
return 0;
|
|
172
344
|
}
|
|
173
345
|
async function wsCreate(name, opts) {
|
|
174
346
|
const cloud = cloudBase(opts.cloud);
|
|
175
|
-
|
|
347
|
+
if (opts.via !== void 0) {
|
|
348
|
+
const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
349
|
+
let v;
|
|
350
|
+
try {
|
|
351
|
+
const principal2 = resolveGovernancePrincipal2(opts);
|
|
352
|
+
v = await signAndPostGovernance2(
|
|
353
|
+
opts.via,
|
|
354
|
+
"createWorkspace",
|
|
355
|
+
{ name, tier: "edge", parent: opts.via === "root" ? "" : opts.via, principal: principal2, requestedBy: principal2, requestedAt: Date.now() },
|
|
356
|
+
opts
|
|
357
|
+
);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
err2(e.message);
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
if (!v.ok) {
|
|
363
|
+
err2(`createWorkspace '${name}' refused (${v.status}) at ${opts.via}: ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
364
|
+
return 1;
|
|
365
|
+
}
|
|
366
|
+
out2(`\u2713 createWorkspace '${name}' requested via signed governance offer to ${opts.via} on ${cloud}`);
|
|
367
|
+
out2(` the worker provisions it on first touch; deploy is open (kernel-gated)`);
|
|
368
|
+
out2(` (want the direct birth instead? drop --via: githolon ws create ${name})`);
|
|
369
|
+
return 0;
|
|
370
|
+
}
|
|
371
|
+
let principal = opts.principal ?? await sessionToken().catch((e) => (err2(e.message), void 0)) ?? loadCreds().principal;
|
|
176
372
|
if (principal === void 0) {
|
|
177
373
|
const d2 = await gotrue("/auth/v1/signup", "{}");
|
|
178
374
|
const s = sessionFromGotrue(d2);
|
|
179
375
|
if (s === void 0) {
|
|
180
|
-
|
|
376
|
+
err2(
|
|
181
377
|
`a birth needs a principal, and automatic agent login failed: ${d2.error_description ?? d2.error ?? d2.msg ?? JSON.stringify(d2)}
|
|
182
378
|
githolon login --agent (self-onboarding), githolon login --token <refresh>,
|
|
183
379
|
or pass --principal <your-auth-uid or access token> (saved as the default)`
|
|
@@ -185,9 +381,9 @@ async function wsCreate(name, opts) {
|
|
|
185
381
|
return 1;
|
|
186
382
|
}
|
|
187
383
|
saveSession(s);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
384
|
+
out2(`no principal on file \u2014 self-onboarded an anonymous agent identity (githolon login --agent)`);
|
|
385
|
+
out2(` principal uid ${s.uid} (linkable to a full account later)`);
|
|
386
|
+
out2(` session saved \u2192 ${credsPath()} (auto-refreshed; births ride x-nomos-auth)`);
|
|
191
387
|
principal = s.accessToken;
|
|
192
388
|
}
|
|
193
389
|
const r = await fetch(`${cloud}/v2/workspaces/${name}`, {
|
|
@@ -196,20 +392,12 @@ async function wsCreate(name, opts) {
|
|
|
196
392
|
});
|
|
197
393
|
const d = await r.json().catch(() => void 0);
|
|
198
394
|
if (!r.ok || d?.ok !== true) {
|
|
199
|
-
|
|
395
|
+
err2(`create '${name}' refused (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
200
396
|
return 1;
|
|
201
397
|
}
|
|
202
398
|
if (opts.principal !== void 0) setPrincipal(opts.principal);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
out(`\u2713 workspace ${name} created on ${cloud}`);
|
|
206
|
-
out(`\u2713 workspaceSecret saved \u2192 ${credsPath()} (githolon deploy uses it automatically)`);
|
|
207
|
-
} else {
|
|
208
|
-
out(`\u2713 workspace ${name} already exists on ${cloud} (no new secret issued)`);
|
|
209
|
-
if (getSecret(cloud, name) === void 0) {
|
|
210
|
-
out(` no stored secret for it here \u2014 if you own it: githolon secret set ${name} <secret>`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
399
|
+
out2(`\u2713 workspace ${name} created on ${cloud}`);
|
|
400
|
+
out2(` deploy is open (kernel-gated): githolon deploy ${name}`);
|
|
213
401
|
return 0;
|
|
214
402
|
}
|
|
215
403
|
async function wsStatus(name, opts) {
|
|
@@ -217,39 +405,61 @@ async function wsStatus(name, opts) {
|
|
|
217
405
|
const r = await fetch(`${cloud}/v2/workspaces/${name}`);
|
|
218
406
|
const text = await r.text();
|
|
219
407
|
if (!r.ok) {
|
|
220
|
-
|
|
408
|
+
err2(`status '${name}' (${r.status}): ${text}`);
|
|
221
409
|
return 1;
|
|
222
410
|
}
|
|
223
|
-
|
|
411
|
+
out2(text);
|
|
224
412
|
return 0;
|
|
225
413
|
}
|
|
226
414
|
async function wsRetire(ws, opts) {
|
|
227
415
|
const cloud = cloudBase(opts.cloud);
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
416
|
+
const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
417
|
+
const parent = opts.parent ?? "root";
|
|
418
|
+
let v;
|
|
419
|
+
try {
|
|
420
|
+
const principal = resolveGovernancePrincipal2(opts);
|
|
421
|
+
v = await signAndPostGovernance2(
|
|
422
|
+
parent,
|
|
423
|
+
"retireWorkspace",
|
|
424
|
+
{ workspaceId: ws, retiredBy: principal, retiredAt: Date.now() },
|
|
425
|
+
opts
|
|
233
426
|
);
|
|
427
|
+
} catch (e) {
|
|
428
|
+
err2(e.message);
|
|
234
429
|
return 1;
|
|
235
430
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
headers: { authorization: `Bearer ${secret}` }
|
|
239
|
-
});
|
|
240
|
-
const d = await r.json().catch(() => void 0);
|
|
241
|
-
if (r.status === 401) {
|
|
242
|
-
err(`retire refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
|
|
431
|
+
if (!v.ok) {
|
|
432
|
+
err2(`retire '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
243
433
|
return 1;
|
|
244
434
|
}
|
|
245
|
-
|
|
246
|
-
|
|
435
|
+
out2(`\u2713 workspace ${ws} retired via signed governance offer to ${parent} on ${cloud}`);
|
|
436
|
+
out2(" nothing was deleted \u2014 the ledger and its data are retained per the Nomos Pre-Release License");
|
|
437
|
+
out2(" it no longer counts toward your workspace allowance; reads keep serving, deploys/pushes refuse");
|
|
438
|
+
return 0;
|
|
439
|
+
}
|
|
440
|
+
async function wsReclaim(ws, opts) {
|
|
441
|
+
const cloud = cloudBase(opts.cloud);
|
|
442
|
+
const { signAndPostGovernance: signAndPostGovernance2, resolveGovernancePrincipal: resolveGovernancePrincipal2 } = await Promise.resolve().then(() => (init_governance(), governance_exports));
|
|
443
|
+
const parent = opts.parent ?? "root";
|
|
444
|
+
let v;
|
|
445
|
+
try {
|
|
446
|
+
const principal = resolveGovernancePrincipal2(opts);
|
|
447
|
+
v = await signAndPostGovernance2(
|
|
448
|
+
parent,
|
|
449
|
+
"reclaimWorkspace",
|
|
450
|
+
{ workspaceId: ws, reclaimedBy: principal, reclaimedAt: Date.now() },
|
|
451
|
+
opts
|
|
452
|
+
);
|
|
453
|
+
} catch (e) {
|
|
454
|
+
err2(e.message);
|
|
247
455
|
return 1;
|
|
248
456
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
457
|
+
if (!v.ok) {
|
|
458
|
+
err2(`reclaim '${ws}' refused (${v.status}): ${typeof v.body === "string" ? v.body : JSON.stringify(v.body)}`);
|
|
459
|
+
return 1;
|
|
460
|
+
}
|
|
461
|
+
out2(`\u2713 workspace ${ws} reclaimed via signed governance offer to ${parent} on ${cloud}`);
|
|
462
|
+
out2(" the custodian drops the ledger bytes on seeing this fact folded; the reclaimed FACT is append-only");
|
|
253
463
|
return 0;
|
|
254
464
|
}
|
|
255
465
|
async function deploy(ws, opts) {
|
|
@@ -258,37 +468,24 @@ async function deploy(ws, opts) {
|
|
|
258
468
|
try {
|
|
259
469
|
file = discoverDeployJson(process.cwd(), opts.file);
|
|
260
470
|
} catch (e) {
|
|
261
|
-
|
|
262
|
-
return 1;
|
|
263
|
-
}
|
|
264
|
-
const secret = getSecret(cloud, ws);
|
|
265
|
-
if (secret === void 0) {
|
|
266
|
-
err(
|
|
267
|
-
`no stored secret for ${ws} on ${cloud}
|
|
268
|
-
githolon ws create ${ws} (a fresh workspace saves its secret here)
|
|
269
|
-
githolon secret set ${ws} <secret> (import one you already hold)`
|
|
270
|
-
);
|
|
471
|
+
err2(e.message);
|
|
271
472
|
return 1;
|
|
272
473
|
}
|
|
273
474
|
const before = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).then((br) => br.json()).then((bd) => bd.ok === true ? bd.domains ?? [] : []).catch(() => []);
|
|
274
475
|
const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`, {
|
|
275
476
|
method: "POST",
|
|
276
|
-
headers: { "content-type": "application/json"
|
|
477
|
+
headers: { "content-type": "application/json" },
|
|
277
478
|
body: readFileSync(file, "utf8")
|
|
278
479
|
});
|
|
279
480
|
const d = await r.json().catch(() => void 0);
|
|
280
|
-
if (r.status === 401) {
|
|
281
|
-
err(`deploy refused (401) \u2014 the stored secret for ${ws} is not the workspace's. githolon secret set ${ws} <secret>`);
|
|
282
|
-
return 1;
|
|
283
|
-
}
|
|
284
481
|
if (!r.ok || d?.ok !== true) {
|
|
285
|
-
|
|
482
|
+
err2(`deploy '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
286
483
|
return 1;
|
|
287
484
|
}
|
|
288
485
|
const phase = d.installation?.[0]?.data?.["status.phase"];
|
|
289
|
-
|
|
486
|
+
out2(`\u2713 deployed ${file.split("/").pop()} \u2192 ${ws}${typeof phase === "string" ? ` (${phase})` : ""}`);
|
|
290
487
|
if (typeof d.domainHash === "string") {
|
|
291
|
-
|
|
488
|
+
out2(` law ${describeLawMove(before, d.domainHash)}`);
|
|
292
489
|
}
|
|
293
490
|
return 0;
|
|
294
491
|
}
|
|
@@ -300,30 +497,19 @@ function describeLawMove(before, newHash) {
|
|
|
300
497
|
}
|
|
301
498
|
async function secretSet(ws, secret, opts) {
|
|
302
499
|
setSecret(cloudBase(opts.cloud), ws, secret);
|
|
303
|
-
|
|
500
|
+
out2(`\u2713 git push password for ${ws} saved \u2192 ${credsPath()} (used by the push-to-create birth lane)`);
|
|
304
501
|
return 0;
|
|
305
502
|
}
|
|
306
503
|
async function secretRotate(ws, opts) {
|
|
307
504
|
const cloud = cloudBase(opts.cloud);
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
method: "POST",
|
|
315
|
-
headers: { authorization: `Bearer ${current}` }
|
|
316
|
-
});
|
|
317
|
-
const d = await r.json().catch(() => void 0);
|
|
318
|
-
if (!r.ok || d?.ok !== true || typeof d.workspaceSecret !== "string") {
|
|
319
|
-
err(`rotate '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}`);
|
|
320
|
-
return 1;
|
|
321
|
-
}
|
|
322
|
-
setSecret(cloud, ws, d.workspaceSecret);
|
|
323
|
-
out(`\u2713 secret for ${ws} rotated and saved \u2192 ${credsPath()}`);
|
|
324
|
-
return 0;
|
|
505
|
+
err2(
|
|
506
|
+
`secret rotate is retired (host-authority strip) \u2014 there is no host-held workspace secret on ${cloud}
|
|
507
|
+
ownership is sha256(push password) bound in the chain; re-own by re-birthing via the upload lane
|
|
508
|
+
with a new push password (see: githolon git remote ${ws} && git push nomos main)`
|
|
509
|
+
);
|
|
510
|
+
return 1;
|
|
325
511
|
}
|
|
326
|
-
var DEFAULT_CLOUD, DEFAULT_AUTH_URL, DEFAULT_AUTH_ANON_KEY, secretKey,
|
|
512
|
+
var DEFAULT_CLOUD, DEFAULT_AUTH_URL, DEFAULT_AUTH_ANON_KEY, secretKey, out2, err2;
|
|
327
513
|
var init_cloud = __esm({
|
|
328
514
|
"src/cloud.ts"() {
|
|
329
515
|
"use strict";
|
|
@@ -331,8 +517,8 @@ var init_cloud = __esm({
|
|
|
331
517
|
DEFAULT_AUTH_URL = "https://kjbcjkihxskuwwfdqklt.supabase.co";
|
|
332
518
|
DEFAULT_AUTH_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtqYmNqa2loeHNrdXd3ZmRxa2x0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTA3MDU2OTAsImV4cCI6MjA2NjI4MTY5MH0.V9e7XsuTlTOLqefOIedTqlBiTxUSn4O5FZSPWwAxiSI";
|
|
333
519
|
secretKey = (cloud, ws) => `${cloud} ${ws}`;
|
|
334
|
-
|
|
335
|
-
|
|
520
|
+
out2 = (s) => void process.stdout.write(s + "\n");
|
|
521
|
+
err2 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
336
522
|
}
|
|
337
523
|
});
|
|
338
524
|
|
|
@@ -453,13 +639,13 @@ var init_tree = __esm({
|
|
|
453
639
|
function concatBytes(chunks) {
|
|
454
640
|
let n = 0;
|
|
455
641
|
for (const c of chunks) n += c.length;
|
|
456
|
-
const
|
|
642
|
+
const out10 = new Uint8Array(n);
|
|
457
643
|
let o = 0;
|
|
458
644
|
for (const c of chunks) {
|
|
459
|
-
|
|
645
|
+
out10.set(c, o);
|
|
460
646
|
o += c.length;
|
|
461
647
|
}
|
|
462
|
-
return
|
|
648
|
+
return out10;
|
|
463
649
|
}
|
|
464
650
|
function pkt(payload) {
|
|
465
651
|
const body = typeof payload === "string" ? enc2.encode(payload) : payload;
|
|
@@ -612,13 +798,13 @@ async function sha256hex(t) {
|
|
|
612
798
|
function osEntropyBuffer(n) {
|
|
613
799
|
const bytes = new Uint8Array(n * 8);
|
|
614
800
|
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
615
|
-
const
|
|
801
|
+
const out10 = new Array(n), T = 2 ** 53;
|
|
616
802
|
for (let i = 0; i < n; i++) {
|
|
617
803
|
let z = 0n;
|
|
618
804
|
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
619
|
-
|
|
805
|
+
out10[i] = Number(z >> 11n) / T;
|
|
620
806
|
}
|
|
621
|
-
return
|
|
807
|
+
return out10;
|
|
622
808
|
}
|
|
623
809
|
function stringifyBig(o) {
|
|
624
810
|
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
@@ -716,13 +902,19 @@ function mintId(eng, typeTag) {
|
|
|
716
902
|
}
|
|
717
903
|
function author(eng, ws, domain, directiveId, payload, controllerHash, opts = {}) {
|
|
718
904
|
const seq = eng.seq++;
|
|
719
|
-
const
|
|
905
|
+
const capPorts = { clock: { physical: Date.now(), logical: 0, replica: eng.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) };
|
|
906
|
+
if (opts.validFromEpoch !== void 0) capPorts.valid_from_epoch = opts.validFromEpoch;
|
|
907
|
+
if (opts.principalVerified !== void 0) {
|
|
908
|
+
capPorts.principal = opts.actor ?? "";
|
|
909
|
+
capPorts.principal_verified = !!opts.principalVerified;
|
|
910
|
+
}
|
|
911
|
+
const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
|
|
720
912
|
writeWork(eng, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
|
|
721
913
|
writeWork(eng, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
722
914
|
const genesis = !controllerHash;
|
|
723
915
|
const defer = opts.deferProjection !== false;
|
|
724
|
-
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 ?? "", 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));
|
|
725
|
-
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
916
|
+
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));
|
|
917
|
+
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;
|
|
726
918
|
if (defer && res.ok) (eng.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
727
919
|
return res;
|
|
728
920
|
}
|
|
@@ -763,7 +955,7 @@ function offerIntent(eng, ws, bytes, opts) {
|
|
|
763
955
|
}
|
|
764
956
|
function offerAdmit(eng, ws, bytes, opts) {
|
|
765
957
|
const v = offerIntent(eng, ws, bytes, opts);
|
|
766
|
-
return v.outcome === "admitted" ? { ok: true, head: v.head } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
958
|
+
return v.outcome === "admitted" ? { ok: true, head: v.head, ...v.born ? { born: v.born } : {} } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
767
959
|
}
|
|
768
960
|
function applyIntentBytes(eng, ws, bytes, opts) {
|
|
769
961
|
return offerAdmit(eng, ws, bytes, opts);
|
|
@@ -919,21 +1111,21 @@ function sealedIntentOf(eng, ws, res) {
|
|
|
919
1111
|
}
|
|
920
1112
|
function printVerdict(v) {
|
|
921
1113
|
if (v["valid"] === true) {
|
|
922
|
-
|
|
923
|
-
|
|
1114
|
+
out3(`\u2713 chain VALID \u2014 head ${String(v["head"]).slice(0, 12)}\u2026, ${v["intents"]} intent(s), ${v["plansRerun"]} plan(s) re-run`);
|
|
1115
|
+
out3(` controller ${String(v["controllerHash"]).slice(0, 12)}\u2026 | installed: ${v["installedDomains"]?.map((h) => h.slice(0, 12) + "\u2026").join(", ") ?? "\u2014"}`);
|
|
924
1116
|
return true;
|
|
925
1117
|
}
|
|
926
|
-
|
|
1118
|
+
err3(`chain INVALID at ${v["check"]}${v["atIndex"] !== void 0 ? ` (intent ${v["atIndex"]})` : ""}: ${v["error"]}`);
|
|
927
1119
|
return false;
|
|
928
1120
|
}
|
|
929
|
-
var
|
|
1121
|
+
var out3, err3;
|
|
930
1122
|
var init_local_holon = __esm({
|
|
931
1123
|
"src/local_holon.ts"() {
|
|
932
1124
|
"use strict";
|
|
933
1125
|
init_engine();
|
|
934
1126
|
init_cloud();
|
|
935
|
-
|
|
936
|
-
|
|
1127
|
+
out3 = (s) => void process.stdout.write(s + "\n");
|
|
1128
|
+
err3 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
937
1129
|
}
|
|
938
1130
|
});
|
|
939
1131
|
|
|
@@ -941,6 +1133,7 @@ var init_local_holon = __esm({
|
|
|
941
1133
|
var proof_offline_exports = {};
|
|
942
1134
|
__export(proof_offline_exports, {
|
|
943
1135
|
parseOfflineLegs: () => parseOfflineLegs,
|
|
1136
|
+
runChildBirthLeg: () => runChildBirthLeg,
|
|
944
1137
|
runOfflineLegs: () => runOfflineLegs,
|
|
945
1138
|
runOfflineProofStandalone: () => runOfflineProofStandalone
|
|
946
1139
|
});
|
|
@@ -994,7 +1187,7 @@ function parseOfflineLegs(proofSource) {
|
|
|
994
1187
|
...countLeg !== void 0 ? { count: countLeg } : {}
|
|
995
1188
|
};
|
|
996
1189
|
}
|
|
997
|
-
function runOfflineLegs(eng, ws, lawHash, legs) {
|
|
1190
|
+
function runOfflineLegs(eng, ws, lawHash, legs, frameworkHash) {
|
|
998
1191
|
const t0 = performance.now();
|
|
999
1192
|
const done = (ok, lines2, error) => ({
|
|
1000
1193
|
ok,
|
|
@@ -1003,33 +1196,108 @@ function runOfflineLegs(eng, ws, lawHash, legs) {
|
|
|
1003
1196
|
...error !== void 0 ? { error } : {}
|
|
1004
1197
|
});
|
|
1005
1198
|
const lines = [];
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1199
|
+
const kinds = legs.legs ?? (legs.directiveId !== void 0 ? ["standalone-provable"] : []);
|
|
1200
|
+
if (legs.directiveId !== void 0) {
|
|
1201
|
+
const payload = { ...legs.payload };
|
|
1202
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1203
|
+
for (const f of legs.autoStamped ?? []) payload[f] = stamp;
|
|
1204
|
+
if (legs.mintField !== void 0 && legs.aggregateId !== void 0) {
|
|
1205
|
+
payload[legs.mintField] = mintId(eng, legs.aggregateId);
|
|
1206
|
+
}
|
|
1207
|
+
const res = author(eng, ws, legs.domain, legs.directiveId, payload, lawHash);
|
|
1208
|
+
if (res.ok === false) {
|
|
1209
|
+
return done(false, lines, `dispatch ${legs.domain}/${legs.directiveId} refused: ${res.error ?? "unknown"} \u2014 the law itself rejected its own synthesized sample; check the directive's plan/payload schema`);
|
|
1210
|
+
}
|
|
1211
|
+
const createdId = (sealedIntentOf(eng, ws, res)?.events ?? []).find((e) => e.marker === "Create")?.aggregate;
|
|
1212
|
+
lines.push(`\u2713 dispatch ${legs.domain}/${legs.directiveId} \u2014 offline write under the new law${createdId !== void 0 ? ` (${createdId.slice(0, 28)}\u2026)` : ""}`);
|
|
1213
|
+
if (legs.query !== void 0) {
|
|
1214
|
+
const rows = query(eng, ws, legs.query.id, JSON.stringify(legs.query.params));
|
|
1215
|
+
if (rows.length !== 1 || createdId !== void 0 && rows[0].id !== createdId) {
|
|
1216
|
+
return done(false, lines, `declared query ${legs.query.id} expected the 1 created row, got ${JSON.stringify(rows.map((r) => r.id))} \u2014 check the query's key fields against the directive's payload`);
|
|
1217
|
+
}
|
|
1218
|
+
lines.push(`\u2713 declared query ${legs.query.id} answers locally \u2014 1 row`);
|
|
1219
|
+
}
|
|
1220
|
+
if (legs.count !== void 0) {
|
|
1221
|
+
const c = count(eng, ws, legs.count.id, legs.count.group).count;
|
|
1222
|
+
if (c !== 1) {
|
|
1223
|
+
return done(false, lines, `declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) expected 1, got ${c} \u2014 check the count's grouping against the created row`);
|
|
1224
|
+
}
|
|
1225
|
+
lines.push(`\u2713 declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) = 1 locally`);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (legs.childBirth !== void 0) {
|
|
1229
|
+
const v = runChildBirthLeg(eng, ws, lawHash, legs.childBirth, frameworkHash);
|
|
1230
|
+
lines.push(...v.legs);
|
|
1231
|
+
if (!v.ok) return done(false, lines, v.error);
|
|
1232
|
+
}
|
|
1233
|
+
if (legs.directiveId === void 0 && legs.childBirth === void 0) {
|
|
1234
|
+
lines.push(`\u2713 ${legs.domain} \u2014 compile-valid (the law compiled; no standalone create or .births() to prove a write)${kinds.length ? ` [${kinds.join(", ")}]` : ""}`);
|
|
1011
1235
|
}
|
|
1012
|
-
|
|
1236
|
+
return done(true, lines);
|
|
1237
|
+
}
|
|
1238
|
+
function runChildBirthLeg(eng, ws, lawHash, cb, frameworkHash) {
|
|
1239
|
+
const t0 = performance.now();
|
|
1240
|
+
const lines = [];
|
|
1241
|
+
const done = (ok, error) => ({ ok, ms: performance.now() - t0, legs: lines, ...error !== void 0 ? { error } : {} });
|
|
1242
|
+
const fw = frameworkHash ?? eng.hashes?.nomos;
|
|
1243
|
+
const payload = { ...cb.birthPayload };
|
|
1244
|
+
if (cb.frameworkHashField !== void 0) payload[cb.frameworkHashField] = fw;
|
|
1245
|
+
if (cb.lawHashField !== void 0) payload[cb.lawHashField] = lawHash;
|
|
1246
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1247
|
+
for (const f of cb.birthAutoStamped) payload[f] = stamp;
|
|
1248
|
+
const res = author(eng, ws, cb.parentDomain, cb.birthDirectiveId, payload, lawHash);
|
|
1013
1249
|
if (res.ok === false) {
|
|
1014
|
-
return done(false,
|
|
1250
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} refused: ${res.error ?? "unknown"} \u2014 the parent's own gate rejected the birth offer (check the .births() plan + payload)`);
|
|
1251
|
+
}
|
|
1252
|
+
const childWs = (res.born ?? []).map((b) => b?.workspace).find((w) => typeof w === "string" && w.length > 0);
|
|
1253
|
+
if (childWs === void 0) {
|
|
1254
|
+
return done(false, `child birth ${cb.parentDomain}/${cb.birthDirectiveId} admitted but the offer-effect spawned no child (born=${JSON.stringify(res.born ?? [])}) \u2014 the run_birth_effect did not fold a child (is the vendored kernel current? does the recipe carry a genesis chain?)`);
|
|
1015
1255
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
return
|
|
1256
|
+
lines.push(`\u2713 child birth ${cb.parentDomain}/${cb.birthDirectiveId} \u2014 ${childWs} born (the offer-effect folded the child genesis through the child's OWN gate)`);
|
|
1257
|
+
const installed = (hash) => {
|
|
1258
|
+
try {
|
|
1259
|
+
return qById(eng, childWs, `domain-installation:${hash}`).length > 0;
|
|
1260
|
+
} catch {
|
|
1261
|
+
return false;
|
|
1022
1262
|
}
|
|
1023
|
-
|
|
1263
|
+
};
|
|
1264
|
+
if (fw !== void 0 && !installed(fw)) {
|
|
1265
|
+
return done(false, `the born child ${childWs} did not install the framework controller in-chain (domain-installation:${String(fw).slice(0, 12)}\u2026 absent) \u2014 the genesis bootstrap/installDomain did not fold`);
|
|
1024
1266
|
}
|
|
1025
|
-
if (
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1267
|
+
if (!installed(lawHash)) {
|
|
1268
|
+
return done(false, `the born child ${childWs} did not install the child law in-chain (domain-installation:${lawHash.slice(0, 12)}\u2026 absent) \u2014 the genesis nomos/installDomain did not fold (by-hash byte routing failed: does the parent fold this law?)`);
|
|
1269
|
+
}
|
|
1270
|
+
lines.push(`\u2713 the born child installed the framework + child law IN-CHAIN (the genesis installDomain\xD72 folded)`);
|
|
1271
|
+
let seedRows = [];
|
|
1272
|
+
if (cb.childSeedInstanceId !== void 0) {
|
|
1273
|
+
try {
|
|
1274
|
+
seedRows = qById(eng, childWs, cb.childSeedInstanceId);
|
|
1275
|
+
} catch (e) {
|
|
1276
|
+
return done(false, `reading the child seed (${cb.childSeedInstanceId}) failed: ${e.message}`);
|
|
1277
|
+
}
|
|
1278
|
+
} else if (cb.childQuery !== void 0) {
|
|
1279
|
+
const params = Object.fromEntries(cb.childQuery.key.map((k) => [k, payload[k] ?? ""]));
|
|
1280
|
+
try {
|
|
1281
|
+
seedRows = query(eng, childWs, cb.childQuery.id, JSON.stringify(params));
|
|
1282
|
+
} catch (e) {
|
|
1283
|
+
return done(false, `the child declared query ${cb.childQuery.id} failed: ${e.message}`);
|
|
1029
1284
|
}
|
|
1030
|
-
lines.push(`\u2713 declared count ${legs.count.id}(${JSON.stringify(legs.count.group)}) = 1 locally`);
|
|
1031
1285
|
}
|
|
1032
|
-
|
|
1286
|
+
if ((cb.childSeedInstanceId !== void 0 || cb.childQuery !== void 0) && seedRows.length < 1) {
|
|
1287
|
+
return done(false, `the born child ${childWs} did not seed ${cb.childSeedAggregateId ?? "its genesis record"} (${cb.childSeedDirectiveId} produced no readable row) \u2014 the child genesis seed did not run`);
|
|
1288
|
+
}
|
|
1289
|
+
lines.push(`\u2713 the born child's genesis seed ran \u2014 ${cb.childSeedDirectiveId} produced ${seedRows.length} readable ${cb.childSeedAggregateId ?? "row"}(s) in ${childWs}`);
|
|
1290
|
+
let verdict;
|
|
1291
|
+
try {
|
|
1292
|
+
verdict = verifyChainLocal(eng, childWs);
|
|
1293
|
+
} catch (e) {
|
|
1294
|
+
return done(false, `verify_chain on the born child ${childWs} threw: ${e.message}`);
|
|
1295
|
+
}
|
|
1296
|
+
if (verdict.valid !== true) {
|
|
1297
|
+
return done(false, `the born child ${childWs} FAILED verify_chain at ${verdict.check ?? "?"}${verdict.atIndex !== void 0 ? ` (intent ${verdict.atIndex})` : ""}: ${verdict.error ?? "invalid"} \u2014 the child's own gate refused its genesis`);
|
|
1298
|
+
}
|
|
1299
|
+
lines.push(`\u2713 verify_chain GREEN on the born child ${childWs} (${verdict.plansRerun ?? "?"} plans re-run \u2014 it self-validates from intent 0 through its OWN gate)`);
|
|
1300
|
+
return done(true);
|
|
1033
1301
|
}
|
|
1034
1302
|
async function runOfflineProofStandalone(proofPath, cloud, domain) {
|
|
1035
1303
|
const src = readFileSync3(proofPath, "utf8");
|
|
@@ -1048,7 +1316,13 @@ async function runOfflineProofStandalone(proofPath, cloud, domain) {
|
|
|
1048
1316
|
throw new Error(`no provable domain '${chosen}' in this package \u2014 it proves: ${keys.join(", ")} (pick one: githolon proof --domain <key>)`);
|
|
1049
1317
|
}
|
|
1050
1318
|
legs = index.domains[chosen];
|
|
1051
|
-
if (keys.length > 1)
|
|
1319
|
+
if (keys.length > 1) {
|
|
1320
|
+
console.log(`githolon proof \u2014 domain '${chosen}' (of ${keys.length}; --domain <key> to switch):`);
|
|
1321
|
+
for (const k of keys) {
|
|
1322
|
+
const kk = index.domains[k].legs ?? (index.domains[k].directiveId !== void 0 ? ["standalone-provable"] : ["compile-valid"]);
|
|
1323
|
+
console.log(` ${k === chosen ? "\u25B8" : " "} ${k.padEnd(20)} ${kk.join(", ")}`);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1052
1326
|
} else {
|
|
1053
1327
|
if (domain !== void 0) throw new Error(`--domain needs a per-domain legs index (build/${packageName}.proof-legs.json) \u2014 recompile with \`githolon compile\` to emit it`);
|
|
1054
1328
|
legs = parseOfflineLegs(src);
|
|
@@ -1061,7 +1335,7 @@ async function runOfflineProofStandalone(proofPath, cloud, domain) {
|
|
|
1061
1335
|
await mountFresh(eng, ws);
|
|
1062
1336
|
author(eng, ws, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, "githolon-proof"), "");
|
|
1063
1337
|
author(eng, ws, "nomos", "installDomain", installPayload(lawHash, deployBody.packageUsda, "githolon-proof"), eng.hashes.nomos);
|
|
1064
|
-
const v = runOfflineLegs(eng, ws, lawHash, legs);
|
|
1338
|
+
const v = runOfflineLegs(eng, ws, lawHash, legs, eng.hashes.nomos);
|
|
1065
1339
|
for (const l of v.legs) console.log(l);
|
|
1066
1340
|
if (!v.ok) {
|
|
1067
1341
|
console.error(`\u2717 ${v.error}`);
|
|
@@ -1267,6 +1541,7 @@ Re-run with --force to replace it.`
|
|
|
1267
1541
|
|
|
1268
1542
|
// src/cli.ts
|
|
1269
1543
|
init_cloud();
|
|
1544
|
+
init_governance();
|
|
1270
1545
|
|
|
1271
1546
|
// src/compile.ts
|
|
1272
1547
|
import { spawn, spawnSync } from "node:child_process";
|
|
@@ -1412,8 +1687,8 @@ async function resolveWorkspace(explicit, cwd, target, usageHint) {
|
|
|
1412
1687
|
|
|
1413
1688
|
// src/dev.ts
|
|
1414
1689
|
init_proof_offline();
|
|
1415
|
-
var
|
|
1416
|
-
var
|
|
1690
|
+
var out4 = (s) => void process.stdout.write(s + "\n");
|
|
1691
|
+
var err4 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1417
1692
|
var WS = "dev";
|
|
1418
1693
|
var DEFAULT_PORT = 7700;
|
|
1419
1694
|
var DEBOUNCE_MS = 200;
|
|
@@ -1536,21 +1811,21 @@ async function dev(opts) {
|
|
|
1536
1811
|
const cwd = process.cwd();
|
|
1537
1812
|
const cloud = cloudBase(opts.cloud);
|
|
1538
1813
|
const project = await loadProject(cwd).catch((e) => {
|
|
1539
|
-
|
|
1814
|
+
err4(e.message);
|
|
1540
1815
|
return void 0;
|
|
1541
1816
|
});
|
|
1542
1817
|
if (project === void 0) {
|
|
1543
1818
|
if (!existsSync6(join7(cwd, CONFIG_FILE))) {
|
|
1544
|
-
|
|
1819
|
+
err4(`no ${CONFIG_FILE} here (${cwd}) \u2014 \`githolon dev\` runs inside a project; scaffold one from examples/guestbook or \`githolon generate domain <name>\``);
|
|
1545
1820
|
}
|
|
1546
1821
|
return 1;
|
|
1547
1822
|
}
|
|
1548
1823
|
const creds = loadCreds();
|
|
1549
1824
|
const installedBy = creds.session?.uid ?? creds.principal ?? "githolon-dev";
|
|
1550
|
-
|
|
1825
|
+
out4(`githolon dev \u2014 ${project.name}: compiling\u2026`);
|
|
1551
1826
|
const firstCompile = await compileAsync(cwd);
|
|
1552
1827
|
if (!firstCompile.ok) {
|
|
1553
|
-
|
|
1828
|
+
err4(`compile failed \u2014 the dev loop needs one green compile to mint the holon:
|
|
1554
1829
|
${firstCompile.output}`);
|
|
1555
1830
|
return 1;
|
|
1556
1831
|
}
|
|
@@ -1558,14 +1833,14 @@ ${firstCompile.output}`);
|
|
|
1558
1833
|
try {
|
|
1559
1834
|
deployBody = readDeployBody(cwd, project.name);
|
|
1560
1835
|
} catch (e) {
|
|
1561
|
-
|
|
1836
|
+
err4(e.message);
|
|
1562
1837
|
return 1;
|
|
1563
1838
|
}
|
|
1564
1839
|
let engBoot;
|
|
1565
1840
|
try {
|
|
1566
1841
|
engBoot = await holonEngine(cloud, deployBody, installedBy);
|
|
1567
1842
|
} catch (e) {
|
|
1568
|
-
|
|
1843
|
+
err4(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
1569
1844
|
the runtime rides ${cloud}/v1/runtime/* and is cached in ~/.holon/runtime after one fetch \u2014 connect once, then dev runs offline`);
|
|
1570
1845
|
return 1;
|
|
1571
1846
|
}
|
|
@@ -1588,19 +1863,19 @@ ${firstCompile.output}`);
|
|
|
1588
1863
|
s.intents++;
|
|
1589
1864
|
const installed = await installLaw(s, deployBody, installedBy);
|
|
1590
1865
|
if (!installed.ok) {
|
|
1591
|
-
|
|
1866
|
+
err4(installed.error ?? "law install failed");
|
|
1592
1867
|
return 1;
|
|
1593
1868
|
}
|
|
1594
1869
|
s.proof = await runProofCycle(s, cwd, deployBody, installedBy);
|
|
1595
1870
|
if (opts.once === true) {
|
|
1596
|
-
|
|
1871
|
+
out4(statusScreen({ ...s, watchRoots: ["(--once: no watch)"] }));
|
|
1597
1872
|
return s.proof === void 0 || s.proof.ok ? 0 : 1;
|
|
1598
1873
|
}
|
|
1599
1874
|
let server;
|
|
1600
1875
|
try {
|
|
1601
1876
|
server = await startServer(s);
|
|
1602
1877
|
} catch (e) {
|
|
1603
|
-
|
|
1878
|
+
err4(e.message);
|
|
1604
1879
|
return 1;
|
|
1605
1880
|
}
|
|
1606
1881
|
const moduleDirs = /* @__PURE__ */ new Set();
|
|
@@ -1628,7 +1903,7 @@ ${firstCompile.output}`);
|
|
|
1628
1903
|
s.compileMs = run.ms;
|
|
1629
1904
|
if (!run.ok) {
|
|
1630
1905
|
s.lastError = run.output.trim() || "compile failed";
|
|
1631
|
-
|
|
1906
|
+
out4(`\u2717 cycle ${s.cycle}: compile failed \u2014 the previous law keeps serving
|
|
1632
1907
|
${run.output}`);
|
|
1633
1908
|
} else {
|
|
1634
1909
|
s.lastError = void 0;
|
|
@@ -1637,16 +1912,16 @@ ${run.output}`);
|
|
|
1637
1912
|
const inst = await installLaw(s, body, installedBy);
|
|
1638
1913
|
if (!inst.ok) {
|
|
1639
1914
|
s.lastError = inst.error ?? "install failed";
|
|
1640
|
-
|
|
1915
|
+
out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1641
1916
|
} else {
|
|
1642
1917
|
s.proof = await runProofCycle(s, cwd, body, installedBy);
|
|
1643
1918
|
const total = performance.now() - t0;
|
|
1644
|
-
|
|
1645
|
-
|
|
1919
|
+
out4(statusScreen(s));
|
|
1920
|
+
out4(` cycle edit \u2192 law-live \u2192 proof in ${total.toFixed(0)} ms total`);
|
|
1646
1921
|
}
|
|
1647
1922
|
} catch (e) {
|
|
1648
1923
|
s.lastError = String(e.message ?? e);
|
|
1649
|
-
|
|
1924
|
+
out4(`\u2717 cycle ${s.cycle}: ${s.lastError}`);
|
|
1650
1925
|
}
|
|
1651
1926
|
}
|
|
1652
1927
|
cycling = false;
|
|
@@ -1672,8 +1947,8 @@ ${run.output}`);
|
|
|
1672
1947
|
})
|
|
1673
1948
|
);
|
|
1674
1949
|
s.watchRoots.push(CONFIG_FILE);
|
|
1675
|
-
|
|
1676
|
-
|
|
1950
|
+
out4(statusScreen(s));
|
|
1951
|
+
out4(" (ctrl-c stops; edits in the watched paths recompile + re-prove automatically)");
|
|
1677
1952
|
return new Promise((resolveExit) => {
|
|
1678
1953
|
const stop = () => {
|
|
1679
1954
|
for (const w of watchers) w.close();
|
|
@@ -1690,8 +1965,8 @@ init_cloud();
|
|
|
1690
1965
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1691
1966
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1692
1967
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
1693
|
-
var
|
|
1694
|
-
var
|
|
1968
|
+
var out5 = (s) => void process.stdout.write(s + "\n");
|
|
1969
|
+
var err5 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1695
1970
|
function git(args, opts = {}) {
|
|
1696
1971
|
const r = spawnSync2("git", args, { stdio: opts.quiet === true ? "ignore" : "inherit" });
|
|
1697
1972
|
return r.status ?? 1;
|
|
@@ -1723,31 +1998,31 @@ async function gitCredential(action) {
|
|
|
1723
1998
|
if (kv["protocol"] !== "https" || ws === void 0) return 0;
|
|
1724
1999
|
const cloud = `https://${kv["host"]}`;
|
|
1725
2000
|
const token = await sessionToken().catch(() => void 0);
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
2001
|
+
out5(`username=${token ?? "nomos"}`);
|
|
2002
|
+
out5(`password=${ensureSecret(cloud, ws)}`);
|
|
2003
|
+
out5("");
|
|
1729
2004
|
return 0;
|
|
1730
2005
|
}
|
|
1731
2006
|
async function gitSetup(opts) {
|
|
1732
2007
|
const cloud = cloudBase(opts.cloud);
|
|
1733
2008
|
const self = process.argv[1];
|
|
1734
2009
|
if (self === void 0) {
|
|
1735
|
-
|
|
2010
|
+
err5("cannot locate the githolon CLI entry to install as a credential helper");
|
|
1736
2011
|
return 1;
|
|
1737
2012
|
}
|
|
1738
2013
|
const helper = `!"${process.execPath}" "${self}" git-credential`;
|
|
1739
2014
|
if (git(["config", "--global", `credential.${cloud}.helper`, helper]) !== 0 || git(["config", "--global", `credential.${cloud}.useHttpPath`, "true"]) !== 0) {
|
|
1740
|
-
|
|
2015
|
+
err5("git config failed \u2014 is git installed?");
|
|
1741
2016
|
return 1;
|
|
1742
2017
|
}
|
|
1743
|
-
|
|
1744
|
-
|
|
2018
|
+
out5(`\u2713 git credential helper installed for ${cloud} (global gitconfig)`);
|
|
2019
|
+
out5(` pushes/clones of ${cloud}/v2/workspaces/<ws>/git now authenticate from ~/.holon`);
|
|
1745
2020
|
return 0;
|
|
1746
2021
|
}
|
|
1747
2022
|
async function gitRemote(ws, name, opts) {
|
|
1748
2023
|
const cloud = cloudBase(opts.cloud);
|
|
1749
2024
|
if (git(["rev-parse", "--git-dir"], { quiet: true }) !== 0) {
|
|
1750
|
-
|
|
2025
|
+
err5("not a git repository \u2014 run inside your repo (git init first)");
|
|
1751
2026
|
return 1;
|
|
1752
2027
|
}
|
|
1753
2028
|
const setupRc = await gitSetup(opts);
|
|
@@ -1756,7 +2031,7 @@ async function gitRemote(ws, name, opts) {
|
|
|
1756
2031
|
const url = `${cloud}/v2/workspaces/${ws}/git`;
|
|
1757
2032
|
if (git(["remote", "add", name, url], { quiet: true }) !== 0) {
|
|
1758
2033
|
if (git(["remote", "set-url", name, url], { quiet: true }) !== 0) {
|
|
1759
|
-
|
|
2034
|
+
err5(`could not add or update remote '${name}'`);
|
|
1760
2035
|
return 1;
|
|
1761
2036
|
}
|
|
1762
2037
|
}
|
|
@@ -1764,7 +2039,7 @@ async function gitRemote(ws, name, opts) {
|
|
|
1764
2039
|
if (token === void 0) {
|
|
1765
2040
|
const principal = loadCreds().principal;
|
|
1766
2041
|
if (principal === void 0) {
|
|
1767
|
-
|
|
2042
|
+
err5(
|
|
1768
2043
|
`remote '${name}' \u2192 ${url} is wired, but a BIRTH push needs a principal:
|
|
1769
2044
|
githolon login --agent (then just push)
|
|
1770
2045
|
\u2014 or re-run after githolon ws create/login so a principal is on file`
|
|
@@ -1772,13 +2047,13 @@ async function gitRemote(ws, name, opts) {
|
|
|
1772
2047
|
return 1;
|
|
1773
2048
|
}
|
|
1774
2049
|
if (git(["config", `http.${url}.extraHeader`, `x-nomos-principal: ${principal}`]) !== 0) {
|
|
1775
|
-
|
|
2050
|
+
err5("git config failed setting the principal header");
|
|
1776
2051
|
return 1;
|
|
1777
2052
|
}
|
|
1778
2053
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2054
|
+
out5(`\u2713 remote '${name}' \u2192 ${url}`);
|
|
2055
|
+
out5(` workspace secret on file; ${token !== void 0 ? "births ride your session (verified)" : "births ride x-nomos-principal (bare uid)"}`);
|
|
2056
|
+
out5(` birth/deploy by push: git push ${name} main`);
|
|
1782
2057
|
return 0;
|
|
1783
2058
|
}
|
|
1784
2059
|
|
|
@@ -1858,18 +2133,50 @@ var HELP = {
|
|
|
1858
2133
|
what: "Clear the session. Stored workspace secrets are KEPT."
|
|
1859
2134
|
},
|
|
1860
2135
|
ws: {
|
|
1861
|
-
usage: "githolon ws <create|status|retire> [<name>] [--principal <uid|token>] [--cloud <url>] [--target <name>]",
|
|
1862
|
-
what: "Workspace lifecycle on Nomos Cloud. `create` births one (
|
|
2136
|
+
usage: "githolon ws <create|status|retire|reclaim> [<name>] [--principal <uid|token>] [--via <parent>] [--parent <ws>] [--cloud <url>] [--target <name>]",
|
|
2137
|
+
what: "Workspace lifecycle on Nomos Cloud. `create` births one (NO host secret is issued \u2014\nownership is a ledger fact; deploy is open + kernel-gated) or, with --via <parent>, requests it\nas a SIGNED createWorkspace governance offer. `status` shows lineage/phase/verdicts. `retire`\nand `reclaim` are SIGNED governance offers to the parent (default root) \u2014 never host-author\nendpoints; retire withdraws the law (data retained), reclaim authorizes the bailiff to drop\ncustody (only a retired workspace; never root). `status` with no <name> resolves the project's\n`workspace` binding.",
|
|
1863
2138
|
flags: [
|
|
1864
2139
|
["--principal <p>", "the birth's principal (uid or access token; remembered as the default)"],
|
|
2140
|
+
["--via <parent>", "ws create: sign a createWorkspace offer at <parent> instead of the raw birth"],
|
|
2141
|
+
["--parent <ws>", "retire/reclaim: the governance workspace to sign against (default root)"],
|
|
1865
2142
|
["--cloud <url>", "target cloud (default: $NOMOS_CLOUD or the public cloud)"],
|
|
1866
2143
|
["--target <name>", "ws status: pick among the project's named workspace targets"]
|
|
1867
2144
|
],
|
|
1868
|
-
examples: ["githolon ws create my-guestbook", "githolon ws status", "githolon ws retire my-guestbook"]
|
|
2145
|
+
examples: ["githolon ws create my-guestbook", "githolon ws create app1 --via co2", "githolon ws status", "githolon ws retire my-guestbook"]
|
|
2146
|
+
},
|
|
2147
|
+
key: {
|
|
2148
|
+
usage: "githolon key <enroll|show> [--principal <uid>] [--parent <ws>] [--cloud <url>]",
|
|
2149
|
+
what: "The signing DEVICE KEY (ed25519) the SIGNED-governance lane authors with. `enroll` mints one\n(if needed) and enrolls its PUBLIC half on-chain (a signed enrollSigner offer at the parent,\ndefault root) so the warrant gate can prove this device authors as the principal; `show` prints\nthe public identity. The SECRET stays in ~/.holon/credentials.json (0600) \u2014 never on chain.",
|
|
2150
|
+
flags: [
|
|
2151
|
+
["--principal <p>", "the principal the key authors as (default: the logged-in session uid)"],
|
|
2152
|
+
["--parent <ws>", "the governance workspace to enroll at (default root)"],
|
|
2153
|
+
["--cloud <url>", "target cloud"]
|
|
2154
|
+
],
|
|
2155
|
+
examples: ["githolon key enroll", "githolon key show"]
|
|
2156
|
+
},
|
|
2157
|
+
grant: {
|
|
2158
|
+
usage: "githolon grant <admin|creation|quota> <principal> [--max <N>] [--label <L>] [--parent <ws>] [--cloud <url>]",
|
|
2159
|
+
what: "Grant authority as a SIGNED governance offer to the parent (default root) \u2014 never a host\nendpoint. The grant is attributed to YOUR signing principal; only an admin's signature is\nadmitted by the far-side gate. `admin`/`creation` grant the principal-centric relation; `quota`\ngrants a per-principal workspace allowance (--max). Enroll a device key first (githolon key enroll).",
|
|
2160
|
+
flags: [
|
|
2161
|
+
["--max <N>", "grant quota: the principal's workspace allowance (positive int)"],
|
|
2162
|
+
["--label <L>", "grant admin/creation: a provenance label"],
|
|
2163
|
+
["--parent <ws>", "the governance workspace to sign against (default root)"],
|
|
2164
|
+
["--cloud <url>", "target cloud"]
|
|
2165
|
+
],
|
|
2166
|
+
examples: ["githolon grant creation 32c7100e-\u2026", "githolon grant admin 32c7100e-\u2026 --label lieutenant", "githolon grant quota 32c7100e-\u2026 --max 200"]
|
|
2167
|
+
},
|
|
2168
|
+
revoke: {
|
|
2169
|
+
usage: "githolon revoke <admin|creation> <principal> [--parent <ws>] [--cloud <url>]",
|
|
2170
|
+
what: "Revoke authority as a SIGNED governance offer to the parent (default root). The revoked FACT\n(who, when, by whom) rides the chain forever \u2014 audit is structural. Admin-gated, like grant.",
|
|
2171
|
+
flags: [
|
|
2172
|
+
["--parent <ws>", "the governance workspace to sign against (default root)"],
|
|
2173
|
+
["--cloud <url>", "target cloud"]
|
|
2174
|
+
],
|
|
2175
|
+
examples: ["githolon revoke creation 32c7100e-\u2026", "githolon revoke admin 32c7100e-\u2026"]
|
|
1869
2176
|
},
|
|
1870
2177
|
deploy: {
|
|
1871
2178
|
usage: "githolon deploy [<ws>] [--file <deploy.json>] [--cloud <url>] [--target <name>]",
|
|
1872
|
-
what: "POST build/*.deploy.json
|
|
2179
|
+
what: "POST build/*.deploy.json on the OPEN deploy lane (kernel-gated \u2014 no host secret). Prints what\nMOVED (old \u2192 new law hash). With no <ws>, the project's `workspace` binding in nomos.package.mjs\nresolves it.",
|
|
1873
2180
|
flags: [
|
|
1874
2181
|
["--file <path>", "an explicit deploy body (default: the one build/*.deploy.json)"],
|
|
1875
2182
|
["--target <name>", "pick among named targets (workspace: { dev: \u2026, prod: \u2026 })"],
|
|
@@ -1878,9 +2185,9 @@ var HELP = {
|
|
|
1878
2185
|
examples: ["githolon deploy", "githolon deploy my-guestbook", "githolon deploy --target prod"]
|
|
1879
2186
|
},
|
|
1880
2187
|
secret: {
|
|
1881
|
-
usage: "githolon secret <set <ws> <
|
|
1882
|
-
what: "The
|
|
1883
|
-
examples: ["githolon secret set my-ws
|
|
2188
|
+
usage: "githolon secret <set <ws> <push-password> | rotate <ws>> [--cloud <url>]",
|
|
2189
|
+
what: "The git push-password store (~/.holon/credentials.json, 0600) for the push-to-create birth lane\n(sha256(it) becomes the workspace owner bound IN THE CHAIN). The open deploy lane uses no secret.\n`set` imports a push password you already hold; `rotate` is RETIRED (the host holds no ownership\ncredential to rotate \u2014 410).",
|
|
2190
|
+
examples: ["githolon secret set my-ws holon_v1_\u2026"]
|
|
1884
2191
|
},
|
|
1885
2192
|
git: {
|
|
1886
2193
|
usage: "githolon git <setup | remote [<ws>] [<remoteName>]> [--cloud <url>] [--target <name>]",
|
|
@@ -1916,20 +2223,20 @@ init_local_holon();
|
|
|
1916
2223
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1917
2224
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1918
2225
|
import { join as join8 } from "node:path";
|
|
1919
|
-
var
|
|
1920
|
-
var
|
|
2226
|
+
var out6 = (s) => void process.stdout.write(s + "\n");
|
|
2227
|
+
var err6 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1921
2228
|
var WS2 = "local";
|
|
1922
2229
|
async function ledgerInit(dir, opts) {
|
|
1923
2230
|
const cloud = cloudBase(opts.cloud);
|
|
1924
2231
|
if (existsSync7(dir) && readdirSync3(dir).length > 0) {
|
|
1925
|
-
|
|
2232
|
+
err6(`refusing to mint into non-empty directory: ${dir}`);
|
|
1926
2233
|
return 1;
|
|
1927
2234
|
}
|
|
1928
2235
|
let deployFile;
|
|
1929
2236
|
try {
|
|
1930
2237
|
deployFile = discoverDeployJson(process.cwd(), opts.file);
|
|
1931
2238
|
} catch (e) {
|
|
1932
|
-
|
|
2239
|
+
err6(e.message);
|
|
1933
2240
|
return 1;
|
|
1934
2241
|
}
|
|
1935
2242
|
const deployBody = JSON.parse(readFileSync6(deployFile, "utf8"));
|
|
@@ -1937,10 +2244,10 @@ async function ledgerInit(dir, opts) {
|
|
|
1937
2244
|
const c = loadCreds();
|
|
1938
2245
|
const principal = await sessionToken().catch(() => void 0) !== void 0 ? c.session.uid : c.principal;
|
|
1939
2246
|
if (principal === void 0) {
|
|
1940
|
-
|
|
2247
|
+
err6("a holon is born OF someone \u2014 githolon login --agent first (or githolon ws create --principal <uid> once)");
|
|
1941
2248
|
return 1;
|
|
1942
2249
|
}
|
|
1943
|
-
|
|
2250
|
+
out6(`minting a holon locally (law ${domainHash.slice(0, 12)}\u2026, installedBy ${principal})`);
|
|
1944
2251
|
const { eng } = await holonEngine(cloud, deployBody, principal);
|
|
1945
2252
|
await mountFresh(eng, WS2);
|
|
1946
2253
|
author(eng, WS2, "bootstrap", "installDomain", installPayload(eng.hashes.nomos, eng.nomosPkg, principal), "");
|
|
@@ -1953,16 +2260,16 @@ async function ledgerInit(dir, opts) {
|
|
|
1953
2260
|
const cfgPath = join8(gitDir, "config");
|
|
1954
2261
|
writeFileSync4(cfgPath, readFileSync6(cfgPath, "utf8").replace(/bare = true/, "bare = false"), "utf8");
|
|
1955
2262
|
spawnSync3("git", ["-C", dir, "reset", "--hard", "main"], { stdio: "ignore" });
|
|
1956
|
-
|
|
1957
|
-
|
|
2263
|
+
out6(`\u2713 holon written \u2192 ${dir} (a normal git repo; git log IS the audit trail)`);
|
|
2264
|
+
out6(`
|
|
1958
2265
|
Birth it on Nomos Cloud (the cloud re-derives this exact verdict):`);
|
|
1959
|
-
|
|
2266
|
+
out6(` cd ${dir} && npx githolon git remote <ws> && git push nomos main`);
|
|
1960
2267
|
return 0;
|
|
1961
2268
|
}
|
|
1962
2269
|
async function ledgerVerify(dir, opts) {
|
|
1963
2270
|
const gitDir = existsSync7(join8(dir, ".git")) ? join8(dir, ".git") : dir;
|
|
1964
2271
|
if (!existsSync7(join8(gitDir, "HEAD"))) {
|
|
1965
|
-
|
|
2272
|
+
err6(`${dir} is not a git repo (no HEAD)`);
|
|
1966
2273
|
return 1;
|
|
1967
2274
|
}
|
|
1968
2275
|
const { eng } = await holonEngine(cloudBase(opts.cloud), void 0, "verify");
|
|
@@ -1980,8 +2287,8 @@ init_local_holon();
|
|
|
1980
2287
|
import { existsSync as existsSync8 } from "node:fs";
|
|
1981
2288
|
import { join as join9 } from "node:path";
|
|
1982
2289
|
import git2 from "isomorphic-git";
|
|
1983
|
-
var
|
|
1984
|
-
var
|
|
2290
|
+
var out7 = (s) => void process.stdout.write(s + "\n");
|
|
2291
|
+
var err7 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
1985
2292
|
var SOURCE = "source";
|
|
1986
2293
|
var REPLAY = "replay";
|
|
1987
2294
|
function summarizePayload(payload, max = 100) {
|
|
@@ -2017,9 +2324,9 @@ function capJsonStrings(v, max = 2048) {
|
|
|
2017
2324
|
if (typeof v === "string") return v.length > max ? `${v.slice(0, max)}\u2026(${v.length} chars)` : v;
|
|
2018
2325
|
if (Array.isArray(v)) return v.map((x) => capJsonStrings(x, max));
|
|
2019
2326
|
if (typeof v === "object" && v !== null) {
|
|
2020
|
-
const
|
|
2021
|
-
for (const [k, x] of Object.entries(v))
|
|
2022
|
-
return
|
|
2327
|
+
const out10 = {};
|
|
2328
|
+
for (const [k, x] of Object.entries(v)) out10[k] = capJsonStrings(x, max);
|
|
2329
|
+
return out10;
|
|
2023
2330
|
}
|
|
2024
2331
|
return v;
|
|
2025
2332
|
}
|
|
@@ -2066,12 +2373,12 @@ function readKey() {
|
|
|
2066
2373
|
async function replay(target, opts) {
|
|
2067
2374
|
const cloud = cloudBase(opts.cloud);
|
|
2068
2375
|
const json = opts.json === true;
|
|
2069
|
-
const emit = (o) =>
|
|
2376
|
+
const emit = (o) => out7(JSON.stringify(o));
|
|
2070
2377
|
let eng;
|
|
2071
2378
|
try {
|
|
2072
2379
|
({ eng } = await holonEngine(cloud, void 0, "githolon-replay"));
|
|
2073
2380
|
} catch (e) {
|
|
2074
|
-
|
|
2381
|
+
err7(`cannot boot the local holon: ${String(e.message ?? e)}
|
|
2075
2382
|
the runtime rides ${cloud}/v1/runtime/* (cached in ~/.holon/runtime after one fetch)`);
|
|
2076
2383
|
return 1;
|
|
2077
2384
|
}
|
|
@@ -2079,7 +2386,7 @@ async function replay(target, opts) {
|
|
|
2079
2386
|
if (isDir) {
|
|
2080
2387
|
const gitDir = existsSync8(join9(target, ".git")) ? join9(target, ".git") : target;
|
|
2081
2388
|
if (!existsSync8(join9(gitDir, "HEAD"))) {
|
|
2082
|
-
|
|
2389
|
+
err7(`${target} is not a git repo (no HEAD) \u2014 pass a holon directory (githolon ledger init <dir>) or a workspace name`);
|
|
2083
2390
|
return 1;
|
|
2084
2391
|
}
|
|
2085
2392
|
await mountFresh(eng, SOURCE);
|
|
@@ -2087,7 +2394,7 @@ async function replay(target, opts) {
|
|
|
2087
2394
|
} else {
|
|
2088
2395
|
const m = await mountWorkspace(eng, SOURCE, { remote: `${cloud}/v2/workspaces/${target}/git`, headers: {} });
|
|
2089
2396
|
if (m.restored !== true) {
|
|
2090
|
-
|
|
2397
|
+
err7(
|
|
2091
2398
|
`workspace '${target}' has no refs/heads/main on ${cloud} \u2014 nothing to replay` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : "") + `
|
|
2092
2399
|
check the name (githolon ws status ${target}), or pass a local holon directory instead`
|
|
2093
2400
|
);
|
|
@@ -2096,7 +2403,7 @@ async function replay(target, opts) {
|
|
|
2096
2403
|
}
|
|
2097
2404
|
const chain = await walkChain(eng, SOURCE);
|
|
2098
2405
|
if (chain.length === 0) {
|
|
2099
|
-
|
|
2406
|
+
err7(`the chain on '${target}' carries no intents \u2014 nothing to replay`);
|
|
2100
2407
|
return 1;
|
|
2101
2408
|
}
|
|
2102
2409
|
await mountFresh(eng, REPLAY);
|
|
@@ -2105,8 +2412,8 @@ async function replay(target, opts) {
|
|
|
2105
2412
|
const tallies = /* @__PURE__ */ new Map();
|
|
2106
2413
|
let autoRun = !interactive;
|
|
2107
2414
|
if (!json) {
|
|
2108
|
-
|
|
2109
|
-
if (interactive)
|
|
2415
|
+
out7(`replaying ${chain.length} intent(s) from ${isDir ? target : `${cloud} :: ${target}`} \u2014 every step re-admitted through the wasm gate`);
|
|
2416
|
+
if (interactive) out7(" keys: enter = next intent \xB7 a = run to the end \xB7 q = quit\n");
|
|
2110
2417
|
}
|
|
2111
2418
|
for (let i = 0; i < chain.length; i++) {
|
|
2112
2419
|
const { oid, bytes, doc } = chain[i];
|
|
@@ -2120,8 +2427,8 @@ async function replay(target, opts) {
|
|
|
2120
2427
|
if (res.ok === false) {
|
|
2121
2428
|
const msg = `replay REFUSED at intent ${i} (${domain}/${directive}, ${oid.slice(0, 10)}): ${res.error ?? "unknown"}`;
|
|
2122
2429
|
if (json) emit({ step: i, oid, domain, directive, refused: true, error: res.error ?? "unknown" });
|
|
2123
|
-
else
|
|
2124
|
-
|
|
2430
|
+
else err7(msg);
|
|
2431
|
+
err7("the chain's own gate rejects this entry on a fresh fold \u2014 the source it came from would refuse it too (verify below names the check)");
|
|
2125
2432
|
printVerdict(verifyChainLocal(eng, SOURCE));
|
|
2126
2433
|
return 1;
|
|
2127
2434
|
}
|
|
@@ -2142,31 +2449,31 @@ async function replay(target, opts) {
|
|
|
2142
2449
|
tallies: Object.fromEntries(tallies)
|
|
2143
2450
|
});
|
|
2144
2451
|
} else {
|
|
2145
|
-
|
|
2146
|
-
|
|
2452
|
+
out7(`[${String(i + 1).padStart(String(chain.length).length)}/${chain.length}] ${domain}/${directive} (${oid.slice(0, 10)}, ${ms.toFixed(0)} ms)`);
|
|
2453
|
+
out7(` payload ${summarizePayload(doc.payload?.payload)}`);
|
|
2147
2454
|
for (const ev of doc.events ?? []) {
|
|
2148
2455
|
const id = ev.aggregate ?? "?";
|
|
2149
2456
|
const was = before.get(id);
|
|
2150
2457
|
const verb = ev.marker === "Create" ? "+ created" : ev.marker === "Delete" ? "- deleted" : was === void 0 ? "~ touched" : "~ updated";
|
|
2151
|
-
|
|
2458
|
+
out7(` ${verb} ${id}`);
|
|
2152
2459
|
const ops = summarizeOps(ev);
|
|
2153
|
-
if (ops.length > 0)
|
|
2460
|
+
if (ops.length > 0) out7(` ${ops}`);
|
|
2154
2461
|
}
|
|
2155
2462
|
const tallyLine = [...tallies.entries()].map(([t, n]) => `${t}:${n}`).join(" ");
|
|
2156
2463
|
const showTally = stepEvery > 0 ? (i + 1) % stepEvery === 0 || i === chain.length - 1 : true;
|
|
2157
|
-
if (showTally && tallyLine.length > 0)
|
|
2464
|
+
if (showTally && tallyLine.length > 0) out7(` rows ${tallyLine}`);
|
|
2158
2465
|
}
|
|
2159
2466
|
if (interactive && !autoRun && i < chain.length - 1) {
|
|
2160
2467
|
const key = await readKey();
|
|
2161
2468
|
if (key === "quit") {
|
|
2162
|
-
|
|
2469
|
+
out7(`
|
|
2163
2470
|
stopped at intent ${i + 1}/${chain.length} \u2014 the verify verdict below covers the WHOLE chain as delivered`);
|
|
2164
2471
|
break;
|
|
2165
2472
|
}
|
|
2166
2473
|
if (key === "all") autoRun = true;
|
|
2167
2474
|
}
|
|
2168
2475
|
}
|
|
2169
|
-
if (!json)
|
|
2476
|
+
if (!json) out7("");
|
|
2170
2477
|
const verdict = verifyChainLocal(eng, SOURCE);
|
|
2171
2478
|
if (json) {
|
|
2172
2479
|
emit({ finale: true, verdict });
|
|
@@ -2180,32 +2487,32 @@ init_local_holon();
|
|
|
2180
2487
|
init_cloud();
|
|
2181
2488
|
init_engine();
|
|
2182
2489
|
var SOURCE2 = "source";
|
|
2183
|
-
var
|
|
2490
|
+
var out8 = (s) => {
|
|
2184
2491
|
process.stdout.write(s + "\n");
|
|
2185
2492
|
};
|
|
2186
|
-
var
|
|
2493
|
+
var err8 = (s) => {
|
|
2187
2494
|
process.stderr.write(`error: ${s}
|
|
2188
2495
|
`);
|
|
2189
2496
|
};
|
|
2190
2497
|
async function decrypt(ws, opts) {
|
|
2191
2498
|
const cloud = cloudBase(opts.cloud);
|
|
2192
2499
|
if (!opts.as) {
|
|
2193
|
-
|
|
2500
|
+
err8("--as <principal> is required (who to decrypt as)");
|
|
2194
2501
|
return 1;
|
|
2195
2502
|
}
|
|
2196
2503
|
if (!opts.secret) {
|
|
2197
|
-
|
|
2504
|
+
err8("--secret <base64 X25519 device secret> is required");
|
|
2198
2505
|
return 1;
|
|
2199
2506
|
}
|
|
2200
2507
|
if (!opts.query) {
|
|
2201
|
-
|
|
2508
|
+
err8("--query <queryId> is required (the read to decrypt)");
|
|
2202
2509
|
return 1;
|
|
2203
2510
|
}
|
|
2204
2511
|
let eng;
|
|
2205
2512
|
try {
|
|
2206
2513
|
({ eng } = await holonEngine(cloud, opts.overlay, "githolon-decrypt"));
|
|
2207
2514
|
} catch (e) {
|
|
2208
|
-
|
|
2515
|
+
err8(`cannot boot the local holon: ${String(e.message ?? e)}`);
|
|
2209
2516
|
return 1;
|
|
2210
2517
|
}
|
|
2211
2518
|
const m = await mountWorkspace(eng, SOURCE2, {
|
|
@@ -2213,7 +2520,7 @@ async function decrypt(ws, opts) {
|
|
|
2213
2520
|
headers: { "x-nomos-principal": opts.as }
|
|
2214
2521
|
});
|
|
2215
2522
|
if (m.restored !== true) {
|
|
2216
|
-
|
|
2523
|
+
err8(`workspace '${ws}' has no main on ${cloud}, or its clone was refused for '${opts.as}'` + (m.restoreError ? ` (${String(m.restoreError).split(" | ")[0]})` : ""));
|
|
2217
2524
|
return 1;
|
|
2218
2525
|
}
|
|
2219
2526
|
const wraps = query(eng, SOURCE2, "wrappedKeysByRecipient", JSON.stringify({ recipientPrincipal: opts.as }), opts.as);
|
|
@@ -2228,14 +2535,14 @@ async function decrypt(ws, opts) {
|
|
|
2228
2535
|
}
|
|
2229
2536
|
}
|
|
2230
2537
|
}
|
|
2231
|
-
if (opts.json !== true)
|
|
2538
|
+
if (opts.json !== true) out8(`# resolved ${keys.length} scope key(s) for '${opts.as}': ${keys.map((k) => `${k.scope}@${k.epoch}`).join(", ") || "(none)"}`);
|
|
2232
2539
|
const rows = query(eng, SOURCE2, opts.query, JSON.stringify(opts.params ?? {}), opts.as, keys);
|
|
2233
2540
|
if (rows && !Array.isArray(rows) && rows.ok === false) {
|
|
2234
|
-
|
|
2541
|
+
err8(`the read was refused: ${rows.error}` + (String(rows.error).startsWith("read-forbidden") ? `
|
|
2235
2542
|
'${opts.as}' is not granted this scope's read capability` : ""));
|
|
2236
2543
|
return 1;
|
|
2237
2544
|
}
|
|
2238
|
-
|
|
2545
|
+
out8(JSON.stringify(rows, null, opts.json === true ? 0 : 2));
|
|
2239
2546
|
return 0;
|
|
2240
2547
|
}
|
|
2241
2548
|
|
|
@@ -2243,8 +2550,8 @@ async function decrypt(ws, opts) {
|
|
|
2243
2550
|
init_engine();
|
|
2244
2551
|
init_cloud();
|
|
2245
2552
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
2246
|
-
var
|
|
2247
|
-
var
|
|
2553
|
+
var out9 = (s) => void process.stdout.write(s + "\n");
|
|
2554
|
+
var err9 = (s) => void process.stderr.write("error: " + s + "\n");
|
|
2248
2555
|
function lawDiffVerdict(localHash, installed, ws) {
|
|
2249
2556
|
const active = installed.filter((d) => d.phase === "Active" && typeof d.domainHash === "string");
|
|
2250
2557
|
const currents = active.filter((d) => d.current === true);
|
|
@@ -2282,7 +2589,7 @@ async function status(wsArg, opts) {
|
|
|
2282
2589
|
try {
|
|
2283
2590
|
ws = await resolveWorkspace(wsArg, process.cwd(), opts.target, "githolon status <ws>");
|
|
2284
2591
|
} catch (e) {
|
|
2285
|
-
|
|
2592
|
+
err9(e.message);
|
|
2286
2593
|
return 1;
|
|
2287
2594
|
}
|
|
2288
2595
|
let localHash;
|
|
@@ -2294,35 +2601,35 @@ async function status(wsArg, opts) {
|
|
|
2294
2601
|
}
|
|
2295
2602
|
const r = await fetch(`${cloud}/v2/workspaces/${ws}/domains`).catch(() => void 0);
|
|
2296
2603
|
if (r === void 0) {
|
|
2297
|
-
|
|
2604
|
+
err9(`cannot reach ${cloud} \u2014 githolon status compares against the DEPLOYED law and needs the cloud (local-only: githolon dev)`);
|
|
2298
2605
|
return 1;
|
|
2299
2606
|
}
|
|
2300
2607
|
const d = await r.json().catch(() => void 0);
|
|
2301
2608
|
if (!r.ok || d?.ok !== true) {
|
|
2302
|
-
|
|
2609
|
+
err9(`status '${ws}' failed (${r.status}): ${d ? JSON.stringify(d) : "no body"}
|
|
2303
2610
|
is the workspace born? githolon ws create ${ws}`);
|
|
2304
2611
|
return 1;
|
|
2305
2612
|
}
|
|
2306
|
-
|
|
2613
|
+
out9(`workspace ${ws} on ${cloud}`);
|
|
2307
2614
|
const allDomains = d.domains ?? [];
|
|
2308
2615
|
const controlPlaneHashes = new Set(CONTROL_PLANE_KEYS.map((k) => d.currentLaw?.[k]).filter(Boolean));
|
|
2309
2616
|
const domains = allDomains.filter((dm) => !controlPlaneHashes.has(dm.domainHash));
|
|
2310
2617
|
const controllerCount = allDomains.length - domains.length;
|
|
2311
|
-
if (allDomains.length === 0)
|
|
2618
|
+
if (allDomains.length === 0) out9(`installed (none \u2014 not even the controller; the workspace may be unborn)`);
|
|
2312
2619
|
for (const dom of domains) {
|
|
2313
2620
|
const mark = dom.current === true ? " \u2190 current (the holon resolves dispatch here)" : "";
|
|
2314
|
-
|
|
2621
|
+
out9(`installed ${(dom.domainHash ?? "?").slice(0, 16)}\u2026 ${dom.phase ?? "?"} by ${dom.installedBy ?? "?"}${mark}`);
|
|
2315
2622
|
}
|
|
2316
|
-
if (controllerCount > 0)
|
|
2623
|
+
if (controllerCount > 0) out9(` (+ ${controllerCount} control-plane install(s) \u2014 the nomos lifecycle controller; not tenant law)`);
|
|
2317
2624
|
const tenantActive = domains.filter((dm) => dm.phase === "Active").length;
|
|
2318
|
-
if (tenantActive > 1)
|
|
2625
|
+
if (tenantActive > 1) out9(` (${tenantActive} active tenant installs \u2014 install-beside keeps every prior deploy Active; the holon serves the current one per domain)`);
|
|
2319
2626
|
if (localHash === void 0) {
|
|
2320
|
-
|
|
2627
|
+
out9(`local (no build/*.deploy.json here \u2014 run \`githolon compile\` to build the law this project authors)`);
|
|
2321
2628
|
return 0;
|
|
2322
2629
|
}
|
|
2323
|
-
|
|
2630
|
+
out9(`local ${localHash.slice(0, 16)}\u2026 (build/*.deploy.json)`);
|
|
2324
2631
|
const verdict = lawDiffVerdict(localHash, domains, ws);
|
|
2325
|
-
for (const line of verdict.lines)
|
|
2632
|
+
for (const line of verdict.lines) out9(line);
|
|
2326
2633
|
return verdict.inSync ? 0 : 2;
|
|
2327
2634
|
}
|
|
2328
2635
|
|
|
@@ -2353,18 +2660,32 @@ Identity (~/.holon/credentials.json \u2014 sessions auto-refresh):
|
|
|
2353
2660
|
githolon login --token <refresh_token> adopt an existing captain app session
|
|
2354
2661
|
githolon whoami show the principal births will ride
|
|
2355
2662
|
githolon logout clear the session (workspace secrets kept)
|
|
2663
|
+
githolon key enroll [--principal <uid>] [--parent <ws>] mint a signing device key + enroll it on-chain
|
|
2664
|
+
(secret stays local; warrants SIGNED governance)
|
|
2665
|
+
githolon key show [--principal <uid>] the device key's public identity (never the secret)
|
|
2666
|
+
|
|
2667
|
+
Governance (SIGNED client-side, relayed as {intentBytes} \u2014 never a host-author endpoint):
|
|
2668
|
+
githolon grant <admin|creation> <principal> [--label L] grant authority at the parent (default root)
|
|
2669
|
+
githolon grant quota <principal> --max <N> grant a per-principal workspace allowance
|
|
2670
|
+
githolon revoke <admin|creation> <principal> revoke it (the revoked fact rides the chain)
|
|
2671
|
+
(all gated by the parent's law: only an admin's
|
|
2672
|
+
signature is admitted; --parent <p> targets a platform)
|
|
2356
2673
|
|
|
2357
|
-
Nomos Cloud (
|
|
2358
|
-
|
|
2359
|
-
githolon ws create <name> [--principal <uid|token>] birth a workspace
|
|
2674
|
+
Nomos Cloud (a bare <ws> resolves from nomos.package.mjs's optional \`workspace\` binding;
|
|
2675
|
+
deploy is OPEN + kernel-gated \u2014 births return no host secret, ownership is a ledger fact):
|
|
2676
|
+
githolon ws create <name> [--principal <uid|token>] birth a workspace (no host secret is issued)
|
|
2360
2677
|
(no identity on file? it self-onboards an agent login)
|
|
2678
|
+
githolon ws create <name> --via <parent> request it as a SIGNED createWorkspace governance
|
|
2679
|
+
offer at the parent
|
|
2361
2680
|
githolon ws status [<name>] workspace status (lineage, phase, verdicts)
|
|
2362
|
-
githolon ws retire <name>
|
|
2363
|
-
|
|
2364
|
-
githolon
|
|
2681
|
+
githolon ws retire <name> [--parent <ws>] retire a workspace via a SIGNED governance offer;
|
|
2682
|
+
quota freed; data retained; reads keep serving
|
|
2683
|
+
githolon ws reclaim <name> [--parent <ws>] lawful custody discard via a SIGNED governance offer
|
|
2684
|
+
(only a retired workspace; never root)
|
|
2685
|
+
githolon deploy [<ws>] [--file <deploy.json>] POST build/*.deploy.json (open deploy lane);
|
|
2365
2686
|
prints what moved (old \u2192 new law hash)
|
|
2366
|
-
githolon secret set <ws> <
|
|
2367
|
-
githolon secret rotate <ws>
|
|
2687
|
+
githolon secret set <ws> <push-password> store a git push password (push-to-create birth lane)
|
|
2688
|
+
githolon secret rotate <ws> RETIRED \u2014 host holds no ownership credential (410)
|
|
2368
2689
|
|
|
2369
2690
|
Git lane (stock git push as deploy/birth \u2014 see also: push-to-create):
|
|
2370
2691
|
githolon git setup install the credential helper for the cloud host
|
|
@@ -2387,15 +2708,23 @@ Options:
|
|
|
2387
2708
|
function cloudArgs(rest) {
|
|
2388
2709
|
const pos = [];
|
|
2389
2710
|
const opts = {};
|
|
2711
|
+
const VALUE_FLAGS = ["--cloud", "--principal", "--file", "--target", "--parent", "--via", "--max", "--label"];
|
|
2390
2712
|
for (let i = 0; i < rest.length; i++) {
|
|
2391
2713
|
const a = rest[i];
|
|
2392
|
-
if (a
|
|
2714
|
+
if (VALUE_FLAGS.includes(a)) {
|
|
2393
2715
|
const v = rest[++i];
|
|
2394
2716
|
if (v === void 0) return { pos, opts, bad: `${a} requires a value` };
|
|
2395
2717
|
if (a === "--cloud") opts.cloud = v;
|
|
2396
2718
|
else if (a === "--principal") opts.principal = v;
|
|
2397
2719
|
else if (a === "--target") opts.target = v;
|
|
2398
|
-
else opts.
|
|
2720
|
+
else if (a === "--parent") opts.parent = v;
|
|
2721
|
+
else if (a === "--via") opts.via = v;
|
|
2722
|
+
else if (a === "--label") opts.label = v;
|
|
2723
|
+
else if (a === "--max") {
|
|
2724
|
+
const n = Number(v);
|
|
2725
|
+
if (!Number.isInteger(n) || n <= 0) return { pos, opts, bad: `--max requires a positive integer` };
|
|
2726
|
+
opts.max = n;
|
|
2727
|
+
} else opts.file = v;
|
|
2399
2728
|
} else if (a.startsWith("--")) {
|
|
2400
2729
|
return { pos, opts, bad: `unknown flag '${a}'` };
|
|
2401
2730
|
} else {
|
|
@@ -2428,7 +2757,8 @@ ${USAGE}`);
|
|
|
2428
2757
|
}
|
|
2429
2758
|
}
|
|
2430
2759
|
if (sub2 === "retire" && name !== void 0) return wsRetire(name, opts);
|
|
2431
|
-
|
|
2760
|
+
if (sub2 === "reclaim" && name !== void 0) return wsReclaim(name, opts);
|
|
2761
|
+
return usageFail("expected: githolon ws <create|status|retire|reclaim> <name>");
|
|
2432
2762
|
}
|
|
2433
2763
|
if (command === "deploy") {
|
|
2434
2764
|
try {
|
|
@@ -2700,6 +3030,40 @@ ${commandHelp("status")}`);
|
|
|
2700
3030
|
if (argv[0] === "ws" || argv[0] === "deploy" || argv[0] === "secret") {
|
|
2701
3031
|
return runCloud(argv);
|
|
2702
3032
|
}
|
|
3033
|
+
if (argv[0] === "key") {
|
|
3034
|
+
const { pos, opts, bad } = cloudArgs(argv.slice(1));
|
|
3035
|
+
if (bad !== void 0) {
|
|
3036
|
+
process.stderr.write(`error: ${bad}
|
|
3037
|
+
|
|
3038
|
+
${USAGE}`);
|
|
3039
|
+
return 1;
|
|
3040
|
+
}
|
|
3041
|
+
const [sub] = pos;
|
|
3042
|
+
if (sub === "enroll") return keyEnroll(opts);
|
|
3043
|
+
if (sub === "show") return keyShow(opts);
|
|
3044
|
+
process.stderr.write(`error: expected: githolon key <enroll|show> [--principal <uid>] [--parent <ws>]
|
|
3045
|
+
|
|
3046
|
+
${USAGE}`);
|
|
3047
|
+
return 1;
|
|
3048
|
+
}
|
|
3049
|
+
if (argv[0] === "grant" || argv[0] === "revoke") {
|
|
3050
|
+
const verb = argv[0];
|
|
3051
|
+
const { pos, opts, bad } = cloudArgs(argv.slice(1));
|
|
3052
|
+
if (bad !== void 0) {
|
|
3053
|
+
process.stderr.write(`error: ${bad}
|
|
3054
|
+
|
|
3055
|
+
${USAGE}`);
|
|
3056
|
+
return 1;
|
|
3057
|
+
}
|
|
3058
|
+
const [kind, subject] = pos;
|
|
3059
|
+
if (kind === void 0 || subject === void 0) {
|
|
3060
|
+
process.stderr.write(`error: expected: githolon ${verb} <admin|creation${verb === "grant" ? "|quota" : ""}> <principal>
|
|
3061
|
+
|
|
3062
|
+
${USAGE}`);
|
|
3063
|
+
return 1;
|
|
3064
|
+
}
|
|
3065
|
+
return grantOrRevoke(verb, kind, subject, opts);
|
|
3066
|
+
}
|
|
2703
3067
|
if (argv[0] === "login") {
|
|
2704
3068
|
const agent = argv.includes("--agent");
|
|
2705
3069
|
const tokenAt = argv.indexOf("--token");
|
|
@@ -2756,8 +3120,8 @@ ${USAGE}`);
|
|
|
2756
3120
|
let parsed;
|
|
2757
3121
|
try {
|
|
2758
3122
|
parsed = parseArgs(argv);
|
|
2759
|
-
} catch (
|
|
2760
|
-
process.stderr.write(`error: ${
|
|
3123
|
+
} catch (err10) {
|
|
3124
|
+
process.stderr.write(`error: ${err10.message}
|
|
2761
3125
|
|
|
2762
3126
|
${USAGE}`);
|
|
2763
3127
|
return 1;
|
|
@@ -2776,8 +3140,8 @@ ${USAGE}`);
|
|
|
2776
3140
|
process.stdout.write(`created ${path}
|
|
2777
3141
|
`);
|
|
2778
3142
|
return 0;
|
|
2779
|
-
} catch (
|
|
2780
|
-
process.stderr.write(`error: ${
|
|
3143
|
+
} catch (err10) {
|
|
3144
|
+
process.stderr.write(`error: ${err10.message}
|
|
2781
3145
|
`);
|
|
2782
3146
|
return 1;
|
|
2783
3147
|
}
|