@githolon/dsl 0.108.7 → 0.110.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 (307) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +76 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +211 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +267 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23905 -15774
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +548 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_span_trace_test.dart +23 -0
  74. package/dart/test/replica_readiness_test.dart +156 -0
  75. package/dart/test/runtime_projections_test.dart +92 -0
  76. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  77. package/dart/test/signing_identity_kernel_test.dart +75 -0
  78. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  79. package/dart/test/sync_status_reducer_test.dart +419 -0
  80. package/dart/test/test_harness_test.dart +71 -95
  81. package/dart/test/typed_query_watch_test.dart +170 -0
  82. package/dart/test/typed_watch_registry_test.dart +249 -0
  83. package/dart_lints/README.md +18 -0
  84. package/dart_lints/lib/nomos_lints.dart +70 -0
  85. package/dart_lints/pubspec.lock +580 -0
  86. package/dart_lints/pubspec.yaml +12 -0
  87. package/dart_lints/test/reference_identity_test.dart +75 -0
  88. package/dart_types/CHANGELOG.md +18 -0
  89. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  90. package/dart_types/lib/nomos_types.dart +9 -1
  91. package/dart_types/lib/src/admission_decision.dart +37 -0
  92. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  93. package/dart_types/lib/src/approved_model.dart +65 -0
  94. package/dart_types/lib/src/custody.dart +10 -18
  95. package/dart_types/lib/src/error_info.dart +4 -4
  96. package/dart_types/lib/src/errors.dart +29 -29
  97. package/dart_types/lib/src/foreign_ref.dart +62 -33
  98. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  99. package/dart_types/lib/src/nomos_ref.dart +3 -1
  100. package/dart_types/lib/src/principal.dart +2 -2
  101. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  102. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  103. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  105. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  107. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  149. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  150. package/dart_types/lib/src/subscriptions.dart +116 -23
  151. package/dart_types/pubspec.yaml +1 -1
  152. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  153. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  154. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  155. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  156. package/operation_contract_check.mjs +24 -0
  157. package/package.json +11 -6
  158. package/src/aggregate.ts +70 -10
  159. package/src/application_topology.ts +18 -11
  160. package/src/attested.ts +3 -0
  161. package/src/attested_core.ts +46 -3
  162. package/src/birth_meter_ensure.ts +19 -7
  163. package/src/births.ts +29 -4
  164. package/src/build_package.ts +140 -13
  165. package/src/business_birth.ts +181 -4
  166. package/src/codegen_call_proto.ts +1274 -0
  167. package/src/codegen_call_usda.ts +49 -6
  168. package/src/codegen_dart.ts +79 -44
  169. package/src/codegen_dart_client.ts +458 -800
  170. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  171. package/src/codegen_dart_framework_calls.ts +320 -37
  172. package/src/codegen_dart_indexed_variant.ts +159 -0
  173. package/src/codegen_dart_scenario.ts +42 -13
  174. package/src/codegen_proof.ts +3 -3
  175. package/src/codegen_provider_dart.ts +5 -0
  176. package/src/codegen_query_proto.ts +158 -0
  177. package/src/codegen_refusal.ts +189 -0
  178. package/src/codegen_result_proto.ts +931 -0
  179. package/src/codegen_result_usda.ts +4 -0
  180. package/src/codegen_ts.ts +464 -616
  181. package/src/codegen_workspace_access.ts +105 -0
  182. package/src/compatibility_graph.ts +759 -0
  183. package/src/compatibility_usda.ts +264 -0
  184. package/src/compile_package_main.ts +1994 -1189
  185. package/src/custody_placement.ts +156 -0
  186. package/src/custody_placement_usda.ts +91 -0
  187. package/src/decl_shapes.ts +21 -2
  188. package/src/describe_model.ts +1 -0
  189. package/src/directive.ts +59 -2
  190. package/src/drivers.ts +51 -0
  191. package/src/engine_entry.ts +52 -8
  192. package/src/evolve_preview.ts +11 -4
  193. package/src/exists.ts +2 -0
  194. package/src/fields.ts +75 -11
  195. package/src/flow_gate.ts +2 -1
  196. package/src/framework/application_home.ts +170 -18
  197. package/src/framework/attested_receipts.ts +8 -3
  198. package/src/framework/auth_provider_law.ts +169 -9
  199. package/src/framework/birth_certificate_law.ts +4 -0
  200. package/src/framework/capability.ts +34 -3
  201. package/src/framework/child_authentication.ts +346 -0
  202. package/src/framework/console_governance.ts +295 -0
  203. package/src/framework/delegated_roles.ts +301 -0
  204. package/src/framework/domain_lifecycle.ts +26 -13
  205. package/src/framework/founding_genesis.ts +193 -0
  206. package/src/framework/framework_aggregates.ts +1 -1
  207. package/src/framework/genesis.ts +6 -1
  208. package/src/framework/governance_relations.ts +19 -0
  209. package/src/framework/home.ts +5 -734
  210. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  211. package/src/framework/model_authority_repair.ts +116 -0
  212. package/src/framework/model_authority_seed.ts +185 -0
  213. package/src/framework/model_transition.ts +211 -0
  214. package/src/framework/owned_custody.ts +1 -1
  215. package/src/framework/principal_enrollment.ts +302 -0
  216. package/src/framework/receiving_group.ts +857 -0
  217. package/src/framework/receiving_group_recovery.ts +314 -0
  218. package/src/framework/relations.ts +4 -8
  219. package/src/framework/signing_identity.ts +242 -95
  220. package/src/framework/source_roster.ts +11 -5
  221. package/src/framework/workspace_access.ts +149 -0
  222. package/src/framework/workspace_invariant.ts +73 -52
  223. package/src/framework/workspaces.ts +407 -40
  224. package/src/framework_call_definitions.ts +32 -3
  225. package/src/generated_client_bundle_boundary.ts +97 -0
  226. package/src/generated_client_names.ts +1 -0
  227. package/src/identity_usda.ts +27 -0
  228. package/src/index.ts +183 -12
  229. package/src/indexed_variant.ts +83 -0
  230. package/src/intent.ts +173 -45
  231. package/src/law_check.ts +8 -5
  232. package/src/law_dependency_index.ts +231 -0
  233. package/src/law_graph.ts +508 -71
  234. package/src/law_graph_usda.ts +170 -24
  235. package/src/law_identity.ts +119 -81
  236. package/src/law_source_map.ts +4 -1
  237. package/src/law_units.ts +498 -0
  238. package/src/law_value_type.ts +7 -3
  239. package/src/member_authentication.ts +302 -0
  240. package/src/null_meta.ts +1 -1
  241. package/src/openusd_compiled_layer.ts +132 -0
  242. package/src/operation_contract_check.ts +31 -0
  243. package/src/ordered_read.ts +2 -1
  244. package/src/package_config.ts +33 -12
  245. package/src/package_dependencies.ts +1 -0
  246. package/src/plan_source_slice.ts +30 -1
  247. package/src/protobuf_field_number.ts +36 -0
  248. package/src/provenance_wire.ts +6 -0
  249. package/src/query.ts +2 -1
  250. package/src/read.ts +50 -0
  251. package/src/relation.ts +4 -1
  252. package/src/relation_schema.ts +15 -2
  253. package/src/report.ts +2 -2
  254. package/src/sqlite_computation_bundle.ts +78 -0
  255. package/src/sqlite_computed.ts +102 -0
  256. package/src/sqlite_counts.ts +154 -0
  257. package/src/sqlite_layout.ts +823 -0
  258. package/src/sqlite_reads.ts +275 -0
  259. package/src/sqlite_samples.ts +185 -0
  260. package/src/stable_id_identity_bridge.ts +1 -1
  261. package/src/stable_ids_for_module.ts +16 -4
  262. package/src/test_call_usda.ts +6 -6
  263. package/src/usda_relationship_integrity.ts +20 -3
  264. package/src/usda_text.ts +21 -1
  265. package/src/value_object.ts +81 -11
  266. package/src/wire.ts +22 -0
  267. package/src/wire_encode.ts +227 -20
  268. package/src/workspace_sharding.ts +15 -6
  269. package/src/workspace_sharing.ts +51 -49
  270. package/src/zod_introspection.ts +2 -0
  271. package/compat_package.mjs +0 -46
  272. package/dart/lib/src/home.dart +0 -15
  273. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  274. package/dart/lib/src/source_trust.dart +0 -51
  275. package/dart/lib/src/webview_bridge.dart +0 -6540
  276. package/dart/lib/src/workspace_name.dart +0 -30
  277. package/dart/test/acquire_session_credentials_test.dart +0 -150
  278. package/dart/test/acquire_session_test.dart +0 -534
  279. package/dart/test/adv_realm_interference_test.dart +0 -600
  280. package/dart/test/cloud_bound_topology_test.dart +0 -168
  281. package/dart/test/cluster_births_test.dart +0 -202
  282. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  283. package/dart/test/cluster_local_autosync_test.dart +0 -123
  284. package/dart/test/cluster_scale_test.dart +0 -122
  285. package/dart/test/compat_test.dart +0 -61
  286. package/dart/test/dlq_session_surface_test.dart +0 -222
  287. package/dart/test/first_app_gauntlet_test.dart +0 -386
  288. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  289. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  290. package/dart/test/home_parity_test.dart +0 -13
  291. package/dart/test/local_sync_inert_test.dart +0 -107
  292. package/dart/test/node_runner_transport_test.dart +0 -78
  293. package/dart/test/parent_signer_recovery_test.dart +0 -155
  294. package/dart/test/residency_refusal_test.dart +0 -49
  295. package/dart/test/sealing_client_test.dart +0 -181
  296. package/dart/test/session_bound_test.dart +0 -285
  297. package/dart/test/stale_bundle_test.dart +0 -80
  298. package/dart/test/vo_read_typing_test.dart +0 -131
  299. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  300. package/dart/test/webview_bridge_test.dart +0 -1930
  301. package/dart/test/workspace_name_test.dart +0 -24
  302. package/runtime/generated-application-host.js +0 -22860
  303. package/src/codegen_query_usda.ts +0 -214
  304. package/src/compat_manifest.ts +0 -26
  305. package/src/compat_package_main.ts +0 -224
  306. package/src/shape_compat.ts +0 -352
  307. package/src/usd_state.ts +0 -505
@@ -0,0 +1,857 @@
1
+ // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
2
+ // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
3
+ // wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
4
+ // If a file isn't this / hosting this / authoring for this / proving this — it's gone.
5
+
6
+ /**
7
+ * framework/receiving_group.ts — THE RECEIVING GROUP (the identity role; `architecture/
8
+ * receiving_group_lifecycle.md`, `home_holons_and_cross_custody.md` §4). One holon, three roles —
9
+ * "workspace," "platform," "receiving group" are the SAME githolon wearing the role its installed LAW
10
+ * gives it. A receiving group runs THIS law: an identity's subject-side root — the one small workspace
11
+ * an enrolled device can always clone first. "Home" is a TENANT's word for the business law it composes
12
+ * into this anchor; the framework never uses it (this file was `home.ts` until 2026-09-11; that path
13
+ * remains a deprecated re-export shim for one release).
14
+ *
15
+ * It holds only PUBLIC material + signed facts (private keys never leave the client, so the cloud hosting
16
+ * it stays operator-blind):
17
+ * - KEY DIRECTORY — the identity's public device keys: the `signer` relation
18
+ * (`identity:<principal>#signer@key:<hash>`, `framework/signing_identity.ts`). Re-exported here so a
19
+ * home holon's law carries it: "wrap the scope key to U's device" becomes "read U's home key roster."
20
+ * - SHARED-WITH-ME index (§5) — one record per verified cross-custody attestation delivered here, so
21
+ * "show me everything shared with me" is a LOCAL query, no fan-out.
22
+ * - INBOX — grants/invites/notifications/dead-letters addressed to the identity.
23
+ *
24
+ * THE FORWARD-CHECK DOCTRINE (§5): the index is a DISCOVERY HINT, not the authority. When the identity
25
+ * actually opens the asserting workspace, THAT workspace's gate does the live `Check` — so a stale share
26
+ * (the asserter revoked it) is fail-safe: the identity sees a workspace it can no longer open, the forward
27
+ * check denies, the hint is pruned. (The cross-custody ATTESTATION that proves a delivered share is signed
28
+ * + verifiable is §3; the host DELIVERY lane that fills the index is §5. This file is the law that holds
29
+ * the result, and the owner-authored lane to maintain it.)
30
+ */
31
+ import { z } from "zod";
32
+ import type { ApplicationHomeGenesis } from "../application_home_genesis.js";
33
+ import { aggregate, instance, OWNED_READ_RELATION } from "../aggregate.js";
34
+ import { sha256Digest, t } from "../fields.js";
35
+ import { Lww } from "../drivers.js";
36
+ import { directive } from "../directive.js";
37
+ import { ensure } from "../authoring.js";
38
+ import { set, withMarker, type PlannedOp } from "../ops.js";
39
+ import { query } from "../query.js";
40
+ import { AUTH_PROVIDER_EPOCH_CONSUMER, birth, type WorkspaceRole } from "../births.js";
41
+ import {
42
+ authProvider as authProviderQuery,
43
+ authProviderCertificates as authProviderCertificatesQuery,
44
+ authProviderKeys as authProviderKeysQuery,
45
+ readAuthProviderVerifyingConfiguration,
46
+ seedAuthProvider,
47
+ } from "./auth_provider_law.js";
48
+ import { readRef, readSet, workspaceInvariant, refAs } from "./workspace_invariant.js";
49
+ import {
50
+ identityRelations,
51
+ } from "./signing_identity.js";
52
+ import {
53
+ RelationTuple,
54
+ relationTuplesByObject,
55
+ writeTuple,
56
+ } from "./relations.js";
57
+ import { inheritedModelAuthorityGenesisStep } from "./model_authority_seed.js";
58
+ import { relationType, self, user, selfObject } from "../relation_schema.js";
59
+ import { requireParentAttestedBirth, type ParentAttestedBirthMarker } from "../births.js";
60
+ import { GenesisWarrant, seedGenesis } from "./genesis.js";
61
+ import { idOf } from "../value_object.js";
62
+ import {
63
+ DomainInstallExecutor,
64
+ domainInstallGenesisOffer,
65
+ } from "./domain_lifecycle.js";
66
+ import { birthCertificatePayload } from "./birth_certificate_law.js";
67
+ import { recordReceivedWorkspaceAccess } from "./workspace_access.js";
68
+ import { readInheritedOperators, seedFoundingPrincipal } from "./founding_genesis.js";
69
+ import { readInheritedEnrollers, relationTuplesByRelation } from "./principal_enrollment.js";
70
+ import { inheritedGovernanceStep } from "./console_governance.js";
71
+ import { delegatedRolesSteps, RECEIVING_GROUP_CHILD_KIND } from "./delegated_roles.js";
72
+ export { recordReceivedWorkspaceAccess, receivedWorkspaceAccess } from "./workspace_access.js";
73
+
74
+ // `nomos.sha256` — the TRUSTLESS, pure hash computed IN the githolon (Rust sha2 bound in the sealed engine;
75
+ // see deterministic-rquickjs). Available as a global to plans + invariant predicates (like `nomos.mint`); it
76
+ // lets the home law re-derive `home-<hash(owner)>` itself, so the SQUAT invariant runs in-law on every peer.
77
+ declare const nomos: { sha256(input: string): string };
78
+
79
+ /** The KEY DIRECTORY is the `signer` relation — re-exported so a home holon's law composes it (the
80
+ * identity's public device keys, enrolled via `enrollSigner`, checked by the gate). */
81
+ export { identityRelations };
82
+
83
+ /** THE GENESIS WARRANT primitives — re-exported so a HOME law carries them in the `home` domain key. The
84
+ * birthReceivingGroup genesis chain runs `home/seedGenesis` to record the home's unique self-id (== homeName), so
85
+ * `workspace_self_id` resolves on the home and BOTH the signed-author regime + the parent-attested-birth
86
+ * cert door FIRE on every home (BREAK 1). Without this the home is unwarranted and both doors are inert. */
87
+ export { GenesisWarrant, seedGenesis };
88
+
89
+ /**
90
+ * THE RECEIVING GROUP — one sovereign framework identity whose enrolled devices clone this same holon.
91
+ * It is not a tenant Person, an IdP account or a host-owned account. The native global
92
+ * `identity:<principal>#signer@key:<hash>` relation is its device roster; this singleton binds the group's
93
+ * stable principal to the workspace that carries that roster and its access manifest.
94
+ *
95
+ * The wire identifiers are deliberately obscure framework names. Tenant business packages may model their
96
+ * own `ReceivingGroup`, `home` or `group` concepts without silently colliding with this implementation.
97
+ */
98
+ export const RECEIVING_GROUP_ID = "nomos-internal-receiving-group-7c1f5e:self";
99
+ /** A receiving group's self-committed workspace-name prefix. */
100
+ export const RECEIVING_GROUP_NAME_PREFIX = "nomos-rg-";
101
+ export const RECEIVING_GROUP_NAME_HASH_LEN = 40;
102
+ export const RECEIVING_GROUP_POLICY_ID = "nomos-internal-receiving-group-policy-7c1f5e:self";
103
+ export const ReceivingGroup = aggregate("NomosInternalReceivingGroup7c1f5e", {
104
+ /** The stable Zanzibar principal represented by this group. Credentials may locate it but do not own it. */
105
+ principal: t.string().merge(Lww),
106
+ /** The group's self-committed workspace name, deterministically bound to its stable principal. */
107
+ workspaceName: t.string().merge(Lww),
108
+ /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
109
+ bornAt: t.string().merge(Lww),
110
+ // The group's genesis seats its controller in OWNED_READ_RELATION below. Declaring that same capability
111
+ // here makes the law's coarse pack gate recognise the seat, so a newly enrolled device can clone its own
112
+ // receiving group before any tenant records or tenant-authored grants exist. This remains ordinary,
113
+ // replayable Zanzibar state: revocation removes the seat; there is no kernel-level founder bypass.
114
+ }, { cap: 1 }).readableBy(OWNED_READ_RELATION);
115
+
116
+ /**
117
+ * The framework-owned device lifecycle policy for one receiving group. This is deliberately data, not a
118
+ * client-side branch: every peer replays the same rule. The first supported posture is intentionally small
119
+ * and secure: a live, verified device may add or revoke another device; keyless IdP enrolment is only the
120
+ * pre-group bootstrap lane.
121
+ *
122
+ * ALL-DEVICE-LOSS RECOVERY — CO2 already recovers through its platform AuthProvider, but the implementation
123
+ * reaches that result by repeating birth after this home's direct enrollment policy refuses. The corrected
124
+ * design (`architecture/guardian_device_recovery.md`, reconciled 2026-08-10) makes recovery authority an
125
+ * explicit tenant-selected policy: platform identity provider or guardian quorum. The missing work is to
126
+ * evaluate that choice at the existing child's frontier and delete the client-side birth-repeat bypass.
127
+ */
128
+ export const ReceivingGroupPolicy = aggregate("NomosInternalReceivingGroupPolicy7c1f5e", {
129
+ additionalDevice: t.string().merge(Lww),
130
+ revocation: t.string().merge(Lww),
131
+ recovery: t.string().merge(Lww),
132
+ guardianQuorum: t.int().merge(Lww),
133
+ setAt: t.string().merge(Lww),
134
+ }, { cap: 1 }).public();
135
+
136
+ export const DEFAULT_RECEIVING_GROUP_POLICY = {
137
+ additionalDevice: "current-device",
138
+ revocation: "current-device",
139
+ recovery: "guardian-quorum",
140
+ guardianQuorum: 1,
141
+ } as const;
142
+
143
+ /** The recovery authorities a receiving group may explicitly select. The default remains guardian
144
+ * quorum; an application must deliberately choose its attested parent identity provider. */
145
+ export const RECEIVING_GROUP_RECOVERY_POLICIES = [
146
+ "guardian-quorum",
147
+ "platform-identity-provider",
148
+ ] as const;
149
+ export const receivingGroupRecoveryPolicy = z.enum(RECEIVING_GROUP_RECOVERY_POLICIES);
150
+ export type ReceivingGroupRecoveryPolicy = z.infer<typeof receivingGroupRecoveryPolicy>;
151
+ export const PLATFORM_IDENTITY_PROVIDER_RECOVERY: ReceivingGroupRecoveryPolicy =
152
+ "platform-identity-provider";
153
+
154
+ /** The one policy write: genesis seeds it, and the framework's recovery acceptor re-seats it for a
155
+ * receiving group born under the untouched framework default (`receiving_group_recovery.ts`). */
156
+ export function writeReceivingGroupPolicy(p: {
157
+ setAt: string;
158
+ recovery?: ReceivingGroupRecoveryPolicy | undefined;
159
+ }): PlannedOp[] {
160
+ const policy = instance(ReceivingGroupPolicy, RECEIVING_GROUP_POLICY_ID);
161
+ return [
162
+ set(policy, "additionalDevice", DEFAULT_RECEIVING_GROUP_POLICY.additionalDevice),
163
+ set(policy, "revocation", DEFAULT_RECEIVING_GROUP_POLICY.revocation),
164
+ set(policy, "recovery", p.recovery ?? DEFAULT_RECEIVING_GROUP_POLICY.recovery),
165
+ set(policy, "guardianQuorum", DEFAULT_RECEIVING_GROUP_POLICY.guardianQuorum),
166
+ set(policy, "setAt", p.setAt),
167
+ ];
168
+ }
169
+
170
+ /** Genesis-only default policy. Policy changes become a separate, guardian-aware ceremony. */
171
+ export const seedReceivingGroupPolicy = directive("nomosInternalSeedReceivingGroupPolicy7c1f5e")
172
+ .ensures(ReceivingGroupPolicy)
173
+ .payload(z.object({
174
+ setAt: z.string().min(1),
175
+ recovery: receivingGroupRecoveryPolicy.optional(),
176
+ }))
177
+ .plan((p) => writeReceivingGroupPolicy(p));
178
+
179
+ /**
180
+ * The group's controller relation binds its stable principal inside the same genesis. Business representation
181
+ * remains separate Zanzibar edges; this relation only establishes who controls this receiving-group holon.
182
+ */
183
+ export const receivingGroupRelations = relationType("nomos.internal.receiving-group.7c1f5e").relation("controller", {
184
+ rewrite: self(),
185
+ allow: [user()],
186
+ });
187
+
188
+ /** Stamp the receiving group and atomically seat its controller relation. */
189
+ export function writeReceivingGroup(p: { principal: string; workspaceName: string; bornAt: string }): PlannedOp[] {
190
+ const h = instance(ReceivingGroup, RECEIVING_GROUP_ID);
191
+ return [
192
+ set(h, "principal", p.principal),
193
+ set(h, "workspaceName", p.workspaceName),
194
+ set(h, "bornAt", p.bornAt),
195
+ ...writeTuple({
196
+ object: selfObject(receivingGroupRelations),
197
+ relation: "controller",
198
+ subject: p.principal,
199
+ grantedBy: p.principal,
200
+ grantedAt: p.bornAt,
201
+ }),
202
+ // A device clones the encrypted workspace before record-level ownership can be evaluated locally.
203
+ // Seat the group's controller for that ciphertext transport in the same genesis; individual owned
204
+ // records still require their own per-record reader edge before they decrypt or appear in reads.
205
+ ...writeTuple({
206
+ object: "workspace:self",
207
+ relation: OWNED_READ_RELATION,
208
+ subject: p.principal,
209
+ grantedBy: p.principal,
210
+ grantedAt: p.bornAt,
211
+ }),
212
+ ];
213
+ }
214
+
215
+ /**
216
+ * Genesis-only receiving-group stamp. The name is caller-supplied but the invariant below re-derives it.
217
+ */
218
+ export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f5e")
219
+ .ensures(ReceivingGroup)
220
+ .payload(z.object({ principal: z.string().min(1), workspaceName: z.string().min(1), bornAt: z.string().min(1) }))
221
+ .plan((p) => writeReceivingGroup(p))
222
+ .alsoEnsures(RelationTuple);
223
+
224
+ /**
225
+ * THE SQUAT INVARIANT (home_holons §5) — a home holon's self-committed `homeName` MUST be the hash of its
226
+ * owner: `homeName == home-<sha256(principal)>`. Declared by the home law, it EXECUTES at the one gate — and
227
+ * crucially at `verify_chain` (the bailiff's upload-birth): a squatter's chain (claiming `home-<hash(bob)>`
228
+ * but owned by `mallory`) FAILS the home law's OWN gate during adoption — refused IN THE GITHOLON, BY THE LAW,
229
+ * never by a host check. The host only routes the chain to the name it commits to (custody). `nomos.sha256`
230
+ * re-derives the hash in-law on every peer, so the verdict is identical everywhere.
231
+ */
232
+ export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGroupNameBindsPrincipal")
233
+ .on("nomosInternalSeedReceivingGroup7c1f5e")
234
+ .reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
235
+ .declassify("the receiving-group address is a public hash of its stable principal; refusing a mismatch reveals no private field")
236
+ .assert(({ group }) => {
237
+ const g = (group ?? {}) as { principal?: string; workspaceName?: string };
238
+ const expected = RECEIVING_GROUP_NAME_PREFIX
239
+ + nomos.sha256(String(g.principal ?? "")).slice(0, RECEIVING_GROUP_NAME_HASH_LEN);
240
+ return g.workspaceName === expected
241
+ ? { accept: true }
242
+ : { reject: "a receiving group's workspace name must hash its stable principal" };
243
+ });
244
+
245
+ const receivingGroupPolicyRefs = readSet(
246
+ readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID),
247
+ readRef("policy", ReceivingGroupPolicy).at(RECEIVING_GROUP_POLICY_ID),
248
+ );
249
+
250
+ /** An IdP token may bootstrap the parent before the group exists; it may never bypass an existing group's
251
+ * device policy. An existing group chooses whether recovery is delegated to its platform AuthProvider or to
252
+ * guardians; the current default below says guardian, while CO2's current product behaviour is platform IdP.
253
+ *
254
+ * KNOWN GAP (`architecture/guardian_device_recovery.md`): this invariant is declared `.on("enrollFirstDevice")`
255
+ * and only fires when that directive targets THIS home directly. It is NOT re-applied when the birth
256
+ * offer-effect (`run_birth_effect`, repeating `nomosInternalBirthReceivingGroup` against an ALREADY-founded
257
+ * home) folds a signer seat via the SAME underlying mechanism. That is how CO2 recovery works today, but its
258
+ * home frontier still says `guardian-quorum`. The fix is not to ban platform recovery: add the explicit
259
+ * platform-identity-provider policy, make CO2 choose it, and have the child gate verify the chosen evidence. */
260
+ export const receivingGroupRejectsKeylessAdditionalDevice = workspaceInvariant("ReceivingGroupRejectsKeylessAdditionalDevice")
261
+ .on("enrollFirstDevice")
262
+ .reads(receivingGroupPolicyRefs)
263
+ .declassify("device enrollment deliberately reveals that this receiving group has already completed first-device bootstrap")
264
+ .assert(({ group }) => typeof group?.principal !== "string" || group.principal.length === 0
265
+ ? { accept: true }
266
+ : { reject: "receiving-group-additional-device-requires-current-device-or-guardian" });
267
+
268
+ /** The shipped additional-device policy: an already enrolled, verified device signs the self-bound enrol. */
269
+ export const receivingGroupAdditionalDeviceFollowsPolicy = workspaceInvariant("ReceivingGroupAdditionalDeviceFollowsPolicy")
270
+ .on("enrollSignerSelf")
271
+ .reads(receivingGroupPolicyRefs)
272
+ .declassify("device lifecycle policy intentionally branches on principal_verified")
273
+ .assert(({ group, policy }, ctx) => {
274
+ if (typeof group?.principal !== "string" || group.principal.length === 0) return { accept: true };
275
+ const p = policy as { additionalDevice?: string } | undefined;
276
+ return p?.additionalDevice === "current-device" && ctx?.principalVerified === true
277
+ ? { accept: true }
278
+ : { reject: "receiving-group-additional-device-policy-refused" };
279
+ });
280
+
281
+ /** The shipped revocation policy: a live verified group device authors the remove-wins signer change. */
282
+ export const receivingGroupRevocationFollowsPolicy = workspaceInvariant("ReceivingGroupRevocationFollowsPolicy")
283
+ .on("revokeSigner")
284
+ .reads(receivingGroupPolicyRefs)
285
+ .declassify("device lifecycle policy intentionally branches on principal_verified")
286
+ .assert(({ group, policy }, ctx) => {
287
+ if (typeof group?.principal !== "string" || group.principal.length === 0) return { accept: true };
288
+ const p = policy as { revocation?: string } | undefined;
289
+ return p?.revocation === "current-device" && ctx?.principalVerified === true
290
+ ? { accept: true }
291
+ : { reject: "receiving-group-revocation-policy-refused" };
292
+ });
293
+
294
+ /** Re-derive a receiving group's workspace name in-law. */
295
+ export function receivingGroupNameOf(principal: string): string {
296
+ return RECEIVING_GROUP_NAME_PREFIX
297
+ + nomos.sha256(String(principal)).slice(0, RECEIVING_GROUP_NAME_HASH_LEN);
298
+ }
299
+
300
+ /** The speaker is established by the ordinary carried-offer gate. This law binds
301
+ * its otherwise source-scoped access claim to the actual recipient of this Home. */
302
+ export const receivedWorkspaceAccessIsForOwner = workspaceInvariant("ReceivedWorkspaceAccessIsForOwner")
303
+ .on(recordReceivedWorkspaceAccess.id)
304
+ .reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
305
+ .declassify("acceptance discloses only that this access claim was addressed to the receiving principal")
306
+ .assert(({ group }, ctx) => group?.principal === ctx?.intent.recipient
307
+ ? { accept: true }
308
+ : { reject: "workspace-access-wrong-recipient" });
309
+
310
+ /**
311
+ * `birthReceivingGroup` (née `birthHome`) — THE FRAMEWORK BIRTH PRIMITIVE for a receiving group. This is
312
+ * the MECHANICS (OURS) so a tenant never hand-builds a home genesis: the parent (the platform whose law includes this directive)
313
+ * ORDERS the birth of an identity's home holon, and the kernel's offer-effect spawns it + self-validates it
314
+ * through the child's OWN gate from intent 0 (the `.births()` declared consequence; `architecture/
315
+ * home_holons_and_cross_custody.md` + `sovereign_adapter.md` "birth is a kernel offer-effect").
316
+ *
317
+ * SELF-BOUND — squatting is structurally impossible (no separate grant needed; the policy gate is the
318
+ * tenant's `setAuthProvider` auth scoping, which decides whether the offering principal is verified at all):
319
+ * 1. The home OWNER rides the payload (`owner`, a `user:<uid>` subject). `ctx.principal()` is opaque
320
+ * PrincipalEvidence — it CANNOT be read as a raw string in-plan to compute the slot name — so the owner
321
+ * is payload-carried, but the binding below makes it sound.
322
+ * 2. The workspace name + the seeded `homeName` are BOTH derived from THE SAME payload `owner` via
323
+ * {@link homeNameOf} — they cannot diverge in this plan.
324
+ * 3. The child's genesis includes `seedHomeOwner(owner, homeNameOf(owner), bornAt)`, and the home law's
325
+ * {@link homeNameBindsOwner} invariant RE-DERIVES `home-<sha256(owner)>` at the child's gate (author AND
326
+ * verify_chain). So a chain that claims `home-<hash(alice)>` but seeds owner `bob` FAILS the child's own
327
+ * law — refused in the githolon, by the law, never by a host. A verified principal is required upstream
328
+ * (no anonymous home birth); the offer principal IS the auth boundary (the tenant's setAuthProvider).
329
+ *
330
+ * The genesis chain it authors (reusing the order-declares-the-law install shape — bytes resolved by hash
331
+ * from the parent's typed USDA closure custody):
332
+ * [ bootstrap/installDomain(frameworkHash), nomos/home installDomain(lawHash), seedHomeOwner(owner, …) ]
333
+ * `frameworkHash` (the bootstrap controller hash) + `lawHash` (the HOME law package domainHash) are
334
+ * tenant-PINNED at their governance compile (mirroring `birthChild`) — the end-user app supplies neither;
335
+ * the tenant's HomeBirth.viaCoordinator buildBody / their governance fills them. `seedHomeOwner` is INCLUDED
336
+ * so HomeOwner is seeded and `homeNameBindsOwner` passes (a birth without it would be an invalid, un-owned
337
+ * home).
338
+ */
339
+ // Standalone framework law explicitly declares `home`; compiler-composed business applications explicitly
340
+ // declare the obscure APPLICATION_HOME_DOMAIN. The kernel refuses law with no attestation-seed registrar.
341
+ export const RECEIVING_GROUP_LAW_DOMAIN = "home";
342
+ /**
343
+ * Build the private home-birth primitive for the domain key that carries the home law.
344
+ *
345
+ * Most framework-authored laws still use the historic public `home` key through
346
+ * {@link birthReceivingGroup}. Business packages use an obscure compiler-owned key instead, so a
347
+ * tenant remains free to model a business concept called "home". The key is closed over
348
+ * here and written into every child-genesis step; it is never a tenant payload field.
349
+ */
350
+ export function birthReceivingGroupForDomain(
351
+ groupLawDomain: string,
352
+ maxChildWorkspacesPerUser: number,
353
+ birthPolicyDomains: readonly string[],
354
+ homeGenesis?: ApplicationHomeGenesis,
355
+ parentWorkspaceRoles: readonly WorkspaceRole[] = [],
356
+ recoveryPosture?: ReceivingGroupRecoveryPolicy,
357
+ ) {
358
+ if (groupLawDomain.length === 0) {
359
+ throw new Error("birthReceivingGroupForDomain requires a non-empty receiving-group law domain");
360
+ }
361
+ if (recoveryPosture !== undefined && !RECEIVING_GROUP_RECOVERY_POLICIES.includes(recoveryPosture)) {
362
+ throw new Error(
363
+ `birthReceivingGroupForDomain: unknown receiving-group recovery posture '${String(recoveryPosture)}'`,
364
+ );
365
+ }
366
+ if (!Number.isInteger(maxChildWorkspacesPerUser) || maxChildWorkspacesPerUser < 0) {
367
+ throw new Error(
368
+ "birthReceivingGroupForDomain requires an explicit non-negative maxChildWorkspacesPerUser; " +
369
+ "Nomos has no compiled birth-policy fallback",
370
+ );
371
+ }
372
+ const policyDomains = [...new Set(birthPolicyDomains)];
373
+ if (
374
+ policyDomains.length === 0 ||
375
+ policyDomains.some((domain) => domain.trim().length === 0) ||
376
+ !policyDomains.includes(groupLawDomain)
377
+ ) {
378
+ throw new Error(
379
+ "birthReceivingGroupForDomain requires every co-resident birth-capable law domain, " +
380
+ "including its own domain; Nomos will not birth partially configured law",
381
+ );
382
+ }
383
+ const birthDirective = directive("nomosInternalBirthReceivingGroup7c1f5e")
384
+ .creates(ReceivingGroup)
385
+ .payload(
386
+ z.object({
387
+ /** The identity this home belongs to (a `user:<uid>` subject). The workspace name + seeded homeName are
388
+ * both `home-<sha256(owner)>` — self-bound, so you can only birth YOUR OWN home slot. */
389
+ owner: z.string().min(1),
390
+ /** The bootstrap framework controller content hash — tenant-pinned at governance compile (the child
391
+ * installs it IN-CHAIN as intent 0; the kernel resolves the bytes by hash from the parent's custody). */
392
+ frameworkHash: sha256Digest(),
393
+ /** The HOME law package domainHash — tenant-pinned at governance compile (the child's second install). */
394
+ lawHash: sha256Digest(),
395
+ /** ISO-8601 birth time, caller-stamped (the timestamp doctrine — the plan reads no clock). */
396
+ bornAt: z.string().min(1),
397
+ /** AUTHPROVIDER INHERITANCE — DEFAULT ON. Birth copies the current verifying material once;
398
+ * later rotations arrive through the ordinary lazy provider-adoption offer. */
399
+ inheritAuthProvider: z.boolean().optional(),
400
+ /** TOKEN-FREE FOUNDING (identity target model D22): sha256hex of the owner's device public key.
401
+ * Present ⇒ the owner FOUNDS their own group — `seedFoundingPrincipal` seats this key as the
402
+ * owner's signer and the owner as admin, with the parent's operators and enrollers — and the
403
+ * recipe carries no provider verifier, certificate or parent role, so the kernel's birth effect
404
+ * takes the founded lane. The birth must then be authored by the owner (see
405
+ * {@link receivingGroupFoundedBirthBindsOwner}). Absent ⇒ the historical recipe. */
406
+ foundingKeyHash: sha256Digest().optional(),
407
+ }),
408
+ )
409
+ .plan((p) => {
410
+ const founder = p.owner; // the actor for the child's own genesis steps (its self-bound owner subject)
411
+ const groupName = receivingGroupNameOf(p.owner);
412
+ const founded = p.foundingKeyHash !== undefined;
413
+ const inheritedProvider = founded || p.inheritAuthProvider === false
414
+ ? undefined
415
+ : readAuthProviderVerifyingConfiguration();
416
+ const bornAtMs = Date.parse(p.bornAt);
417
+ if (founded && !Number.isFinite(bornAtMs)) {
418
+ throw new Error("a founded receiving-group birth needs bornAt as an ISO-8601 instant");
419
+ }
420
+ // The owner founds the group: its device key, the owner as admin, and the parent's operators and
421
+ // enrollers (D15/D22), in the one requires-free founding step on the warrant-free prefix. It closes
422
+ // the recipe, in the group's own law domain: the kernel appends the birth origin and the warrant to
423
+ // the LAST step's domain, so the recipe must end in the law that declares them (a tenant
424
+ // home-genesis step last would route them to a tenant domain that has neither).
425
+ const inheritedEnrollers = founded ? readInheritedEnrollers() : [];
426
+ const inheritedOperators = founded ? readInheritedOperators() : [];
427
+ const foundingSteps = founded
428
+ ? [{
429
+ domain: groupLawDomain,
430
+ directiveId: seedFoundingPrincipal.id,
431
+ actor: founder,
432
+ payload: seedFoundingPrincipal.payloadSchema.parse({
433
+ principal: p.owner,
434
+ keyHash: p.foundingKeyHash,
435
+ operators: [...inheritedOperators],
436
+ enrollers: inheritedEnrollers.map((enroller) => ({ scope: enroller.scope, subject: enroller.subject })),
437
+ grantedAt: bornAtMs,
438
+ }) as Record<string, unknown>,
439
+ },
440
+ // Each inherited enroller's keys, so it can act here (enrol the owner's next device): keys live
441
+ // on every workspace, which is what keeps each one independently replayable.
442
+ // The inherited operators' and enrollers' keys, and installDomain for the operators, so the
443
+ // jurisdiction can enrol the owner's next device and roll out law here without a re-found.
444
+ ...inheritedGovernanceStep(groupLawDomain, founder, inheritedOperators, inheritedEnrollers, bornAtMs,
445
+ { principal: p.owner, keyHash: p.foundingKeyHash as string })]
446
+ : [];
447
+ // The roles the parent delegates into every group its owner's scope founds (formerly a per-intent
448
+ // delegation certificate), as state tuples for the owner — each seated in the domain whose law
449
+ // declares it. They precede the founding steps, which must close the recipe (see above).
450
+ const delegatedRoleSteps = founded
451
+ ? delegatedRolesSteps(founder, p.owner, RECEIVING_GROUP_CHILD_KIND, bornAtMs)
452
+ : [];
453
+ const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string }> = [
454
+ // THE ORDER-DECLARED LAW INSTALL: two typed closure identities. The kernel resolves each
455
+ // closure from the parent's custody when it folds the birth; law bytes never ride the intent.
456
+ domainInstallGenesisOffer(
457
+ DomainInstallExecutor.StageZero,
458
+ p.frameworkHash,
459
+ founder,
460
+ ),
461
+ domainInstallGenesisOffer(
462
+ DomainInstallExecutor.LifecycleController,
463
+ p.lawHash,
464
+ founder,
465
+ ),
466
+ // THE POSTURE IS BORN, NOT MIGRATED. The application's receiving-group declaration names the
467
+ // recovery authority its anchors are born with; an application that declares none seeds the
468
+ // framework default exactly as before (the field is omitted, so the recipe is byte-identical).
469
+ {
470
+ domain: groupLawDomain,
471
+ directiveId: "nomosInternalSeedReceivingGroupPolicy7c1f5e",
472
+ actor: founder,
473
+ payload: recoveryPosture === undefined
474
+ ? { setAt: p.bornAt }
475
+ : { setAt: p.bornAt, recovery: recoveryPosture },
476
+ },
477
+ // SEED THE OWNER — the home is born OWNED; the homeNameBindsOwner invariant in the child's law proves
478
+ // homeName == home-<sha256(owner)> at the child's own gate, so the slot can ONLY be its rightful owner's.
479
+ {
480
+ domain: groupLawDomain,
481
+ directiveId: "nomosInternalSeedReceivingGroup7c1f5e",
482
+ actor: founder,
483
+ payload: { principal: p.owner, workspaceName: groupName, bornAt: p.bornAt },
484
+ },
485
+ // The receiving group is born with its child-workspace allowance as explicit law-state. The
486
+ // value is closed over by the package's application policy, so neither the host nor the end
487
+ // user can substitute it in the offer payload.
488
+ ...policyDomains.map((domain) => ({
489
+ domain,
490
+ directiveId: "seedBirthPolicy",
491
+ actor: founder,
492
+ payload: {
493
+ maxBirthsPerPrincipal: maxChildWorkspacesPerUser,
494
+ setBy: p.owner,
495
+ setAt: p.bornAt,
496
+ },
497
+ })),
498
+ // Tenant founding law is an ordered list of ordinary offers. Values derive solely from the
499
+ // canonical home facts; every payload is parsed by the target directive before it reaches
500
+ // the child's author gate. There is no host append point and no framework-selected fallback.
501
+ ...(homeGenesis?.steps ?? []).map((step) => ({
502
+ domain: step.domain,
503
+ directiveId: step.directive.id,
504
+ actor: founder,
505
+ payload: step.directive.payloadSchema.parse(step.payload({
506
+ owner: p.owner,
507
+ workspaceName: groupName,
508
+ bornAt: p.bornAt,
509
+ })),
510
+ })),
511
+ ...delegatedRoleSteps,
512
+ ...foundingSteps,
513
+ ];
514
+ if (!founded && p.inheritAuthProvider !== false) {
515
+ if (inheritedProvider !== undefined) {
516
+ genesisChain.push({
517
+ domain: groupLawDomain,
518
+ directiveId: "seedAuthProvider",
519
+ actor: founder,
520
+ payload: {
521
+ ...inheritedProvider,
522
+ label: "inherited-from-parent",
523
+ setBy: p.owner,
524
+ setAt: 1,
525
+ },
526
+ });
527
+ }
528
+ }
529
+ // MODEL AUTHORITY DESCENDS (architecture/parent_directed_model_transition.md §4, lane A): the anchor is
530
+ // born holding its platform's current installDomain holders + their devices, read from THIS
531
+ // workspace's committed relation state, so the platform's own signer may lawfully stage and promote
532
+ // law in every receiving group it births. A parent holding no seat (a chain born before the seat
533
+ // existed) embeds nothing; the parent-directed transition seats it later.
534
+ // A founded group inherits law authority as `operator` tuples in its founding step instead.
535
+ const inheritedAuthority = founded
536
+ ? undefined
537
+ : inheritedModelAuthorityGenesisStep(groupLawDomain, founder, p.bornAt);
538
+ if (inheritedAuthority !== undefined) genesisChain.push(inheritedAuthority);
539
+ // THE DECLARED BIRTH EFFECT (the unix model): the recipe rides the committed intent as a declared output
540
+ // (captured_ports.births). The kernel — reading the `.births()` declaration off the law manifest — takes
541
+ // the recipe and spawns + self-validates the child through the child's OWN gate (each step self-verifies).
542
+ // A founded birth declares no parent role: parent roles ride the certificate lane's attestation.
543
+ birth({
544
+ workspace: groupName,
545
+ genesisChain,
546
+ ...(!founded && parentWorkspaceRoles.length > 0 ? { parentWorkspaceRoles } : {}),
547
+ });
548
+ // Parent/child lineage is an ordinary global Zanzibar relation in parent custody. The exact child
549
+ // address comes from the same plan which births it, so neither the host nor the caller can redirect
550
+ // this seat. Application law can then authorize upward workspace effects with the ordinary relation.
551
+ // THE INVERSE EPOCH EDGE rides the same plan: the born receiving group may ask THIS workspace for its
552
+ // current verifier epoch (`nomosInternalServeAuthProviderEpoch7c1f5e` gates on it), so a group whose
553
+ // verifier went stale refreshes it through the one framework lane instead of a tenant copy.
554
+ return [
555
+ ...writeTuple({
556
+ object: "workspace:self",
557
+ relation: "child",
558
+ subject: `workspace:${groupName}`,
559
+ grantedBy: p.owner,
560
+ grantedAt: p.bornAt,
561
+ }),
562
+ // A founded group has no provider verifier, so it never asks for an epoch.
563
+ ...(founded ? [] : writeTuple({
564
+ object: "workspace:self",
565
+ relation: AUTH_PROVIDER_EPOCH_CONSUMER,
566
+ subject: `workspace:${groupName}`,
567
+ grantedBy: p.owner,
568
+ grantedAt: p.bornAt,
569
+ })),
570
+ ];
571
+ })
572
+ .reads(authProviderQuery)
573
+ .reads(authProviderKeysQuery)
574
+ .reads(authProviderCertificatesQuery)
575
+ .reads(relationTuplesByObject)
576
+ // The parent's enroller tuples a founded group inherits (D22) — readInheritedEnrollers's one read.
577
+ .reads(relationTuplesByRelation)
578
+ .alsoEnsures(RelationTuple)
579
+ .births(); // BIRTH IS A DECLARED KERNEL PRIMITIVE — emits the `nomosBirthGate` era key; the kernel folds the recipe
580
+ return {
581
+ ...birthDirective,
582
+ ...(homeGenesis === undefined || homeGenesis.steps.length === 0
583
+ ? {}
584
+ : {
585
+ birthGenesisRequirements: homeGenesis.steps.map((step) => ({
586
+ domain: step.domain,
587
+ directiveId: step.directive.id,
588
+ })),
589
+ }),
590
+ };
591
+ }
592
+
593
+ /**
594
+ * A FOUNDED receiving-group birth (D22, `foundingKeyHash` present) carries no certificate or token to
595
+ * prove who the owner is, so the parent's own admission proves it: the birth is admitted only when its
596
+ * author — the principal the kernel authenticated from a signed key — IS the owner it founds. Nobody can
597
+ * found someone else's group, and the owner is authenticated exactly as for any other write (for an
598
+ * application, a `user:<uid>` key its enroller enrolled). The historical recipe is unchanged.
599
+ */
600
+ export const receivingGroupFoundedBirthBindsOwner = workspaceInvariant("ReceivingGroupFoundedBirthBindsOwner")
601
+ .on("nomosInternalBirthReceivingGroup7c1f5e")
602
+ .reads(readSet())
603
+ .declassify("the founded-birth refusal reveals only whether the author is the owner it names")
604
+ .assert((_snapshots, ctx) => {
605
+ const intent = (ctx?.intent ?? {}) as { owner?: string; foundingKeyHash?: string };
606
+ if (intent.foundingKeyHash === undefined) return { accept: true };
607
+ return typeof intent.owner === "string" && intent.owner.length > 0 && ctx?.principal === intent.owner
608
+ ? { accept: true }
609
+ : { reject: "founded-receiving-group-birth-must-be-authored-by-its-owner" };
610
+ });
611
+
612
+ /** Build the standalone framework receiving-group law with an explicit allowance. */
613
+ export const birthReceivingGroup = (maxChildWorkspacesPerUser: number) =>
614
+ birthReceivingGroupForDomain(
615
+ RECEIVING_GROUP_LAW_DOMAIN,
616
+ maxChildWorkspacesPerUser,
617
+ [RECEIVING_GROUP_LAW_DOMAIN],
618
+ );
619
+ /** @deprecated the old name of {@link birthReceivingGroup}; kept as an alias for one release. */
620
+ export const birthHome = birthReceivingGroup;
621
+
622
+ /**
623
+ * THE SHARED-WITH-ME INDEX (§5) — one record per cross-custody attestation delivered to this home holon:
624
+ * a signed edge `<asserterWorkspace> : <object> # <relation> @ this-identity`, indexed LOCALLY so the
625
+ * access list is a local query (no fan-out across every resource holon). A DISCOVERY HINT — the live authz
626
+ * stays the forward `Check` on the asserting holon when the identity opens it.
627
+ */
628
+ export const SharedWithMe = aggregate("SharedWithMe", {
629
+ /** The holon that signed + delivered this edge — WHERE the live forward `Check` runs when opened. */
630
+ asserterWorkspace: t.string().merge(Lww),
631
+ /** The shared OBJECT — the whole workspace (`workspace:self`) or a specific record (`Entry:<id>`). */
632
+ object: t.string().merge(Lww),
633
+ /** The relation granted (`reader` / `editor` / a read cap). */
634
+ relation: t.string().merge(Lww),
635
+ /** The asserter's OWNER principal — whose key signed the attestation (verified at §3 admission). */
636
+ asserterPrincipal: t.string().merge(Lww),
637
+ /** ISO-8601, caller-stamped (determinism — no clock in the plan). */
638
+ attestedAt: t.string().merge(Lww),
639
+ /** PRUNED (forward-check fail-safe): set when the live `Check` on the asserter denies — the hint is
640
+ * dropped from the list but the audit row stays. `""` = live. */
641
+ prunedAt: t.string().merge(Lww),
642
+ }).public();
643
+
644
+ /** One idempotent access-manifest row per source edge in this home. Delivery is at-least-once: a retry,
645
+ * reconnect or source outbox replay must update the same bookmark rather than manufacture another thing the
646
+ * recipient appears to have access to. The home itself scopes the recipient, so the edge coordinate is the
647
+ * source workspace + protected object + granted relation. */
648
+ export function sharedWithMeId(asserterWorkspace: string, object: string, relation: string): string {
649
+ const coordinate = JSON.stringify([asserterWorkspace, object, relation]);
650
+ return `SharedWithMe_${nomos.sha256(coordinate)}`;
651
+ }
652
+
653
+ /**
654
+ * `recordShare` — index a cross-custody share into the home holon's access list. AUTHORED on the home
655
+ * holon (the identity's own subject-side root). The payload carries the §3 CROSS-CUSTODY ATTESTATION — a
656
+ * SIGNED QUOTE: the asserter's device `publicKey` + a `signature` over the edge
657
+ * `<asserterWorkspace>:<object>#<relation>@<this-home-owner>`. The home holon's OWN gate verifies it
658
+ * (`executor::record_share_attestation_violation`, bound to the warrant owner), so the BAILIFF can deliver
659
+ * it HOST-FREE (the host carries bytes, vouches nothing — the gate vouches). The owner lane authors the
660
+ * SAME shape (the owner holds the quote the asserter gave it). The signature is a verification input, not
661
+ * stored — only the edge facts fold into the index. The live authority stays the forward `Check` on the
662
+ * asserter when the identity opens it (§5 fail-safe); a stale/forged quote can't enter the list at all.
663
+ */
664
+ export const recordShare = directive("recordShare")
665
+ .ensures(SharedWithMe)
666
+ .payload(
667
+ z.object({
668
+ asserterWorkspace: z.string().min(1),
669
+ object: z.string().min(1),
670
+ relation: z.string().min(1),
671
+ asserterPrincipal: z.string().min(1),
672
+ attestedAt: z.string().min(1),
673
+ /** The asserter's device PUBLIC KEY (base64) — what the §3 gate verifies the quote against. */
674
+ publicKey: z.string().min(1),
675
+ /** The asserter's SIGNATURE (base64) over the edge bytes, subject = this home's owner. */
676
+ signature: z.string().min(1),
677
+ }),
678
+ )
679
+ .plan((p) => {
680
+ // ENSURE, never CREATE: signed delivery is retryable and therefore at-least-once. Re-delivery (including
681
+ // a later re-grant after prune) refreshes this exact access edge and makes it live again.
682
+ ensure(SharedWithMe, sharedWithMeId(p.asserterWorkspace, p.object, p.relation))
683
+ .set("asserterWorkspace", p.asserterWorkspace)
684
+ .set("object", p.object)
685
+ .set("relation", p.relation)
686
+ .set("asserterPrincipal", p.asserterPrincipal)
687
+ .set("attestedAt", p.attestedAt)
688
+ .set("prunedAt", "");
689
+ return [];
690
+ })
691
+ // CROSS-CUSTODY ATTESTATION-AUTHORIZED (§3/§5): the payload's signed quote IS the authorization (lowered
692
+ // into the `nomosAttestationGate` era key) — the one gate verifies it against the home's owner (the
693
+ // LAW-DECLARED owner-source below, NOT a kernel-hardcoded id) + pins the principal to the attestation key,
694
+ // so the bailiff delivers it host-free to an OFFLINE subject (no device key, no author block).
695
+ .attestationAuthorized({ ownerObject: RECEIVING_GROUP_ID, ownerField: "principal" });
696
+
697
+ /**
698
+ * `pruneShare` — fail-safe drop of a hint whose forward `Check` denied (the asserter revoked it). The row
699
+ * stays for audit; `prunedAt` removes it from the live list. (The forward check itself is the resource
700
+ * holon's job — this records the verdict locally.)
701
+ */
702
+ export const pruneShare = directive("pruneShare")
703
+ .mutates(SharedWithMe)
704
+ .payload(z.object({ shareId: idOf(SharedWithMe), prunedAt: z.string().min(1) }))
705
+ .plan((p) => [set(instance(SharedWithMe, p.shareId), "prunedAt", p.prunedAt)]);
706
+
707
+ /** "Show me everything <asserter> shared with me" — an indexed probe over the local access list. */
708
+ export const sharedByWorkspace = query("sharedByWorkspace").key("asserterWorkspace").returns(SharedWithMe);
709
+
710
+ /** "Show me every share for this protected object" — the indexed access-list probe used by generated
711
+ * application discovery. A home commonly has a very large installed law, so discovery must never fall back
712
+ * to the generic `aggregatesByType(SharedWithMe)` scan merely to find whole-workspace shares. */
713
+ export const sharedByObject = query("sharedByObject").key("object").returns(SharedWithMe);
714
+
715
+ // ─────────────────── THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain) ───────────────────
716
+
717
+ /**
718
+ * THE BIRTH ATTESTATION (the cert, on-chain for AUDIT). When a parent P births this home via the offer-
719
+ * effect, the kernel seeds a SIGNED [executor::birth_cert::BirthCert] {child, founder, verified, parentKey,
720
+ * parentId, epoch} into the home's genesis (it travels with the home's `GenesisWarrant.bornUnder` so a peer
721
+ * verifies it OFFLINE), AND folds this singleton record so the home's projection surfaces "who attested my
722
+ * birth, and was my founder verified". `verified` + `founder` are the parent's GATE verdict (captured-port,
723
+ * CAS-as-law), never the offer payload. A peer's TRUST decision uses the travelling signed chain (the kernel
724
+ * verify doors); this aggregate is the human-readable mirror.
725
+ */
726
+ export const BIRTH_ATTESTATION_ID = "birth-attestation:self";
727
+ export const BIRTH_ATTESTATION_LINK_PREFIX = "birth-attestation-link:";
728
+ export const BirthAttestation = aggregate("BirthAttestation", {
729
+ /** This home's self-id (`home-<sha256(founder)>`) — the cert's `child`. */
730
+ child: t.string().merge(Lww),
731
+ /** The founding principal the parent attested (== {@link HomeOwner} `principal`). */
732
+ founder: t.string().merge(Lww),
733
+ /** THE PARENT'S GATE VERDICT: was `founder` VERIFIED at the parent's offer gate (an IdP vouched)? */
734
+ verified: t.string().merge(Lww), // "true"/"false" (string for the Lww scalar projection)
735
+ /** The parent's workspace self-id (lineage). */
736
+ parentId: t.string().merge(Lww),
737
+ /** The parent's enrolled signer PUBLIC KEY (base64) — `K_P`, the cert's signer. */
738
+ parentKey: t.string().merge(Lww),
739
+ /** The parent's SignerEpoch at the attestation (informational; the cert is judged at birth-time validity). */
740
+ epoch: t.int().merge(Lww),
741
+ /** Legacy replay slot for histories authored before `BirthAttestationLink`. New histories write the
742
+ * travelling chain only as ordered, typed link records below. */
743
+ certChain: t.string().merge(Lww).optional(),
744
+ }).public();
745
+
746
+ /** One exact link in the travelling certificate chain. The zero-padded index is custody order; every
747
+ * certificate member is a separate typed USDA property. `certChain` above remains only as a replay fallback
748
+ * for histories authored before this aggregate existed. */
749
+ export const BirthAttestationLink = aggregate("BirthAttestationLink", {
750
+ index: t.int().merge(Lww),
751
+ child: t.string().merge(Lww),
752
+ founder: t.string().merge(Lww),
753
+ verified: t.bool().merge(Lww),
754
+ parentKey: t.string().merge(Lww),
755
+ parentId: t.string().merge(Lww),
756
+ epoch: t.int().merge(Lww),
757
+ signature: t.string().merge(Lww),
758
+ verifiedVia: t.string().merge(Lww).optional(),
759
+ idpToken: t.string().merge(Lww).optional(),
760
+ capturedClock: t.int().merge(Lww).optional(),
761
+ authProviderIssuer: t.string().merge(Lww).optional(),
762
+ authProviderJwks: t.string().merge(Lww).optional(),
763
+ authProviderAudience: t.string().merge(Lww).optional(),
764
+ }).public();
765
+
766
+ /** Audit read: this home's birth attestation (who attested it + the verified verdict). */
767
+ export const birthAttestation = query("birthAttestation").key("child").returns(BirthAttestation);
768
+
769
+ /**
770
+ * `seedBirthAttestation` — THE GENESIS-DOOR step the parent's offer-effect INJECTS into the home's genesis
771
+ * chain (the kernel appends it in `run_birth_effect` carrying the assembled + signed cert chain). It folds
772
+ * the [`BirthAttestation`] singleton so the home's own state carries its travelling cert. A CONTROL-PLANE
773
+ * write seeded once at birth (the open session lane structurally refuses it by directive id). The values
774
+ * are the parent's GATE VERDICT (`founder`/`verified` lowered from the captured port — CAS-as-law, never
775
+ * the offer payload) + the typed signed links; the home's verify doors re-derive trust from the chain.
776
+ */
777
+ export const seedBirthAttestation = directive("seedBirthAttestation")
778
+ .ensures(BirthAttestation)
779
+ .payload(
780
+ z.object({
781
+ child: z.string().min(1),
782
+ founder: z.string().min(1),
783
+ verified: z.string().min(1), // "true"/"false"
784
+ /** The parent's workspace self-id (lineage). EMPTY for a root-DIRECT birth (root carries no self-id
785
+ * binding in the unwarranted lane) — the cert's terminal link is then signed by K_root directly. */
786
+ parentId: z.string(),
787
+ parentKey: z.string().min(1), // base64 K_P
788
+ epoch: z.number().int().nonnegative(),
789
+ links: z.array(birthCertificatePayload).min(1),
790
+ /** Law-declared target roles for the verified immediate parent workspace. The kernel injects this
791
+ * value from the admitted birth recipe and supplies `parentId`; no host chooses either fact. */
792
+ parentWorkspaceRoles: z.array(z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/)).optional(),
793
+ }).superRefine((payload, ctx) => {
794
+ if ((payload.parentWorkspaceRoles?.length ?? 0) > 0 && payload.parentId.length === 0) {
795
+ ctx.addIssue({ code: "custom", message: "parent workspace roles require a verified non-root parentId" });
796
+ }
797
+ }),
798
+ )
799
+ .plan((p) => {
800
+ const h = instance(BirthAttestation, BIRTH_ATTESTATION_ID);
801
+ const header = [
802
+ set(h, "child", p.child),
803
+ set(h, "founder", p.founder),
804
+ set(h, "verified", p.verified),
805
+ set(h, "parentId", p.parentId),
806
+ set(h, "parentKey", p.parentKey),
807
+ set(h, "epoch", p.epoch),
808
+ ];
809
+ const links = p.links.flatMap((certificate, index) => {
810
+ const link = instance(BirthAttestationLink, `${BIRTH_ATTESTATION_LINK_PREFIX}${String(index).padStart(8, "0")}`);
811
+ return [
812
+ withMarker(set(link, "index", index), "ensures"),
813
+ withMarker(set(link, "child", certificate.child), "ensures"),
814
+ withMarker(set(link, "founder", certificate.founder), "ensures"),
815
+ withMarker(set(link, "verified", certificate.verified), "ensures"),
816
+ withMarker(set(link, "parentKey", certificate.parentKey), "ensures"),
817
+ withMarker(set(link, "parentId", certificate.parentId), "ensures"),
818
+ withMarker(set(link, "epoch", certificate.epoch), "ensures"),
819
+ withMarker(set(link, "signature", certificate.signature), "ensures"),
820
+ ...(certificate.verifiedVia ? [withMarker(set(link, "verifiedVia", certificate.verifiedVia), "ensures")] : []),
821
+ ...(certificate.idpToken ? [withMarker(set(link, "idpToken", certificate.idpToken), "ensures")] : []),
822
+ ...(certificate.capturedClock === undefined ? [] : [withMarker(set(link, "capturedClock", certificate.capturedClock), "ensures")]),
823
+ ...(certificate.authProviderIssuer ? [withMarker(set(link, "authProviderIssuer", certificate.authProviderIssuer), "ensures")] : []),
824
+ ...(certificate.authProviderJwks ? [withMarker(set(link, "authProviderJwks", certificate.authProviderJwks), "ensures")] : []),
825
+ ...(certificate.authProviderAudience ? [withMarker(set(link, "authProviderAudience", certificate.authProviderAudience), "ensures")] : []),
826
+ ];
827
+ });
828
+ const parentAuthority = (p.parentWorkspaceRoles ?? []).flatMap((relation) => writeTuple({
829
+ object: "workspace:self",
830
+ relation,
831
+ subject: `workspace:${p.parentId}`,
832
+ grantedBy: `workspace:${p.parentId}`,
833
+ grantedAt: String(p.epoch),
834
+ }));
835
+ return [...header, ...links, ...parentAuthority];
836
+ })
837
+ .alsoEnsures(BirthAttestationLink)
838
+ .alsoEnsures(RelationTuple);
839
+
840
+ /**
841
+ * THE ERA DECLARATION (the `nomosParentAttestedBirth` USD key). Exporting this sentinel from the home law
842
+ * tells the kernel's verify doors to VERIFY THE TRAVELLING CERT CHAIN to the pinned `K_root` on every lane
843
+ * (author / receive / verify_chain) — so a warranted home that self-asserts a verified founder WITHOUT a
844
+ * parent-signed chain rooting at root is refused (`ParentDidNotAttest` / `CertChainBroken` /
845
+ * `NotRootedAtRoot`). Era-ruled: a home law that does NOT export this carries no key and replays byte-
846
+ * identical (the squat-only `homeNameBindsOwner` check still runs; the cert check is simply inert).
847
+ */
848
+ export const parentAttestedBirth: ParentAttestedBirthMarker = {
849
+ ...requireParentAttestedBirth,
850
+ // DECLARE the owner + cert locators so the KERNEL reads them (it names neither): the home owner lives at
851
+ // the `HomeOwner` singleton's `principal`. `certObject`/`certField` retain the legacy chain locator for
852
+ // old histories; current histories are discovered through the typed `BirthAttestationLink` aggregate.
853
+ ownerObject: RECEIVING_GROUP_ID,
854
+ ownerField: "principal",
855
+ certObject: BIRTH_ATTESTATION_ID,
856
+ certField: "certChain",
857
+ };