@githolon/dsl 0.16.2 → 0.17.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/dart/CHANGELOG.md +5 -0
- package/dart/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/src/codegen_ts.ts +12 -8
- package/src/framework/workspaces.ts +204 -128
package/dart/CHANGELOG.md
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
## 0.17.0
|
|
2
|
+
- Lockstep bump with the birth-primitive release (see `@githolon/dsl` 0.17.0): birth is one law primitive
|
|
3
|
+
(`birthChild`), the platform-creator tier collapsed onto `#creator`, `grantCreation` takes a user or key
|
|
4
|
+
subject. The Dart wire types track the current /v2 governance surface.
|
|
5
|
+
|
|
1
6
|
## 0.16.0
|
|
2
7
|
- First pub.dev release: kernel wire types, `NomosBridge` protocol, typed offer/query/watch surface.
|
package/dart/pubspec.yaml
CHANGED
|
@@ -11,7 +11,7 @@ description: >-
|
|
|
11
11
|
Nomos 2 Dart frontend types — the hand-written kernel wire types plus the
|
|
12
12
|
code-generated per-domain aggregate + directive-payload types. (De)serializes
|
|
13
13
|
the EXACT serde JSON the Rust kernel + projection produce/consume.
|
|
14
|
-
version: 0.
|
|
14
|
+
version: 0.17.0
|
|
15
15
|
|
|
16
16
|
environment:
|
|
17
17
|
sdk: ^3.5.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@githolon/dsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
package/src/codegen_ts.ts
CHANGED
|
@@ -534,7 +534,7 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
|
|
|
534
534
|
// the obligation the birth provider materializes. parentWs = "root" for a top-level platform,
|
|
535
535
|
// else the parent holon's own name. timestamps ride IN the payload (the determinism doctrine);
|
|
536
536
|
// a platform's foundingAdminHash = sha256(your chosen platformAdminKey) — the host never mints it.
|
|
537
|
-
const emit = (name: string, doc: string, params: string[], lane: { kind: "boot" } | { kind: "offer"; parentWs: string; directiveId: "createWorkspace" | "
|
|
537
|
+
const emit = (name: string, doc: string, params: string[], lane: { kind: "boot" } | { kind: "offer"; parentWs: string; directiveId: "createWorkspace" | "birthChild" }) => {
|
|
538
538
|
if (emittedBirths.has(name)) return;
|
|
539
539
|
emittedBirths.add(name);
|
|
540
540
|
const sig = `opts: NomosBirthAuth & { cloud: string; ${params.join("; ")} }`;
|
|
@@ -547,14 +547,18 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
|
|
|
547
547
|
);
|
|
548
548
|
return;
|
|
549
549
|
}
|
|
550
|
-
|
|
550
|
+
// A platform birth is birthChild WITH genesis (the tier collapsed onto #creator; birthPlatform is
|
|
551
|
+
// gone). The genesis stamps ride IN the payload (the determinism doctrine): foundingAdminHash =
|
|
552
|
+
// sha256(your chosen platformAdminKey), bornAt = the ISO birth time. The host replays the
|
|
553
|
+
// law-authored chain — it mints no genesis.
|
|
554
|
+
const isPlatformOffer = lane.directiveId === "birthChild";
|
|
551
555
|
const bodyFields = [
|
|
552
556
|
`name: opts.name`,
|
|
553
557
|
`parent: ${lane.parentWs}`,
|
|
554
558
|
`principal: opts.principal ?? ""`,
|
|
555
559
|
`requestedBy: opts.principal ?? ""`,
|
|
556
560
|
`requestedAt: Date.now()`,
|
|
557
|
-
...(isPlatformOffer ? [`foundingAdminHash: await nomosKeyHash(opts.platformAdminKey)`] : []),
|
|
561
|
+
...(isPlatformOffer ? [`foundingAdminHash: await nomosKeyHash(opts.platformAdminKey)`, `bornAt: new Date().toISOString()`] : []),
|
|
558
562
|
];
|
|
559
563
|
out.push(
|
|
560
564
|
`/** ${doc} */`,
|
|
@@ -570,13 +574,13 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
|
|
|
570
574
|
const isPlatform = t.childIds.length > 0;
|
|
571
575
|
if (!childIds.has(t.id)) {
|
|
572
576
|
// A TOP-LEVEL type: a workspace births on the boot lane; a platform-kind
|
|
573
|
-
// coordinator births via the root holon's
|
|
577
|
+
// coordinator births via the root holon's birthChild offer (with genesis).
|
|
574
578
|
emit(
|
|
575
579
|
fnName(t.id),
|
|
576
|
-
`Birth a '${t.id}' workspace${isPlatform ? " (a platform-kind coordinator: hosts its own subtree; PRESENT your chosen platformAdminKey) via root's
|
|
580
|
+
`Birth a '${t.id}' workspace${isPlatform ? " (a platform-kind coordinator: hosts its own subtree; PRESENT your chosen platformAdminKey) via root's birthChild offer" : " on the boot lane (the one-time secret comes back)"} — derived from the taxonomy. Placement is automatic (measured shape) — no tier to pick.`,
|
|
577
581
|
isPlatform ? [`name: string`, `platformAdminKey: string`] : [`name: string`],
|
|
578
582
|
isPlatform
|
|
579
|
-
? { kind: "offer", parentWs: `"root"`, directiveId: "
|
|
583
|
+
? { kind: "offer", parentWs: `"root"`, directiveId: "birthChild" }
|
|
580
584
|
: { kind: "boot" },
|
|
581
585
|
);
|
|
582
586
|
}
|
|
@@ -587,11 +591,11 @@ export function generateTsClient(modules: readonly DomainModule[], opts: TsClien
|
|
|
587
591
|
const childIsPlatform = child.childIds.length > 0;
|
|
588
592
|
emit(
|
|
589
593
|
fnName(child.id),
|
|
590
|
-
`Birth a '${child.id}' under a '${t.id}' — the taxonomy's hasMany edge, a governance offer on the parent holon (POST /v2/workspaces/<${parentParam}>/${childIsPlatform ? "
|
|
594
|
+
`Birth a '${child.id}' under a '${t.id}' — the taxonomy's hasMany edge, a governance offer on the parent holon (POST /v2/workspaces/<${parentParam}>/${childIsPlatform ? "birthChild" : "createWorkspace"}; lineage + pool enforced by the parent's law).${childIsPlatform ? " PRESENT your chosen platformAdminKey." : ""} Placement is automatic (measured shape).`,
|
|
591
595
|
childIsPlatform
|
|
592
596
|
? [`${parentParam}: string`, `name: string`, `platformAdminKey: string`]
|
|
593
597
|
: [`${parentParam}: string`, `name: string`],
|
|
594
|
-
{ kind: "offer", parentWs: `opts.${parentParam}`, directiveId: childIsPlatform ? "
|
|
598
|
+
{ kind: "offer", parentWs: `opts.${parentParam}`, directiveId: childIsPlatform ? "birthChild" : "createWorkspace" },
|
|
595
599
|
);
|
|
596
600
|
}
|
|
597
601
|
}
|
|
@@ -96,18 +96,24 @@ export const WORKSPACE_ROLES = ["cloud-root", "platform", "tenant"] as const;
|
|
|
96
96
|
const WORKSPACE_SELF_ID = "workspaceself-self";
|
|
97
97
|
const SELF_ADMIN_OBJECT = "workspace:self";
|
|
98
98
|
export const WorkspaceSelf = aggregate("WorkspaceSelf", {
|
|
99
|
+
// VESTIGIAL node-class label (the same smell as `kind`): NOTHING decides on its value — the kernel, host,
|
|
100
|
+
// and gate read it nowhere; the write-once invariants below key on `name`'s PRESENCE (the "born" marker),
|
|
101
|
+
// never role. No longer set on new records (birthCloud/ensureWorkspaceSelf stopped writing it). Kept only
|
|
102
|
+
// for era-safety (old records carry it); slated for removal with `kind` in one #58 disposition pass.
|
|
99
103
|
role: t.enum(WORKSPACE_ROLES).merge(Lww),
|
|
100
104
|
name: t.string().merge(Lww),
|
|
101
105
|
});
|
|
102
106
|
// The one-off SELF-IDENTITY backfill for an already-born workspace (root predates WorkspaceSelf). UNGATED
|
|
103
107
|
// by the admin gate — it must run BEFORE the admin tuple/gate are live — but WRITE-ONCE (the invariant
|
|
104
|
-
// below refuses a re-set once the
|
|
105
|
-
//
|
|
108
|
+
// below refuses a re-set once the self-record exists), so a race or a malicious re-set cannot freeze
|
|
109
|
+
// governance. The HOST authors this in the serialized genesis re-entry, winning any race.
|
|
106
110
|
export const ensureWorkspaceSelf = directive("ensureWorkspaceSelf")
|
|
107
111
|
.ensures(WorkspaceSelf)
|
|
108
|
-
|
|
112
|
+
// `role` accepted-but-ignored (vestigial — see WorkspaceSelf): callers may still pass it; the plan no
|
|
113
|
+
// longer sets it. `name` is the self-record's only meaningful field + the born marker.
|
|
114
|
+
.payload(z.object({ role: z.enum(WORKSPACE_ROLES).optional(), name: z.string().min(1) }))
|
|
109
115
|
.plan((p) => {
|
|
110
|
-
ensure(WorkspaceSelf, WORKSPACE_SELF_ID).set("
|
|
116
|
+
ensure(WorkspaceSelf, WORKSPACE_SELF_ID).set("name", p.name);
|
|
111
117
|
return [];
|
|
112
118
|
});
|
|
113
119
|
export const workspaceSelfWriteOnce = workspaceInvariant("WorkspaceSelfWriteOnce")
|
|
@@ -118,8 +124,11 @@ export const workspaceSelfWriteOnce = workspaceInvariant("WorkspaceSelfWriteOnce
|
|
|
118
124
|
// 2026-06-15). Bare matches — exactly the convention the sharding invariants already fire under.
|
|
119
125
|
.on("ensureWorkspaceSelf")
|
|
120
126
|
.reads(() => [refAs("self", "WorkspaceSelf", "pre:" + WORKSPACE_SELF_ID)])
|
|
127
|
+
// WRITE-ONCE on the self-record's PRESENCE via `name` (not the vestigial `role`). At genesis name + role
|
|
128
|
+
// were always co-written, so an existing born workspace has `name` ⇒ behaviour preserved; a fresh one has
|
|
129
|
+
// neither ⇒ first set admitted.
|
|
121
130
|
.assert(({ self }) =>
|
|
122
|
-
(self as {
|
|
131
|
+
(self as { name?: string })?.name ? { reject: "workspace-self-already-set" } : { accept: true },
|
|
123
132
|
);
|
|
124
133
|
|
|
125
134
|
// ── THE GOVERNANCE RELATION SCHEMA (full-native Zanzibar — the ONE authz model) ──────────────────────
|
|
@@ -136,8 +145,10 @@ export const workspaceSelfWriteOnce = workspaceInvariant("WorkspaceSelfWriteOnce
|
|
|
136
145
|
// side) DISCARDED at evaluation. Each relation is a flat `self()` (directly-stored tuples) — NO rewrite
|
|
137
146
|
// algebra (no computed/from/union): control-plane authority is direct membership only.
|
|
138
147
|
// * admin — grantAdmin/revokeAdmin + every admin-mutating governance directive.
|
|
139
|
-
// * creator —
|
|
140
|
-
//
|
|
148
|
+
// * creator — THE birth authority: createWorkspace + birthChild (leaf OR platform). The tier
|
|
149
|
+
// collapsed onto this one relation (2026-06-24); a subject is user: or key:.
|
|
150
|
+
// * platformCreator — VESTIGIAL: its directives (birthPlatform/grant·revokePlatformCreation) are removed;
|
|
151
|
+
// no .requires references it. Kept until the #58 disposition pass (with kind/role).
|
|
141
152
|
// SELF-RELATIVE: custody guarantees a workspace only ever folds its OWN relations table, so
|
|
142
153
|
// `workspace:root#admin` collapses to `workspace:self#admin` — the SAME law governs root and every
|
|
143
154
|
// born platform with no name plumbing. (A pure tenant runs no governance ⇒ no schema ⇒ the gate is
|
|
@@ -224,9 +235,12 @@ export const CloudWorkspace = aggregate("CloudWorkspace", {
|
|
|
224
235
|
*/
|
|
225
236
|
byteCap: t.int().merge(Lww).optional(),
|
|
226
237
|
/**
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
238
|
+
* DERIVED LABEL (the `kind`-collapse, architecture/provisioning_as_obligations.md): set to `"platform"`
|
|
239
|
+
* iff the birth order carried a genesis (a `foundingAdminHash`), else absent/`"workspace"`. It is NOT a
|
|
240
|
+
* node-class and NOTHING decides on it — the birth provider keys off `foundingAdminHash`, the genuine
|
|
241
|
+
* signal that a child is born running the governance law. Kept only so old readers/queries that filter by
|
|
242
|
+
* label still resolve; slated for removal (a #58 disposition) once no reader depends on it. "All
|
|
243
|
+
* workspaces are equal until a domain is installed" — a platform is one with the governance domain.
|
|
230
244
|
*/
|
|
231
245
|
kind: t.enum(["workspace", "platform"]).merge(Lww).optional(),
|
|
232
246
|
/**
|
|
@@ -237,6 +251,17 @@ export const CloudWorkspace = aggregate("CloudWorkspace", {
|
|
|
237
251
|
* request at the cloud level).
|
|
238
252
|
*/
|
|
239
253
|
pool: t.int().merge(Lww).optional(),
|
|
254
|
+
/**
|
|
255
|
+
* THE LAW-AUTHORED GENESIS CHAIN (P1, architecture/provisioning_as_obligations.md "law and law alone").
|
|
256
|
+
* For a genesis birth, `birthChild` authors the child's genesis RECIPE here as data — a JSON array of
|
|
257
|
+
* `{domain, directiveId, payload, actor}` steps (`seedGenesis → grantAdmin → birthCloud`). The birth
|
|
258
|
+
* capability provider REPLAYS this opaque list on the child (it names nothing — the directive ids come
|
|
259
|
+
* from the law, not host source); that is the difference between the host *reconstructing* the genesis
|
|
260
|
+
* (today) and the host *delivering* a law-authored one. Absent for plain births + pre-P1 records (the
|
|
261
|
+
* provider falls back to its inline recipe — flag-gated until live byte-equivalence is proven). Naming
|
|
262
|
+
* the genesis directives HERE is law naming law (legitimate); the prohibition binds only the host.
|
|
263
|
+
*/
|
|
264
|
+
genesisChain: t.json().merge(Lww).optional(),
|
|
240
265
|
});
|
|
241
266
|
|
|
242
267
|
// AdminGrant / CreationGrant aggregates: DELETED (full-native Zanzibar). Admin/creation authority is
|
|
@@ -356,11 +381,79 @@ export const CapabilityBinding = aggregate("CapabilityBinding", {
|
|
|
356
381
|
|
|
357
382
|
// --- workspace directives ---------------------------------------------------------
|
|
358
383
|
|
|
384
|
+
// ── THE ONE BIRTH RECORD (the `kind`-collapse — architecture/provisioning_as_obligations.md) ───────────
|
|
385
|
+
// Every birth — a plain child OR a platform — mints the SAME `CloudWorkspace` order; the ONLY difference is
|
|
386
|
+
// the GENESIS DATA the order carries. `genesis` present (a founding-admin hash + pool) ⇒ this child is born
|
|
387
|
+
// running the governance law: the birth capability provider drains it (the order carries a foundingAdminHash)
|
|
388
|
+
// and seeds the genesis. Absent ⇒ a plain lineage record, materialized by boot-on-touch (never drained).
|
|
389
|
+
// `kind` is set PURELY as a derived label off genesis presence — a vestige for old readers; NOTHING decides
|
|
390
|
+
// on it (the host now keys off foundingAdminHash, not the label). There is NO node-class: a platform is just
|
|
391
|
+
// a workspace whose birth order carried a genesis. (`birthChild` is the one unified directive — the tier
|
|
392
|
+
// was collapsed onto `#creator`; `birthPlatform`/`createWorkspace` remain only as deprecated compat aliases.)
|
|
393
|
+
// Returns the record builder so a caller (birthChild) can author the law-authored genesis chain onto it.
|
|
394
|
+
function planBirthRecord(
|
|
395
|
+
p: {
|
|
396
|
+
name: string;
|
|
397
|
+
parent: string;
|
|
398
|
+
principal: string;
|
|
399
|
+
tier: (typeof WORKSPACE_TIERS)[number];
|
|
400
|
+
requestedBy: string;
|
|
401
|
+
requestedAt: number;
|
|
402
|
+
},
|
|
403
|
+
genesis?: { foundingAdminHash: string; pool: number },
|
|
404
|
+
) {
|
|
405
|
+
const rec = create(CloudWorkspace)
|
|
406
|
+
.set("name", p.name)
|
|
407
|
+
.set("parent", p.parent)
|
|
408
|
+
.set("principal", p.principal)
|
|
409
|
+
.set("tier", p.tier)
|
|
410
|
+
.set("status", "requested" as const)
|
|
411
|
+
.set("ownerGrantHash", "") // no owner credential until provisioned
|
|
412
|
+
.set("createdBy", p.requestedBy)
|
|
413
|
+
.set("createdAt", p.requestedAt);
|
|
414
|
+
if (genesis) {
|
|
415
|
+
rec
|
|
416
|
+
.set("kind", "platform" as const) // DERIVED LABEL ONLY — the order's foundingAdminHash is the real signal
|
|
417
|
+
.set("pool", genesis.pool)
|
|
418
|
+
.set("foundingAdminHash", genesis.foundingAdminHash); // the provider grants admin to this hash at materialize
|
|
419
|
+
}
|
|
420
|
+
return rec;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ── THE LAW-AUTHORED GENESIS CHAIN (P1 — "law and law alone") ──────────────────────────────────────────
|
|
424
|
+
// The parent's `birthChild` AUTHORS the child's genesis recipe as DATA — byte-faithful to what the host's
|
|
425
|
+
// `/materialize` reconstructs today (seedGenesis → grantAdmin → birthCloud), but now owned by the LAW. The
|
|
426
|
+
// host replays this opaque list on the child (naming nothing). Timestamps ride IN the payload (the doctrine):
|
|
427
|
+
// `bornAt` (ISO) for the warrant, `requestedAt` (epoch-ms) for the ms-stamped grants — both caller-stamped,
|
|
428
|
+
// so the chain is a pure function of the payload (deterministic on every replaying peer). Naming the genesis
|
|
429
|
+
// directives here is law naming law; the prohibition binds only the host. The law package bytes are NOT in
|
|
430
|
+
// here — the install-by-hash stays a custody concern of the provider (the bytes-in-pack refinement is later).
|
|
431
|
+
export function buildGenesisChain(p: {
|
|
432
|
+
name: string;
|
|
433
|
+
principal: string;
|
|
434
|
+
tier: (typeof WORKSPACE_TIERS)[number];
|
|
435
|
+
foundingAdminHash: string;
|
|
436
|
+
bornAt: string;
|
|
437
|
+
requestedAt: number;
|
|
438
|
+
}): string {
|
|
439
|
+
const founder = "user:" + p.principal;
|
|
440
|
+
return JSON.stringify([
|
|
441
|
+
{ domain: "workspaces", directiveId: "seedGenesis", actor: founder, payload: { principal: founder, workspaceId: "", bornAt: p.bornAt } },
|
|
442
|
+
{ domain: "workspaces", directiveId: "grantAdmin", actor: founder, payload: { keyHash: p.foundingAdminHash, label: `founding admin (born for ${p.principal})`, grantedBy: p.principal, grantedAt: p.requestedAt } },
|
|
443
|
+
// No `role` — it was a vestigial node-class label (collapsed with `kind`); birthCloud ignores it now.
|
|
444
|
+
{ domain: "workspaces", directiveId: "birthCloud", actor: "", payload: { principal: p.principal, name: p.name, tier: p.tier, bornAt: p.requestedAt } },
|
|
445
|
+
]);
|
|
446
|
+
}
|
|
447
|
+
|
|
359
448
|
/**
|
|
360
449
|
* createWorkspace — record the lawful REQUEST for a workspace. Nomos MINTS the
|
|
361
450
|
* `CloudWorkspace` id (the payload carries none); the record is born
|
|
362
451
|
* `status:"requested"` with `ownerGrantHash:""` — no bearer exists until the
|
|
363
452
|
* worker provisions and authors {@link provisionWorkspace}.
|
|
453
|
+
*
|
|
454
|
+
* @deprecated SUPERSEDED by {@link birthChild} (the `kind`-collapse) — a plain birth is `birthChild`
|
|
455
|
+
* with no genesis. Kept as a compat alias (same `#creator` gate) during migration; the host still
|
|
456
|
+
* authors it by name for namespaced-child + upload births. Retire once those callers move to birthChild.
|
|
364
457
|
*/
|
|
365
458
|
export const createWorkspace = directive("createWorkspace")
|
|
366
459
|
.creates(CloudWorkspace)
|
|
@@ -378,17 +471,8 @@ export const createWorkspace = directive("createWorkspace")
|
|
|
378
471
|
}),
|
|
379
472
|
)
|
|
380
473
|
.plan((p) => {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
.set("parent", p.parent)
|
|
384
|
-
.set("principal", p.principal)
|
|
385
|
-
.set("tier", p.tier)
|
|
386
|
-
.set("status", "requested" as const)
|
|
387
|
-
.set("ownerGrantHash", "") // no owner credential until provisioned
|
|
388
|
-
.set("createdBy", p.requestedBy)
|
|
389
|
-
.set("createdAt", p.requestedAt);
|
|
390
|
-
// A plain workspace record (no kind:platform) — boot materializes it directly; the birth capability
|
|
391
|
-
// provider only drains kind:platform records, so this is a lineage record, not a genesis order.
|
|
474
|
+
// No genesis ⇒ a plain lineage record (no foundingAdminHash) — never drained; materialized by boot-on-touch.
|
|
475
|
+
planBirthRecord(p);
|
|
392
476
|
return [];
|
|
393
477
|
})
|
|
394
478
|
.requires("creator"); // posture-gated creation: only a #creator holder may request a workspace
|
|
@@ -409,9 +493,9 @@ export const birthCloud = directive("birthCloud")
|
|
|
409
493
|
/** The genesis auth-provider principal uid upon which the world rests. */
|
|
410
494
|
principal: z.string().min(1),
|
|
411
495
|
name: z.string().min(1).default("root"),
|
|
412
|
-
/**
|
|
413
|
-
*
|
|
414
|
-
role: z.enum(WORKSPACE_ROLES).
|
|
496
|
+
/** VESTIGIAL (see WorkspaceSelf): accepted-but-ignored — the value drives nothing. The authz gate is
|
|
497
|
+
* self-relative (`workspace:self#<relation>`), NOT role-keyed. Kept optional for caller compat. */
|
|
498
|
+
role: z.enum(WORKSPACE_ROLES).optional(),
|
|
415
499
|
tier: z.enum(WORKSPACE_TIERS).default("edge"),
|
|
416
500
|
bornAt: epochMs,
|
|
417
501
|
}),
|
|
@@ -428,8 +512,9 @@ export const birthCloud = directive("birthCloud")
|
|
|
428
512
|
.set("createdAt", p.bornAt)
|
|
429
513
|
.set("provisionedAt", p.bornAt);
|
|
430
514
|
// The self-identity singleton at the CONSTANT id (ungated — birthCloud is not admin-mutating), so the
|
|
431
|
-
// gate's self-record exists before any admin write. ensure() = deterministic-id sibling birth.
|
|
432
|
-
|
|
515
|
+
// gate's self-record exists before any admin write. ensure() = deterministic-id sibling birth. `name` is
|
|
516
|
+
// the born marker; `role` is no longer written (vestigial node-class — see WorkspaceSelf).
|
|
517
|
+
ensure(WorkspaceSelf, WORKSPACE_SELF_ID).set("name", p.name);
|
|
433
518
|
return [];
|
|
434
519
|
});
|
|
435
520
|
|
|
@@ -552,67 +637,75 @@ export const reclaimWorkspace = directive("reclaimWorkspace")
|
|
|
552
637
|
})
|
|
553
638
|
.requires("admin");
|
|
554
639
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
640
|
+
// ─────────────────────────── BIRTH AS A NOMOS PRIMITIVE ──────────────────────────────────────────────
|
|
641
|
+
// (birthPlatform is GONE — the tier collapsed onto #creator; birthChild is the one birth directive.)
|
|
642
|
+
// THE RECORD IS THE OBLIGATION (architecture/githolon_host_contract.md "Birth is a capability" +
|
|
643
|
+
// provisioning_as_obligations.md). Birth is an IMPURE capability: the parent ORDERS (the law-gated
|
|
644
|
+
// birthChild mints a CloudWorkspace at status:requested carrying foundingAdminHash + the LAW-AUTHORED
|
|
645
|
+
// genesisChain — gated by .requires("creator")), the BIRTH CAPABILITY PROVIDER fulfils it (materialize:
|
|
646
|
+
// install the parent's law + REPLAY the genesisChain opaquely — the child's OWN kernel admitting each), and
|
|
647
|
+
// authors provisionWorkspace (the receipt) the parent TRUSTS. No separate obligation aggregate — the minted
|
|
648
|
+
// CloudWorkspace IS the order. The provider owns only the impure boot + replay; the LAW owns the recipe (P1)
|
|
649
|
+
// AND who/what (the gate); the parent trusts the DID-IT-HAPPEN. A child born this way runs THIS SAME law and
|
|
650
|
+
// births its children identically — the recursion is the law.
|
|
651
|
+
|
|
652
|
+
// ─────────────────────────── birthChild — THE ONE BIRTH DIRECTIVE (the `kind`-collapse) ───────────────
|
|
653
|
+
// "There are only workspaces, all equal until a domain is installed." birthChild is the SINGLE directive
|
|
654
|
+
// that births any child — a plain leaf OR a platform; the difference is the GENESIS DATA the payload carries,
|
|
655
|
+
// never a node-class. THE TIER IS COLLAPSED (Jack, 2026-06-24): `#creator` is THE one birth authority — a
|
|
656
|
+
// creator may spawn a leaf AND a platform (the prior `#platformCreator` second tier is retired; platform
|
|
657
|
+
// birth under root is still invite-only because `creator` on root is admin-granted, just renamed/unified).
|
|
658
|
+
// • genesis ABSENT → a plain lineage record (no foundingAdminHash); boot-on-touch, never drained.
|
|
659
|
+
// • genesis PRESENT → carries {foundingAdminHash, pool}: the birth capability provider drains it
|
|
660
|
+
// (the order carries a foundingAdminHash) → materialize + seed genesis. A platform.
|
|
661
|
+
// Self-relative: the gate is `workspace:self#creator`, so the SAME directive on root births a platform and
|
|
662
|
+
// on a platform births a child — no per-level host code (the recursion is the law). Supersedes
|
|
663
|
+
// createWorkspace + birthPlatform (kept as compat aliases during migration). See
|
|
664
|
+
// architecture/provisioning_as_obligations.md "The `kind`-collapse".
|
|
665
|
+
export const birthChild = directive("birthChild")
|
|
564
666
|
.creates(CloudWorkspace)
|
|
565
667
|
.payload(
|
|
566
|
-
z
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
668
|
+
z
|
|
669
|
+
.object({
|
|
670
|
+
name: z.string().min(1),
|
|
671
|
+
/** The auth-provider principal uid the request claimed; `""` = unclaimed (a plain leaf may be). */
|
|
672
|
+
principal: z.string().default(""),
|
|
673
|
+
/** Parent CloudWorkspace aggregate id; `""` (default) = root. */
|
|
674
|
+
parent: z.string().default(""),
|
|
675
|
+
/** Hosting tier; defaults to the edge DO (a genesis/platform birth typically passes `heavy`). */
|
|
676
|
+
tier: z.enum(WORKSPACE_TIERS).default("edge"),
|
|
677
|
+
requestedBy: z.string().min(1),
|
|
678
|
+
requestedAt: epochMs,
|
|
679
|
+
/** GENESIS (optional): present ⇒ this child is born running the governance law (a platform). The
|
|
680
|
+
* sha256 of the founding-admin key the requester presented — the provider grants admin to the hash. */
|
|
681
|
+
foundingAdminHash: sha256Hex.optional(),
|
|
682
|
+
/** PLATFORM POOL: how many live children the born platform may parent. Only meaningful with genesis. */
|
|
683
|
+
pool: z.number().int().positive().default(50),
|
|
684
|
+
/** GENESIS birth time as an ISO string (caller-stamped — the timestamp doctrine). Arms the warrant
|
|
685
|
+
* in the law-authored genesis chain; required for (and only used by) a genesis birth. */
|
|
686
|
+
bornAt: z.string().optional(),
|
|
687
|
+
})
|
|
688
|
+
// A genesis birth (a platform) must carry a principal AND a bornAt — it owns a subtree, a lineage
|
|
689
|
+
// anchor, and a law-authored genesis chain whose warrant is stamped at bornAt.
|
|
690
|
+
.refine((d) => !d.foundingAdminHash || (d.principal.length > 0 && !!d.bornAt), {
|
|
691
|
+
message: "a genesis birth (foundingAdminHash present) requires a non-empty principal and bornAt (ISO)",
|
|
692
|
+
path: ["principal"],
|
|
693
|
+
}),
|
|
581
694
|
)
|
|
582
695
|
.plan((p) => {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
.set("
|
|
588
|
-
|
|
589
|
-
.set("tier", p.tier)
|
|
590
|
-
.set("status", "requested" as const)
|
|
591
|
-
.set("ownerGrantHash", "")
|
|
592
|
-
.set("foundingAdminHash", p.foundingAdminHash) // the provider grants admin to this hash at materialize
|
|
593
|
-
.set("createdBy", p.requestedBy)
|
|
594
|
-
.set("createdAt", p.requestedAt);
|
|
595
|
-
// THE RECORD IS THE OBLIGATION: a kind:platform CloudWorkspace at status:requested with no ownerGrantHash
|
|
596
|
-
// is an order the birth capability provider drains (materialize + install the parent's law + seed genesis
|
|
597
|
-
// + grant the founding admin + birthCloud), closing it with provisionWorkspace (the receipt). No separate
|
|
598
|
-
// obligation aggregate — the minted CloudWorkspace IS it (sidesteps the id-mint gate; reuses the lifecycle).
|
|
696
|
+
const rec = planBirthRecord(p, p.foundingAdminHash ? { foundingAdminHash: p.foundingAdminHash, pool: p.pool } : undefined);
|
|
697
|
+
// LAW AUTHORS THE GENESIS RECIPE (P1): a genesis birth carries its child's seedGenesis→grantAdmin→
|
|
698
|
+
// birthCloud chain as data; the provider replays it opaquely. (refine guarantees principal + bornAt here.)
|
|
699
|
+
if (p.foundingAdminHash) {
|
|
700
|
+
rec.set("genesisChain", buildGenesisChain({ name: p.name, principal: p.principal, tier: p.tier, foundingAdminHash: p.foundingAdminHash, bornAt: p.bornAt as string, requestedAt: p.requestedAt }));
|
|
701
|
+
}
|
|
599
702
|
return [];
|
|
600
703
|
})
|
|
601
|
-
.requires("
|
|
704
|
+
.requires("creator"); // THE ONE birth authority — a #creator may birth a leaf OR a platform (tier collapsed)
|
|
602
705
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
// status:requested, kind:platform, carrying foundingAdminHash — gated by .requires("platformCreator")),
|
|
607
|
-
// the BIRTH CAPABILITY PROVIDER fulfils it (materialize: install the PARENT's law + seed genesis + grant
|
|
608
|
-
// the founding admin + birthCloud — the child's OWN kernel admitting each), and authors provisionWorkspace
|
|
609
|
-
// (the receipt) the parent TRUSTS. No separate obligation aggregate — the minted CloudWorkspace IS the
|
|
610
|
-
// order (it sidesteps the id-mint gate and reuses the requested→provisioned lifecycle). The provider knows
|
|
611
|
-
// HOW (the Capability Exception sanctions a declared effect); the law owns WHO/WHAT; the parent trusts the
|
|
612
|
-
// DID-IT-HAPPEN. A child born this way runs THIS SAME law and births its children identically.
|
|
613
|
-
|
|
614
|
-
/** workspacesByStatus — the birth provider drains `requested` CloudWorkspaces (kind:platform, no
|
|
615
|
-
* ownerGrantHash) → materialize → provisionWorkspace. The law's order queue; the host polls, never decides. */
|
|
706
|
+
/** workspacesByStatus — the birth provider drains `requested` CloudWorkspaces that CARRY A foundingAdminHash
|
|
707
|
+
* (a genesis order, no ownerGrantHash) → materialize → provisionWorkspace. Plain requested records (no
|
|
708
|
+
* foundingAdminHash) are lineage only, boot-on-touch. The law's order queue; the host polls, never decides. */
|
|
616
709
|
export const workspacesByStatus = query("workspacesByStatus").key("status").returns(CloudWorkspace);
|
|
617
710
|
|
|
618
711
|
/** setPlatformPool — the pool edition of a granted quota request. */
|
|
@@ -632,39 +725,10 @@ export const setPlatformPool = directive("setPlatformPool")
|
|
|
632
725
|
})
|
|
633
726
|
.requires("admin");
|
|
634
727
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
// grantPlatformCreation — become-a-platform authority IS the self-relative `#platformCreator` tuple
|
|
640
|
-
// (one authz spine, all the way down). Writes `workspace:self#platformCreator@user:<principal>` (no
|
|
641
|
-
// PlatformGrant aggregate) — the host's platform-birth invite check reads the SAME tuple via Check.
|
|
642
|
-
export const grantPlatformCreation = directive("grantPlatformCreation")
|
|
643
|
-
.ensures(RelationTuple)
|
|
644
|
-
.payload(
|
|
645
|
-
z.object({
|
|
646
|
-
principal: z.string().min(1),
|
|
647
|
-
label: z.string().min(1),
|
|
648
|
-
grantedBy: z.string().min(1),
|
|
649
|
-
grantedAt: epochMs,
|
|
650
|
-
}),
|
|
651
|
-
)
|
|
652
|
-
.plan((p) => writeTuple({ object: SELF_ADMIN_OBJECT, relation: "platformCreator", subject: "user:" + p.principal, grantedBy: p.grantedBy, grantedAt: String(p.grantedAt) }))
|
|
653
|
-
.requires("admin"); // only an admin opens the become-a-platform door
|
|
654
|
-
|
|
655
|
-
// revokePlatformCreation — flip the `#platformCreator@user:<principal>` leaf to revoked. BREAKING vs the
|
|
656
|
-
// aggregate model: keyed by PRINCIPAL (the tuple subject), not a minted grantId.
|
|
657
|
-
export const revokePlatformCreation = directive("revokePlatformCreation")
|
|
658
|
-
.ensures(RelationTuple)
|
|
659
|
-
.payload(
|
|
660
|
-
z.object({
|
|
661
|
-
principal: z.string().min(1),
|
|
662
|
-
revokedBy: z.string().min(1),
|
|
663
|
-
revokedAt: epochMs,
|
|
664
|
-
}),
|
|
665
|
-
)
|
|
666
|
-
.plan((p) => removeTuple({ object: SELF_ADMIN_OBJECT, relation: "platformCreator", subject: "user:" + p.principal, grantedBy: p.revokedBy, grantedAt: String(p.revokedAt), revokedBy: p.revokedBy, revokedAt: String(p.revokedAt) }))
|
|
667
|
-
.requires("admin");
|
|
728
|
+
// grantPlatformCreation / revokePlatformCreation — REMOVED (the tier collapsed onto #creator, 2026-06-24).
|
|
729
|
+
// Platform-birth authority is now just #creator (grantCreation, which takes a user OR key subject). The
|
|
730
|
+
// `#platformCreator` relation + its (vestigial) PlatformGrant aggregate / platformGrantByPrincipal query
|
|
731
|
+
// retire in the same #58 disposition pass as kind/role; any historical #platformCreator tuple is inert data.
|
|
668
732
|
|
|
669
733
|
/**
|
|
670
734
|
* AuthProvider — BYOAuth: WHO VOUCHES FOR THIS SUBTREE'S PRINCIPALS. A law-state
|
|
@@ -980,30 +1044,41 @@ export const revokeAdmin = directive("revokeAdmin")
|
|
|
980
1044
|
// grantCreation — creation authority IS the self-relative `#creator` tuple the CreationGate reads.
|
|
981
1045
|
// Writes `workspace:self#creator@key:<keyHash>` (no CreationGrant aggregate). Under posture invite/closed
|
|
982
1046
|
// the gate admits only holders of an active #creator subject; under open/unset it admits anyone.
|
|
1047
|
+
// #creator is ONE relation over ONE principal graph; a subject is `key:<hash>` (proof by holding a secret —
|
|
1048
|
+
// a Bearer) OR `user:<uid>` (proof of identity — an auth-provider token). The relation allows BOTH
|
|
1049
|
+
// (allow:[user(),key()]); these are not two authorities, just two ways a principal authenticates. So
|
|
1050
|
+
// grantCreation takes EITHER — exactly one of keyHash / principal. This is what lets a USER birth platforms
|
|
1051
|
+
// via birthChild (no "creator key" dance) and dissolves the user/key migration wrinkle.
|
|
983
1052
|
export const grantCreation = directive("grantCreation")
|
|
984
1053
|
.ensures(RelationTuple)
|
|
985
1054
|
.payload(
|
|
986
|
-
z
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1055
|
+
z
|
|
1056
|
+
.object({
|
|
1057
|
+
keyHash: sha256Hex.optional(),
|
|
1058
|
+
principal: z.string().min(1).optional(),
|
|
1059
|
+
label: z.string().min(1),
|
|
1060
|
+
grantedBy: z.string().min(1),
|
|
1061
|
+
grantedAt: epochMs,
|
|
1062
|
+
})
|
|
1063
|
+
.refine((d) => !!d.keyHash !== !!d.principal, { message: "grantCreation needs exactly one of keyHash (a key: subject) or principal (a user: subject)", path: ["keyHash"] }),
|
|
992
1064
|
)
|
|
993
|
-
.plan((p) => writeTuple({ object: SELF_ADMIN_OBJECT, relation: "creator", subject: "key:" + p.keyHash, grantedBy: p.grantedBy, grantedAt: String(p.grantedAt) }))
|
|
1065
|
+
.plan((p) => writeTuple({ object: SELF_ADMIN_OBJECT, relation: "creator", subject: p.principal ? "user:" + p.principal : "key:" + p.keyHash, grantedBy: p.grantedBy, grantedAt: String(p.grantedAt) }))
|
|
994
1066
|
.requires("admin"); // only an admin grants creation authority
|
|
995
1067
|
|
|
996
|
-
/** revokeCreation — flip the `#creator
|
|
1068
|
+
/** revokeCreation — flip the `#creator@<subject>` leaf to revoked (key OR user, mirroring grantCreation). */
|
|
997
1069
|
export const revokeCreation = directive("revokeCreation")
|
|
998
1070
|
.ensures(RelationTuple)
|
|
999
1071
|
.payload(
|
|
1000
|
-
z
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1072
|
+
z
|
|
1073
|
+
.object({
|
|
1074
|
+
keyHash: sha256Hex.optional(),
|
|
1075
|
+
principal: z.string().min(1).optional(),
|
|
1076
|
+
revokedBy: z.string().min(1),
|
|
1077
|
+
revokedAt: epochMs,
|
|
1078
|
+
})
|
|
1079
|
+
.refine((d) => !!d.keyHash !== !!d.principal, { message: "revokeCreation needs exactly one of keyHash or principal", path: ["keyHash"] }),
|
|
1005
1080
|
)
|
|
1006
|
-
.plan((p) => removeTuple({ object: SELF_ADMIN_OBJECT, relation: "creator", subject: "key:" + p.keyHash, grantedBy: p.revokedBy, grantedAt: String(p.revokedAt), revokedBy: p.revokedBy, revokedAt: String(p.revokedAt) }))
|
|
1081
|
+
.plan((p) => removeTuple({ object: SELF_ADMIN_OBJECT, relation: "creator", subject: p.principal ? "user:" + p.principal : "key:" + p.keyHash, grantedBy: p.revokedBy, grantedAt: String(p.revokedAt), revokedBy: p.revokedBy, revokedAt: String(p.revokedAt) }))
|
|
1007
1082
|
.requires("admin");
|
|
1008
1083
|
|
|
1009
1084
|
// --- declared read queries (the worker's ENTIRE enforcement surface) -----------------
|
|
@@ -1156,8 +1231,9 @@ export const founderSignerWriteOnce = workspaceInvariant("FounderSignerWriteOnce
|
|
|
1156
1231
|
export const birthCloudOnce = workspaceInvariant("BirthCloudOnce")
|
|
1157
1232
|
.on("birthCloud")
|
|
1158
1233
|
.reads(() => [refAs("self", "WorkspaceSelf", "pre:" + WORKSPACE_SELF_ID)])
|
|
1234
|
+
// WRITE-ONCE on the self-record's PRESENCE via `name` (not the vestigial `role`) — a workspace is born once.
|
|
1159
1235
|
.assert(({ self }) =>
|
|
1160
|
-
(self as {
|
|
1236
|
+
(self as { name?: string })?.name ? { reject: "cloud-already-born" } : { accept: true },
|
|
1161
1237
|
);
|
|
1162
1238
|
|
|
1163
1239
|
/** provisionWorkspace: the birth RECEIPT may only close a `requested` record (no hijacking a live
|