create-githolon 0.18.0 → 0.20.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/package.json +1 -1
- package/template/docs/08-births.md +78 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-githolon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffold a Nomos domain package: the starter domain + compile config + live e2e. `npm create githolon my-app`.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Birthing child workspaces (law-at-birth)
|
|
2
|
+
|
|
3
|
+
A directive in your law can **birth a child workspace** that is born already running its own domain law.
|
|
4
|
+
A home births estate children; a platform births workspaces; root births platforms — all the **same one
|
|
5
|
+
way**. You do *not* ask Nomos for a new primitive and you do *not* hand a finished workspace to the cloud.
|
|
6
|
+
|
|
7
|
+
## How it works (birth is a kernel concern, defined by the ledger)
|
|
8
|
+
|
|
9
|
+
Your directive declares `.births()` and, in its `plan`, authors the child's **genesis recipe** — the
|
|
10
|
+
ordered intents the child will run — then calls `birth({ workspace, genesisChain })`. That's it. When the
|
|
11
|
+
parent's offer is admitted, the kernel's birth offer-effect:
|
|
12
|
+
|
|
13
|
+
1. spawns the child's (empty) custody,
|
|
14
|
+
2. **installs the child's law in-chain** and folds the genesis recipe **through the child's own gate**,
|
|
15
|
+
3. so the child **self-validates from intent 0** — it is not trusted, it proves itself.
|
|
16
|
+
|
|
17
|
+
The host installs nothing and decides nothing. The recipe is a **ledger fact**, not a runtime trick.
|
|
18
|
+
|
|
19
|
+
## The leaf recipe = two installs + your seed
|
|
20
|
+
|
|
21
|
+
A leaf child (a home, an estate item) is born with exactly:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
[ bootstrap/installDomain(<frameworkHash>), // the bootstrap controller (bytes-by-hash, kernel-resolved)
|
|
25
|
+
nomos/installDomain(<lawHash>), // YOUR child's domain law package
|
|
26
|
+
<your own seed step(s)> ] // e.g. seed the owner / initial state
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`frameworkHash` + `lawHash` are the content hashes of the two packages, pinned at compile time (the kernel
|
|
30
|
+
resolves the bytes from custody by hash — they are never shipped on the wire).
|
|
31
|
+
|
|
32
|
+
## Copy `birthHome` — the canonical template
|
|
33
|
+
|
|
34
|
+
`birthHome` (in `@githolon/dsl`'s framework) is exactly this pattern for the **home** domain. To birth an
|
|
35
|
+
**estate child** (or any leaf running your law), copy it and swap the seed:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { directive, birth, z } from "@githolon/dsl";
|
|
39
|
+
|
|
40
|
+
export const birthEstateChild = directive("birthEstateChild")
|
|
41
|
+
.creates(EstateChild) // the child lineage record on YOUR chain
|
|
42
|
+
.payload(z.object({
|
|
43
|
+
child: z.string().min(1), // the child workspace name
|
|
44
|
+
owner: z.string().min(1), // the verified owner (a user:<uid> subject)
|
|
45
|
+
frameworkHash: z.string().min(1), // pinned at your governance compile
|
|
46
|
+
lawHash: z.string().min(1), // your ESTATE domain law package hash
|
|
47
|
+
bornAt: z.string().min(1), // ISO-8601, caller-stamped (determinism)
|
|
48
|
+
}))
|
|
49
|
+
.plan((p) => {
|
|
50
|
+
const actor = "user:" + p.owner;
|
|
51
|
+
birth({
|
|
52
|
+
workspace: p.child,
|
|
53
|
+
genesisChain: [
|
|
54
|
+
{ domain: "bootstrap", directiveId: "installDomain", actor, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash, domainPackageB64: "" },
|
|
55
|
+
{ domain: "nomos", directiveId: "installDomain", actor, payload: { domainHash: p.lawHash }, domainHash: p.lawHash, domainPackageB64: "" },
|
|
56
|
+
{ domain: "estate", directiveId: "seedEstateChild", actor, payload: { owner: p.owner, bornAt: p.bornAt } },
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
return [];
|
|
60
|
+
})
|
|
61
|
+
.births() // declares the birth — the kernel folds the recipe
|
|
62
|
+
.requires("creator"); // the birth authority (see below)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Authority — who may birth
|
|
66
|
+
|
|
67
|
+
`.requires("creator")` gates the birth on a relation tuple. Grant the home owner `#creator` on their own
|
|
68
|
+
home (self-bound at birth, the same way the owner is seeded) so a verified owner can birth their estate
|
|
69
|
+
children — and only their own. (Relations: `grant` / `writeTuple`, see `07-security.md`. Use whatever
|
|
70
|
+
relation models your policy; `creator` is the convention `birthChild` uses.)
|
|
71
|
+
|
|
72
|
+
## What NOT to do
|
|
73
|
+
|
|
74
|
+
- **Don't ask for a "child-birth primitive."** It already exists — `.births()` + `birth(recipe)`. A home is
|
|
75
|
+
a first-class workspace; it births children exactly as root births platforms. Recursion is the law.
|
|
76
|
+
- **Don't hand-roll a second recipe *builder*** across your domains — keep the recipe inline (it's small)
|
|
77
|
+
or share one tiny helper *in your own code*; the framework deliberately keeps **one** birth verb.
|
|
78
|
+
- **Don't put a clock/random in the plan** — `bornAt` rides the payload (the timestamp doctrine).
|