@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.
- package/assets/operation-contract.wasm +0 -0
- package/check_package.mjs +12 -15
- package/compile_package.mjs +17 -18
- package/compile_proof.mjs +16 -1
- package/dart/CHANGELOG.md +76 -1
- package/dart/README.md +25 -2
- package/dart/lib/src/admission_decision.dart +41 -0
- package/dart/lib/src/application_framework_calls.dart +52 -2
- package/dart/lib/src/application_model_adoption.dart +187 -0
- package/dart/lib/src/application_status.dart +73 -0
- package/dart/lib/src/console_enrolment.dart +271 -0
- package/dart/lib/src/console_http_io.dart +26 -0
- package/dart/lib/src/console_http_stub.dart +7 -0
- package/dart/lib/src/dev_cloud.dart +69 -22
- package/dart/lib/src/generated_call.dart +83 -23
- package/dart/lib/src/kernel_primitives.dart +119 -0
- package/dart/lib/src/nms_conformance_runtime.dart +1 -1
- package/dart/lib/src/nms_generated_runtime.dart +32 -10
- package/dart/lib/src/nms_test_environment.dart +1 -1
- package/dart/lib/src/node_runner_transport.dart +41 -46
- package/dart/lib/src/observed_session_directory.dart +70 -0
- package/dart/lib/src/operation_receipts.dart +112 -0
- package/dart/lib/src/peer_application_streams.dart +103 -0
- package/dart/lib/src/reception/dart_reception.dart +4280 -0
- package/dart/lib/src/reception/event_desk.dart +211 -0
- package/dart/lib/src/reception/extremum_read.dart +46 -0
- package/dart/lib/src/reception/local_copies.dart +371 -0
- package/dart/lib/src/reception/peer_office_connection.dart +11 -0
- package/dart/lib/src/reception/progress_desk.dart +267 -0
- package/dart/lib/src/reception/reception_contract.dart +11 -0
- package/dart/lib/src/reception/reception_session.dart +284 -0
- package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
- package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
- package/dart/lib/src/replica_readiness.dart +123 -0
- package/dart/lib/src/runner/node-entry.mjs +23905 -15774
- package/dart/lib/src/runtime_v2_gateway.dart +56 -3
- package/dart/lib/src/runtime_v2_transport.dart +544 -3
- package/dart/lib/src/session_bound.dart +200 -311
- package/dart/lib/src/signing_identity_kernel.dart +84 -0
- package/dart/lib/src/sync_status_projection.dart +548 -0
- package/dart/lib/src/sync_status_reducer.dart +170 -0
- package/dart/lib/src/test_harness.dart +809 -1153
- package/dart/lib/src/testing/test_custody.dart +66 -0
- package/dart/lib/src/testing/test_peer.dart +328 -0
- package/dart/lib/src/typed_watch_registry.dart +169 -0
- package/dart/lib/src/workspace_open_progress.dart +27 -0
- package/dart/pubspec.lock +24 -1
- package/dart/pubspec.yaml +7 -2
- package/dart/pubspec_overrides.yaml +2 -0
- package/dart/test/admission_decision_test.dart +48 -0
- package/dart/test/background_model_adoption_test.dart +228 -0
- package/dart/test/bundle_version_pairing_test.dart +4 -4
- package/dart/test/child_authentication_repair_test.dart +359 -0
- package/dart/test/cloud_bound_local_shell_test.dart +119 -0
- package/dart/test/console_enrolment_refusal_test.dart +52 -0
- package/dart/test/console_enrolment_test.dart +227 -0
- package/dart/test/errors_test.dart +5 -5
- package/dart/test/event_plane_seam_test.dart +50 -164
- package/dart/test/extremum_read_test.dart +58 -0
- package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
- package/dart/test/generated_query_test.dart +32 -0
- package/dart/test/harness_phase_timeout_test.dart +18 -23
- package/dart/test/kernel_author_after_read_test.dart +242 -0
- package/dart/test/kernel_fence_test.dart +137 -0
- package/dart/test/local_copies_test.dart +222 -0
- package/dart/test/model_adoption_test.dart +198 -0
- package/dart/test/node_peer_local_custody_test.dart +153 -0
- package/dart/test/offer_authentication_proof_test.dart +352 -52
- package/dart/test/operation_compatibility_test.dart +151 -0
- package/dart/test/platform_credential_effect_test.dart +386 -0
- package/dart/test/receiving_group_open_test.dart +213 -0
- package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
- package/dart/test/reception_span_trace_test.dart +23 -0
- package/dart/test/replica_readiness_test.dart +156 -0
- package/dart/test/runtime_projections_test.dart +92 -0
- package/dart/test/runtime_v2_gateway_test.dart +131 -0
- package/dart/test/signing_identity_kernel_test.dart +75 -0
- package/dart/test/sync_status_preparation_report_test.dart +29 -0
- package/dart/test/sync_status_reducer_test.dart +419 -0
- package/dart/test/test_harness_test.dart +71 -95
- package/dart/test/typed_query_watch_test.dart +170 -0
- package/dart/test/typed_watch_registry_test.dart +249 -0
- package/dart_lints/README.md +18 -0
- package/dart_lints/lib/nomos_lints.dart +70 -0
- package/dart_lints/pubspec.lock +580 -0
- package/dart_lints/pubspec.yaml +12 -0
- package/dart_lints/test/reference_identity_test.dart +75 -0
- package/dart_types/CHANGELOG.md +18 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
- package/dart_types/lib/nomos_types.dart +9 -1
- package/dart_types/lib/src/admission_decision.dart +37 -0
- package/dart_types/lib/src/aggregate_meta.dart +1 -1
- package/dart_types/lib/src/approved_model.dart +65 -0
- package/dart_types/lib/src/custody.dart +10 -18
- package/dart_types/lib/src/error_info.dart +4 -4
- package/dart_types/lib/src/errors.dart +29 -29
- package/dart_types/lib/src/foreign_ref.dart +62 -33
- package/dart_types/lib/src/local_work_rejected.dart +3 -3
- package/dart_types/lib/src/nomos_ref.dart +3 -1
- package/dart_types/lib/src/principal.dart +2 -2
- package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
- package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/runtime_outcome.dart +133 -0
- package/dart_types/lib/src/subscriptions.dart +116 -23
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/extremum_subscriptions_test.dart +40 -0
- package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
- package/dart_types/test/nomos_ref_address_test.dart +25 -0
- package/dart_types/test/ordered_subscriptions_test.dart +34 -0
- package/operation_contract_check.mjs +24 -0
- package/package.json +11 -6
- package/src/aggregate.ts +70 -10
- package/src/application_topology.ts +18 -11
- package/src/attested.ts +3 -0
- package/src/attested_core.ts +46 -3
- package/src/birth_meter_ensure.ts +19 -7
- package/src/births.ts +29 -4
- package/src/build_package.ts +140 -13
- package/src/business_birth.ts +181 -4
- package/src/codegen_call_proto.ts +1274 -0
- package/src/codegen_call_usda.ts +49 -6
- package/src/codegen_dart.ts +79 -44
- package/src/codegen_dart_client.ts +458 -800
- package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
- package/src/codegen_dart_framework_calls.ts +320 -37
- package/src/codegen_dart_indexed_variant.ts +159 -0
- package/src/codegen_dart_scenario.ts +42 -13
- package/src/codegen_proof.ts +3 -3
- package/src/codegen_provider_dart.ts +5 -0
- package/src/codegen_query_proto.ts +158 -0
- package/src/codegen_refusal.ts +189 -0
- package/src/codegen_result_proto.ts +931 -0
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +464 -616
- package/src/codegen_workspace_access.ts +105 -0
- package/src/compatibility_graph.ts +759 -0
- package/src/compatibility_usda.ts +264 -0
- package/src/compile_package_main.ts +2083 -1248
- package/src/custody_placement.ts +156 -0
- package/src/custody_placement_usda.ts +91 -0
- package/src/decl_shapes.ts +21 -2
- package/src/describe_model.ts +1 -0
- package/src/directive.ts +59 -2
- package/src/drivers.ts +51 -0
- package/src/engine_entry.ts +52 -8
- package/src/evolve_preview.ts +11 -4
- package/src/exists.ts +2 -0
- package/src/fields.ts +75 -11
- package/src/flow_gate.ts +2 -1
- package/src/framework/application_home.ts +170 -18
- package/src/framework/attested_receipts.ts +8 -3
- package/src/framework/auth_provider_law.ts +169 -9
- package/src/framework/birth_certificate_law.ts +4 -0
- package/src/framework/capability.ts +34 -3
- package/src/framework/child_authentication.ts +346 -0
- package/src/framework/console_governance.ts +295 -0
- package/src/framework/delegated_roles.ts +301 -0
- package/src/framework/domain_lifecycle.ts +26 -13
- package/src/framework/founding_genesis.ts +193 -0
- package/src/framework/framework_aggregates.ts +1 -1
- package/src/framework/genesis.ts +6 -1
- package/src/framework/governance_relations.ts +19 -0
- package/src/framework/home.ts +5 -734
- package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
- package/src/framework/model_authority_repair.ts +116 -0
- package/src/framework/model_authority_seed.ts +185 -0
- package/src/framework/model_transition.ts +211 -0
- package/src/framework/owned_custody.ts +1 -1
- package/src/framework/principal_enrollment.ts +302 -0
- package/src/framework/receiving_group.ts +857 -0
- package/src/framework/receiving_group_recovery.ts +314 -0
- package/src/framework/relations.ts +4 -8
- package/src/framework/signing_identity.ts +242 -95
- package/src/framework/source_roster.ts +11 -5
- package/src/framework/workspace_access.ts +149 -0
- package/src/framework/workspace_invariant.ts +73 -52
- package/src/framework/workspaces.ts +407 -40
- package/src/framework_call_definitions.ts +32 -3
- package/src/generated_client_bundle_boundary.ts +97 -0
- package/src/generated_client_names.ts +1 -0
- package/src/identity_usda.ts +27 -0
- package/src/index.ts +183 -12
- package/src/indexed_variant.ts +83 -0
- package/src/intent.ts +173 -45
- package/src/law_check.ts +8 -5
- package/src/law_dependency_index.ts +231 -0
- package/src/law_graph.ts +508 -71
- package/src/law_graph_usda.ts +170 -24
- package/src/law_identity.ts +119 -81
- package/src/law_source_map.ts +4 -1
- package/src/law_units.ts +498 -0
- package/src/law_value_type.ts +7 -3
- package/src/member_authentication.ts +302 -0
- package/src/null_meta.ts +1 -1
- package/src/openusd_compiled_layer.ts +132 -0
- package/src/operation_contract_check.ts +31 -0
- package/src/ordered_read.ts +2 -1
- package/src/package_config.ts +33 -12
- package/src/package_dependencies.ts +1 -0
- package/src/plan_source_slice.ts +30 -1
- package/src/protobuf_field_number.ts +36 -0
- package/src/provenance_wire.ts +6 -0
- package/src/query.ts +2 -1
- package/src/read.ts +50 -0
- package/src/relation.ts +4 -1
- package/src/relation_schema.ts +15 -2
- package/src/report.ts +2 -2
- package/src/sqlite_computation_bundle.ts +78 -0
- package/src/sqlite_computed.ts +102 -0
- package/src/sqlite_counts.ts +154 -0
- package/src/sqlite_layout.ts +823 -0
- package/src/sqlite_reads.ts +275 -0
- package/src/sqlite_samples.ts +185 -0
- package/src/stable_id_identity_bridge.ts +1 -1
- package/src/stable_ids_for_module.ts +16 -4
- package/src/test_call_usda.ts +6 -6
- package/src/usda_relationship_integrity.ts +20 -3
- package/src/usda_text.ts +21 -1
- package/src/value_object.ts +81 -11
- package/src/wire.ts +22 -0
- package/src/wire_encode.ts +227 -20
- package/src/workspace_sharding.ts +15 -6
- package/src/workspace_sharing.ts +51 -49
- package/src/zod_introspection.ts +2 -0
- package/compat_package.mjs +0 -46
- package/dart/lib/src/home.dart +0 -15
- package/dart/lib/src/runner/offline-install.mjs +0 -15367
- package/dart/lib/src/source_trust.dart +0 -51
- package/dart/lib/src/webview_bridge.dart +0 -6540
- package/dart/lib/src/workspace_name.dart +0 -30
- package/dart/test/acquire_session_credentials_test.dart +0 -150
- package/dart/test/acquire_session_test.dart +0 -534
- package/dart/test/adv_realm_interference_test.dart +0 -600
- package/dart/test/cloud_bound_topology_test.dart +0 -168
- package/dart/test/cluster_births_test.dart +0 -202
- package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
- package/dart/test/cluster_local_autosync_test.dart +0 -123
- package/dart/test/cluster_scale_test.dart +0 -122
- package/dart/test/compat_test.dart +0 -61
- package/dart/test/dlq_session_surface_test.dart +0 -222
- package/dart/test/first_app_gauntlet_test.dart +0 -386
- package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
- package/dart/test/four_layer_nested_topology_test.dart +0 -290
- package/dart/test/home_parity_test.dart +0 -13
- package/dart/test/local_sync_inert_test.dart +0 -107
- package/dart/test/node_runner_transport_test.dart +0 -78
- package/dart/test/parent_signer_recovery_test.dart +0 -155
- package/dart/test/residency_refusal_test.dart +0 -49
- package/dart/test/sealing_client_test.dart +0 -181
- package/dart/test/session_bound_test.dart +0 -285
- package/dart/test/stale_bundle_test.dart +0 -80
- package/dart/test/vo_read_typing_test.dart +0 -131
- package/dart/test/watch_after_offer_stall_test.dart +0 -100
- package/dart/test/webview_bridge_test.dart +0 -1930
- package/dart/test/workspace_name_test.dart +0 -24
- package/runtime/generated-application-host.js +0 -22860
- package/src/codegen_query_usda.ts +0 -214
- package/src/compat_manifest.ts +0 -26
- package/src/compat_package_main.ts +0 -224
- package/src/shape_compat.ts +0 -352
- package/src/usd_state.ts +0 -505
|
@@ -5,7 +5,14 @@ import 'package:fixnum/fixnum.dart' as fixnum;
|
|
|
5
5
|
import 'package:meta/meta.dart' show internal;
|
|
6
6
|
import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
|
|
7
7
|
import 'package:nomos_types/nomos_types.dart'
|
|
8
|
-
show
|
|
8
|
+
show
|
|
9
|
+
NomosErrorInfo,
|
|
10
|
+
NomosRefusalReason,
|
|
11
|
+
NomosRefused,
|
|
12
|
+
NomosRuntimeFailure,
|
|
13
|
+
NomosRuntimeFailureKind,
|
|
14
|
+
NomosUnavailable,
|
|
15
|
+
nomosErrorFromInfo;
|
|
9
16
|
|
|
10
17
|
import 'runtime_v2_transport.dart';
|
|
11
18
|
|
|
@@ -16,12 +23,11 @@ final class NmsRuntimeV2Gateway {
|
|
|
16
23
|
NmsRuntimeV2Gateway(this._transport);
|
|
17
24
|
|
|
18
25
|
final NomosRuntimeV2Transport _transport;
|
|
19
|
-
int _sequence = 0;
|
|
20
26
|
|
|
21
27
|
Future<nomos_protocol.RuntimeResponse> invoke(
|
|
22
28
|
nomos_protocol.RuntimeRequest request,
|
|
23
29
|
) async {
|
|
24
|
-
final requestId = fixnum.Int64(
|
|
30
|
+
final requestId = fixnum.Int64(_transport.nextRuntimeRequestId());
|
|
25
31
|
request
|
|
26
32
|
..protocolVersion = 2
|
|
27
33
|
..requestId = requestId;
|
|
@@ -36,8 +42,55 @@ final class NmsRuntimeV2Gateway {
|
|
|
36
42
|
'Runtime v2 response ${response.requestId} belongs to another request',
|
|
37
43
|
);
|
|
38
44
|
}
|
|
45
|
+
if (response.hasUnavailable()) {
|
|
46
|
+
throw NomosUnavailable.fromRuntime(response.unavailable);
|
|
47
|
+
}
|
|
48
|
+
if (response.hasRefused()) {
|
|
49
|
+
final refused = response.refused;
|
|
50
|
+
throw NomosRefused(
|
|
51
|
+
reason: switch (refused.reason) {
|
|
52
|
+
nomos_protocol.RuntimeRefusalReason
|
|
53
|
+
.RUNTIME_REFUSAL_REASON_INVALID_CREDENTIAL =>
|
|
54
|
+
NomosRefusalReason.invalidCredential,
|
|
55
|
+
nomos_protocol
|
|
56
|
+
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_ACCESS_DENIED =>
|
|
57
|
+
NomosRefusalReason.accessDenied,
|
|
58
|
+
nomos_protocol
|
|
59
|
+
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_PLACEMENT_POLICY =>
|
|
60
|
+
NomosRefusalReason.placementPolicy,
|
|
61
|
+
nomos_protocol
|
|
62
|
+
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_SESSION_UNBOUND =>
|
|
63
|
+
NomosRefusalReason.sessionUnbound,
|
|
64
|
+
_ => NomosRefusalReason.unknown,
|
|
65
|
+
},
|
|
66
|
+
workspace: refused.workspace,
|
|
67
|
+
retryable: refused.retryable,
|
|
68
|
+
detail: refused.detail,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
39
71
|
if (response.hasFailure()) {
|
|
40
72
|
final failure = response.failure;
|
|
73
|
+
if (failure.kind !=
|
|
74
|
+
nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_UNSPECIFIED) {
|
|
75
|
+
throw NomosRuntimeFailure(
|
|
76
|
+
kind: switch (failure.kind) {
|
|
77
|
+
nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_INTERNAL =>
|
|
78
|
+
NomosRuntimeFailureKind.internal,
|
|
79
|
+
nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_PROTOCOL =>
|
|
80
|
+
NomosRuntimeFailureKind.protocol,
|
|
81
|
+
nomos_protocol.RuntimeFailureKind.RUNTIME_FAILURE_KIND_INTEGRITY =>
|
|
82
|
+
NomosRuntimeFailureKind.integrity,
|
|
83
|
+
nomos_protocol
|
|
84
|
+
.RuntimeFailureKind.RUNTIME_FAILURE_KIND_UNSUPPORTED =>
|
|
85
|
+
NomosRuntimeFailureKind.unsupported,
|
|
86
|
+
_ => NomosRuntimeFailureKind.unknown,
|
|
87
|
+
},
|
|
88
|
+
code: failure.code,
|
|
89
|
+
message: failure.message,
|
|
90
|
+
component: failure.component,
|
|
91
|
+
workspace: failure.hasWorkspace() ? failure.workspace : null,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
41
94
|
throw nomosErrorFromInfo(
|
|
42
95
|
NomosErrorInfo(
|
|
43
96
|
code: failure.code,
|
|
@@ -2,16 +2,32 @@
|
|
|
2
2
|
library;
|
|
3
3
|
|
|
4
4
|
import 'dart:async';
|
|
5
|
+
import 'dart:convert';
|
|
6
|
+
import 'dart:developer' as developer;
|
|
7
|
+
import 'dart:typed_data';
|
|
5
8
|
|
|
6
9
|
import 'package:fixnum/fixnum.dart' as fixnum;
|
|
7
10
|
import 'package:meta/meta.dart' show internal;
|
|
11
|
+
import 'package:crypto/crypto.dart' show sha256;
|
|
12
|
+
import 'package:nomos_peer_host/nomos_peer_host.dart'
|
|
13
|
+
show
|
|
14
|
+
NomosDeviceIdentityStore,
|
|
15
|
+
NomosLawPackageSource,
|
|
16
|
+
NomosProviderCredentialStore,
|
|
17
|
+
NomosSignIn;
|
|
8
18
|
import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
|
|
19
|
+
import 'package:nomos_types/nomos_types.dart' show NomosUnavailable;
|
|
20
|
+
|
|
21
|
+
import 'console_enrolment.dart';
|
|
22
|
+
import 'console_http_stub.dart' if (dart.library.io) 'console_http_io.dart'
|
|
23
|
+
as console_http;
|
|
9
24
|
|
|
10
25
|
/// Binary generated-message path from an application host to the kernel.
|
|
11
26
|
abstract interface class NomosKernelCallTransport {
|
|
12
27
|
Future<nomos_protocol.CallResponse> callKernel({
|
|
13
28
|
required String session,
|
|
14
29
|
required nomos_protocol.CallRequest request,
|
|
30
|
+
required nomos_protocol.CredentialUse credentialUse,
|
|
15
31
|
});
|
|
16
32
|
}
|
|
17
33
|
|
|
@@ -21,6 +37,12 @@ abstract interface class NomosRuntimeV2Transport {
|
|
|
21
37
|
Future<nomos_protocol.RuntimeResponse> invokeRuntime(
|
|
22
38
|
nomos_protocol.RuntimeRequest request,
|
|
23
39
|
);
|
|
40
|
+
|
|
41
|
+
/// ONE request-id space per transport. Every caller that frames a Runtime v2 request over this
|
|
42
|
+
/// transport (the reception's gateway, direct kernel calls, the native plane) must draw its id
|
|
43
|
+
/// here; two independent counters on one transport collided in flight ("request N is already
|
|
44
|
+
/// pending") the moment a child workspace session opened beside its parent.
|
|
45
|
+
int nextRuntimeRequestId();
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
/// Generated asynchronous runtime event lane. It is separate from invocation
|
|
@@ -29,10 +51,519 @@ abstract interface class NomosRuntimeV2EventTransport {
|
|
|
29
51
|
Stream<nomos_protocol.RuntimeEvent> get runtimeEvents;
|
|
30
52
|
}
|
|
31
53
|
|
|
54
|
+
/// What a genuine cloud acquisition needs when the peer reports that its live
|
|
55
|
+
/// identity proof is no longer usable. The platform credential provider owns
|
|
56
|
+
/// how this is obtained; generated clients and Reception only receive the
|
|
57
|
+
/// refreshed proof selected for a runtime session.
|
|
58
|
+
final class NomosSessionCredentials {
|
|
59
|
+
const NomosSessionCredentials({
|
|
60
|
+
required this.cloud,
|
|
61
|
+
this.authToken,
|
|
62
|
+
this.authorSecret,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
final String cloud;
|
|
66
|
+
final String? authToken;
|
|
67
|
+
final String? authorSecret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Platform-shell capability supplying renewable identity proof to a peer.
|
|
71
|
+
///
|
|
72
|
+
/// This is deliberately not part of a generated application client and it is
|
|
73
|
+
/// not workspace authority. Firebase, an OS account provider, or another
|
|
74
|
+
/// platform service may produce a token; the kernel still decides whether the
|
|
75
|
+
/// proof is admissible. A child session aliases the same capability rather
|
|
76
|
+
/// than copying either its callback or its current token.
|
|
77
|
+
abstract interface class NomosPlatformCredentialProviders {
|
|
78
|
+
void registerCredentialProvider({
|
|
79
|
+
required String capability,
|
|
80
|
+
required String cloud,
|
|
81
|
+
required NomosSignIn signIn,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
Future<NomosSessionCredentials> Function({String? deviceChallenge})?
|
|
85
|
+
credentialProviderForCapability(String capability);
|
|
86
|
+
|
|
87
|
+
/// The shell's durable device identity store, answering the runtime's
|
|
88
|
+
/// load/save effect. A shell that registers none refuses that effect typed.
|
|
89
|
+
void registerDeviceIdentityStore(NomosDeviceIdentityStore store);
|
|
90
|
+
|
|
91
|
+
/// Register the platform's durable ID-token cache. Native shells use
|
|
92
|
+
/// Keychain/Keystore; a shell without one simply asks its provider.
|
|
93
|
+
void registerProviderCredentialStore(NomosProviderCredentialStore store);
|
|
94
|
+
|
|
95
|
+
/// The application's compiled law package by identity, answering the
|
|
96
|
+
/// runtime's law-package effect when a workspace must stage the package it
|
|
97
|
+
/// was released against. A shell that registers none refuses that effect
|
|
98
|
+
/// typed; the runtime reports the adoption deferred.
|
|
99
|
+
void registerLawPackageSource(NomosLawPackageSource source);
|
|
100
|
+
|
|
101
|
+
/// Replace the shell's console transport (D22 enrolment). Native shells
|
|
102
|
+
/// default to `dart:io`; a shell without it must register one.
|
|
103
|
+
void registerConsolePost(NomosConsolePost post);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
final class _NmsPlatformCredentialProvider {
|
|
107
|
+
_NmsPlatformCredentialProvider({required this.cloud, required this.signIn});
|
|
108
|
+
|
|
109
|
+
final String cloud;
|
|
110
|
+
final NomosSignIn signIn;
|
|
111
|
+
|
|
112
|
+
/// One flight per challenge: a burst of refreshes for the same proof shares
|
|
113
|
+
/// one provider call, while a device-bound request never rides a plain one.
|
|
114
|
+
final Map<String, Future<String>> _refreshFlights = {};
|
|
115
|
+
|
|
116
|
+
Future<NomosSessionCredentials> credentials({String? deviceChallenge}) async {
|
|
117
|
+
final key = deviceChallenge ?? '';
|
|
118
|
+
final token =
|
|
119
|
+
await (_refreshFlights[key] ?? _startRefresh(key, deviceChallenge));
|
|
120
|
+
return NomosSessionCredentials(cloud: cloud, authToken: token);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Future<String> _startRefresh(String key, String? deviceChallenge) {
|
|
124
|
+
final flight = () async {
|
|
125
|
+
final token =
|
|
126
|
+
(await signIn(deviceChallenge: deviceChallenge))?.trim() ?? '';
|
|
127
|
+
if (token.isEmpty) {
|
|
128
|
+
throw StateError(
|
|
129
|
+
'Nomos could not refresh the signed-in identity for this peer session',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return token;
|
|
133
|
+
}();
|
|
134
|
+
_refreshFlights[key] = flight;
|
|
135
|
+
return flight.whenComplete(() {
|
|
136
|
+
if (identical(_refreshFlights[key], flight)) _refreshFlights.remove(key);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Reusable platform-shell implementation. Concrete native, WebView, browser
|
|
142
|
+
/// and Node connections mix this in; Reception merely uses the capability.
|
|
143
|
+
/// The map is keyed by the opaque capability identity issued by Runtime v2.
|
|
144
|
+
/// Session-to-capability ownership remains exclusively in the peer runtime;
|
|
145
|
+
/// this platform adapter therefore never mirrors or inherits session state.
|
|
146
|
+
mixin NmsPlatformCredentialProviderHost
|
|
147
|
+
implements NomosPlatformCredentialProviders {
|
|
148
|
+
final Map<String, _NmsPlatformCredentialProvider>
|
|
149
|
+
_nmsCredentialProvidersByCapability = {};
|
|
150
|
+
NomosDeviceIdentityStore? _nmsDeviceIdentityStore;
|
|
151
|
+
NomosProviderCredentialStore? _nmsProviderCredentialStore;
|
|
152
|
+
|
|
153
|
+
@override
|
|
154
|
+
void registerCredentialProvider({
|
|
155
|
+
required String capability,
|
|
156
|
+
required String cloud,
|
|
157
|
+
required NomosSignIn signIn,
|
|
158
|
+
}) {
|
|
159
|
+
_nmsCredentialProvidersByCapability[capability] =
|
|
160
|
+
_NmsPlatformCredentialProvider(cloud: cloud, signIn: signIn);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@override
|
|
164
|
+
Future<NomosSessionCredentials> Function({String? deviceChallenge})?
|
|
165
|
+
credentialProviderForCapability(String capability) {
|
|
166
|
+
final provider = _nmsCredentialProvidersByCapability[capability];
|
|
167
|
+
return provider == null ? null : provider.credentials;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@override
|
|
171
|
+
void registerDeviceIdentityStore(NomosDeviceIdentityStore store) {
|
|
172
|
+
_nmsDeviceIdentityStore = store;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@override
|
|
176
|
+
void registerProviderCredentialStore(NomosProviderCredentialStore store) {
|
|
177
|
+
_nmsProviderCredentialStore = store;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
NomosLawPackageSource? _nmsLawPackageSource;
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
void registerLawPackageSource(NomosLawPackageSource source) {
|
|
184
|
+
_nmsLawPackageSource = source;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
NomosConsolePost _nmsConsolePost = console_http.nomosDefaultConsolePost;
|
|
188
|
+
|
|
189
|
+
@override
|
|
190
|
+
void registerConsolePost(NomosConsolePost post) {
|
|
191
|
+
_nmsConsolePost = post;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// D22/D23: enrol this device's key with the application's console enroller.
|
|
195
|
+
/// Peer Office decides when and how often; this performs one attempt. The
|
|
196
|
+
/// provider is asked for a FRESH plain identity token (no device challenge,
|
|
197
|
+
/// never the durable cache: the console spends each token once), the request
|
|
198
|
+
/// is signed with the device secret this shell's identity store holds, and
|
|
199
|
+
/// the token goes only to the console. Neither the token nor the secret is
|
|
200
|
+
/// returned to the runtime.
|
|
201
|
+
Future<nomos_protocol.PeerEffectReceipt> performConsoleEnrolment(
|
|
202
|
+
nomos_protocol.PeerConsoleEnrolmentEffect effect,
|
|
203
|
+
) async {
|
|
204
|
+
nomos_protocol.PeerEffectReceipt failed(String message) =>
|
|
205
|
+
nomos_protocol.PeerEffectReceipt(
|
|
206
|
+
effectId: effect.effectId,
|
|
207
|
+
failure: nomos_protocol.PeerEffectFailure(
|
|
208
|
+
reason: nomos_protocol
|
|
209
|
+
.PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
|
|
210
|
+
message: message,
|
|
211
|
+
),
|
|
212
|
+
);
|
|
213
|
+
nomos_protocol.PeerEffectReceipt refused(int status, String message,
|
|
214
|
+
{String code = '', bool? retryable}) =>
|
|
215
|
+
nomos_protocol.PeerEffectReceipt(
|
|
216
|
+
effectId: effect.effectId,
|
|
217
|
+
consoleEnrolmentRefused: nomos_protocol.PeerConsoleEnrolmentRefused(
|
|
218
|
+
status: status,
|
|
219
|
+
message: message,
|
|
220
|
+
code: code,
|
|
221
|
+
retryable: retryable,
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
final store = _nmsDeviceIdentityStore;
|
|
225
|
+
if (store == null) {
|
|
226
|
+
return failed(
|
|
227
|
+
'this platform shell keeps no durable device identity store');
|
|
228
|
+
}
|
|
229
|
+
final provider = _nmsCredentialProvidersByCapability[effect.capability];
|
|
230
|
+
if (provider == null) {
|
|
231
|
+
return failed(
|
|
232
|
+
'this platform shell has no identity provider for the session');
|
|
233
|
+
}
|
|
234
|
+
final Uint8List? secret;
|
|
235
|
+
try {
|
|
236
|
+
secret = await store.load(effect.deviceIdentity);
|
|
237
|
+
} catch (_) {
|
|
238
|
+
return failed(
|
|
239
|
+
'the platform device identity store could not load the device identity');
|
|
240
|
+
}
|
|
241
|
+
if (secret == null || secret.length != 32) {
|
|
242
|
+
return failed('this device holds no usable identity to enrol');
|
|
243
|
+
}
|
|
244
|
+
final String token;
|
|
245
|
+
try {
|
|
246
|
+
token = (await provider.signIn(deviceChallenge: null))?.trim() ?? '';
|
|
247
|
+
} catch (_) {
|
|
248
|
+
return failed('the identity provider could not sign this person in');
|
|
249
|
+
}
|
|
250
|
+
if (token.isEmpty) {
|
|
251
|
+
return failed('the identity provider returned no identity token');
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
final enrolled = await enrolWithConsole(
|
|
255
|
+
console: effect.console,
|
|
256
|
+
tenant: effect.tenant,
|
|
257
|
+
scope: effect.scope,
|
|
258
|
+
providerToken: token,
|
|
259
|
+
device: NomosConsoleEnrolmentDevice.fromSeed(secret),
|
|
260
|
+
workspaces: effect.workspaces,
|
|
261
|
+
post: _nmsConsolePost,
|
|
262
|
+
);
|
|
263
|
+
// The console's fan-out into the person's other workspaces: what it
|
|
264
|
+
// could not enrol stays visible (those workspaces refuse this device's
|
|
265
|
+
// writes until an enrolment lands there).
|
|
266
|
+
for (final entry in enrolled.refused.entries) {
|
|
267
|
+
developer.log(
|
|
268
|
+
'console enrolment into ${entry.key} was refused: ${entry.value}',
|
|
269
|
+
name: 'nomos.console_enrolment');
|
|
270
|
+
}
|
|
271
|
+
return nomos_protocol.PeerEffectReceipt(
|
|
272
|
+
effectId: effect.effectId,
|
|
273
|
+
consoleEnrolled: nomos_protocol.PeerConsoleEnrolled(
|
|
274
|
+
principal: enrolled.principal,
|
|
275
|
+
keyHash: enrolled.keyHash,
|
|
276
|
+
heads: enrolled.workspaces.entries,
|
|
277
|
+
),
|
|
278
|
+
);
|
|
279
|
+
} on NomosConsoleEnrolmentRefused catch (refusal) {
|
|
280
|
+
return refused(refusal.status, refusal.message,
|
|
281
|
+
code: refusal.code, retryable: refusal.retryable);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
// Never echo the request: it carries the token.
|
|
284
|
+
return failed('the console could not be reached (${error.runtimeType})');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// Hand the runtime the application's law package for the identity it
|
|
289
|
+
/// names. The runtime decides when a package is needed (a workspace that
|
|
290
|
+
/// must stage the approved model); this only performs the physical read.
|
|
291
|
+
/// A shell without a source, or without that package, answers typed.
|
|
292
|
+
Future<nomos_protocol.PeerEffectReceipt> performLawPackageEffect(
|
|
293
|
+
nomos_protocol.PeerLawPackageEffect effect,
|
|
294
|
+
) async {
|
|
295
|
+
nomos_protocol.PeerEffectReceipt refused(String message) =>
|
|
296
|
+
nomos_protocol.PeerEffectReceipt(
|
|
297
|
+
effectId: effect.effectId,
|
|
298
|
+
failure: nomos_protocol.PeerEffectFailure(
|
|
299
|
+
reason: nomos_protocol
|
|
300
|
+
.PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
|
|
301
|
+
message: message,
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
final source = _nmsLawPackageSource;
|
|
305
|
+
if (source == null) {
|
|
306
|
+
return refused(
|
|
307
|
+
'this platform shell supplies no application law packages');
|
|
308
|
+
}
|
|
309
|
+
if (!RegExp(r'^[0-9a-f]{64}$').hasMatch(effect.lawSha256)) {
|
|
310
|
+
return refused('law package effect names no package identity');
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
final bytes = await source.load(effect.lawSha256);
|
|
314
|
+
if (bytes == null || bytes.isEmpty) {
|
|
315
|
+
return refused(
|
|
316
|
+
'this application holds no law package with identity ${effect.lawSha256}');
|
|
317
|
+
}
|
|
318
|
+
return nomos_protocol.PeerEffectReceipt(
|
|
319
|
+
effectId: effect.effectId,
|
|
320
|
+
lawPackage: nomos_protocol.PeerLawPackage(lawUsdz: bytes),
|
|
321
|
+
);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return refused('the application law package could not be read: $error');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// Execute the opaque platform capability requested by Peer Office. The
|
|
328
|
+
/// runtime owns the decision to refresh (a refusal, or renewal ahead of
|
|
329
|
+
/// expiry), the device challenge it carries, and any subsequent offer retry.
|
|
330
|
+
Future<nomos_protocol.PeerEffectReceipt> performCredentialRefresh(
|
|
331
|
+
nomos_protocol.PeerCredentialRefreshEffect effect,
|
|
332
|
+
) async {
|
|
333
|
+
try {
|
|
334
|
+
final registered = _nmsCredentialProvidersByCapability[effect.capability];
|
|
335
|
+
final provider = registered?.credentials;
|
|
336
|
+
final challenge =
|
|
337
|
+
effect.hasDeviceChallenge() ? effect.deviceChallenge : null;
|
|
338
|
+
final entry = registered == null
|
|
339
|
+
? null
|
|
340
|
+
: sha256
|
|
341
|
+
.convert(utf8.encode(
|
|
342
|
+
'nomos-provider-credential-v1\u0000${registered.cloud}'
|
|
343
|
+
'\u0000${effect.capability}\u0000${challenge ?? ''}',
|
|
344
|
+
))
|
|
345
|
+
.toString();
|
|
346
|
+
String? token;
|
|
347
|
+
final durable = _nmsProviderCredentialStore;
|
|
348
|
+
if (effect.allowCached && durable != null && entry != null) {
|
|
349
|
+
try {
|
|
350
|
+
final rejected = effect.hasRejectedAuthTokenSha256()
|
|
351
|
+
? effect.rejectedAuthTokenSha256
|
|
352
|
+
: const <int>[];
|
|
353
|
+
for (final candidate in await durable.loadCandidates(entry)) {
|
|
354
|
+
final value = candidate.trim();
|
|
355
|
+
if (value.isEmpty) continue;
|
|
356
|
+
final digest = sha256.convert(utf8.encode(value)).bytes;
|
|
357
|
+
if (rejected.length == digest.length) {
|
|
358
|
+
var same = true;
|
|
359
|
+
for (var i = 0; i < digest.length; i += 1) {
|
|
360
|
+
if (rejected[i] != digest[i]) {
|
|
361
|
+
same = false;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (same) continue;
|
|
366
|
+
}
|
|
367
|
+
token = value;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
} catch (_) {
|
|
371
|
+
// A damaged/unavailable cache never blocks the provider. The store
|
|
372
|
+
// exposes no token material through diagnostics.
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
NomosSessionCredentials? credentials;
|
|
376
|
+
if (token == null && provider != null) {
|
|
377
|
+
credentials = await provider(deviceChallenge: challenge);
|
|
378
|
+
token = credentials.authToken?.trim();
|
|
379
|
+
if (token != null &&
|
|
380
|
+
token.isNotEmpty &&
|
|
381
|
+
durable != null &&
|
|
382
|
+
entry != null) {
|
|
383
|
+
try {
|
|
384
|
+
await durable.retainCandidate(entry, token);
|
|
385
|
+
} catch (_) {
|
|
386
|
+
// The fresh proof remains usable for this process even when its
|
|
387
|
+
// durable cache cannot be updated.
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
final secret = credentials?.authorSecret?.trim();
|
|
392
|
+
return nomos_protocol.PeerEffectReceipt(
|
|
393
|
+
effectId: effect.effectId,
|
|
394
|
+
credentialRefreshed: nomos_protocol.PeerCredentialRefreshed(
|
|
395
|
+
authToken: token == null || token.isEmpty ? null : utf8.encode(token),
|
|
396
|
+
authorSecret:
|
|
397
|
+
secret == null || secret.isEmpty ? null : base64Decode(secret),
|
|
398
|
+
),
|
|
399
|
+
);
|
|
400
|
+
} catch (_) {
|
|
401
|
+
return nomos_protocol.PeerEffectReceipt(
|
|
402
|
+
effectId: effect.effectId,
|
|
403
|
+
failure: nomos_protocol.PeerEffectFailure(
|
|
404
|
+
reason: nomos_protocol
|
|
405
|
+
.PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
|
|
406
|
+
message:
|
|
407
|
+
'Platform credential provider could not refresh identity proof',
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/// Move one identity's secret between the runtime and this shell's durable
|
|
414
|
+
/// store. The runtime owns the sequence (load before the first signed offer,
|
|
415
|
+
/// save once a secret exists); this only performs the named physical effect.
|
|
416
|
+
/// A shell without a store answers typed; it never invents a secret.
|
|
417
|
+
Future<nomos_protocol.PeerEffectReceipt> performDeviceIdentityEffect(
|
|
418
|
+
nomos_protocol.PeerDeviceIdentityEffect effect,
|
|
419
|
+
) async {
|
|
420
|
+
final store = _nmsDeviceIdentityStore;
|
|
421
|
+
nomos_protocol.PeerEffectReceipt refused(String message) =>
|
|
422
|
+
nomos_protocol.PeerEffectReceipt(
|
|
423
|
+
effectId: effect.effectId,
|
|
424
|
+
failure: nomos_protocol.PeerEffectFailure(
|
|
425
|
+
reason: nomos_protocol
|
|
426
|
+
.PeerEffectFailureReason.PEER_EFFECT_FAILURE_REASON_TRANSPORT,
|
|
427
|
+
message: message,
|
|
428
|
+
),
|
|
429
|
+
);
|
|
430
|
+
if (store == null) {
|
|
431
|
+
return refused(
|
|
432
|
+
'this platform shell keeps no durable device identity store');
|
|
433
|
+
}
|
|
434
|
+
if (effect.identity.isEmpty) {
|
|
435
|
+
return refused('device identity effect names no identity');
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
Uint8List? secret;
|
|
439
|
+
nomos_protocol.DeviceRecoveryRecord? recovery;
|
|
440
|
+
if (effect.hasLoad()) {
|
|
441
|
+
secret = await store.load(effect.identity);
|
|
442
|
+
// Dart's protobuf bytes field elides empty values. Refuse corruption
|
|
443
|
+
// here so it cannot cross the wire disguised as an absent identity.
|
|
444
|
+
if (secret != null && secret.length != 32) {
|
|
445
|
+
return refused('the stored device identity is malformed');
|
|
446
|
+
}
|
|
447
|
+
} else if (effect.hasSave()) {
|
|
448
|
+
await store.save(
|
|
449
|
+
effect.identity, Uint8List.fromList(effect.save.secret));
|
|
450
|
+
} else if (effect.hasLoadRecovery()) {
|
|
451
|
+
final bytes =
|
|
452
|
+
await store.load('nomos-device-recovery-v1|${effect.identity}');
|
|
453
|
+
if (bytes != null) {
|
|
454
|
+
recovery = nomos_protocol.DeviceRecoveryRecord.fromBuffer(bytes);
|
|
455
|
+
}
|
|
456
|
+
} else if (effect.hasSaveRecovery()) {
|
|
457
|
+
if (!effect.saveRecovery.hasRecord()) {
|
|
458
|
+
return refused('device recovery save contains no record');
|
|
459
|
+
}
|
|
460
|
+
await store.save('nomos-device-recovery-v1|${effect.identity}',
|
|
461
|
+
effect.saveRecovery.record.writeToBuffer());
|
|
462
|
+
} else {
|
|
463
|
+
return refused('device identity effect has no operation');
|
|
464
|
+
}
|
|
465
|
+
return nomos_protocol.PeerEffectReceipt(
|
|
466
|
+
effectId: effect.effectId,
|
|
467
|
+
deviceIdentity: nomos_protocol.PeerDeviceIdentity(
|
|
468
|
+
secret: secret,
|
|
469
|
+
recovery: recovery,
|
|
470
|
+
),
|
|
471
|
+
);
|
|
472
|
+
} catch (_) {
|
|
473
|
+
// Never print store exceptions: they may name the key path.
|
|
474
|
+
return refused(
|
|
475
|
+
'the platform device identity store could not ${effect.hasSave() ? 'save' : 'load'} the device identity');
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
Future<nomos_protocol.RuntimeResponse> driveCredentialEffects(
|
|
480
|
+
nomos_protocol.RuntimeRequest request,
|
|
481
|
+
Future<nomos_protocol.RuntimeResponse> Function(
|
|
482
|
+
nomos_protocol.RuntimeRequest)
|
|
483
|
+
invokeFrame,
|
|
484
|
+
) async {
|
|
485
|
+
var next = request;
|
|
486
|
+
for (;;) {
|
|
487
|
+
final response = await invokeFrame(next);
|
|
488
|
+
if (response.requestId != request.requestId ||
|
|
489
|
+
response.protocolVersion != 2) {
|
|
490
|
+
throw StateError(
|
|
491
|
+
'Credential effect response belongs to another runtime request');
|
|
492
|
+
}
|
|
493
|
+
if (!response.hasCorrespondencePending() ||
|
|
494
|
+
(!response.correspondencePending.hasRefreshCredential() &&
|
|
495
|
+
!response.correspondencePending.hasDeviceIdentity() &&
|
|
496
|
+
!response.correspondencePending.hasLawPackage() &&
|
|
497
|
+
!response.correspondencePending.hasConsoleEnrolment() &&
|
|
498
|
+
!response.correspondencePending.hasWake())) return response;
|
|
499
|
+
final pending = response.correspondencePending;
|
|
500
|
+
final nomos_protocol.PeerEffectReceipt receipt;
|
|
501
|
+
if (pending.hasDeviceIdentity()) {
|
|
502
|
+
receipt = await performDeviceIdentityEffect(pending.deviceIdentity);
|
|
503
|
+
} else if (pending.hasLawPackage()) {
|
|
504
|
+
receipt = await performLawPackageEffect(pending.lawPackage);
|
|
505
|
+
} else if (pending.hasConsoleEnrolment()) {
|
|
506
|
+
receipt = await performConsoleEnrolment(pending.consoleEnrolment);
|
|
507
|
+
} else if (pending.hasWake()) {
|
|
508
|
+
// The shared runtime chooses the delay and checks its own clock.
|
|
509
|
+
// Waiting outside the serial Node frame lane keeps it available for
|
|
510
|
+
// credential-provider calls while authentication is pending.
|
|
511
|
+
await Future<void>.delayed(
|
|
512
|
+
Duration(milliseconds: pending.wake.delayMillis));
|
|
513
|
+
receipt = nomos_protocol.PeerEffectReceipt(
|
|
514
|
+
effectId: pending.wake.effectId,
|
|
515
|
+
woken: nomos_protocol.PeerWoken(),
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
receipt = await performCredentialRefresh(pending.refreshCredential);
|
|
519
|
+
}
|
|
520
|
+
next = nomos_protocol.RuntimeRequest(
|
|
521
|
+
protocolVersion: 2,
|
|
522
|
+
requestId: request.requestId,
|
|
523
|
+
continueCorrespondence: nomos_protocol.ContinueCorrespondenceRequest(
|
|
524
|
+
workId: pending.workId,
|
|
525
|
+
receipt: receipt,
|
|
526
|
+
),
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
bool consumeCredentialRefreshEvent(
|
|
532
|
+
nomos_protocol.RuntimeEvent event,
|
|
533
|
+
NomosRuntimeV2Transport transport,
|
|
534
|
+
void Function(Object, StackTrace) onError,
|
|
535
|
+
) {
|
|
536
|
+
if (!event.hasCredentialRefreshRequested()) return false;
|
|
537
|
+
final pending = event.credentialRefreshRequested.pending;
|
|
538
|
+
if (!pending.hasRefreshCredential()) {
|
|
539
|
+
throw StateError('Credential refresh event omitted its physical effect');
|
|
540
|
+
}
|
|
541
|
+
Future<void> complete() async {
|
|
542
|
+
final receipt = await performCredentialRefresh(pending.refreshCredential);
|
|
543
|
+
final response =
|
|
544
|
+
await transport.invokeRuntime(nomos_protocol.RuntimeRequest(
|
|
545
|
+
protocolVersion: 2,
|
|
546
|
+
requestId: fixnum.Int64(transport.nextRuntimeRequestId()),
|
|
547
|
+
continueCorrespondence: nomos_protocol.ContinueCorrespondenceRequest(
|
|
548
|
+
workId: pending.workId,
|
|
549
|
+
receipt: receipt,
|
|
550
|
+
),
|
|
551
|
+
));
|
|
552
|
+
if (response.hasFailure()) {
|
|
553
|
+
throw StateError(
|
|
554
|
+
'${response.failure.code}: ${response.failure.message}');
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
unawaited(complete().catchError(onError));
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
32
563
|
/// A level-triggered generated event stream. Runtime readiness can arrive while
|
|
33
|
-
/// a WebView script is loading, before
|
|
564
|
+
/// a WebView script is loading, before Reception exists; a broadcast stream
|
|
34
565
|
/// would silently discard it and turn a successful boot into an infinite wait.
|
|
35
|
-
/// Buffering ends when
|
|
566
|
+
/// Buffering ends when Reception first subscribes, after which events remain a
|
|
36
567
|
/// normal broadcast stream.
|
|
37
568
|
final class NmsBufferedRuntimeEvents {
|
|
38
569
|
NmsBufferedRuntimeEvents() {
|
|
@@ -83,12 +614,16 @@ mixin NomosRuntimeV2KernelCalls
|
|
|
83
614
|
implements NomosRuntimeV2Transport, NomosKernelCallTransport {
|
|
84
615
|
int _nmsRuntimeRequestSequence = 0;
|
|
85
616
|
|
|
617
|
+
@override
|
|
618
|
+
int nextRuntimeRequestId() => ++_nmsRuntimeRequestSequence;
|
|
619
|
+
|
|
86
620
|
@override
|
|
87
621
|
Future<nomos_protocol.CallResponse> callKernel({
|
|
88
622
|
required String session,
|
|
89
623
|
required nomos_protocol.CallRequest request,
|
|
624
|
+
required nomos_protocol.CredentialUse credentialUse,
|
|
90
625
|
}) async {
|
|
91
|
-
final requestId = fixnum.Int64(
|
|
626
|
+
final requestId = fixnum.Int64(nextRuntimeRequestId());
|
|
92
627
|
final response = await invokeRuntime(
|
|
93
628
|
nomos_protocol.RuntimeRequest(
|
|
94
629
|
protocolVersion: 2,
|
|
@@ -96,6 +631,7 @@ mixin NomosRuntimeV2KernelCalls
|
|
|
96
631
|
kernelCall: nomos_protocol.KernelCallRequest(
|
|
97
632
|
session: session,
|
|
98
633
|
request: request,
|
|
634
|
+
credentialUse: credentialUse,
|
|
99
635
|
),
|
|
100
636
|
),
|
|
101
637
|
);
|
|
@@ -107,6 +643,11 @@ mixin NomosRuntimeV2KernelCalls
|
|
|
107
643
|
if (response.hasFailure()) {
|
|
108
644
|
throw StateError('${response.failure.code}: ${response.failure.message}');
|
|
109
645
|
}
|
|
646
|
+
// Typed, as on the native transport: a copy still being fetched, opened or indexed is a
|
|
647
|
+
// retryable wait that reads sit out (replica_readiness.dart), never a bare error.
|
|
648
|
+
if (response.hasUnavailable()) {
|
|
649
|
+
throw NomosUnavailable.fromRuntime(response.unavailable);
|
|
650
|
+
}
|
|
110
651
|
if (!response.hasKernelCall()) {
|
|
111
652
|
throw StateError(
|
|
112
653
|
'Runtime v2 returned ${response.whichOutcome().name} for a kernel call',
|