@githolon/dsl 0.60.2 → 0.61.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.60.2",
3
+ "version": "0.61.1",
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",
@@ -116,6 +116,41 @@ const SUBJECT_CANDIDATES: readonly string[] = [`user:${ZERO_UUID}`, `key:${HEX64
116
116
  * schema and return the first that validates. If none fits an exotic constraint, fall back to the
117
117
  * readable placeholder (behaviour unchanged from before — that directive is proven by hand).
118
118
  */
119
+ /** BIRTHS ISOLATION (multi-`.births()` parent): a parent with SEVERAL birth legs (co2:
120
+ * birthCatalogueWorkspace + birthEstateWorkspace) synthesizes the SAME readable `sample-<field>` payloads,
121
+ * so each leg's plan DERIVES THE SAME child workspace name and the second birth collides on an
122
+ * already-born name. Give each leg a distinct child by suffixing its `sample-`-prefixed string values with
123
+ * a per-leg discriminator — but ONLY when the suffixed payload STILL satisfies the directive's own schema
124
+ * (a constrained field is left untouched, never broken). A single-birth parent is unaffected (no suffix). */
125
+ function uniquifyBirthPayload(
126
+ payload: Record<string, unknown>,
127
+ disc: string,
128
+ schema: z.ZodTypeAny | undefined,
129
+ ): Record<string, unknown> {
130
+ const suffixed: Record<string, unknown> = {};
131
+ let changed = false;
132
+ for (const [k, v] of Object.entries(payload)) {
133
+ if (typeof v === "string" && v.startsWith("sample-")) { suffixed[k] = `${v}-${disc}`; changed = true; }
134
+ else suffixed[k] = v;
135
+ }
136
+ if (!changed) return payload;
137
+ // Re-validate PER FIELD (the birthPayload has its framework/law-hash fields deleted — they are injected at
138
+ // runtime — so a whole-payload parse would spuriously fail on the missing required fields). For a
139
+ // ZodObject we check each suffixed value against ITS OWN field schema; a field whose schema rejects the
140
+ // suffix (a length/regex bound) is reverted to its original value (never break a constrained field).
141
+ const shape =
142
+ schema !== undefined && typeof (schema as { shape?: unknown }).shape === "object"
143
+ ? ((schema as unknown as { shape: Record<string, z.ZodTypeAny> }).shape)
144
+ : undefined;
145
+ for (const [k, v] of Object.entries(suffixed)) {
146
+ const orig = payload[k];
147
+ if (v === orig) continue;
148
+ const fieldSchema = shape?.[k];
149
+ if (fieldSchema !== undefined && !fieldSchema.safeParse(v).success) suffixed[k] = orig; // revert the constrained field
150
+ }
151
+ return suffixed;
152
+ }
153
+
119
154
  function sampleString(zt: z.ZodTypeAny, field: string): string {
120
155
  const readable = `sample-${field}`;
121
156
  if (zt.safeParse(readable).success) return readable;
@@ -2071,8 +2106,20 @@ export function legsForDomain(mod: DomainModule, modules: readonly DomainModule[
2071
2106
  // The child-birth legs (one per `.births()` directive) — discovered by running each plan. A parent
2072
2107
  // with SEVERAL births (co2: birthCatalogueWorkspace + birthEstateWorkspace) keeps EVERY leg; keeping
2073
2108
  // only the first left the other children compile-valid-only (unprovable through their birth).
2074
- const childBirths = allBirths.filter((b) => b.parentDomain === domain).map((b) => {
2109
+ const parentBirths = allBirths.filter((b) => b.parentDomain === domain);
2110
+ const childBirths = parentBirths.map((b) => {
2075
2111
  const withParent = attachParentSetup(b, allBirths);
2112
+ // BIRTHS ISOLATION: with >1 birth leg, uniquify each leg's synthesized child so the legs don't collide
2113
+ // on one generated child workspace name (the discriminator = the birth directive id, unique per leg).
2114
+ if (parentBirths.length > 1) {
2115
+ const birthDir = (mod.directives as Directive<unknown>[]).find((d) => d?.id === b.birthDirectiveId);
2116
+ const schema = birthDir?.payloadSchema as z.ZodTypeAny | undefined;
2117
+ const disc = b.birthDirectiveId;
2118
+ const uniq = uniquifyBirthPayload(withParent.birthPayload, disc, schema);
2119
+ if (uniq !== withParent.birthPayload) {
2120
+ return { ...withParent, birthPayload: uniq };
2121
+ }
2122
+ }
2076
2123
  // A GATED birth with no parent-birth authority path: seat its relation via the SAME granter/seed
2077
2124
  // discovery the relation-bootstrap leg uses (the law's own authority path, e.g. co2's
2078
2125
  // seedPlatformFounder → seedPlatformAdmin → grantCo2EstateCreator → birthEstateWorkspace).
package/src/directive.ts CHANGED
@@ -163,6 +163,18 @@ export interface Directive<P = unknown> {
163
163
  /** The payload field holding the claimed issuer (the gate asserts it == the prefix AuthProvider.issuer). */
164
164
  readonly verifiedViaField: string;
165
165
  };
166
+
167
+ /**
168
+ * THE LAWFUL-RECOVERY GATE (architecture/lawful_recovery.md). Present IFF this directive declares
169
+ * `.certifiesEra()`: it is the CUSTODIAN-authored `certifyEra` verb. Lowered into the `nomosEraCertGate`
170
+ * USD era key (the directive id + the payload field carrying the signed `EraCertification`), so the kernel
171
+ * admits this intent by verifying its author against the pinned `K_root` via the carried certification —
172
+ * NOT the workspace signer roster — and the walk waives the identity attestation for the oids it covers.
173
+ * The kernel hardcodes NEITHER; it reads both from this declaration. Framework `certifyEra` default field.
174
+ */
175
+ readonly certifiesEraGate?: {
176
+ readonly certField: string;
177
+ };
166
178
  /**
167
179
  * The directive's DECLARED read boundary: the ref types its `plan` may READ (the
168
180
  * "IR for law boundaries" decision — the boundary is part of the domain identity).
@@ -318,6 +330,14 @@ export interface RequirableDirective<P> extends Directive<P> {
318
330
  keyHashField?: string;
319
331
  verifiedViaField?: string;
320
332
  }): RequirableDirective<P>;
333
+ /**
334
+ * Declare this directive the CUSTODIAN-authored `certifyEra` recovery verb (architecture/lawful_recovery.md).
335
+ * Its author is verified to the pinned `K_root` via the carried [`EraCertification`] (NOT the workspace
336
+ * signer roster), and the kernel walk WAIVES the identity attestation for the oids the certification covers
337
+ * (the law/plan gate stays enforced). Emits the `nomosEraCertGate` era key naming this directive + the
338
+ * payload field holding the artifact (default `certification`). Returns a NEW requirable directive.
339
+ */
340
+ certifiesEra(opts?: { certField?: string }): RequirableDirective<P>;
321
341
  /**
322
342
  * Declare the directive's READ boundary. TWO argument kinds, one declaration site:
323
343
  * * a STRING declares a ref TYPE its `plan` may read (the original boundary —
@@ -679,6 +699,16 @@ function makeRequirable<P>(base: Directive<P>): RequirableDirective<P> {
679
699
  },
680
700
  });
681
701
  },
702
+ certifiesEra(opts?: { certField?: string }): RequirableDirective<P> {
703
+ // LAWFUL RECOVERY: this directive is the custodian-authored `certifyEra` verb. Lower its id + the
704
+ // payload field carrying the signed `EraCertification` into the `nomosEraCertGate` era key — the kernel
705
+ // reads both from the law (hardcoding neither) to admit the intent (author verified to K_root) + waive
706
+ // the identity attestation for the certified oids. Default field HERE (the framework convention).
707
+ return makeRequirable({
708
+ ...base,
709
+ certifiesEraGate: { certField: opts?.certField ?? "certification" },
710
+ });
711
+ },
682
712
  reads(...reads: (string | QueryDecl)[]): RequirableDirective<P> {
683
713
  // Split by argument kind: strings are the ref-type boundary; QueryDecls are the
684
714
  // captured-read lane (#58). Both accumulate, dedup (ref types by value, queries
@@ -0,0 +1,79 @@
1
+ // NOMOS — LAWFUL RECOVERY (architecture/lawful_recovery.md). The `certifyEra` verb, shipped in the `nomos`
2
+ // CONTROLLER domain (installed in EVERY workspace via bootstrap/installDomain) so ANY chain can fold a
3
+ // custodian's recovery fact. A CUSTODIAN (root operator / a platform over its subtree) folds a SIGNED,
4
+ // in-chain `EraCertification` that WAIVES the IDENTITY ATTESTATION for a named set of intent oids — while
5
+ // the law/plan gate stays fully enforced. A custodian may VOUCH IDENTITY (heal a ForgedPrincipal/BadSignature
6
+ // scar left by a lost enrollment or a compaction), never authorize an unlawful write.
7
+ //
8
+ // THE CRYPTO LIVES IN THE KERNEL: `.certifiesEra()` emits the `nomosEraCertGate` era key (this directive id +
9
+ // the payload field carrying the artifact). The kernel then (a) ADMITS this intent by verifying its author
10
+ // against the pinned `K_root` via the carried certification — NOT the workspace signer roster, since the
11
+ // custodian is not enrolled here — and (b) in verify_chain, pre-scans the timeline for certifications and
12
+ // WAIVES the identity attestation for the oids they cover. The DSL plan only FOLDS the fact (audit surface);
13
+ // the plan CANNOT and does not verify the signature — that is the kernel's, on every lane.
14
+ import { z } from "zod";
15
+ import { aggregate, instance } from "../aggregate.js";
16
+ import { Lww } from "../drivers.js";
17
+ import { t } from "../fields.js";
18
+ import { directive } from "../directive.js";
19
+ import { set, type PlannedOp } from "../ops.js";
20
+ import { query } from "../query.js";
21
+
22
+ /** THE FOLDED RECOVERY FACT — one per certification, content-addressed (`era-certification:<id>`), append-
23
+ * only. The `certification` field holds the exact signed artifact JSON the kernel verified; the flat fields
24
+ * make it queryable (who certified what, when, why). NOT truth about the waiver — the kernel's verify is —
25
+ * just the AUDIT record that a custodian acted, and its attribution. */
26
+ export const EraCertification = aggregate("EraCertification", {
27
+ /** The signed artifact JSON verbatim (workspaceId, intentOids, waives, reason, custodianKey, signature,
28
+ * certChain) — the exact bytes the kernel's `era_cert::verify` checked against K_root + this chain. */
29
+ certification: t.string().merge(Lww),
30
+ /** The custodian's public key (base64) — WHO vouched (audit; the kernel proved it roots at K_root). */
31
+ custodianKey: t.string().merge(Lww),
32
+ /** The operator's mandatory justification (audit). */
33
+ reason: t.string().merge(Lww),
34
+ /** The custodian's HLC at issue (audit). */
35
+ certifiedAt: t.string().merge(Lww),
36
+ });
37
+
38
+ const eraCertPayload = z.object({
39
+ /** The content-addressed id for the folded fact (`sha256(canonical artifact)` — caller-computed). */
40
+ certId: z.string().min(1),
41
+ /** The signed `EraCertification` artifact (the kernel verifies it against K_root + this chain's self-id). */
42
+ certification: z.string().min(1),
43
+ /** The custodian public key (base64) — audit; the kernel derives authority from the artifact's certChain. */
44
+ custodianKey: z.string().min(1),
45
+ /** The mandatory operator justification. */
46
+ reason: z.string().min(1),
47
+ /** ISO-8601, caller-stamped (the timestamp doctrine — the plan reads no clock). */
48
+ certifiedAt: z.string().min(1),
49
+ });
50
+
51
+ /**
52
+ * `certifyEra` — THE LAWFUL OPERATOR RECOVERY VERB. A custodian offers this against the SCARRED workspace;
53
+ * the kernel admits it by verifying the carried certification's author to the pinned `K_root` (root operator
54
+ * = a certification signed directly by K_root; a platform custodian = a certChain to K_root, scoped to its
55
+ * subtree). The plan folds the audit fact; the kernel's verify_chain pre-scan reads the SAME certification
56
+ * from this intent's payload and WAIVES the identity attestation for the oids it names. `.requires` NOTHING:
57
+ * authorization is the K_root-rooted signature (the whole point — the custodian is not an enrolled signer of
58
+ * the child), enforced by the era gate, not a relation.
59
+ */
60
+ export const certifyEra = directive("certifyEra")
61
+ .ensures(EraCertification)
62
+ .payload(eraCertPayload)
63
+ .plan((p): PlannedOp[] => {
64
+ const fact = instance(EraCertification, `era-certification:${p.certId}`);
65
+ return [
66
+ set(fact, "certification", p.certification),
67
+ set(fact, "custodianKey", p.custodianKey),
68
+ set(fact, "reason", p.reason),
69
+ set(fact, "certifiedAt", p.certifiedAt),
70
+ ];
71
+ })
72
+ // Emit the `nomosEraCertGate` era key (this directive id + the `certification` payload field). The kernel
73
+ // reads both from the law, admitting the intent (author verified to K_root) + waiving the certified oids.
74
+ .certifiesEra();
75
+
76
+ /** Read the folded certifications by custodian (audit / `githolon gov certifications`). */
77
+ export const eraCertificationsByCustodian = query("eraCertificationsByCustodian")
78
+ .key("custodianKey")
79
+ .returns(EraCertification);
package/src/usd.ts CHANGED
@@ -348,6 +348,18 @@ export interface UsdLayer {
348
348
  }
349
349
  >;
350
350
  };
351
+ /**
352
+ * THE LAWFUL-RECOVERY GATE (architecture/lawful_recovery.md). Present IFF a directive declares
353
+ * `.certifiesEra()`: names that directive id + the payload field carrying the signed `EraCertification`.
354
+ * The kernel's `nomosEraCertGate` arm admits the custodian-authored intent (verified to `K_root` via the
355
+ * certification, not the signer roster) + the walk waives the identity attestation for the certified oids.
356
+ * OMITTED for law with no certifyEra directive (the era rule — byte-identical hash; the arm inert).
357
+ */
358
+ readonly nomosEraCertGate?: {
359
+ readonly v: 1;
360
+ readonly directiveId: string;
361
+ readonly certField: string;
362
+ };
351
363
  /**
352
364
  * THE BIRTH GATE DECLARATION (G3 — birth is a declared kernel primitive, the unix model). Present IFF ≥1
353
365
  * directive declares `.births()`: the SORTED set of their ids. A directive named here BIRTHS a child
@@ -1204,6 +1216,14 @@ export function emitUsd(
1204
1216
  }
1205
1217
  const nomosVerifiedFirstEnroll =
1206
1218
  Object.keys(firstEnrollReq).length > 0 ? { v: 1 as const, directives: firstEnrollReq } : undefined;
1219
+ // LAWFUL RECOVERY: the sole `.certifiesEra()` directive (if any) → the `nomosEraCertGate` era key.
1220
+ // (One certifyEra verb per controller law; the last declared wins — the framework declares exactly one.)
1221
+ let eraCertGate: UsdLayer["nomosEraCertGate"];
1222
+ for (const d of [...l.module.directives].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))) {
1223
+ if (d.certifiesEraGate !== undefined) {
1224
+ eraCertGate = { v: 1 as const, directiveId: d.id, certField: d.certifiesEraGate.certField };
1225
+ }
1226
+ }
1207
1227
  // THE CAPTURED-READ GATE DECLARATION (#58): collected from the canonical
1208
1228
  // manifest's per-directive `capturedReads` (omit-when-empty — read-free law's
1209
1229
  // USD is byte-identical apart from the always-on stableIds key).
@@ -1358,6 +1378,7 @@ export function emitUsd(
1358
1378
  ...(nomosBirthRecordGate !== undefined ? { nomosBirthRecordGate } : {}),
1359
1379
  ...(nomosParentAttestedBirth !== undefined ? { nomosParentAttestedBirth } : {}),
1360
1380
  ...(nomosVerifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll } : {}),
1381
+ ...(eraCertGate !== undefined ? { nomosEraCertGate: eraCertGate } : {}),
1361
1382
  ...(nomosValueObjects !== undefined ? { nomosValueObjects } : {}),
1362
1383
  ...(Object.keys(visAggregates).length > 0
1363
1384
  ? { nomosReadVisibility: { v: 1 as const, aggregates: visAggregates } }
package/src/usd_layers.ts CHANGED
@@ -200,6 +200,9 @@ function domainLayerAttrLines(domain: string, layer: UsdLayer, indent: string):
200
200
  ...(layer.nomosVerifiedFirstEnroll !== undefined
201
201
  ? [`${indent}custom string nomos:verifiedFirstEnrollHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosVerifiedFirstEnroll)))}`]
202
202
  : []),
203
+ ...(layer.nomosEraCertGate !== undefined
204
+ ? [`${indent}custom string nomos:eraCertGateHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosEraCertGate)))}`]
205
+ : []),
203
206
  ...(layer.nomosValueObjects !== undefined
204
207
  ? [`${indent}custom string nomos:valueObjectsHex = ${usdaString(hexUtf8(JSON.stringify(layer.nomosValueObjects)))}`]
205
208
  : []),
@@ -910,6 +913,11 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
910
913
  verifiedFirstEnroll = { v: 1, directives: rec };
911
914
  }
912
915
  }
916
+ // LAWFUL RECOVERY: the single `nomosEraCertGate` (later-wins across the stack — one certifyEra verb).
917
+ let eraCertGate: UsdLayer["nomosEraCertGate"];
918
+ for (const l of stack) {
919
+ if (l.nomosEraCertGate !== undefined) eraCertGate = l.nomosEraCertGate;
920
+ }
913
921
  // FIRST-CLASS VALUE OBJECTS (+ the VO↔field usage pointers): descriptors merged
914
922
  // later-wins per NAME, usages later-wins per (aggregate, field), both SORTED —
915
923
  // so the layered fold stays byte-homomorphic with `emitUsd`'s composed emission.
@@ -956,6 +964,7 @@ export function flattenLayeredUsd(layers: readonly UsdLayer[]): UsdDocument {
956
964
  ...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
957
965
  ...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
958
966
  ...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
967
+ ...(eraCertGate !== undefined ? { nomosEraCertGate: eraCertGate } : {}),
959
968
  ...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
960
969
  };
961
970
  });
@@ -1094,6 +1103,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
1094
1103
  const birthRecordGateByDomain = new Map<string, UsdLayer["nomosBirthRecordGate"]>();
1095
1104
  const parentAttestedBirthByDomain = new Map<string, UsdLayer["nomosParentAttestedBirth"]>();
1096
1105
  const verifiedFirstEnrollByDomain = new Map<string, UsdLayer["nomosVerifiedFirstEnroll"]>();
1106
+ const eraCertGateByDomain = new Map<string, UsdLayer["nomosEraCertGate"]>();
1097
1107
  const valueObjectsByDomain = new Map<string, UsdLayer["nomosValueObjects"]>();
1098
1108
  for (const p of prims) {
1099
1109
  if (p.attrs.get("nomos:kind") === "domain") {
@@ -1181,6 +1191,10 @@ export function parseUsdaDocument(text: string): UsdDocument {
1181
1191
  if (typeof vfeHex === "string") {
1182
1192
  verifiedFirstEnrollByDomain.set(domain, JSON.parse(unhexUtf8(vfeHex)) as UsdLayer["nomosVerifiedFirstEnroll"]);
1183
1193
  }
1194
+ const ecgHex = p.attrs.get("nomos:eraCertGateHex");
1195
+ if (typeof ecgHex === "string") {
1196
+ eraCertGateByDomain.set(domain, JSON.parse(unhexUtf8(ecgHex)) as UsdLayer["nomosEraCertGate"]);
1197
+ }
1184
1198
  // FIRST-CLASS VALUE OBJECTS: the hex-carried descriptor set (descriptive, not a gate).
1185
1199
  const voHex = p.attrs.get("nomos:valueObjectsHex");
1186
1200
  if (typeof voHex === "string") {
@@ -1290,6 +1304,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
1290
1304
  const birthRecordGate = birthRecordGateByDomain.get(domain);
1291
1305
  const parentAttestedBirth = parentAttestedBirthByDomain.get(domain);
1292
1306
  const verifiedFirstEnroll = verifiedFirstEnrollByDomain.get(domain);
1307
+ const eraCertGate = eraCertGateByDomain.get(domain);
1293
1308
  const valueObjects = valueObjectsByDomain.get(domain);
1294
1309
  return {
1295
1310
  path: `/Nomos/${domain}`,
@@ -1314,6 +1329,7 @@ export function parseUsdaDocument(text: string): UsdDocument {
1314
1329
  ...(birthRecordGate !== undefined ? { nomosBirthRecordGate: birthRecordGate } : {}),
1315
1330
  ...(parentAttestedBirth !== undefined ? { nomosParentAttestedBirth: parentAttestedBirth } : {}),
1316
1331
  ...(verifiedFirstEnroll !== undefined ? { nomosVerifiedFirstEnroll: verifiedFirstEnroll } : {}),
1332
+ ...(eraCertGate !== undefined ? { nomosEraCertGate: eraCertGate } : {}),
1317
1333
  ...(valueObjects !== undefined ? { nomosValueObjects: valueObjects } : {}),
1318
1334
  };
1319
1335
  })