@githolon/dsl 0.108.6 → 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 +2083 -1248
  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
@@ -12,9 +12,15 @@ import { t } from "../fields.js";
12
12
  import { Lww } from "../drivers.js";
13
13
  import { addToSet, set, withMarker, type PlannedOp } from "../ops.js";
14
14
  import { query } from "../query.js";
15
- import { readFrameworkDeclaredRows } from "../read.js";
15
+ import {
16
+ readFrameworkAttestedRows,
17
+ readFrameworkDeclaredRows,
18
+ } from "../read.js";
19
+ import { attestedReadSubject, fromPayload, trustAuthorizedSource } from "../attested_core.js";
16
20
  import { GENESIS_WARRANT_ID } from "./genesis.js";
17
21
  import { readRef, readSet, refAs, workspaceInvariant } from "./workspace_invariant.js";
22
+ import { object, relationType, self } from "../relation_schema.js";
23
+ import { AUTH_PROVIDER_AUTHORITY, AUTH_PROVIDER_EPOCH_CONSUMER } from "../births.js";
18
24
 
19
25
  const epochMs = z.number().int().nonnegative();
20
26
 
@@ -29,9 +35,40 @@ export const AUTH_PROVIDER_KEY_ID_PREFIX = "auth-provider-key:";
29
35
  /** Stable prefix for ordered x5c certificate entries belonging to one typed public key. */
30
36
  export const AUTH_PROVIDER_CERTIFICATE_ID_PREFIX = "auth-provider-certificate:";
31
37
 
38
+ /**
39
+ * The ordinary workspace relation which names the sovereign source allowed to
40
+ * publish replacement provider material into this workspace.
41
+ *
42
+ * It is neither ancestry nor placement. A birth may deliberately seat its
43
+ * immediate parent, copy an already chosen provider root, or choose another
44
+ * workspace; the adoption directive only asks this relation what current law
45
+ * authorises.
46
+ */
47
+ export { AUTH_PROVIDER_AUTHORITY, AUTH_PROVIDER_EPOCH_CONSUMER };
48
+ export const authProviderSourceRelations = relationType("workspace")
49
+ .asGlobal()
50
+ .relation(AUTH_PROVIDER_AUTHORITY, {
51
+ rewrite: self(),
52
+ allow: [object("workspace")],
53
+ });
54
+ /**
55
+ * The inverse edge: the parent's own permission for one immediate child to ask for its current
56
+ * verifier epoch (`nomosInternalServeAuthProviderEpoch7c1f5e` gates on it). Seated by every
57
+ * framework birth for the child it births; declared here once so no tenant re-declares it.
58
+ */
59
+ export const authProviderEpochConsumerRelations = relationType("workspace")
60
+ .asGlobal()
61
+ .relation(AUTH_PROVIDER_EPOCH_CONSUMER, {
62
+ rewrite: self(),
63
+ allow: [object("workspace")],
64
+ });
65
+
32
66
  export const AuthProvider = aggregate("AuthProvider", {
33
- /** The singleton key — always `"platform"`. */
67
+ /** Workspace-local lookup key. Historical default-provider state uses `"platform"`. */
34
68
  scope: t.string().merge(Lww),
69
+ /** Stable provider namespace selected by new member law. It survives issuer and key rotation;
70
+ * historical default-provider rows omit it to preserve their original interpretation. */
71
+ identitySha256: t.string().merge(Lww).optional(),
35
72
  /** The token issuer (the `iss` claim tokens must carry). */
36
73
  issuer: t.string().merge(Lww),
37
74
  /** The provider's public JWKS endpoint (asymmetric verification keys). The KERNEL never fetches — it's a
@@ -117,6 +154,14 @@ export const authProviderCertificates = query("authProviderCertificates")
117
154
  .key("providerEpoch")
118
155
  .returns(AuthProviderCertificate);
119
156
 
157
+ /** The declared read serving a workspace's own AuthProvider singleton. */
158
+ export const authProvider = query("authProvider").key("scope").returns(AuthProvider);
159
+
160
+ /** The workspace-local source binding authorises the named workspace's signed publication.
161
+ * It is independent of lineage and placement. Source custody, when present, reconciles the
162
+ * signer and exact read immediately; otherwise the attributable statement remains replayable. */
163
+ export const identityProviderSourceTrust = trustAuthorizedSource("identityProviderSource");
164
+
120
165
  const authProviderAttestation = z.object({
121
166
  child: z.string().min(1),
122
167
  founder: z.string().min(1),
@@ -212,6 +257,13 @@ function readCurrentAuthProviderValue(): {
212
257
  if (provider?.issuer === undefined || provider.issuer.length === 0) return undefined;
213
258
  const keyEpoch = provider.keyEpoch;
214
259
  const providerEpoch = keyEpoch === undefined ? undefined : `platform:${keyEpoch}`;
260
+ // Keep execution in the exact order declared on every directive which calls
261
+ // this helper: provider, keys, certificates. Captured reads are an ordered
262
+ // replay transcript, so moving the convenient certificate join ahead of the
263
+ // key read would make the authored plan disagree with its own law contract.
264
+ const keyRows = providerEpoch === undefined
265
+ ? []
266
+ : readFrameworkDeclaredRows(authProviderKeys, AuthProviderKey, { providerEpoch });
215
267
  const certificates = providerEpoch === undefined
216
268
  ? []
217
269
  : readFrameworkDeclaredRows(
@@ -219,9 +271,7 @@ function readCurrentAuthProviderValue(): {
219
271
  AuthProviderCertificate,
220
272
  { providerEpoch },
221
273
  );
222
- const keys = providerEpoch === undefined
223
- ? []
224
- : readFrameworkDeclaredRows(authProviderKeys, AuthProviderKey, { providerEpoch })
274
+ const keys = keyRows
225
275
  .sort((a, b) => (a.ordinal ?? Number.MAX_SAFE_INTEGER) - (b.ordinal ?? Number.MAX_SAFE_INTEGER))
226
276
  .map((key) => authProviderPublicKey.parse({
227
277
  kty: key.kty,
@@ -398,7 +448,120 @@ export const setAuthProvider = directive("setAuthProvider")
398
448
  .plan(updateAuthProviderConfiguration)
399
449
  .alsoEnsuresMany(AuthProviderKey)
400
450
  .alsoEnsuresMany(AuthProviderCertificate)
401
- .requires("admin");
451
+ .requires("admin")
452
+ // Updating the provider verifier cannot require a token verified by that
453
+ // same (possibly stale) verifier. The active enrolled device authenticates
454
+ // the author; the independent admin relation still authorizes the change.
455
+ .authenticatesWithEnrolledDevice();
456
+
457
+ /**
458
+ * One lazy provider repair, expressed as ordinary law.
459
+ *
460
+ * The payload carries only the routable source workspace. The verifier
461
+ * material is read from that sovereign workspace through Nomos's existing
462
+ * attested-read machinery, captured on this ordinary intent, and folded into
463
+ * this workspace's normal AuthProvider aggregates. The peer runtime may reuse
464
+ * the same attestation across many local workspaces, but after this offer is
465
+ * admitted it is absent from every subsequent authentication operation.
466
+ */
467
+ export const adoptIdentityProviderWitness = directive("adoptIdentityProviderWitness")
468
+ .mutates(AuthProvider)
469
+ .payload(z.object({ sourceWorkspace: z.string().min(1) }).strict())
470
+ .plan(({ sourceWorkspace }) => {
471
+ const providers = readFrameworkAttestedRows(
472
+ authProvider,
473
+ AuthProvider,
474
+ { scope: "platform" },
475
+ sourceWorkspace,
476
+ );
477
+ const provider = providers.find((row) => row.issuer !== undefined);
478
+ if (provider?.issuer === undefined || provider.issuer.length === 0) {
479
+ throw new Error("identity-provider source has no current provider publication");
480
+ }
481
+ if (provider.keyEpoch === undefined) {
482
+ throw new Error("identity-provider source has no typed key epoch");
483
+ }
484
+ const providerEpoch = `platform:${provider.keyEpoch}`;
485
+ const keyRows = readFrameworkAttestedRows(
486
+ authProviderKeys,
487
+ AuthProviderKey,
488
+ { providerEpoch },
489
+ sourceWorkspace,
490
+ );
491
+ const certificateRows = readFrameworkAttestedRows(
492
+ authProviderCertificates,
493
+ AuthProviderCertificate,
494
+ { providerEpoch },
495
+ sourceWorkspace,
496
+ );
497
+ const keys = keyRows
498
+ .sort((left, right) =>
499
+ (left.ordinal ?? Number.MAX_SAFE_INTEGER) -
500
+ (right.ordinal ?? Number.MAX_SAFE_INTEGER)
501
+ )
502
+ .map((key) => authProviderPublicKey.parse({
503
+ kty: key.kty,
504
+ kid: key.kid,
505
+ alg: key.alg,
506
+ use: key.use,
507
+ crv: key.crv,
508
+ x: key.x,
509
+ y: key.y,
510
+ n: key.n,
511
+ e: key.e,
512
+ keyOps: key.keyOps,
513
+ x5c: certificateRows
514
+ .filter((certificate) => certificate.keyOrdinal === key.ordinal)
515
+ .sort((left, right) =>
516
+ (left.certificateOrdinal ?? Number.MAX_SAFE_INTEGER) -
517
+ (right.certificateOrdinal ?? Number.MAX_SAFE_INTEGER)
518
+ )
519
+ .map((certificate) => certificate.derBase64),
520
+ }));
521
+ if (keys.length === 0) {
522
+ throw new Error("identity-provider source published an empty typed key epoch");
523
+ }
524
+ return updateAuthProviderConfiguration(authProviderConfiguration.parse({
525
+ issuer: provider.issuer,
526
+ jwksUrl: provider.jwksUrl && provider.jwksUrl.length > 0
527
+ ? provider.jwksUrl
528
+ : provider.issuer,
529
+ ...(provider.audience && provider.audience.length > 0
530
+ ? { audience: provider.audience }
531
+ : {}),
532
+ keySet: { epoch: provider.keyEpoch, keys },
533
+ ...(provider.principalClaim && provider.principalClaim.length > 0
534
+ ? { principalClaim: provider.principalClaim }
535
+ : {}),
536
+ ...(provider.rootCert && provider.rootCert.length > 0
537
+ ? { rootCert: provider.rootCert }
538
+ : {}),
539
+ ...(provider.deviceBindingClaim && provider.deviceBindingClaim.length > 0
540
+ ? { deviceBindingClaim: provider.deviceBindingClaim }
541
+ : {}),
542
+ ...(provider.requireDeviceBinding === "true" || provider.requireDeviceBinding === "false"
543
+ ? { requireDeviceBinding: provider.requireDeviceBinding }
544
+ : {}),
545
+ label: `adopted from ${sourceWorkspace}`,
546
+ setBy: `workspace:${sourceWorkspace}`,
547
+ setAt: Date.now(),
548
+ }));
549
+ })
550
+ .alsoEnsuresMany(AuthProviderKey)
551
+ .alsoEnsuresMany(AuthProviderCertificate)
552
+ .readsFrom(fromPayload("sourceWorkspace"), authProvider, {
553
+ policy: identityProviderSourceTrust,
554
+ })
555
+ .readsFrom(fromPayload("sourceWorkspace"), authProviderKeys, {
556
+ policy: identityProviderSourceTrust,
557
+ })
558
+ .readsFrom(fromPayload("sourceWorkspace"), authProviderCertificates, {
559
+ policy: identityProviderSourceTrust,
560
+ })
561
+ .requires("authProviderAuthority", {
562
+ object: "workspace:self",
563
+ subjectFrom: attestedReadSubject(authProvider),
564
+ });
402
565
 
403
566
  /**
404
567
  * Private child-genesis twin of {@link setAuthProvider}. It deliberately has no role requirement because
@@ -423,6 +586,3 @@ export const authProviderSeedBeforeWarrant = workspaceInvariant("AuthProviderSee
423
586
  ? { accept: true }
424
587
  : { reject: "seedAuthProvider is a child-genesis primitive; use the role-gated setAuthProvider after birth" };
425
588
  });
426
-
427
- /** The declared read serving a workspace's OWN AuthProvider row (and birthHome's inheritance capture). */
428
- export const authProvider = query("authProvider").key("scope").returns(AuthProvider);
@@ -4,6 +4,7 @@
4
4
  // It is never lowered to a JSON string. The JWKS member is the external standardized JWKS document itself;
5
5
  // the certificate envelope and chain structure remain typed Nomos data.
6
6
  import { z } from "zod";
7
+ import { valueObject } from "../value_object.js";
7
8
 
8
9
  export const birthCertificatePayload = z.object({
9
10
  child: z.string().min(1),
@@ -22,3 +23,6 @@ export const birthCertificatePayload = z.object({
22
23
  });
23
24
 
24
25
  export type BirthCertificatePayload = z.infer<typeof birthCertificatePayload>;
26
+
27
+ /** One signed value. Merge operations must never combine members from different certificates. */
28
+ export const BirthDelegationCertificate = valueObject("BirthDelegationCertificate", birthCertificatePayload.shape);
@@ -157,6 +157,18 @@ export interface CapabilityDecl<
157
157
  * rest at `requested` with nowhere to go (the deploy notice says so).
158
158
  */
159
159
  readonly endpoint?: string;
160
+ /**
161
+ * Where the obligation's impure execution is allowed to happen. This is law,
162
+ * not host configuration: the kernel resolves it with the rest of the
163
+ * capability declaration and seals the resolved value into each admitted
164
+ * outbox offer. The policy exists even before a provider endpoint is bound:
165
+ * capability addressing is mutable law-state, while execution placement is
166
+ * part of the capability's semantic declaration. `custodian/singleton` is
167
+ * deliberately the only executable policy today. It means one live custodian
168
+ * for the source workspace; replicas and retained archives may carry the offer
169
+ * but must not perform it.
170
+ */
171
+ readonly execution?: CapabilityExecutionPolicy;
160
172
  /**
161
173
  * THE DECLASSIFY WAIVER (nomosFlowGate rule 5 — no private bytes in an outbound offer): present
162
174
  * iff the author acknowledged that this capability POSTs data derived from a private/encrypted
@@ -186,6 +198,11 @@ export interface CapabilityDecl<
186
198
  };
187
199
  }
188
200
 
201
+ export interface CapabilityExecutionPolicy {
202
+ readonly placement: "custodian";
203
+ readonly cardinality: "singleton";
204
+ }
205
+
189
206
  /**
190
207
  * Declare a typed marketplace capability. Sugar over {@link impureCapability} —
191
208
  * nothing new on the wire; the one piece of REAL work it adds is the
@@ -203,6 +220,11 @@ export function capability<
203
220
  request: Req;
204
221
  receipt?: Rec;
205
222
  endpoint?: string;
223
+ /** Defaults to `{ placement: "custodian", cardinality: "singleton" }`. */
224
+ execution?: {
225
+ placement: "custodian";
226
+ cardinality?: "singleton";
227
+ };
206
228
  declassify?: string;
207
229
  /** Authorization required to request the capability. Declare it here so the
208
230
  * capability's one obligation object remains identical everywhere it is composed. */
@@ -239,6 +261,10 @@ export function capability<
239
261
  * `declassify`, replayable and auditable, never a silent default.
240
262
  */
241
263
  openReceipts?: string;
264
+ /** The workspace relation that may grant and revoke provider authority. Defaults to `admin`; a
265
+ * capability whose provider is operated infrastructure names `operator`, so an admin who is not an
266
+ * operator cannot seat itself as the provider and close its own obligations. */
267
+ providerGrantedBy?: "admin" | "operator";
242
268
  }): CapabilityDecl<Req, Rec> {
243
269
  if (!CAPABILITY_NAME_RE.test(name)) {
244
270
  throw new Error(
@@ -352,7 +378,8 @@ export function capability<
352
378
  // the hard way (re-exporting them registered an ungated `workspace:self#admin@…` writer — a
353
379
  // self-grant-admin hole). A law that exported them alongside this fence would let anyone
354
380
  // self-grant provider authority and close their own obligations. So provider authority is
355
- // seated ONLY through these admin-gated wrappers, scoped to THIS capability's object.
381
+ // seated ONLY through these gated wrappers (admin, or the operator by `providerGrantedBy`), scoped to
382
+ // THIS capability's object.
356
383
  const grantPayload = z.object({
357
384
  subject: z.string().min(1),
358
385
  grantedBy: z.string().min(1),
@@ -370,7 +397,7 @@ export function capability<
370
397
  grantedAt: p.grantedAt,
371
398
  }),
372
399
  )
373
- .requires("admin");
400
+ .requires(spec.providerGrantedBy ?? "admin");
374
401
  const revokeProvider = directive(`revoke${pascal}Provider`)
375
402
  .ensures(RelationTuple)
376
403
  .payload(grantPayload.extend({ revokedBy: z.string().min(1), revokedAt: z.string().min(1) }))
@@ -385,7 +412,7 @@ export function capability<
385
412
  revokedAt: p.revokedAt,
386
413
  }),
387
414
  )
388
- .requires("admin");
415
+ .requires(spec.providerGrantedBy ?? "admin");
389
416
 
390
417
  // THE FENCE, APPLIED. Every Receipt verb is gated on provider authority for THIS capability
391
418
  // unless the law carries an explicit `openReceipts` waiver. The gate is the shipped native
@@ -437,6 +464,10 @@ export function capability<
437
464
  instanceId: impureCapabilityInstanceId,
438
465
  name,
439
466
  ...(spec.endpoint !== undefined ? { endpoint: spec.endpoint } : {}),
467
+ execution: Object.freeze({
468
+ placement: spec.execution?.placement ?? "custodian",
469
+ cardinality: spec.execution?.cardinality ?? "singleton",
470
+ }),
440
471
  ...(spec.declassify !== undefined ? { declassify: spec.declassify } : {}),
441
472
  requestSchema: z.object(requestShape),
442
473
  receiptSchema: z.object(receiptShape),
@@ -0,0 +1,346 @@
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
+ * CHILD AUTHENTICATION — framework law for every workspace that births business children.
8
+ *
9
+ * A child workspace verifies tokens against ITS OWN copied verifier and seats only the device keys it has
10
+ * been told about. Both go stale on their own: the identity provider rotates keys, or the person enrols a
11
+ * new device on the parent AFTER the child was born. The parent is the child's provider authority (the
12
+ * child seated {@link AUTH_PROVIDER_AUTHORITY} for it at genesis), so the parent repairs the child. Three
13
+ * lanes, all parameterised by the target workspace and none naming a child KIND:
14
+ *
15
+ * 1. THE VERIFIER-EPOCH PAIR — the child asks (`pull`, an enrolled-device request to its verified
16
+ * parent), the parent answers (`serve`, gated on the parent's own {@link AUTH_PROVIDER_EPOCH_CONSUMER}
17
+ * seat for that child) by replying with its CURRENT typed provider, and the child adopts it through the
18
+ * verified-birth-parent recovery door (`acceptParentAuthProviderEpoch`, which also re-seats the
19
+ * parent's authority so a child born before that seat existed heals itself).
20
+ * 2. CHILD-AUTHENTICATION REPAIR — a child refusing a device key it never seated
21
+ * (`nomos.authz.signer_enrollment_missing`) is repaired by ONE parent action addressed to that child:
22
+ * the parent discloses its current provider and its current signer grant for the person's exact
23
+ * device; the child seats both atomically under its {@link AUTH_PROVIDER_AUTHORITY} gate.
24
+ * 3. THE GATE — the repair is an enrolled-device intent with NO parent-local write that addresses the
25
+ * exact child from its typed input (the `repairsChildAuthenticationFor` shape), asserted here at
26
+ * construction, and a device may repair only its own authenticated identity.
27
+ *
28
+ * COMPOSITION: the application-home policy law (`application_home.ts`) composes this set, presentation-
29
+ * internal, into the compiler-owned receiving-group lifecycle domain whenever the package declares any
30
+ * `.births()` child (business births or a tenant `.births()` directive). That domain runs on the platform
31
+ * and is installed into every born workspace, so one declaration serves both sides of every parent/child
32
+ * edge, and the compiler's exact-export rule (a workspace-effect target has exactly one owning domain) is
33
+ * met by construction. A package that births nothing composes none of it and is byte-identical.
34
+ *
35
+ * PROVENANCE: the framework form of CO2's per-child-kind copies (`co2_home/auth_provider_epoch_handler.ts`,
36
+ * `child_auth_provider_epoch_accept.ts`, `catalogue/auth_provider_epoch_accept.ts`,
37
+ * `co2_platform/auth_provider_epoch.ts`). The semantics are reproduced exactly, including the first
38
+ * disclosure carrying only VERIFYING material (no optional attestation member), so a child running law from
39
+ * before the attestation member existed can reopen and then adopt the complete provider through the second
40
+ * effect.
41
+ */
42
+ import { z } from "zod";
43
+ import type { Ports } from "../ctx.js";
44
+ import { directive, nomosImplementationDetail } from "../directive.js";
45
+ import { assertRepairsChildAuthentication, intent } from "../intent.js";
46
+ import { query } from "../query.js";
47
+ import { readFrameworkDeclaredRows } from "../read.js";
48
+ import type { RelationTypeDecl } from "../relation_schema.js";
49
+ import {
50
+ AUTH_PROVIDER_AUTHORITY,
51
+ AUTH_PROVIDER_EPOCH_CONSUMER,
52
+ AuthProvider,
53
+ AuthProviderCertificate,
54
+ AuthProviderKey,
55
+ authProvider,
56
+ authProviderCertificates,
57
+ authProviderConfiguration,
58
+ authProviderEpochConsumerRelations,
59
+ authProviderKeys,
60
+ authProviderSourceRelations,
61
+ readAuthProviderConfiguration,
62
+ writeAuthProviderConfiguration,
63
+ } from "./auth_provider_law.js";
64
+ import { BirthAttestation } from "./receiving_group.js";
65
+ import { RelationTuple, relationTuplesByObject, writeTuple } from "./relations.js";
66
+ import { SIGNER_RELATION, signerObject } from "./signing_identity.js";
67
+ import { readSet, workspaceInvariant } from "./workspace_invariant.js";
68
+ import { repairChildAuthProviderEpochConsumer } from "./legacy/child_auth_provider_epoch.js";
69
+
70
+ // Kept in a compatibility module, registered through this framework declaration
71
+ // boundary so runtime plan slicing resolves it just like the current lanes.
72
+ export { repairChildAuthProviderEpochConsumer } from "./legacy/child_auth_provider_epoch.js";
73
+
74
+ /** Wire ids — framework-internal, obscure on purpose (the 7c1f5e suffix convention). */
75
+ export const ACCEPT_PARENT_AUTH_PROVIDER_EPOCH_ID = "nomosInternalAcceptParentAuthProviderEpoch7c1f5e";
76
+ export const ACCEPT_CHILD_AUTHENTICATION_RECOVERY_ID = "nomosInternalAcceptChildAuthenticationRecovery7c1f5e";
77
+ export const ACCEPT_CHILD_AUTHENTICATION_RECOVERY_EFFECT_ID =
78
+ "nomosInternalAcceptChildAuthenticationRecoveryEffect7c1f5e";
79
+ export const SERVE_AUTH_PROVIDER_EPOCH_ID = "nomosInternalServeAuthProviderEpoch7c1f5e";
80
+ export const PULL_PARENT_AUTH_PROVIDER_EPOCH_ID = "nomosInternalPullParentAuthProviderEpoch7c1f5e";
81
+ export const REPAIR_CHILD_AUTHENTICATION_ID = "nomosInternalRepairChildAuthentication7c1f5e";
82
+
83
+ /** The minimal typed state a parent discloses to one immediate child when that child can no longer
84
+ * recognise the person's current device/provider epoch. The parent derives every member from its
85
+ * committed signer relation; callers identify the child, their authenticated user and the exact device. */
86
+ export const childAuthenticationRecoveryEvidence = z
87
+ .object({
88
+ user: z.string().min(1),
89
+ deviceKeySubject: z.string().regex(/^key:[0-9a-f]{64}$/),
90
+ enrolledAt: z.string().min(1),
91
+ verifiedVia: z.string().min(1),
92
+ validFromEpoch: z.number().int().nonnegative(),
93
+ authProvider: authProviderConfiguration,
94
+ })
95
+ .strict();
96
+ export type ChildAuthenticationRecoveryEvidence = z.infer<typeof childAuthenticationRecoveryEvidence>;
97
+
98
+ /** The parent's typed call: the affected child, the authenticated person and (when the person holds more
99
+ * than one active device on the parent) the exact device. Omitting the device is compatible only with a
100
+ * single active signer. */
101
+ export const childAuthenticationRepairInput = z
102
+ .object({
103
+ workspace: z.string().min(1),
104
+ user: z.string().min(1),
105
+ deviceKeySubject: z.string().regex(/^key:[0-9a-f]{64}$/).optional(),
106
+ })
107
+ .strict();
108
+ export type ChildAuthenticationRepairInput = z.infer<typeof childAuthenticationRepairInput>;
109
+
110
+ /** The verified singleton birth record, addressed only by a law-authored constant, so the
111
+ * parent-authority seat can choose neither the parent workspace nor the relation it grants. */
112
+ export const verifiedBirthAttestation = nomosImplementationDetail(
113
+ query("nomosInternalVerifiedBirthAttestation7c1f5e").key("verified").returns(BirthAttestation),
114
+ );
115
+
116
+ function writeVerifiedBirthParentAuthProviderAuthority(ctx: Ports) {
117
+ const births = readFrameworkDeclaredRows(verifiedBirthAttestation, BirthAttestation, { verified: "true" });
118
+ if (births.length !== 1) {
119
+ throw new Error(
120
+ `receiving-group recovery requires exactly one verified BirthAttestation; found ${births.length}`,
121
+ );
122
+ }
123
+ const birth = births[0]!;
124
+ if (birth.verified !== "true" || !birth.parentId?.trim()) {
125
+ throw new Error("receiving-group recovery requires a verified non-root parent");
126
+ }
127
+ return writeTuple({
128
+ object: "workspace:self",
129
+ relation: AUTH_PROVIDER_AUTHORITY,
130
+ subject: `workspace:${birth.parentId}`,
131
+ grantedBy: ctx.principal().asAttribution(),
132
+ grantedAt: String(ctx.clock().physical),
133
+ });
134
+ }
135
+
136
+ /** The one private consequence shared by exact-parent recovery transitions: the authority subject is
137
+ * derived from the child's verified birth record; neither sender nor host supplies it. */
138
+ export const seatVerifiedBirthParentAuthority = nomosImplementationDetail(
139
+ directive("nomosInternalSeatVerifiedBirthParentAuthProviderAuthority7c1f5e")
140
+ .ensures(RelationTuple)
141
+ .payload(z.object({}).strict())
142
+ .plan((_payload, ctx) => writeVerifiedBirthParentAuthProviderAuthority(ctx))
143
+ .reads(verifiedBirthAttestation),
144
+ );
145
+
146
+ /** Accept only the parent's exact typed verifier configuration. */
147
+ export const acceptParentAuthProviderEpochEffect = nomosImplementationDetail(
148
+ directive("nomosInternalAcceptParentAuthProviderEpochEffect7c1f5e")
149
+ .ensures(AuthProvider)
150
+ .payload(authProviderConfiguration)
151
+ .plan(writeAuthProviderConfiguration)
152
+ .alsoEnsuresMany(AuthProviderKey)
153
+ .alsoEnsuresMany(AuthProviderCertificate),
154
+ );
155
+
156
+ /** ONE child-authored transition seats the current device and verifier together. The caller cannot
157
+ * manufacture either value: parent law reads them from committed parent custody before emitting this
158
+ * exact effect, while the child independently requires the parent's authProviderAuthority relation. */
159
+ export const acceptChildAuthenticationRecoveryEffect = nomosImplementationDetail(
160
+ directive(ACCEPT_CHILD_AUTHENTICATION_RECOVERY_EFFECT_ID)
161
+ .ensures(RelationTuple)
162
+ .payload(childAuthenticationRecoveryEvidence)
163
+ .plan((evidence, ctx) => [
164
+ ...writeTuple({
165
+ object: signerObject(evidence.user),
166
+ relation: SIGNER_RELATION,
167
+ subject: evidence.deviceKeySubject,
168
+ grantedBy: ctx.principal().asAttribution(),
169
+ grantedAt: evidence.enrolledAt,
170
+ verifiedVia: evidence.verifiedVia,
171
+ validFromEpoch: evidence.validFromEpoch,
172
+ }),
173
+ ...writeAuthProviderConfiguration(evidence.authProvider),
174
+ ])
175
+ .alsoEnsures(AuthProvider)
176
+ .alsoEnsuresMany(AuthProviderKey)
177
+ .alsoEnsuresMany(AuthProviderCertificate)
178
+ .requires(AUTH_PROVIDER_AUTHORITY, { object: "workspace:self" })
179
+ .acceptsWorkspaceEffect(),
180
+ );
181
+
182
+ function currentAuthProviderConfiguration() {
183
+ const configuration = readAuthProviderConfiguration();
184
+ if (configuration === undefined) {
185
+ throw new Error("serving a verifier epoch requires this workspace's current typed AuthProvider");
186
+ }
187
+ return configuration;
188
+ }
189
+
190
+ /**
191
+ * Existing child laws may predate the current nominal identity of the optional lineage attestation
192
+ * record. They only need the provider's VERIFYING configuration in order to reopen and lawfully enrol the
193
+ * current device, so the first disclosure omits that optional audit member. The complete current typed
194
+ * provider (attestation included) still travels in the subsequent child-authentication recovery effect
195
+ * once the child has adopted current law.
196
+ */
197
+ function currentVerifyingAuthProviderConfiguration() {
198
+ const { attestation: _attestation, ...configuration } = currentAuthProviderConfiguration();
199
+ return configuration;
200
+ }
201
+
202
+ function currentChildAuthenticationRecoveryEvidence(user: string, deviceKeySubject?: string) {
203
+ // The parent is the child's authority. Its current signer relation is the fact to disclose, including
204
+ // grants whose old enrolment certificate was not retained. Re-reading that relation also prevents recovery
205
+ // from resurrecting a revoked key merely because an old certificate still exists.
206
+ const rows = readFrameworkDeclaredRows(relationTuplesByObject, RelationTuple, {
207
+ object: signerObject(user),
208
+ });
209
+ const matches = rows
210
+ .filter((row) => row.object === signerObject(user) && row.relation === SIGNER_RELATION)
211
+ .flatMap((row) =>
212
+ Object.entries(row.subjects ?? {}).filter(
213
+ ([subject, grant]) =>
214
+ subject.startsWith("key:") &&
215
+ grant.status === "active" &&
216
+ (deviceKeySubject === undefined || subject === deviceKeySubject),
217
+ ),
218
+ );
219
+ if (matches.length !== 1) {
220
+ throw new Error(
221
+ `child authentication repair requires exactly one current signer grant for '${user}' and the requested device; found ${matches.length}`,
222
+ );
223
+ }
224
+ const [subject, grant] = matches[0]!;
225
+ const provider = currentAuthProviderConfiguration();
226
+ if (grant.verifiedVia !== provider.issuer || grant.validFromEpoch === undefined) {
227
+ throw new Error(
228
+ "child authentication repair requires the parent's provider-verified signer grant and its recorded epoch",
229
+ );
230
+ }
231
+ return childAuthenticationRecoveryEvidence.parse({
232
+ user,
233
+ deviceKeySubject: subject,
234
+ enrolledAt: grant.grantedAt,
235
+ verifiedVia: grant.verifiedVia,
236
+ validFromEpoch: grant.validFromEpoch,
237
+ authProvider: provider,
238
+ });
239
+ }
240
+
241
+ /** The empty request body of the epoch pull/serve pair (a strict object: no field may ride it). */
242
+ const emptyRequest = z.object({}).strict();
243
+
244
+ /** The child adopts its parent's exact current verifier through the verified-birth-parent door, and
245
+ * re-seats that parent as its provider authority (a child born before the seat existed heals itself). */
246
+ export const acceptParentAuthProviderEpoch = nomosImplementationDetail(
247
+ intent(ACCEPT_PARENT_AUTH_PROVIDER_EPOCH_ID)
248
+ .contract({ accepts: authProviderConfiguration })
249
+ .directs(acceptParentAuthProviderEpochEffect, (configuration) => configuration)
250
+ .directs(seatVerifiedBirthParentAuthority, () => ({}))
251
+ .acceptsVerifiedBirthParentRecoveryEffect()
252
+ .atomically(),
253
+ );
254
+
255
+ /** The nominal route of the gated acceptor effect — the target the parent's repair addresses. */
256
+ export const acceptChildAuthenticationRecovery = nomosImplementationDetail(
257
+ intent(ACCEPT_CHILD_AUTHENTICATION_RECOVERY_ID).performs(acceptChildAuthenticationRecoveryEffect),
258
+ );
259
+
260
+ /** A verified child asks; parent law replies with its current typed verifier epoch. */
261
+ export const serveAuthProviderEpoch = nomosImplementationDetail(
262
+ intent(SERVE_AUTH_PROVIDER_EPOCH_ID)
263
+ .contract({ accepts: emptyRequest })
264
+ .reads(authProvider, authProviderKeys, authProviderCertificates)
265
+ .repliesWorkspace(acceptParentAuthProviderEpoch, { payload: currentAuthProviderConfiguration })
266
+ .requires(AUTH_PROVIDER_EPOCH_CONSUMER, { object: "workspace:self" })
267
+ .acceptsWorkspaceEffect()
268
+ .atomically(),
269
+ );
270
+
271
+ /** The child asks its verified parent to refresh the verifier epoch. The route is resolved by the kernel
272
+ * from birth custody; no workspace string enters the payload. */
273
+ export const pullParentAuthProviderEpoch = nomosImplementationDetail(
274
+ intent(PULL_PARENT_AUTH_PROVIDER_EPOCH_ID)
275
+ .contract({ accepts: emptyRequest })
276
+ .directsParentWorkspace(serveAuthProviderEpoch, { payload: () => ({}) })
277
+ .authenticatesWithEnrolledDevice()
278
+ .atomically(),
279
+ );
280
+
281
+ /** A child-open failure selects this exact parent action. The parent derives the recovery state from its
282
+ * own committed custody; the call identifies the affected child, the authenticated principal and the exact
283
+ * device. The first effect lets a child with a stale verifier reopen; the second seats the device. */
284
+ export const repairChildAuthentication = nomosImplementationDetail(
285
+ intent(REPAIR_CHILD_AUTHENTICATION_ID)
286
+ .contract({ accepts: childAuthenticationRepairInput })
287
+ .reads(relationTuplesByObject, authProvider, authProviderKeys, authProviderCertificates)
288
+ .directsWorkspace(acceptParentAuthProviderEpoch, {
289
+ workspace: ({ workspace }) => workspace,
290
+ payload: currentVerifyingAuthProviderConfiguration,
291
+ })
292
+ .directsWorkspace(acceptChildAuthenticationRecovery, {
293
+ workspace: ({ workspace }) => workspace,
294
+ payload: ({ user, deviceKeySubject }) =>
295
+ currentChildAuthenticationRecoveryEvidence(user, deviceKeySubject),
296
+ })
297
+ .authenticatesWithEnrolledDevice()
298
+ .atomically(),
299
+ );
300
+ // THE GATE, applied by the framework: enrolled-device proof, no parent-local write, the exact child
301
+ // addressed from the typed input. Asserted at definition so no composition can carry a differently
302
+ // shaped repair.
303
+ assertRepairsChildAuthentication(repairChildAuthentication, "framework child-authentication repair");
304
+
305
+ /** A device may repair only its own authenticated identity. */
306
+ export const repairChildAuthenticationBindsPrincipal = workspaceInvariant("RepairChildAuthenticationBindsPrincipal")
307
+ .on(REPAIR_CHILD_AUTHENTICATION_ID)
308
+ .reads(readSet())
309
+ .assert((_snapshots, ctx) =>
310
+ ctx?.principal === (ctx?.intent as { user?: unknown } | undefined)?.user
311
+ ? { accept: true }
312
+ : { reject: "child-authentication-repair-must-bind-authenticated-user" },
313
+ );
314
+
315
+ /** The relation schema the lanes above gate on, declared once by the framework. The private-read lane
316
+ * already supplies the provider-authority fragment; both are the canonical singletons, so composing them
317
+ * beside it deduplicates by identity, and a tenant's byte-identical copy in another domain passes the
318
+ * cross-domain divergence guard. */
319
+ export const childAuthenticationRelationFragments: readonly RelationTypeDecl[] = [
320
+ authProviderSourceRelations,
321
+ authProviderEpochConsumerRelations,
322
+ ];
323
+
324
+ /**
325
+ * The export map the application-home policy law composes. Keys are the obscure `__nomos_*` slots; values
326
+ * are THE singletons above — the law graph folds an intent's step closure by object identity, so the
327
+ * exported effect must be the very object its intent directs. The gated acceptor effect is carried only
328
+ * through its nominal intent's step closure (the compiler qualifies relation-gated directives into fresh
329
+ * objects, so a second export slot would resolve the one effect to two declarations).
330
+ */
331
+ export function childAuthenticationExports(): Readonly<Record<string, object>> {
332
+ return {
333
+ __nomos_verifiedBirthAttestation: verifiedBirthAttestation,
334
+ __nomos_seatVerifiedBirthParentAuthority: seatVerifiedBirthParentAuthority,
335
+ __nomos_acceptParentAuthProviderEpochEffect: acceptParentAuthProviderEpochEffect,
336
+ __nomos_acceptParentAuthProviderEpoch: acceptParentAuthProviderEpoch,
337
+ __nomos_acceptChildAuthenticationRecovery: acceptChildAuthenticationRecovery,
338
+ __nomos_serveAuthProviderEpoch: serveAuthProviderEpoch,
339
+ __nomos_pullParentAuthProviderEpoch: pullParentAuthProviderEpoch,
340
+ __nomos_repairChildAuthentication: repairChildAuthentication,
341
+ __nomos_repairChildAuthenticationBindsPrincipal: repairChildAuthenticationBindsPrincipal,
342
+ __nomos_repairChildAuthProviderEpochConsumer: repairChildAuthProviderEpochConsumer,
343
+ __nomos_authProviderSourceRelations: authProviderSourceRelations,
344
+ __nomos_authProviderEpochConsumerRelations: authProviderEpochConsumerRelations,
345
+ };
346
+ }