@githolon/dsl 0.102.5 → 0.103.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.
Files changed (131) hide show
  1. package/compile_package.mjs +43 -2
  2. package/compile_proof.d.mts +47 -0
  3. package/compile_proof.mjs +343 -0
  4. package/dart/CHANGELOG.md +7 -0
  5. package/dart/lib/src/dev_cloud.dart +8 -8
  6. package/dart/lib/src/nms_generated_runtime.dart +2 -0
  7. package/dart/lib/src/runner/node-entry.mjs +5504 -1236
  8. package/dart/lib/src/runner/offline-install.mjs +4578 -528
  9. package/dart/lib/src/test_harness.dart +206 -152
  10. package/dart/lib/src/webview_bridge.dart +196 -89
  11. package/dart/pubspec.lock +1 -1
  12. package/dart/pubspec.yaml +2 -2
  13. package/dart/test/acquire_session_credentials_test.dart +4 -3
  14. package/dart/test/adv_realm_interference_test.dart +7 -6
  15. package/dart/test/cloud_bound_topology_test.dart +3 -3
  16. package/dart/test/cluster_births_test.dart +4 -3
  17. package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
  18. package/dart/test/cluster_local_autosync_test.dart +3 -2
  19. package/dart/test/cluster_scale_test.dart +3 -2
  20. package/dart/test/dlq_session_surface_test.dart +7 -6
  21. package/dart/test/first_app_gauntlet_test.dart +14 -21
  22. package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
  23. package/dart/test/four_layer_nested_topology_test.dart +4 -4
  24. package/dart/test/harness_phase_timeout_test.dart +2 -2
  25. package/dart/test/local_sync_inert_test.dart +1 -1
  26. package/dart/test/parent_signer_recovery_test.dart +2 -2
  27. package/dart/test/sealing_client_test.dart +4 -3
  28. package/dart/test/session_bound_test.dart +8 -7
  29. package/dart/test/test_harness_test.dart +13 -12
  30. package/dart/test/vo_read_typing_test.dart +4 -3
  31. package/dart/test/watch_after_offer_stall_test.dart +4 -3
  32. package/dart/test/webview_bridge_test.dart +102 -5
  33. package/dart_types/CHANGELOG.md +5 -0
  34. package/dart_types/lib/nomos_types.dart +9 -0
  35. package/dart_types/lib/src/nomos_ref.dart +20 -0
  36. package/dart_types/lib/src/optional_ref.dart +85 -0
  37. package/dart_types/lib/src/sha256_digest.dart +41 -0
  38. package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
  39. package/dart_types/pubspec.yaml +1 -1
  40. package/dart_types/test/optional_ref_test.dart +44 -0
  41. package/dart_types/test/sha256_digest_test.dart +18 -0
  42. package/package.json +9 -5
  43. package/runtime/generated-application-host.js +5365 -1105
  44. package/src/aggregate.ts +18 -0
  45. package/src/application_composition.ts +6 -32
  46. package/src/attested_core.ts +6 -3
  47. package/src/birth_meter.ts +1 -0
  48. package/src/birth_meter_ensure.ts +28 -2
  49. package/src/births.ts +1 -1
  50. package/src/build_package.ts +162 -538
  51. package/src/business_birth.ts +19 -3
  52. package/src/canonical_json.ts +21 -0
  53. package/src/check_package_main.ts +73 -33
  54. package/src/clear_directive.ts +19 -8
  55. package/src/codegen_dart.ts +305 -22
  56. package/src/codegen_dart_application.ts +844 -292
  57. package/src/codegen_dart_client.ts +1084 -407
  58. package/src/codegen_dart_scenario.ts +12 -12
  59. package/src/codegen_proof.ts +169 -39
  60. package/src/codegen_provider_dart.ts +17 -1
  61. package/src/codegen_ts.ts +298 -106
  62. package/src/compat_manifest.ts +2 -2
  63. package/src/compat_package_main.ts +8 -12
  64. package/src/compile_package_main.ts +1858 -590
  65. package/src/compose.ts +1 -1
  66. package/src/directive.ts +208 -15
  67. package/src/engine_entry.ts +165 -111
  68. package/src/evolve_preview.ts +29 -69
  69. package/src/extremum.ts +1 -1
  70. package/src/fields.ts +183 -4
  71. package/src/flow_gate.ts +75 -2
  72. package/src/framework/application_home.ts +2 -0
  73. package/src/framework/auth_provider_law.ts +2 -2
  74. package/src/framework/birth_meter.ts +7 -19
  75. package/src/framework/bootstrap.ts +0 -2
  76. package/src/framework/business_corrections.ts +9 -5
  77. package/src/framework/domain_lifecycle.ts +15 -29
  78. package/src/framework/encryption_scope.ts +2 -1
  79. package/src/framework/framework_aggregates.ts +2 -0
  80. package/src/framework/genesis.ts +16 -14
  81. package/src/framework/home.ts +20 -14
  82. package/src/framework/owned_custody.ts +9 -3
  83. package/src/framework/platform_admin.ts +3 -3
  84. package/src/framework/relations.ts +21 -13
  85. package/src/framework/signing_identity.ts +45 -40
  86. package/src/framework/workspace_invariant.ts +245 -0
  87. package/src/framework/workspaces.ts +23 -29
  88. package/src/index.ts +36 -16
  89. package/src/intent.ts +178 -6
  90. package/src/law_check.ts +32 -11
  91. package/src/law_graph.ts +2902 -0
  92. package/src/law_graph_usda.ts +1799 -0
  93. package/src/law_identity.ts +435 -0
  94. package/src/law_value_type.ts +230 -0
  95. package/src/lsp_server.ts +58 -29
  96. package/src/ops.ts +7 -0
  97. package/src/package_config.ts +141 -0
  98. package/src/package_dependencies.ts +108 -0
  99. package/src/package_dependency_usage.ts +150 -0
  100. package/src/package_source_imports.ts +71 -0
  101. package/src/plan_source_slice.ts +335 -0
  102. package/src/point_guard.ts +192 -0
  103. package/src/precondition.ts +44 -9
  104. package/src/read_projection_namespace.ts +1 -1
  105. package/src/runtime_zod.ts +44 -0
  106. package/src/secondary_ensure.ts +67 -14
  107. package/src/shape_compat.ts +1 -1
  108. package/src/stable_id_identity_bridge.ts +149 -0
  109. package/src/stable_id_lockfile.ts +140 -6
  110. package/src/stable_ids.ts +1 -1
  111. package/src/stable_ids_for_module.ts +348 -0
  112. package/src/stable_ids_types.ts +1 -1
  113. package/src/sum.ts +1 -1
  114. package/src/usd_state.ts +1 -1
  115. package/src/usda_relationship_integrity.ts +66 -0
  116. package/src/usda_text.ts +18 -0
  117. package/src/usdz.ts +78 -0
  118. package/src/value_object.ts +95 -0
  119. package/src/wire.ts +19 -0
  120. package/src/wire_encode.ts +22 -0
  121. package/src/workspace_sharding.ts +52 -36
  122. package/src/workspace_sharing.ts +28 -4
  123. package/src/zod_introspection.ts +1 -1
  124. package/dart/test/cluster_attested_test.dart +0 -201
  125. package/src/compile_engine.ts +0 -595
  126. package/src/manifest.ts +0 -1476
  127. package/src/read_projection_composition.ts +0 -117
  128. package/src/read_projection_manifest.ts +0 -115
  129. package/src/relation_composition.ts +0 -286
  130. package/src/usd.ts +0 -1967
  131. package/src/usd_layers.ts +0 -1776
@@ -9,10 +9,10 @@
9
9
  * The `nomos` lifecycle/controller surface.
10
10
  *
11
11
  * This is the Kubernetes CRD/API-server split in Nomos terms:
12
- * * `PolicyBundle` is the content-addressed package object (`policy:{hash}.bundle`);
13
- * * `DomainInstallation` is the workspace resource with spec/status/phase;
14
- * * `installDomain` writes BOTH in one intent and declares the `installDomain`
15
- * capability. `stageDomain` writes the same content as Pending without serving it;
12
+ * * the kernel-custodied USDA closure is the content-addressed law object;
13
+ * * `DomainInstallation` is the small workspace resource with spec/status/phase;
14
+ * * `installDomain` writes that reference in one intent and declares the `installDomain`
15
+ * capability. `stageDomain` records the same closure as Pending without serving it;
16
16
  * `promoteDomain` and `rollbackDomain` are phase changes whose admission is judged
17
17
  * against the staged package. This is the richer controller API; bootstrap exposes
18
18
  * a thinner install-only intent for stage-zero.
@@ -43,16 +43,12 @@ export const DOMAIN_INSTALLATION_PHASES = [
43
43
  ] as const;
44
44
  export type DomainInstallationPhase = (typeof DOMAIN_INSTALLATION_PHASES)[number];
45
45
 
46
- export const PolicyBundle = aggregate("PolicyBundle", {
47
- bundle: t.string().merge(Lww),
48
- }).public();
49
-
50
46
  export const DomainInstallation = aggregate("DomainInstallation", {
51
47
  "spec.domainHash": t.string().merge(Lww),
52
48
  "spec.packageHash": t.string().merge(Lww),
53
- "spec.policyAggregate": t.string().merge(Lww),
54
49
  "spec.authorityScope": t.string().merge(Lww),
55
50
  "spec.installedBy": t.string().merge(Lww),
51
+ "spec.lawClosureIdentity": t.string().merge(Lww),
56
52
  "spec.dependencies": t.set(t.string()),
57
53
  "spec.supersedes": t.string().merge(Lww).optional(),
58
54
  "spec.finalizers": t.set(t.string()),
@@ -95,18 +91,10 @@ export const ReleaseReceipt = aggregate(RELEASE_RECEIPT_AGGREGATE, {
95
91
  acceptedAt: t.int().merge(Lww),
96
92
  }).public();
97
93
 
98
- export function policyAggregateId(domainHash: string): string {
99
- return `policy:${domainHash}`;
100
- }
101
-
102
94
  export function domainInstallationAggregateId(domainHash: string): string {
103
95
  return `domain-installation:${domainHash}`;
104
96
  }
105
97
 
106
- function policyBundleInstance(domainHash: string) {
107
- return instance(PolicyBundle, policyAggregateId(domainHash));
108
- }
109
-
110
98
  function domainInstallationInstance(domainHash: string) {
111
99
  return instance(DomainInstallation, domainInstallationAggregateId(domainHash));
112
100
  }
@@ -157,13 +145,15 @@ const preflightReceipt = z.object({
157
145
  });
158
146
 
159
147
  const packagePayload = {
160
- domainHash: z.string(),
161
- packageUsda: z.string(),
148
+ domainHash: lowerHex64,
162
149
  installedBy: z.string(),
150
+ lawClosureIdentity: lowerHex64,
163
151
  authorityScope: z.string().default("workspace/root"),
164
- dependencies: z.array(z.string()).default([]),
152
+ // Omission means the empty set; keep that in the plan instead of encoding a composite default
153
+ // into canonical USDA merely to save `?? []` at runtime.
154
+ dependencies: z.array(z.string()).optional(),
165
155
  supersedes: z.string().optional(),
166
- finalizers: z.array(z.string()).default([]),
156
+ finalizers: z.array(z.string()).optional(),
167
157
  };
168
158
 
169
159
  export const installDomain = directive("installDomain")
@@ -185,15 +175,13 @@ export const installDomain = directive("installDomain")
185
175
  }),
186
176
  )
187
177
  .plan((p): PlannedOp[] => {
188
- const policy = policyBundleInstance(p.domainHash);
189
178
  const install = domainInstallationInstance(p.domainHash);
190
179
  const ops: PlannedOp[] = [
191
- withMarker(set(policy, "bundle", p.packageUsda), "ensures"),
192
- set(install, "spec.domainHash", p.domainHash),
180
+ withMarker(set(install, "spec.domainHash", p.domainHash), "ensures"),
193
181
  set(install, "spec.packageHash", p.domainHash),
194
- set(install, "spec.policyAggregate", policyAggregateId(p.domainHash)),
195
182
  set(install, "spec.authorityScope", p.authorityScope),
196
183
  set(install, "spec.installedBy", p.installedBy),
184
+ set(install, "spec.lawClosureIdentity", p.lawClosureIdentity),
197
185
  set(install, "metadata.generation", 1),
198
186
  set(install, "status.phase", "Active"),
199
187
  set(install, "status.observedGeneration", 1),
@@ -231,15 +219,13 @@ export const stageDomain = directive("stageDomain")
231
219
  { message: "a staged domain package cannot supersede itself", path: ["supersedes"] },
232
220
  ))
233
221
  .plan((p): PlannedOp[] => {
234
- const policy = policyBundleInstance(p.domainHash);
235
222
  const install = domainInstallationInstance(p.domainHash);
236
223
  const ops: PlannedOp[] = [
237
- withMarker(set(policy, "bundle", p.packageUsda), "ensures"),
238
- set(install, "spec.domainHash", p.domainHash),
224
+ withMarker(set(install, "spec.domainHash", p.domainHash), "ensures"),
239
225
  set(install, "spec.packageHash", p.domainHash),
240
- set(install, "spec.policyAggregate", policyAggregateId(p.domainHash)),
241
226
  set(install, "spec.authorityScope", p.authorityScope),
242
227
  set(install, "spec.installedBy", p.installedBy),
228
+ set(install, "spec.lawClosureIdentity", p.lawClosureIdentity),
243
229
  set(install, "metadata.generation", 1),
244
230
  set(install, "status.phase", "Pending"),
245
231
  set(install, "status.observedGeneration", 0),
@@ -146,7 +146,8 @@ export const advanceKeyEpoch = directive("advanceKeyEpoch")
146
146
  wraps: z.array(wrapEntrySchema),
147
147
  }),
148
148
  )
149
- .plan((p) => writeKeyEpoch(p.scope, p.epoch, p.openedAt, p.wraps));
149
+ .plan((p) => writeKeyEpoch(p.scope, p.epoch, p.openedAt, p.wraps))
150
+ .alsoEnsuresMany(WrappedKey);
150
151
 
151
152
  /**
152
153
  * `wrapKey` — write ONE wrap (the single-member handoff). Beyond its use as a primitive,
@@ -36,6 +36,7 @@ import { nomosSourceFrontierEnsure } from "../attested.js";
36
36
  import * as birthMeter from "../birth_meter.js";
37
37
  import * as birthMeterEnsure from "../birth_meter_ensure.js";
38
38
  import * as birthMeterFramework from "./birth_meter.js";
39
+ import * as businessCorrections from "./business_corrections.js";
39
40
  import * as authProviderLaw from "./auth_provider_law.js";
40
41
  import * as disclosure from "./disclosure.js";
41
42
  import * as domainLifecycle from "./domain_lifecycle.js";
@@ -59,6 +60,7 @@ const FRAMEWORK_NAMESPACES: ReadonlyArray<Record<string, unknown>> = [
59
60
  birthMeter,
60
61
  birthMeterEnsure,
61
62
  birthMeterFramework,
63
+ businessCorrections,
62
64
  authProviderLaw,
63
65
  disclosure,
64
66
  domainLifecycle,
@@ -9,18 +9,19 @@
9
9
  *
10
10
  * THE BOOTSTRAP PARADOX: the relation gate refuses a founding control-plane grant
11
11
  * on a VIRGIN chain (an empty `#admin`/`#creator`/… roster) — there is no admin yet
12
- * to gate the first admin. Resolved LAWFULLY (not by a host bypass): at birth the
13
- * host verifies the `x-nomos-auth` token ONCE and records the verified principal as
14
- * an in-history fact — this `GenesisWarrant`. The gate's GENESIS CLAUSE
12
+ * to gate the first admin. Resolved LAWFULLY (not by a host bypass): the root
13
+ * `seedGenesis` is signed by the self-authenticating key principal it installs; a
14
+ * child receives its warrant only from the kernel's already-admitted parent-birth
15
+ * effect and the carried birth certificate rooted at K_root. The gate's GENESIS CLAUSE
15
16
  * (`executor::genesis_admits`) then admits a founding grant on a virgin object IFF
16
17
  * the warrant binds the chain to the captured principal. Strictly additive (only an
17
18
  * otherwise-`Unauthorized` virgin grant), so every sealed chain replays unchanged.
18
19
  *
19
- * CONTROL-PLANE: `seedGenesis` is authored by the BIRTH DOOR (the host, after the
20
- * one-time token verification), NOT by the open session lane — the session lane
21
- * structurally refuses control-plane intents, so a tenant cannot forge a warrant for
22
- * a principal it does not own. The warrant is the host's recorded verification, not
23
- * a host authority decision: every replaying peer re-reads the chain fact.
20
+ * CONTROL-PLANE: `seedGenesis` is never a host assertion. At the root it carries an
21
+ * author block whose public key derives the claimed `key:<sha256>` principal and whose
22
+ * signature binds the exact workspace id. For descendants it is emitted only by the
23
+ * kernel after verifying the parent's admitted birth and K_root-rooted certificate.
24
+ * Every replaying peer re-verifies those cryptographic facts from custody.
24
25
  *
25
26
  * SINGLETON per chain: one founding principal per home/workspace, so the warrant is
26
27
  * a singleton keyed `genesis-warrant:self`. `seedGenesis` is create-once — a second
@@ -32,7 +33,7 @@ import { t } from "../fields.js";
32
33
  import { Lww } from "../drivers.js";
33
34
  import { directive } from "../directive.js";
34
35
  import { set, type PlannedOp } from "../ops.js";
35
- import { writeTuple } from "./relations.js";
36
+ import { RelationTuple, writeTuple } from "./relations.js";
36
37
 
37
38
  /**
38
39
  * Deterministic singleton id of the founding warrant — MUST equal
@@ -42,16 +43,16 @@ export const GENESIS_WARRANT_ID = "genesis-warrant:self";
42
43
 
43
44
  /**
44
45
  * The founding-warrant record. It does THREE jobs at once:
45
- * - `principal` — the x-nomos-auth-verified OWNER (the genesis clause binds the founding grant to it);
46
+ * - `principal` — the cryptographically proven founder (the genesis clause binds the founding grant to it);
46
47
  * - `workspaceId` — this workspace's UNIQUE SELF-ID (the source binding the author signature covers,
47
48
  * re-derived locally by the gate — NOT the shared/deterministic genesis commit, which is identical
48
49
  * across all workspaces and so bound nothing);
49
50
  * - its mere PRESENCE — the SIGNED-AUTHOR REGIME signal (the "era"): a workspace with a warrant
50
- * REQUIRES a verified author block (forge closed); one without falls back to the legacy
51
- * host-stamped principal (pre-feature chains replay green, per-position).
51
+ * REQUIRES a verified author block (forge closed). A root seed proves itself against
52
+ * its own workspace-bound key; descendants additionally carry their parent-attested lineage.
52
53
  */
53
54
  export const GenesisWarrant = aggregate("GenesisWarrant", {
54
- principal: t.string().merge(Lww), // the x-nomos-auth-verified owner, recorded at birth
55
+ principal: t.string().merge(Lww), // the cryptographically proven founder, recorded at birth
55
56
  workspaceId: t.string().merge(Lww), // this workspace's unique self-id (the author-signature source binding)
56
57
  bornAt: t.string().merge(Lww), // ISO-8601, caller-stamped (determinism)
57
58
  }).public();
@@ -104,4 +105,5 @@ export const seedGenesis = directive("seedGenesis")
104
105
  grantedBy: p.principal,
105
106
  grantedAt: p.bornAt,
106
107
  }),
107
- ]);
108
+ ])
109
+ .alsoEnsures(RelationTuple);
@@ -37,9 +37,9 @@ import { query } from "../query.js";
37
37
  import { birth } from "../births.js";
38
38
  import { readFrameworkDeclared } from "../read.js";
39
39
  import { authProvider as authProviderQuery, seedAuthProvider } from "./auth_provider_law.js";
40
- import { workspaceInvariant, refAs } from "./workspace_invariant.js";
40
+ import { readRef, readSet, workspaceInvariant, refAs } from "./workspace_invariant.js";
41
41
  import { identityRelations } from "./signing_identity.js";
42
- import { writeTuple } from "./relations.js";
42
+ import { RelationTuple, writeTuple } from "./relations.js";
43
43
  import { relationType, self, user, selfObject } from "../relation_schema.js";
44
44
  import { requireParentAttestedBirth, type ParentAttestedBirthMarker } from "../births.js";
45
45
  import { GenesisWarrant, seedGenesis } from "./genesis.js";
@@ -168,7 +168,8 @@ export function writeReceivingGroup(p: { principal: string; workspaceName: strin
168
168
  export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f5e")
169
169
  .ensures(ReceivingGroup)
170
170
  .payload(z.object({ principal: z.string().min(1), workspaceName: z.string().min(1), bornAt: z.string().min(1) }))
171
- .plan((p) => writeReceivingGroup(p));
171
+ .plan((p) => writeReceivingGroup(p))
172
+ .alsoEnsures(RelationTuple);
172
173
 
173
174
  /**
174
175
  * THE SQUAT INVARIANT (home_holons §5) — a home holon's self-committed `homeName` MUST be the hash of its
@@ -180,7 +181,7 @@ export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f
180
181
  */
181
182
  export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGroupNameBindsPrincipal")
182
183
  .on("nomosInternalSeedReceivingGroup7c1f5e")
183
- .reads(() => [refAs("group", "NomosInternalReceivingGroup7c1f5e", RECEIVING_GROUP_ID)])
184
+ .reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
184
185
  .declassify("the receiving-group address is a public hash of its stable principal; refusing a mismatch reveals no private field")
185
186
  .assert(({ group }) => {
186
187
  const g = (group ?? {}) as { principal?: string; workspaceName?: string };
@@ -191,10 +192,10 @@ export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGro
191
192
  : { reject: "a receiving group's workspace name must hash its stable principal" };
192
193
  });
193
194
 
194
- const receivingGroupPolicyRefs = () => [
195
- refAs("group", "NomosInternalReceivingGroup7c1f5e", RECEIVING_GROUP_ID),
196
- refAs("policy", "NomosInternalReceivingGroupPolicy7c1f5e", RECEIVING_GROUP_POLICY_ID),
197
- ];
195
+ const receivingGroupPolicyRefs = readSet(
196
+ readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID),
197
+ readRef("policy", ReceivingGroupPolicy).at(RECEIVING_GROUP_POLICY_ID),
198
+ );
198
199
 
199
200
  /** An IdP token may bootstrap the parent before the group exists; it may never bypass an existing group's
200
201
  * device policy. A new group device must be approved by a current device or, later, guardian evidence. */
@@ -259,7 +260,7 @@ export function receivingGroupNameOf(principal: string): string {
259
260
  * (no anonymous home birth); the offer principal IS the auth boundary (the tenant's setAuthProvider).
260
261
  *
261
262
  * The genesis chain it authors (reusing the order-declares-the-law install shape — bytes resolved by hash
262
- * from the parent's custody, `domainPackageB64: ""`):
263
+ * from the parent's typed USDA closure custody):
263
264
  * [ bootstrap/installDomain(frameworkHash), nomos/home installDomain(lawHash), seedHomeOwner(owner, …) ]
264
265
  * `frameworkHash` (the bootstrap controller hash) + `lawHash` (the HOME law package domainHash) are
265
266
  * tenant-PINNED at their governance compile (mirroring `birthChild`) — the end-user app supplies neither;
@@ -352,11 +353,11 @@ export function birthReceivingGroupForDomain(groupLawDomain: string) {
352
353
  let certVerified = false;
353
354
  try { certVerified = p.homeCert !== undefined && JSON.parse(p.homeCert).verified === true; } catch { /* malformed cert ⇒ unverified (the gate will refuse the birth anyway) */ }
354
355
  const verifiedVia = certVerified ? apStr("issuer") : undefined;
355
- const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string; domainPackageB64?: string }> = [
356
- // THE ORDER-DECLARED LAW INSTALL: two layers, bytes-by-hash (empty package — the kernel resolves them
357
- // from the parent's custody when it folds the birth; never the host). Matches buildGenesisChain's shape.
358
- { domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash, domainPackageB64: "" },
359
- { domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash, domainPackageB64: "" },
356
+ const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string }> = [
357
+ // THE ORDER-DECLARED LAW INSTALL: two typed closure identities. The kernel resolves each
358
+ // closure from the parent's custody when it folds the birth; law bytes never ride the intent.
359
+ { domain: "bootstrap", directiveId: "installDomain", actor: founder, payload: { domainHash: p.frameworkHash }, domainHash: p.frameworkHash },
360
+ { domain: "nomos", directiveId: "installDomain", actor: founder, payload: { domainHash: p.lawHash }, domainHash: p.lawHash },
360
361
  {
361
362
  domain: groupLawDomain,
362
363
  directiveId: "nomosInternalSeedReceivingGroupPolicy7c1f5e",
@@ -509,6 +510,11 @@ export const pruneShare = directive("pruneShare")
509
510
  /** "Show me everything <asserter> shared with me" — an indexed probe over the local access list. */
510
511
  export const sharedByWorkspace = query("sharedByWorkspace").key("asserterWorkspace").returns(SharedWithMe);
511
512
 
513
+ /** "Show me every share for this protected object" — the indexed access-list probe used by generated
514
+ * application discovery. A home commonly has a very large installed law, so discovery must never fall back
515
+ * to the generic `aggregatesByType(SharedWithMe)` scan merely to find whole-workspace shares. */
516
+ export const sharedByObject = query("sharedByObject").key("object").returns(SharedWithMe);
517
+
512
518
  // ─────────────────── THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain) ───────────────────
513
519
 
514
520
  /**
@@ -288,7 +288,8 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
288
288
  grantedAt: p.sharedAt,
289
289
  }),
290
290
  ...p.wraps.flatMap((entry) => writeWrap(recordScope(aggregate, p.recordId), entry.epoch, entry)),
291
- ]),
291
+ ])
292
+ .alsoEnsures(RelationTuple),
292
293
  { businessBirthInternal: true as const },
293
294
  );
294
295
 
@@ -313,7 +314,9 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
313
314
  revokedAt: p.changedAt,
314
315
  }),
315
316
  ...writeKeyEpoch(recordScope(aggregate, p.recordId), p.epoch, p.changedAt, p.wraps),
316
- ]),
317
+ ])
318
+ .alsoEnsures(RelationTuple)
319
+ .alsoEnsuresMany(WrappedKey),
317
320
  { businessBirthInternal: true as const },
318
321
  );
319
322
 
@@ -354,7 +357,10 @@ function ownedRecordActionDirectives(aggregate: AggregateHandle) {
354
357
  ops.push(...writeKeyEpoch(scope, p.epoch, p.changedAt, []));
355
358
  for (const entry of p.historicWraps) ops.push(...writeWrap(scope, entry.epoch, entry));
356
359
  return ops;
357
- }),
360
+ })
361
+ .alsoEnsuresMany(RelationTuple)
362
+ .alsoMutates(aggregate)
363
+ .alsoEnsuresMany(WrappedKey),
358
364
  { businessBirthInternal: true as const },
359
365
  );
360
366
 
@@ -102,7 +102,7 @@ import { directive, type RequirableDirective } from "../directive.js";
102
102
  import { set } from "../ops.js";
103
103
  import { query, type QueryDecl } from "../query.js";
104
104
  import { writeTuple, RelationTuple } from "./relations.js";
105
- import { workspaceInvariant, refAs, type WorkspaceInvariantDecl } from "./workspace_invariant.js";
105
+ import { readRef, readSet, workspaceInvariant, refAs, type WorkspaceInvariantDecl } from "./workspace_invariant.js";
106
106
 
107
107
  /** The founder singleton's stable instance id — one founder per platform workspace (`workspace:self`). */
108
108
  export const PLATFORM_FOUNDER_ID = "platform-founder:self";
@@ -215,7 +215,7 @@ export function seedPlatformAdmin(opts: SeedPlatformAdminOptions): PlatformAdmin
215
215
  */
216
216
  const platformFounderWriteOnce = workspaceInvariant(writeOnceInvariantId)
217
217
  .on(founderDirectiveId)
218
- .reads(() => [refAs("prior", founderAggregateId, "pre:" + PLATFORM_FOUNDER_ID)])
218
+ .reads(readSet(readRef("prior", founderAggregateId).pre().at(PLATFORM_FOUNDER_ID)))
219
219
  .assert(({ prior }, ctx) => {
220
220
  const seated = (prior ?? {}) as { principal?: string };
221
221
  // WRITE-ONCE: a founder already recorded ⇒ refuse a re-seat (the seat is immutable after genesis).
@@ -260,7 +260,7 @@ export function seedPlatformAdmin(opts: SeedPlatformAdminOptions): PlatformAdmin
260
260
  */
261
261
  const platformAdminBindsFounder = workspaceInvariant(bindsInvariantId)
262
262
  .on(seatDirectiveId)
263
- .reads(() => [refAs("founder", founderAggregateId, PLATFORM_FOUNDER_ID)])
263
+ .reads(readSet(readRef("founder", founderAggregateId).at(PLATFORM_FOUNDER_ID)))
264
264
  .assert(({ founder }, ctx) => {
265
265
  const rec = (founder ?? {}) as { principal?: string };
266
266
  const intent = (ctx?.intent ?? {}) as { founder?: string };
@@ -15,7 +15,7 @@
15
15
  * ── The aggregate (object-keyed) ─────────────────────────────────────────────
16
16
  * `RelationTuple` is keyed `reltuple-<object>-<relation>` — ONE aggregate per
17
17
  * (object, relation) pair — carrying a `subjects: MapOf(Lww)` map of `subject → leaf`,
18
- * where each leaf is a JSON `{ status }` ("active" | "revoked"). The projection folds an
18
+ * where each leaf is a typed `RelationshipSubject` (`active` | `revoked`). The projection folds an
19
19
  * ACTIVE subject into a `relations(object, relation, subject)` row; a revoked subject
20
20
  * yields no row (Check fails closed).
21
21
  *
@@ -41,7 +41,7 @@
41
41
  */
42
42
  import { z } from "zod";
43
43
  import { aggregate, instance } from "../aggregate.js";
44
- import { t } from "../fields.js";
44
+ import { t, type JsonValue } from "../fields.js";
45
45
  import { Lww, MapOf } from "../drivers.js";
46
46
  import { directive } from "../directive.js";
47
47
  import { set, setEntry, withMarker, type PlannedOp } from "../ops.js";
@@ -70,6 +70,11 @@ import { qualifyReadProjectionType } from "../read_projection_namespace.js";
70
70
  interface DispatchContext {
71
71
  /** The dispatching domain key — the qualifier (`module.domain === intent.domain`). */
72
72
  readonly domain: string;
73
+ /**
74
+ * The durable Zanzibar namespace for this domain. Canonical USDA uses the domain's stable identity
75
+ * (`d_<sid>`), not its mutable dispatch label. Older/test engines may omit this and retain the label.
76
+ */
77
+ readonly relationNamespace?: string;
73
78
  /** `<type>#<relation>` for every LOCAL non-global authored relation of this domain — the qualify set. */
74
79
  readonly qualifiable: ReadonlySet<string>;
75
80
  /**
@@ -122,14 +127,14 @@ function resolveTupleObject(object: string | RelationObjectToken, relation: stri
122
127
  "qualify by). Tokens (selfWorkspace()/selfObject()) resolve only inside a directive plan.",
123
128
  );
124
129
  }
125
- return `${qualifyRelationType(_ctx.domain, object.type)}:${object.id}`;
130
+ return `${qualifyRelationType(_ctx.relationNamespace ?? _ctx.domain, object.type)}:${object.id}`;
126
131
  }
127
132
  // A PLAIN STRING is classified by `(type, relation)` against the dispatching domain's schema.
128
133
  const colon = object.indexOf(":");
129
134
  if (colon < 0 || _ctx === undefined) return object; // malformed / no context ⇒ verbatim
130
135
  const type = object.slice(0, colon);
131
136
  if (_ctx.qualifiable.has(`${type}#${relation}`)) {
132
- return `${qualifyRelationType(_ctx.domain, type)}${object.slice(colon)}`;
137
+ return `${qualifyRelationType(_ctx.relationNamespace ?? _ctx.domain, type)}${object.slice(colon)}`;
133
138
  }
134
139
  return object; // global / unknown / per-record ⇒ BARE
135
140
  }
@@ -185,7 +190,7 @@ export function relationTupleInstanceId(object: string, relation: string): strin
185
190
  *
186
191
  * - object Lww (the address, materialised on first relate so the aggregate exists)
187
192
  * - relation Lww (the edge label)
188
- * - subjects MapOf(Lww) — subject → JSON `{status}` leaf (active drops to a tuple row)
193
+ * - subjects MapOf(Lww) — subject → typed `RelationshipSubject` runtime leaf
189
194
  */
190
195
  export const RelationTuple = aggregate("RelationTuple", {
191
196
  object: t.string().merge(Lww),
@@ -194,7 +199,7 @@ export const RelationTuple = aggregate("RelationTuple", {
194
199
  }).public();
195
200
  export type RelationTupleHandle = typeof RelationTuple;
196
201
 
197
- /** The per-subject leaf the `subjects` map stores (JSON-encoded). Frozen wire shape. */
202
+ /** The per-subject leaf the `subjects` map stores as a typed kernel value. */
198
203
  export interface RelationSubjectLeaf {
199
204
  status: RelationStatus;
200
205
  grantedBy: string;
@@ -222,11 +227,11 @@ export interface RelationSubjectLeaf {
222
227
  ownedCustody?: string;
223
228
  }
224
229
 
225
- /** Strip undefined keys so the JSON leaf is byte-stable (no dropped-slot `"k":undefined`). */
226
- function compact(obj: Record<string, unknown>): RelationSubjectLeaf {
230
+ /** Strip undefined keys so the typed leaf has one stable optional-field shape. */
231
+ function compact(obj: Record<string, unknown>): RelationSubjectLeaf & Record<string, JsonValue> {
227
232
  const out: Record<string, unknown> = {};
228
233
  for (const [k, v] of Object.entries(obj)) if (v !== undefined) out[k] = v;
229
- return out as unknown as RelationSubjectLeaf;
234
+ return out as RelationSubjectLeaf & Record<string, JsonValue>;
230
235
  }
231
236
 
232
237
  interface WriteTupleInput {
@@ -277,9 +282,9 @@ function lowerTuple(
277
282
  // subject's endorsement is recorded on the setEntry op so the discovery harvest lifts it into
278
283
  // nomosFlowGate.endorse; grantedBy is free attribution (materialised, not gated).
279
284
  //
280
- // The captured authoring principal IS already a wire-form subject (`user:<uid>` / `key:<hash>`, the
281
- // host-stamped actor — the SAME form the write gate parses via `Subject::parse`), so an endorsed
282
- // authority subject lowers to that leaf verbatim and the gate's `engine.check` matches it.
285
+ // The captured authoring principal IS already a kernel-proven wire-form subject (`user:<uid>` /
286
+ // `key:<hash>` — the SAME form the write gate parses via `Subject::parse`), so an endorsed authority
287
+ // subject lowers to that leaf verbatim and the gate's `engine.check` matches it.
283
288
  const subjectE = endorsementOf(p.subject);
284
289
  const subjectRaw = subjectE
285
290
  ? subjectE.principal
@@ -300,7 +305,10 @@ function lowerTuple(
300
305
  verifiedVia: p.verifiedVia,
301
306
  ownedCustody: p.ownedCustody,
302
307
  });
303
- const subjectEntry = setEntry(rt, "subjects", subjectRaw, JSON.stringify(leaf));
308
+ const subjectEntry = {
309
+ ...setEntry(rt, "subjects", subjectRaw, leaf),
310
+ kernelValue: { RelationshipSubject: leaf },
311
+ };
304
312
  return [
305
313
  // upsert the aggregate head (first relate creates it); without this the referential
306
314
  // gate would reject a setEntry on an absent id.
@@ -49,7 +49,7 @@ import { directive } from "../directive.js";
49
49
  import { set, withMarker, type PlannedOp } from "../ops.js";
50
50
  import { query } from "../query.js";
51
51
  import { relationType, self, key } from "../relation_schema.js";
52
- import { workspaceInvariant, ref } from "./workspace_invariant.js";
52
+ import { readRef, readSet, workspaceInvariant, ref } from "./workspace_invariant.js";
53
53
  import { endorsementOf, type EndorsedAuthority } from "../principal_evidence.js";
54
54
  import {
55
55
  writeTuple,
@@ -144,7 +144,7 @@ export const advanceSignerEpoch = directive("advanceSignerEpoch")
144
144
  */
145
145
  export const signerEpochStrictlyIncreases = workspaceInvariant("SignerEpochStrictlyIncreases")
146
146
  .on("advanceSignerEpoch")
147
- .reads(() => [ref("SignerEpoch", SIGNER_EPOCH_ID)])
147
+ .reads(readSet(readRef("signerEpoch", SignerEpoch).at(SIGNER_EPOCH_ID)))
148
148
  .assert((snap, ctx) => {
149
149
  const applied = snap["signerEpoch"] as { currentEpoch?: number } | undefined;
150
150
  const declared = Number((ctx?.intent as { epoch?: number } | undefined)?.epoch ?? NaN);
@@ -166,7 +166,7 @@ export const signerEpochStrictlyIncreases = workspaceInvariant("SignerEpochStric
166
166
  */
167
167
  export const revokeSignerEpochStrictlyIncreases = workspaceInvariant("RevokeSignerEpochStrictlyIncreases")
168
168
  .on("revokeSigner")
169
- .reads(() => [ref("SignerEpoch", SIGNER_EPOCH_ID)])
169
+ .reads(readSet(readRef("signerEpoch", SignerEpoch).at(SIGNER_EPOCH_ID)))
170
170
  .assert((snap, ctx) => {
171
171
  const applied = snap["signerEpoch"] as { currentEpoch?: number } | undefined;
172
172
  const declared = Number((ctx?.intent as { newEpoch?: number } | undefined)?.newEpoch ?? NaN);
@@ -289,7 +289,7 @@ export const enrollSignerSelf = directive("enrollSignerSelf")
289
289
  */
290
290
  export const enrollSignerSelfRequiresVerified = workspaceInvariant("EnrollSignerSelfRequiresVerified")
291
291
  .on("enrollSignerSelf")
292
- .reads(() => []) // no aggregate snapshot needed — the verdict is over the captured principal-verified flag
292
+ .reads(readSet()) // no aggregate snapshot needed — the verdict is over the captured principal-verified flag
293
293
  .declassify("the self-enroll gate intentionally branches on principal_verified (an authz fact, not a secret read)")
294
294
  .assert((_snap, ctx) =>
295
295
  ctx?.principalVerified === true
@@ -297,6 +297,44 @@ export const enrollSignerSelfRequiresVerified = workspaceInvariant("EnrollSigner
297
297
  : { reject: "enroll-signer-self-requires-verified-principal" },
298
298
  );
299
299
 
300
+ // ─────────────────── THE ENROLL-ONCE DELEGATION CERT (the peer-sovereign root-of-trust) ───────────────────
301
+
302
+ /**
303
+ * THE SIGNER DELEGATION (the enroll-once delegation cert, `executor::birth_cert`). When the TENANT verifies a
304
+ * user U's IdP token at U's live enroll gate, the tenant's onboarding signer `S_P` signs — ONCE — a
305
+ * DELEGATION CERT binding U's device key `P_U` to the tenant's lineage: a `BirthCert {founder: U,
306
+ * parent_key: P_U, …}` signed by `S_P`. This singleton (keyed by U) records it in the PLATFORM's folded
307
+ * state, so the kernel's birth offer-effect reads U's delegation by-user (trusted, not from the payload) when
308
+ * it assembles U's home cert chain. The cert is OPAQUE on-chain (a JSON `BirthCert`); the kernel parses +
309
+ * verifies it as one chain link (signed by S_P, whose pubkey is attested by the platform's own chain).
310
+ *
311
+ * SELF-BOUND / control-plane: enrolled by the tenant onboarding lane (composed into the tenant's governance
312
+ * law, off the open session lane), so a tenant cannot forge a delegation for a user another tenant verified.
313
+ * The delegation's `founder` IS the gate's binding — the kernel asserts the home cert's founder equals the
314
+ * delegation's founder (the crux founder-binding), so a delegation enrolled for A cannot mint a home for V.
315
+ *
316
+ * Declared before `enrollFirstDevice` because that directive's typed effect signature and optional plan arm
317
+ * both refer to this aggregate during module initialisation.
318
+ */
319
+ export const SIGNER_DELEGATION_PREFIX = "signer-delegation:";
320
+ export const SignerDelegation = aggregate("SignerDelegation", {
321
+ /** The user (a `user:<uid>` subject) this delegation enrolls — the key of the singleton. */
322
+ user: t.string().merge(Lww),
323
+ /** The user's device PUBLIC KEY (base64) the delegation binds — `P_U`, the home_cert's signer. */
324
+ deviceKey: t.string().merge(Lww),
325
+ /** THE DELEGATION CERT (JSON `executor::birth_cert::BirthCert`), signed by the tenant's `S_P`. Opaque
326
+ * on-chain; the kernel parses + verifies it as the `links[1]` delegation in U's travelling chain. */
327
+ certJson: t.string().merge(Lww),
328
+ /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
329
+ enrolledAt: t.string().merge(Lww),
330
+ }).public();
331
+ export type SignerDelegationHandle = typeof SignerDelegation;
332
+
333
+ /** The deterministic singleton id of a user's signer delegation (the kernel reads THIS id by-user). */
334
+ export function signerDelegationInstanceId(user: string): string {
335
+ return SIGNER_DELEGATION_PREFIX + user;
336
+ }
337
+
300
338
  /**
301
339
  * `enrollFirstDevice` (architecture/keyless_verified_onboard.md) — THE KEYLESS VERIFIED FIRST-DEVICE
302
340
  * self-onboard. A brand-new principal (an IdP-verified user with NO enrolled signer key) bootstraps its
@@ -377,6 +415,7 @@ export const enrollFirstDevice = directive("enrollFirstDevice")
377
415
  ];
378
416
  return [...tupleOps, ...delegationOps];
379
417
  })
418
+ .alsoEnsures(SignerDelegation)
380
419
  // THE KEYLESS GATE DECLARATION: the carried device-bound `idpToken` IS this directive's authorization
381
420
  // (re-verified against the workspace AuthProvider on every lane), not a pre-enrolled signer. This lowers
382
421
  // the per-directive payload FIELD MAPPING into the `nomosVerifiedFirstEnroll` era key, so the KERNEL reads
@@ -417,42 +456,8 @@ export const revokeSigner = directive("revokeSigner")
417
456
  // bump the signer epoch so the revoked key ages out (the live-epoch backdate is phase 4).
418
457
  ...writeSignerEpoch(p.newEpoch),
419
458
  ];
420
- });
421
-
422
- // ─────────────────── THE ENROLL-ONCE DELEGATION CERT (the peer-sovereign root-of-trust) ───────────────────
423
-
424
- /**
425
- * THE SIGNER DELEGATION (the enroll-once delegation cert, `executor::birth_cert`). When the TENANT verifies a
426
- * user U's IdP token at U's live enroll gate, the tenant's onboarding signer `S_P` signs — ONCE — a
427
- * DELEGATION CERT binding U's device key `P_U` to the tenant's lineage: a `BirthCert {founder: U,
428
- * parent_key: P_U, …}` signed by `S_P`. This singleton (keyed by U) records it in the PLATFORM's folded
429
- * state, so the kernel's birth offer-effect reads U's delegation by-user (trusted, not from the payload) when
430
- * it assembles U's home cert chain. The cert is OPAQUE on-chain (a JSON `BirthCert`); the kernel parses +
431
- * verifies it as one chain link (signed by S_P, whose pubkey is attested by the platform's own chain).
432
- *
433
- * SELF-BOUND / control-plane: enrolled by the tenant onboarding lane (composed into the tenant's governance
434
- * law, off the open session lane), so a tenant cannot forge a delegation for a user another tenant verified.
435
- * The delegation's `founder` IS the gate's binding — the kernel asserts the home cert's founder equals the
436
- * delegation's founder (the crux founder-binding), so a delegation enrolled for A cannot mint a home for V.
437
- */
438
- export const SIGNER_DELEGATION_PREFIX = "signer-delegation:";
439
- export const SignerDelegation = aggregate("SignerDelegation", {
440
- /** The user (a `user:<uid>` subject) this delegation enrolls — the key of the singleton. */
441
- user: t.string().merge(Lww),
442
- /** The user's device PUBLIC KEY (base64) the delegation binds — `P_U`, the home_cert's signer. */
443
- deviceKey: t.string().merge(Lww),
444
- /** THE DELEGATION CERT (JSON `executor::birth_cert::BirthCert`), signed by the tenant's `S_P`. Opaque
445
- * on-chain; the kernel parses + verifies it as the `links[1]` delegation in U's travelling chain. */
446
- certJson: t.string().merge(Lww),
447
- /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
448
- enrolledAt: t.string().merge(Lww),
449
- }).public();
450
- export type SignerDelegationHandle = typeof SignerDelegation;
451
-
452
- /** The deterministic singleton id of a user's signer delegation (the kernel reads THIS id by-user). */
453
- export function signerDelegationInstanceId(user: string): string {
454
- return SIGNER_DELEGATION_PREFIX + user;
455
- }
459
+ })
460
+ .alsoEnsures(SignerEpoch);
456
461
 
457
462
  /**
458
463
  * `enrollDelegation` — record a user's tenant-signed delegation cert (the enroll-once lane). The tenant's