@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
@@ -0,0 +1,271 @@
1
+ /// Console enrolment, device side — the Dart twin of
2
+ /// `peer-office/src/runtime/console_enrolment.ts` (identity_target_model.md
3
+ /// D22/D23).
4
+ ///
5
+ /// The person signs in with the tenant's provider (a Firebase ID token for
6
+ /// CO2). The device proves it holds its Ed25519 key by signing
7
+ /// [consoleEnrolRequestBytes], then POSTs the token, its public key, the proof
8
+ /// and the workspaces it wants to the console's enroller. The console verifies
9
+ /// the token once, off the ledger, and submits `enrollDevice { principal:
10
+ /// <scope>:<subject>, keyHash }` into each workspace; from then on the device
11
+ /// authors with its own key and no provider token ever reaches an intent.
12
+ ///
13
+ /// The platform shell performs this as the Peer Office's console-enrolment
14
+ /// effect: it holds both the device secret (its identity store) and the
15
+ /// provider. Both byte formats are pinned by the golden vector
16
+ /// `peer-office/test/fixtures/console_enrol_request_vector.json`.
17
+ library;
18
+
19
+ import 'dart:convert';
20
+ import 'dart:typed_data';
21
+
22
+ import 'package:crypto/crypto.dart' show sha256;
23
+ import 'package:ed25519_edwards/ed25519_edwards.dart' as ed25519;
24
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
25
+
26
+ /// Where an enrolled principal's device key is enrolled: the application's
27
+ /// console enroller, its tenant and the enrolled provider scope declared in
28
+ /// law. Application configuration, never authority.
29
+ final class NomosConsoleEnrolment {
30
+ const NomosConsoleEnrolment({
31
+ required this.console,
32
+ required this.tenant,
33
+ required this.scope,
34
+ });
35
+
36
+ /// The console's origin, e.g. `http://127.0.0.1:8797`.
37
+ final String console;
38
+
39
+ /// The console tenant, e.g. `co2-platform`.
40
+ final String tenant;
41
+
42
+ /// The enrolled provider scope, e.g. `co2`; principals are `<scope>:<subject>`.
43
+ final String scope;
44
+
45
+ nomos_protocol.ConsoleEnrolmentRoute toRoute() =>
46
+ nomos_protocol.ConsoleEnrolmentRoute(
47
+ console: console, tenant: tenant, scope: scope);
48
+
49
+ @override
50
+ bool operator ==(Object other) =>
51
+ other is NomosConsoleEnrolment &&
52
+ other.console == console &&
53
+ other.tenant == tenant &&
54
+ other.scope == scope;
55
+
56
+ @override
57
+ int get hashCode => Object.hash(console, tenant, scope);
58
+ }
59
+
60
+ /// `nomos-console-enrol-v1` followed by a NUL byte.
61
+ final String consoleEnrolRequestTag =
62
+ 'nomos-console-enrol-v1${String.fromCharCode(0)}';
63
+ final String _nul = String.fromCharCode(0);
64
+
65
+ /// The key hash the ledger names a device by: sha256 hex of its raw Ed25519 public key.
66
+ String consoleDeviceKeyHash(List<int> publicKey) =>
67
+ sha256.convert(publicKey).toString();
68
+
69
+ /// The bytes a device signs to ask for enrolment: tag, tenant, scope, its key
70
+ /// hash, the requested workspaces (sorted, newline-separated) and the provider
71
+ /// token's sha256, NUL-separated. Byte-identical to the console's
72
+ /// `enrolRequestBytes`.
73
+ Uint8List consoleEnrolRequestBytes({
74
+ required String tenant,
75
+ required String scope,
76
+ required String keyHash,
77
+ required Iterable<String> workspaces,
78
+ required String providerToken,
79
+ }) {
80
+ final sorted = [...workspaces]..sort();
81
+ final head = utf8.encode('$consoleEnrolRequestTag$tenant$_nul$scope$_nul'
82
+ '$keyHash$_nul${sorted.join('\n')}$_nul');
83
+ final digest = sha256.convert(utf8.encode(providerToken)).bytes;
84
+ return Uint8List.fromList([...head, ...digest]);
85
+ }
86
+
87
+ /// Unpadded base64url, the console's wire form for keys and signatures.
88
+ String consoleBase64Url(List<int> bytes) =>
89
+ base64Url.encode(bytes).replaceAll('=', '');
90
+
91
+ /// An Ed25519 device key held by the shell. The secret never leaves [sign].
92
+ final class NomosConsoleEnrolmentDevice {
93
+ NomosConsoleEnrolmentDevice._(this._key);
94
+
95
+ /// The 32-byte RFC 8032 seed the kernel mints and the identity store keeps.
96
+ factory NomosConsoleEnrolmentDevice.fromSeed(List<int> seed) {
97
+ if (seed.length != ed25519.SeedSize) {
98
+ throw ArgumentError('a device secret is a 32-byte Ed25519 seed');
99
+ }
100
+ return NomosConsoleEnrolmentDevice._(
101
+ ed25519.newKeyFromSeed(Uint8List.fromList(seed)));
102
+ }
103
+
104
+ final ed25519.PrivateKey _key;
105
+
106
+ Uint8List get publicKey => Uint8List.fromList(ed25519.public(_key).bytes);
107
+
108
+ Uint8List sign(List<int> message) =>
109
+ ed25519.sign(_key, Uint8List.fromList(message));
110
+ }
111
+
112
+ /// The path and JSON body of one enrolment request, carrying the device's possession proof.
113
+ final class NomosConsoleEnrolmentRequest {
114
+ const NomosConsoleEnrolmentRequest({required this.path, required this.body});
115
+
116
+ final String path;
117
+ final Map<String, Object> body;
118
+ }
119
+
120
+ NomosConsoleEnrolmentRequest consoleEnrolmentRequest({
121
+ required String tenant,
122
+ required String scope,
123
+ required String providerToken,
124
+ required NomosConsoleEnrolmentDevice device,
125
+ required Iterable<String> workspaces,
126
+ }) {
127
+ final publicKey = device.publicKey;
128
+ if (publicKey.length != 32) {
129
+ throw ArgumentError('the device public key must be a 32-byte Ed25519 key');
130
+ }
131
+ if (providerToken.isEmpty) {
132
+ throw ArgumentError('a provider token is required to enrol a device');
133
+ }
134
+ final unique = <String>{...workspaces}.toList();
135
+ if (unique.isEmpty) throw ArgumentError('enrol into at least one workspace');
136
+ final keyHash = consoleDeviceKeyHash(publicKey);
137
+ final signature = device.sign(consoleEnrolRequestBytes(
138
+ tenant: tenant,
139
+ scope: scope,
140
+ keyHash: keyHash,
141
+ workspaces: unique,
142
+ providerToken: providerToken,
143
+ ));
144
+ return NomosConsoleEnrolmentRequest(
145
+ path: '/v1/tenants/${Uri.encodeComponent(tenant)}'
146
+ '/providers/${Uri.encodeComponent(scope)}/enrolments',
147
+ body: {
148
+ 'providerToken': providerToken,
149
+ 'devicePublicKey': consoleBase64Url(publicKey),
150
+ 'deviceSignature': consoleBase64Url(signature),
151
+ 'workspaces': unique,
152
+ },
153
+ );
154
+ }
155
+
156
+ /// The console's record of an enrolled device (console.ts `DeviceRecord`).
157
+ final class NomosConsoleEnrolledDevice {
158
+ const NomosConsoleEnrolledDevice({
159
+ required this.keyHash,
160
+ required this.principal,
161
+ required this.workspaces,
162
+ this.refused = const {},
163
+ });
164
+
165
+ final String keyHash;
166
+ final String principal;
167
+
168
+ /// Workspace → the head its `enrollDevice` was admitted at. Includes the
169
+ /// console's fan-out: every workspace the person belongs to (their Home and
170
+ /// everything born below it), not only those requested.
171
+ final Map<String, String> workspaces;
172
+
173
+ /// Workspace → why the console's fan-out into it was refused.
174
+ final Map<String, String> refused;
175
+ }
176
+
177
+ /// The console answered, but not with an enrolment.
178
+ final class NomosConsoleEnrolmentRefused implements Exception {
179
+ const NomosConsoleEnrolmentRefused(this.status, this.message,
180
+ {this.code = '', this.retryable});
181
+
182
+ final int status;
183
+ final String message;
184
+
185
+ /// The console's stable refusal code (empty from an older console).
186
+ final String code;
187
+
188
+ /// The console's own retry verdict; null when it gave none.
189
+ final bool? retryable;
190
+
191
+ @override
192
+ String toString() => 'console enrolment refused (HTTP $status): $message';
193
+ }
194
+
195
+ /// One console exchange: status and raw body.
196
+ final class NomosConsoleResponse {
197
+ const NomosConsoleResponse({required this.status, required this.body});
198
+
199
+ final int status;
200
+ final String body;
201
+ }
202
+
203
+ /// Minimal POST shape, so a shell can inject its own transport.
204
+ typedef NomosConsolePost = Future<NomosConsoleResponse> Function(
205
+ Uri url, String body);
206
+
207
+ /// Enrol [device] for the person [providerToken] names into [workspaces],
208
+ /// through the console at [console].
209
+ Future<NomosConsoleEnrolledDevice> enrolWithConsole({
210
+ required String console,
211
+ required String tenant,
212
+ required String scope,
213
+ required String providerToken,
214
+ required NomosConsoleEnrolmentDevice device,
215
+ required Iterable<String> workspaces,
216
+ required NomosConsolePost post,
217
+ }) async {
218
+ final request = consoleEnrolmentRequest(
219
+ tenant: tenant,
220
+ scope: scope,
221
+ providerToken: providerToken,
222
+ device: device,
223
+ workspaces: workspaces,
224
+ );
225
+ final base = console.replaceFirst(RegExp(r'/+$'), '');
226
+ final response =
227
+ await post(Uri.parse('$base${request.path}'), jsonEncode(request.body));
228
+ Object? answer;
229
+ try {
230
+ answer = jsonDecode(response.body);
231
+ } catch (_) {
232
+ answer = null;
233
+ }
234
+ final record =
235
+ answer is Map<String, Object?> ? answer : const <String, Object?>{};
236
+ if (response.status != 201) {
237
+ final error = record['error'];
238
+ final code = record['code'];
239
+ final retryable = record['retryable'];
240
+ throw NomosConsoleEnrolmentRefused(
241
+ response.status,
242
+ error is String
243
+ ? error
244
+ : 'the console answered HTTP ${response.status}',
245
+ code: code is String ? code : '',
246
+ retryable: retryable is bool ? retryable : null);
247
+ }
248
+ final keyHash = record['keyHash'];
249
+ final principal = record['principal'];
250
+ final heads = record['workspaces'];
251
+ if (keyHash is! String || principal is! String || heads is! Map) {
252
+ throw NomosConsoleEnrolmentRefused(
253
+ response.status, 'the console answered 201 without a device record');
254
+ }
255
+ final refused = record['refused'];
256
+ return NomosConsoleEnrolledDevice(
257
+ keyHash: keyHash,
258
+ principal: principal,
259
+ workspaces: {
260
+ for (final entry in heads.entries)
261
+ if (entry.key is String && entry.value is String)
262
+ entry.key as String: entry.value as String,
263
+ },
264
+ refused: {
265
+ if (refused is Map)
266
+ for (final entry in refused.entries)
267
+ if (entry.key is String && entry.value is String)
268
+ entry.key as String: entry.value as String,
269
+ },
270
+ );
271
+ }
@@ -0,0 +1,26 @@
1
+ import 'dart:convert';
2
+ import 'dart:io';
3
+
4
+ import 'console_enrolment.dart' show NomosConsoleResponse;
5
+
6
+ /// The native shells' console transport: one JSON POST, the answer's status
7
+ /// and body. No retry and no interpretation: Peer Office owns both.
8
+ Future<NomosConsoleResponse> nomosDefaultConsolePost(
9
+ Uri url, String body) async {
10
+ final client = HttpClient()..connectionTimeout = const Duration(seconds: 30);
11
+ try {
12
+ final request = await client.postUrl(url);
13
+ request.headers
14
+ ..contentType = ContentType.json
15
+ ..set(HttpHeaders.acceptHeader, 'application/json');
16
+ request.add(utf8.encode(body));
17
+ final response = await request.close().timeout(const Duration(seconds: 60));
18
+ final text = await response
19
+ .transform(utf8.decoder)
20
+ .join()
21
+ .timeout(const Duration(seconds: 60));
22
+ return NomosConsoleResponse(status: response.statusCode, body: text);
23
+ } finally {
24
+ client.close(force: true);
25
+ }
26
+ }
@@ -0,0 +1,7 @@
1
+ import 'console_enrolment.dart' show NomosConsoleResponse;
2
+
3
+ /// A platform without `dart:io` has no default console transport; its shell
4
+ /// registers one (`registerConsolePost`) or the enrolment effect is refused.
5
+ Future<NomosConsoleResponse> nomosDefaultConsolePost(Uri url, String body) =>
6
+ Future.error(UnsupportedError(
7
+ 'this platform has no default console transport; register one'));
@@ -1,16 +1,16 @@
1
- // NOMOS TEST CLOUD (co2 #4) — a headless, real-sync test cloud for AppSim: start/stop a local
2
- // `githolon cloud dev` (the SAME engine plane production runs), front it with a PARTITION PROXY so a test can
1
+ // NOMOS TEST CLOUD (co2 #4) — an unattended, real-sync test cloud for AppSim: start/stop a local
2
+ // `githolon cloud dev` (the SAME kernel host production runs), front it with a PARTITION PROXY so a test can
3
3
  // go online/offline deterministically, and inspect the remote head/sequence — all sharing the production
4
- // bridge/session clients (the sessions connect to [origin] exactly like they connect to the real cloud).
4
+ // Reception/session clients (the sessions connect to [origin] exactly like they connect to the real cloud).
5
5
  //
6
6
  // ```dart
7
7
  // final cloud = await NomosTestCloud.start(serverEntry: devServerPath); // spawns the dev cloud + proxy
8
8
  // final harness = await NomosTestHarness.cloudBound(cloud: cloud, lawUsdzPaths: [...], platformWorkspace: 'co2-platform', subject: 'user:test');
9
9
  // // ... author offline ...
10
10
  // cloud.setOnline(false); // PARTITION — the up leg can't reach custody
11
- // await harness.session('estate').sync(admit: true); // stays local; nothing lands remotely
11
+ // await harness.converge(workspaces: ['estate']); // stays local; nothing lands remotely
12
12
  // cloud.setOnline(true); // heal the partition
13
- // await harness.session('estate').sync(admit: true); // converges; the write lands on remote main
13
+ // await harness.converge(workspaces: ['estate']); // converges; the write lands on remote main
14
14
  // final remote = await cloud.remoteHead('estate'); // inspect remote head + commit count
15
15
  // await cloud.stop();
16
16
  // ```
@@ -68,7 +68,14 @@ class NomosTestCloud {
68
68
  final Uri _upstream; // the real dev-cloud origin the proxy forwards to
69
69
  final HttpClient _client;
70
70
  final void Function(String line)? _onDiagnostic;
71
- final String? testKRootPublic;
71
+
72
+ /// Base64 of this universe's pinned ed25519 trust root, when the shell has one.
73
+ /// Every peer in this ephemeral trust universe pins the same anchor before it
74
+ /// replays or births anything; production bakes its own into the kernel.
75
+ final String? trustRootPublicKey;
76
+
77
+ /// The retired name for [trustRootPublicKey]; kept for one release.
78
+ String? get testKRootPublic => trustRootPublicKey;
72
79
  bool _online = true;
73
80
  bool _stopped = false;
74
81
 
@@ -78,10 +85,11 @@ class NomosTestCloud {
78
85
  this._upstream,
79
86
  this._client,
80
87
  this._onDiagnostic,
81
- this.testKRootPublic,
88
+ this.trustRootPublicKey,
82
89
  );
83
90
 
84
- /// The base URL a cloud-BOUND session connects to (`connect(cloud: cloud.origin)`). Points at the PARTITION
91
+ /// The base URL of this development cloud. Placement, not an interaction
92
+ /// session, determines whether a workspace follows it.
85
93
  /// PROXY, so [setOnline] deterministically severs/heals the whole plane's link to custody.
86
94
  String get origin => 'http://${_proxy.address.host}:${_proxy.port}';
87
95
 
@@ -151,11 +159,12 @@ class NomosTestCloud {
151
159
  );
152
160
  }
153
161
  final decoded = jsonDecode(text);
154
- if (decoded case {
155
- 'token': final String token,
156
- 'issuer': final String issuer,
157
- 'audience': final String audience,
158
- } when token.isNotEmpty && issuer.isNotEmpty && audience.isNotEmpty) {
162
+ if (decoded
163
+ case {
164
+ 'token': final String token,
165
+ 'issuer': final String issuer,
166
+ 'audience': final String audience,
167
+ } when token.isNotEmpty && issuer.isNotEmpty && audience.isNotEmpty) {
159
168
  return DevIdentityToken(
160
169
  token: token,
161
170
  issuer: issuer,
@@ -169,7 +178,7 @@ class NomosTestCloud {
169
178
 
170
179
  /// Start the test cloud: spawn `node <serverEntry> --port <n> [--ephemeral] [--data <dir>]`, wait until its
171
180
  /// `/health` answers, then bring up the partition proxy in front of it. [serverEntry] is the dev-server
172
- /// entry (`cloud/dev-server/server.ts` in-repo, or `dist/dev-server.mjs` from a published `githolon`) — the
181
+ /// entry (`platform-shells/node/local-development/server.ts` in-repo, or `dist/dev-server.mjs` from a published `githolon`) — the
173
182
  /// caller resolves it (the same seam the offline harness uses for its install helper). [node] defaults to
174
183
  /// `node` on PATH.
175
184
  static Future<NomosTestCloud> start({
@@ -194,16 +203,50 @@ class NomosTestCloud {
194
203
  /// not made current on the parent merely to make it available to a child.
195
204
  List<String> lawUsdzPaths = const [],
196
205
  int? applicationMaxChildWorkspacesPerUser,
197
- Duration readyTimeout = const Duration(seconds: 30),
206
+
207
+ /// The runtime bundle the shell runs (`holon.wasm`, `kernel.repository.xml`,
208
+ /// `bootstrap.law.usdz`, `nomos.law.usdz`). Defaults to the candidate/offline
209
+ /// runtime this process was pointed at.
210
+ String? runtimeDirectory,
211
+
212
+ /// Mint this universe's ed25519 trust root at boot and pin its public half.
213
+ /// A platform can only be born under a root warranted to a trust root.
214
+ bool? mintTrustRoot,
215
+
216
+ /// Serve this universe's ES256 identity provider (JWKS + the operator mint
217
+ /// door the warranted first-device enrolment onboards through).
218
+ bool? testIdentityProvider,
219
+ Duration readyTimeout = const Duration(seconds: 90),
198
220
  void Function(String line)? onDiagnostic,
199
221
  }) async {
200
222
  final chosenPort = port == 0 ? await _freePort() : port;
223
+ // A platform birth needs this universe's trust root SECRET to sign root's warrant and the
224
+ // platform's birth certificate, and onboarding needs an identity provider: when a platform is
225
+ // requested the shell mints both unless told otherwise.
226
+ final mintsTrustRoot = mintTrustRoot ?? (platform != null);
227
+ final servesIdentityProvider = testIdentityProvider ?? (platform != null);
228
+ final runtime = runtimeDirectory ??
229
+ Platform.environment['NOMOS_CANDIDATE_RUNTIME'] ??
230
+ Platform.environment['NOMOS_OFFLINE_RUNTIME'];
231
+ // The partition proxy is the cloud's public origin: the peer is booted with it as its provider, so
232
+ // the cloud must advertise it (discovery, placement, leases) exactly as production advertises the
233
+ // origin a request arrives at. A peer never talks to an origin it was not booted with.
234
+ final proxy = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
201
235
  final args = <String>[
202
236
  serverEntry,
203
237
  '--port',
204
238
  '$chosenPort',
205
- if (ephemeral) '--ephemeral',
206
- if (dataDir != null) ...['--data', dataDir],
239
+ '--public-origin',
240
+ 'http://${proxy.address.host}:${proxy.port}',
241
+ // NOMOS_TEST_CLOUD_DATA keeps the custodian's Git custody on disk after the run (inspection with
242
+ // git of what the custodian admitted and birthed), instead of the deleted temporary directory.
243
+ if (ephemeral && Platform.environment['NOMOS_TEST_CLOUD_DATA'] == null)
244
+ '--ephemeral',
245
+ if (dataDir ?? Platform.environment['NOMOS_TEST_CLOUD_DATA']
246
+ case final data?) ...['--data', data],
247
+ if (runtime != null) ...['--runtime', runtime],
248
+ if (mintsTrustRoot) '--mint-trust-root',
249
+ if (servesIdentityProvider) '--test-identity-provider',
207
250
  if (platform != null) ...['--platform', platform],
208
251
  if (applicationMaxChildWorkspacesPerUser != null) ...[
209
252
  '--application-max-child-workspaces-per-user',
@@ -224,6 +267,7 @@ class NomosTestCloud {
224
267
  if (serverTail.length > 24) serverTail.removeAt(0);
225
268
  log('$stream: $line');
226
269
  }
270
+
227
271
  proc.stdout
228
272
  .transform(utf8.decoder)
229
273
  .transform(const LineSplitter())
@@ -242,7 +286,7 @@ class NomosTestCloud {
242
286
  // half-born platform's typed refusals.
243
287
  final deadline = DateTime.now().add(readyTimeout);
244
288
  var ready = false;
245
- String? testKRootPublic;
289
+ String? trustRootPublicKey;
246
290
  while (DateTime.now().isBefore(deadline)) {
247
291
  if (prematureExitCode != null) break;
248
292
  try {
@@ -253,7 +297,9 @@ class NomosTestCloud {
253
297
  final health = jsonDecode(body) as Map<String, dynamic>;
254
298
  final platformReady = health['platformReady'];
255
299
  if (platform == null || platformReady == true) {
256
- testKRootPublic = health['testKRootPublic']?.toString();
300
+ trustRootPublicKey =
301
+ (health['trustRootPublicKey'] ?? health['testKRootPublic'])
302
+ ?.toString();
257
303
  ready = true;
258
304
  break;
259
305
  }
@@ -265,8 +311,10 @@ class NomosTestCloud {
265
311
  }
266
312
  if (!ready) {
267
313
  if (prematureExitCode == null) proc.kill(ProcessSignal.sigkill);
314
+ await proxy.close(force: true);
268
315
  final exited = prematureExitCode;
269
- final tail = serverTail.isEmpty ? '' : '\nserver tail:\n${serverTail.join('\n')}';
316
+ final tail =
317
+ serverTail.isEmpty ? '' : '\nserver tail:\n${serverTail.join('\n')}';
270
318
  throw NomosTestCloudException(
271
319
  exited == null
272
320
  ? 'the dev cloud did not answer /health${platform != null ? ' with platformReady:true' : ''} within ${readyTimeout.inSeconds}s (serverEntry: $serverEntry)$tail'
@@ -278,14 +326,13 @@ class NomosTestCloud {
278
326
  );
279
327
 
280
328
  // The partition proxy — forwards to the dev cloud when ONLINE, fails fast when OFFLINE.
281
- final proxy = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
282
329
  final cloud = NomosTestCloud._(
283
330
  proc,
284
331
  proxy,
285
332
  upstream,
286
333
  client,
287
334
  onDiagnostic,
288
- testKRootPublic,
335
+ trustRootPublicKey,
289
336
  );
290
337
  proxy.listen(cloud._handleProxy);
291
338
  log('partition proxy at ${cloud.origin} → $upstream');
@@ -5,27 +5,40 @@ import 'package:meta/meta.dart' show internal;
5
5
  /// One compiler-authored, nominal domain call.
6
6
  ///
7
7
  /// Generated applications construct this directly from their typed input. The
8
- /// host may carry it but cannot reinterpret its value; the kernel reopens the
9
- /// canonical USDA and verifies that [domain] and [directive] resolve to the
10
- /// exact identities embedded in [canonicalUsda].
8
+ /// host may carry it but cannot reinterpret its value; the kernel derives the
9
+ /// exact Protobuf descriptor from installed law and authors canonical USDA.
11
10
  @internal
12
11
  abstract interface class NmsGeneratedCall {
13
12
  String get domain;
14
13
  String get directive;
15
- Uint8List get canonicalUsda;
14
+ String get protobufType;
15
+ Uint8List get protobufPayload;
16
16
  }
17
17
 
18
18
  /// One compiler-authored nominal application query.
19
19
  ///
20
20
  /// The public application method retains its generated Dart parameter types. This value is the
21
- /// cross-language continuation of those types: canonical USDA relates the query and every
22
- /// argument to the exact composed-law identities. Protobuf carries these bytes but does not
23
- /// replace the application's type system with a generic object.
21
+ /// cross-language continuation of those types. Its operation-specific Protobuf
22
+ /// message is generated from the same law graph as the public Dart method and
23
+ /// the kernel's installed descriptor.
24
24
  @internal
25
25
  abstract interface class NmsGeneratedQuery {
26
26
  String get domain;
27
27
  String get query;
28
- Uint8List get canonicalArgumentsUsda;
28
+ String get protobufType;
29
+ Uint8List get protobufPayload;
30
+ }
31
+
32
+ /// One compiler-authored nominal application result.
33
+ ///
34
+ /// The peer office transports the kernel's query-specific Protobuf `Any`
35
+ /// without interpreting it. Generated application code checks [protobufType]
36
+ /// and decodes [protobufPayload] with the operation bindings emitted from the
37
+ /// same composed law graph as its public Dart API.
38
+ @internal
39
+ abstract interface class NmsGeneratedResult {
40
+ String get protobufType;
41
+ Uint8List get protobufPayload;
29
42
  }
30
43
 
31
44
  /// Constructor namespace reserved for generated application code.
@@ -33,21 +46,24 @@ abstract interface class NmsGeneratedQuery {
33
46
  /// Keeping the three transport facts in one immutable value prevents generated
34
47
  /// code from accidentally pairing bytes from one directive with routing hints
35
48
  /// from another. Untrusted callers gain no authority by constructing one: the
36
- /// kernel independently validates the USDA against admitted law.
49
+ /// kernel independently validates the operation Protobuf against admitted law.
37
50
  @internal
38
51
  abstract final class NmsInternalGeneratedCalls {
39
52
  static NmsGeneratedCall create({
40
53
  required String domain,
41
54
  required String directive,
42
- required Uint8List canonicalUsda,
55
+ required String protobufType,
56
+ required Uint8List protobufPayload,
43
57
  }) {
44
58
  if (domain.isEmpty || directive.isEmpty) {
45
- throw ArgumentError('A generated Nomos call needs nominal routing identity.');
59
+ throw ArgumentError(
60
+ 'A generated Nomos call needs nominal routing identity.');
46
61
  }
47
- if (canonicalUsda.isEmpty) {
48
- throw ArgumentError('A generated Nomos call needs canonical USDA.');
62
+ if (!protobufType.startsWith('type.nomos.dev/')) {
63
+ throw ArgumentError(
64
+ 'A generated Nomos call needs one exact typed Protobuf payload.');
49
65
  }
50
- return _GeneratedCall(domain, directive, canonicalUsda);
66
+ return _GeneratedCall(domain, directive, protobufType, protobufPayload);
51
67
  }
52
68
  }
53
69
 
@@ -57,20 +73,47 @@ abstract final class NmsInternalGeneratedQueries {
57
73
  static NmsGeneratedQuery create({
58
74
  required String domain,
59
75
  required String query,
60
- required Uint8List canonicalArgumentsUsda,
76
+ required String protobufType,
77
+ required Uint8List protobufPayload,
61
78
  }) {
62
79
  if (domain.isEmpty || query.isEmpty) {
63
- throw ArgumentError('A generated Nomos query needs nominal routing identity.');
80
+ throw ArgumentError(
81
+ 'A generated Nomos query needs nominal routing identity.');
82
+ }
83
+ // No-argument reads (first/take) canonically encode as zero bytes. The
84
+ // nominal type identifies the message; installed law validates its fields.
85
+ // Payload length cannot distinguish a valid empty message from missing input.
86
+ if (!protobufType.startsWith('type.nomos.dev/')) {
87
+ throw ArgumentError(
88
+ 'A generated Nomos query needs one exact typed Protobuf payload.');
64
89
  }
65
- if (canonicalArgumentsUsda.isEmpty) {
66
- throw ArgumentError('A generated Nomos query needs canonical USDA arguments.');
90
+ return _GeneratedQuery(domain, query, protobufType, protobufPayload);
91
+ }
92
+ }
93
+
94
+ /// Constructor namespace reserved for the shared peer office.
95
+ @internal
96
+ abstract final class NmsInternalGeneratedResults {
97
+ static NmsGeneratedResult create({
98
+ required String protobufType,
99
+ required Uint8List protobufPayload,
100
+ }) {
101
+ // A valid Protobuf message containing only default values has the canonical
102
+ // wire representation of zero bytes. In particular, a generated query
103
+ // whose repeated result is empty must still cross this boundary as its
104
+ // exact nominal result type. The type URL supplies that identity; payload
105
+ // length is not a type or validity signal.
106
+ if (!protobufType.startsWith('type.nomos.dev/')) {
107
+ throw ArgumentError(
108
+ 'A generated Nomos result needs one exact typed Protobuf payload.');
67
109
  }
68
- return _GeneratedQuery(domain, query, canonicalArgumentsUsda);
110
+ return _GeneratedResult(protobufType, protobufPayload);
69
111
  }
70
112
  }
71
113
 
72
114
  final class _GeneratedCall implements NmsGeneratedCall {
73
- const _GeneratedCall(this.domain, this.directive, this.canonicalUsda);
115
+ const _GeneratedCall(
116
+ this.domain, this.directive, this.protobufType, this.protobufPayload);
74
117
 
75
118
  @override
76
119
  final String domain;
@@ -79,11 +122,15 @@ final class _GeneratedCall implements NmsGeneratedCall {
79
122
  final String directive;
80
123
 
81
124
  @override
82
- final Uint8List canonicalUsda;
125
+ final String protobufType;
126
+
127
+ @override
128
+ final Uint8List protobufPayload;
83
129
  }
84
130
 
85
131
  final class _GeneratedQuery implements NmsGeneratedQuery {
86
- const _GeneratedQuery(this.domain, this.query, this.canonicalArgumentsUsda);
132
+ const _GeneratedQuery(
133
+ this.domain, this.query, this.protobufType, this.protobufPayload);
87
134
 
88
135
  @override
89
136
  final String domain;
@@ -92,5 +139,18 @@ final class _GeneratedQuery implements NmsGeneratedQuery {
92
139
  final String query;
93
140
 
94
141
  @override
95
- final Uint8List canonicalArgumentsUsda;
142
+ final String protobufType;
143
+
144
+ @override
145
+ final Uint8List protobufPayload;
146
+ }
147
+
148
+ final class _GeneratedResult implements NmsGeneratedResult {
149
+ const _GeneratedResult(this.protobufType, this.protobufPayload);
150
+
151
+ @override
152
+ final String protobufType;
153
+
154
+ @override
155
+ final Uint8List protobufPayload;
96
156
  }