@githolon/dsl 0.108.7 → 0.110.1
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 +80 -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 +214 -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 +268 -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 +23950 -15776
- 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 +550 -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_event_desk_close_test.dart +16 -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 +1276 -0
- package/src/codegen_call_usda.ts +49 -6
- package/src/codegen_dart.ts +83 -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 +2017 -1195
- 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
|
@@ -42,24 +42,31 @@
|
|
|
42
42
|
* only revokes keys of its OWN identity).
|
|
43
43
|
*/
|
|
44
44
|
import { z } from "zod";
|
|
45
|
-
import { birthCertificatePayload, type BirthCertificatePayload } from "./birth_certificate_law.js";
|
|
45
|
+
import { BirthDelegationCertificate, birthCertificatePayload, type BirthCertificatePayload } from "./birth_certificate_law.js";
|
|
46
46
|
import { aggregate, instance, type BoundAggregate } from "../aggregate.js";
|
|
47
47
|
import { t } from "../fields.js";
|
|
48
|
-
import { Lww } from "../drivers.js";
|
|
48
|
+
import { FirstWriterWins, Lww, MapOf } from "../drivers.js";
|
|
49
49
|
import { directive } from "../directive.js";
|
|
50
|
-
import { set, withMarker, type FieldOp, type PlannedOp } from "../ops.js";
|
|
50
|
+
import { set, setEntry, withMarker, type FieldOp, type PlannedOp } from "../ops.js";
|
|
51
51
|
import { query } from "../query.js";
|
|
52
52
|
import { relationType, self, key } from "../relation_schema.js";
|
|
53
53
|
import { readRef, readSet, workspaceInvariant, ref } from "./workspace_invariant.js";
|
|
54
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
endorsementOf,
|
|
56
|
+
type AttributionPrincipal,
|
|
57
|
+
type EndorsedAuthority,
|
|
58
|
+
} from "../principal_evidence.js";
|
|
55
59
|
import {
|
|
56
60
|
writeTuple,
|
|
57
61
|
removeTuple,
|
|
58
62
|
RelationTuple,
|
|
63
|
+
relationTuplesByObject,
|
|
59
64
|
relationTupleInstanceId,
|
|
60
65
|
} from "./relations.js";
|
|
61
66
|
import { MODEL_LIFECYCLE_AUTHORITY } from "./domain_lifecycle.js";
|
|
62
67
|
import { readFrameworkDeclaredRows } from "../read.js";
|
|
68
|
+
import { formerAggregate, retirement } from "../evolution.js";
|
|
69
|
+
import { valueObject } from "../value_object.js";
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
72
|
* The framework `identity` relation schema — the `signer` relation, LEAF-ONLY
|
|
@@ -91,6 +98,55 @@ export const ADMISSION_CHECKPOINT_PRINCIPAL = "workspace:admission-peer";
|
|
|
91
98
|
|
|
92
99
|
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/, "expected lowercase sha256 hex (the device pubkey hash)");
|
|
93
100
|
|
|
101
|
+
/** Public, typed disclosure of one active admission workload key. Parents carry this exact shape
|
|
102
|
+
* into child law; the corresponding secret remains runtime custody and never enters a domain. */
|
|
103
|
+
export const admissionCheckpointSigner = z.object({
|
|
104
|
+
keyHash: sha256Hex,
|
|
105
|
+
validFromEpoch: z.number().int().nonnegative(),
|
|
106
|
+
grantedAt: z.string().min(1),
|
|
107
|
+
}).strict();
|
|
108
|
+
export type AdmissionCheckpointSigner = z.infer<typeof admissionCheckpointSigner>;
|
|
109
|
+
|
|
110
|
+
/** Read the current workspace's visible admission workload grants through the framework's declared
|
|
111
|
+
* relation index. This is the law-side twin of the kernel's replay verifier, from the same tuple. */
|
|
112
|
+
export function readAdmissionCheckpointSigners(): AdmissionCheckpointSigner[] {
|
|
113
|
+
const rows = readFrameworkDeclaredRows(
|
|
114
|
+
relationTuplesByObject,
|
|
115
|
+
RelationTuple,
|
|
116
|
+
{ object: signerObject(ADMISSION_CHECKPOINT_PRINCIPAL) },
|
|
117
|
+
);
|
|
118
|
+
const signers: AdmissionCheckpointSigner[] = [];
|
|
119
|
+
for (const row of rows) {
|
|
120
|
+
if (row.relation !== SIGNER_RELATION || row.subjects === undefined) continue;
|
|
121
|
+
for (const [subject, leaf] of Object.entries(row.subjects)) {
|
|
122
|
+
if (!subject.startsWith("key:") || leaf.status !== "active") continue;
|
|
123
|
+
signers.push(admissionCheckpointSigner.parse({
|
|
124
|
+
keyHash: subject.slice("key:".length),
|
|
125
|
+
validFromEpoch: leaf.validFromEpoch,
|
|
126
|
+
grantedAt: leaf.grantedAt,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return signers.sort((left, right) => left.keyHash.localeCompare(right.keyHash));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Lower a parent's typed signer disclosure into the child's ordinary relation state. The caller's
|
|
134
|
+
* directive still decides authority; this helper supplies no gate and owns no secret. */
|
|
135
|
+
export function writeAdmissionCheckpointSigners(
|
|
136
|
+
signers: readonly AdmissionCheckpointSigner[],
|
|
137
|
+
grantedBy: string | AttributionPrincipal,
|
|
138
|
+
): PlannedOp[] {
|
|
139
|
+
return signers.flatMap((signer) => writeTuple({
|
|
140
|
+
object: signerObject(ADMISSION_CHECKPOINT_PRINCIPAL),
|
|
141
|
+
relation: SIGNER_RELATION,
|
|
142
|
+
subject: `key:${signer.keyHash}`,
|
|
143
|
+
grantedBy,
|
|
144
|
+
grantedAt: signer.grantedAt,
|
|
145
|
+
validFromEpoch: signer.validFromEpoch,
|
|
146
|
+
verifiedVia: "workspace-law",
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
|
|
94
150
|
// ─────────────────────────────── PHASE 0 — SignerEpoch ───────────────────────────────
|
|
95
151
|
|
|
96
152
|
/** The deterministic singleton id of the per-workspace signer epoch — one per chain. */
|
|
@@ -251,30 +307,78 @@ export const enrollSigner = directive("enrollSigner")
|
|
|
251
307
|
* the secret alone grants nothing. The key certifies that this workspace admitted an exact causal
|
|
252
308
|
* transition; the target directive's independent Zanzibar requirement remains business authority.
|
|
253
309
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
310
|
+
function admissionCheckpointEnrollment(id: string) {
|
|
311
|
+
return directive(id)
|
|
312
|
+
.ensures(RelationTuple)
|
|
313
|
+
.payload(
|
|
314
|
+
z.object({
|
|
315
|
+
keyHash: sha256Hex,
|
|
316
|
+
validFromEpoch: z.number().int().nonnegative(),
|
|
317
|
+
grantedAt: z.string().min(1),
|
|
318
|
+
}).strict(),
|
|
319
|
+
)
|
|
320
|
+
.plan((p, ctx) =>
|
|
321
|
+
writeTuple({
|
|
322
|
+
object: signerObject(ADMISSION_CHECKPOINT_PRINCIPAL),
|
|
323
|
+
relation: SIGNER_RELATION,
|
|
324
|
+
subject: `key:${p.keyHash}`,
|
|
325
|
+
grantedBy: ctx.principal().asAttribution(),
|
|
326
|
+
grantedAt: p.grantedAt,
|
|
327
|
+
validFromEpoch: p.validFromEpoch,
|
|
328
|
+
verifiedVia: "workspace-law",
|
|
329
|
+
}),
|
|
330
|
+
)
|
|
331
|
+
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" });
|
|
332
|
+
}
|
|
333
|
+
export const enrollAdmissionCheckpointSigner = admissionCheckpointEnrollment("_nmsEffectEnrollAdmissionCheckpointSigner");
|
|
334
|
+
/** The same enrolment, called directly by its public name: the platform governance law (root, platforms),
|
|
335
|
+
* which composes no application-home law, publishes it this way. */
|
|
336
|
+
export const enrollAdmissionCheckpointSignerDirect = admissionCheckpointEnrollment("enrollAdmissionCheckpointSigner");
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Retire one admission-checkpoint workload key: the custodian that held its secret no longer vouches this
|
|
340
|
+
* workspace's admissions (a custody move retires the old custodian's key). The same lifecycle authority
|
|
341
|
+
* that enrols a key revokes it; {@link lastAdmissionCheckpointSignerRemains} refuses leaving the workspace
|
|
342
|
+
* with no active checkpoint key, which would stop it receiving any workspace effect.
|
|
343
|
+
*/
|
|
344
|
+
export const REVOKE_ADMISSION_CHECKPOINT_SIGNER_DIRECTIVE = "revokeAdmissionCheckpointSigner" as const;
|
|
345
|
+
export const revokeAdmissionCheckpointSigner = directive(REVOKE_ADMISSION_CHECKPOINT_SIGNER_DIRECTIVE)
|
|
257
346
|
.ensures(RelationTuple)
|
|
258
347
|
.payload(
|
|
259
348
|
z.object({
|
|
260
349
|
keyHash: sha256Hex,
|
|
261
|
-
|
|
262
|
-
grantedAt: z.string().min(1),
|
|
350
|
+
revokedAt: z.string().min(1),
|
|
263
351
|
}).strict(),
|
|
264
352
|
)
|
|
265
353
|
.plan((p, ctx) =>
|
|
266
|
-
|
|
354
|
+
removeTuple({
|
|
267
355
|
object: signerObject(ADMISSION_CHECKPOINT_PRINCIPAL),
|
|
268
356
|
relation: SIGNER_RELATION,
|
|
269
357
|
subject: `key:${p.keyHash}`,
|
|
270
358
|
grantedBy: ctx.principal().asAttribution(),
|
|
271
|
-
grantedAt: p.
|
|
272
|
-
|
|
273
|
-
|
|
359
|
+
grantedAt: p.revokedAt,
|
|
360
|
+
revokedBy: ctx.principal().asAttribution(),
|
|
361
|
+
revokedAt: p.revokedAt,
|
|
274
362
|
}),
|
|
275
363
|
)
|
|
276
364
|
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" });
|
|
277
365
|
|
|
366
|
+
export const lastAdmissionCheckpointSignerRemains = workspaceInvariant("LastAdmissionCheckpointSignerRemains")
|
|
367
|
+
.on(REVOKE_ADMISSION_CHECKPOINT_SIGNER_DIRECTIVE)
|
|
368
|
+
.reads(readSet(
|
|
369
|
+
readRef("tuple", RelationTuple).pre()
|
|
370
|
+
.at(relationTupleInstanceId(signerObject(ADMISSION_CHECKPOINT_PRINCIPAL), SIGNER_RELATION)),
|
|
371
|
+
))
|
|
372
|
+
.assert(({ tuple }, ctx) => {
|
|
373
|
+
const leaving = `key:${String(((ctx?.intent ?? {}) as { keyHash?: string }).keyHash ?? "")}`;
|
|
374
|
+
const subjects = ((tuple as { subjects?: Record<string, { status?: string }> } | undefined)?.subjects) ?? {};
|
|
375
|
+
const active = Object.entries(subjects).filter(([, leaf]) => leaf?.status === "active").map(([subject]) => subject);
|
|
376
|
+
if (!active.includes(leaving)) return { reject: "checkpoint-key-not-active" };
|
|
377
|
+
return active.some((subject) => subject !== leaving)
|
|
378
|
+
? { accept: true }
|
|
379
|
+
: { reject: "last-checkpoint-key-cannot-be-revoked" };
|
|
380
|
+
});
|
|
381
|
+
|
|
278
382
|
/**
|
|
279
383
|
* THE SELF-BIND ENROLL (VA-1 / B2). A device enrolls a key for ITS OWN verified identity — the bound
|
|
280
384
|
* principal is `ctx.principal()` (the captured authoring principal), NOT a payload field, so a device can
|
|
@@ -335,31 +439,36 @@ export const enrollSignerSelfRequiresVerified = workspaceInvariant("EnrollSigner
|
|
|
335
439
|
|
|
336
440
|
// ─────────────────── THE ENROLL-ONCE DELEGATION CERT (the peer-sovereign root-of-trust) ───────────────────
|
|
337
441
|
|
|
338
|
-
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
442
|
+
/** Delegation evidence is retained independently of the live signer relation. New law
|
|
443
|
+
* appends whole signed certificates by device and signature; it never replaces another
|
|
444
|
+
* device's evidence. Read-only predecessor fields keep existing typed custody readable
|
|
445
|
+
* during law adoption. The kernel's new lookup is declared by the compiled law. */
|
|
446
|
+
/** The bounded, typed disclosure a parent workspace may carry when its law recovers a child device.
|
|
447
|
+
* Current cross-workspace law consumes only the nominal certificate fields and the exact signer key
|
|
448
|
+
* subject admitted by the parent. */
|
|
449
|
+
export const signerDelegationRecoveryEvidence = z.object({
|
|
450
|
+
user: z.string().min(1),
|
|
451
|
+
deviceKeySubject: z.string().regex(/^key:[0-9a-f]{64}$/),
|
|
452
|
+
enrolledAt: z.string().min(1),
|
|
453
|
+
certificate: BirthDelegationCertificate.zod,
|
|
454
|
+
}).strict();
|
|
455
|
+
export type SignerDelegationRecoveryEvidence = z.infer<
|
|
456
|
+
typeof signerDelegationRecoveryEvidence
|
|
457
|
+
>;
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
const SignerDelegationEvidence = valueObject(
|
|
461
|
+
"SignerDelegationEvidence", signerDelegationRecoveryEvidence.shape,
|
|
462
|
+
);
|
|
463
|
+
|
|
355
464
|
export const SIGNER_DELEGATION_PREFIX = "signer-delegation:";
|
|
356
465
|
export const SignerDelegation = aggregate("SignerDelegation", {
|
|
357
|
-
/**
|
|
466
|
+
/** Grow by certificate identity. Each entry is one complete signed nominal value. */
|
|
467
|
+
certificates: t.map(SignerDelegationEvidence.field()).merge(MapOf(FirstWriterWins)),
|
|
468
|
+
/** Principal whose certificate collection this is. */
|
|
358
469
|
user: t.string().merge(Lww),
|
|
359
|
-
/**
|
|
360
|
-
deviceKey: t.string().merge(Lww),
|
|
361
|
-
/** Legacy replay slot for histories authored before the typed certificate fields below. */
|
|
362
|
-
certJson: t.string().merge(Lww).optional(),
|
|
470
|
+
/** Read-only predecessor fields. Current enrollment/recovery plans never write these. */
|
|
471
|
+
deviceKey: t.string().merge(Lww).optional(),
|
|
363
472
|
certificateChild: t.string().merge(Lww).optional(),
|
|
364
473
|
certificateFounder: t.string().merge(Lww).optional(),
|
|
365
474
|
certificateVerified: t.bool().merge(Lww).optional(),
|
|
@@ -374,49 +483,44 @@ export const SignerDelegation = aggregate("SignerDelegation", {
|
|
|
374
483
|
certificateAuthProviderJwks: t.string().merge(Lww).optional(),
|
|
375
484
|
certificateAuthProviderAudience: t.string().merge(Lww).optional(),
|
|
376
485
|
/** ISO-8601, caller-stamped (determinism — no clock in the plan). */
|
|
377
|
-
enrolledAt: t.string().merge(Lww),
|
|
486
|
+
enrolledAt: t.string().merge(Lww).optional(),
|
|
378
487
|
}).public();
|
|
379
488
|
export type SignerDelegationHandle = typeof SignerDelegation;
|
|
380
489
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
user: z.string().min(1),
|
|
386
|
-
deviceKeySubject: z.string().regex(/^key:[0-9a-f]{64}$/),
|
|
387
|
-
enrolledAt: z.string().min(1),
|
|
388
|
-
certificate: birthCertificatePayload,
|
|
389
|
-
}).strict();
|
|
390
|
-
export type SignerDelegationRecoveryEvidence = z.infer<
|
|
391
|
-
typeof signerDelegationRecoveryEvidence
|
|
392
|
-
>;
|
|
490
|
+
const formerSignerDelegation = formerAggregate(
|
|
491
|
+
"SignerDelegation",
|
|
492
|
+
["certJson"] as const,
|
|
493
|
+
);
|
|
393
494
|
|
|
394
|
-
/** The
|
|
495
|
+
/** The old opaque certificate document has been replaced by the typed certificate
|
|
496
|
+
* members above. Carry the retirement in framework law so every composed domain
|
|
497
|
+
* proves the same transition without restoring a JSON compatibility slot. */
|
|
498
|
+
export const signerDelegationCertJsonRetired = retirement(
|
|
499
|
+
formerSignerDelegation.fields.certJson,
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
/** Law-owned address of a user's certificate collection. */
|
|
395
503
|
export function signerDelegationInstanceId(user: string): string {
|
|
396
504
|
return SIGNER_DELEGATION_PREFIX + user;
|
|
397
505
|
}
|
|
398
506
|
|
|
507
|
+
/** Both encodings are fixed by the certificate contract. The signed identity makes retries
|
|
508
|
+
* address the same entry, while renewals and different device keys occupy distinct entries. */
|
|
509
|
+
export function signerDelegationCertificateId(certificate: BirthCertificatePayload): string {
|
|
510
|
+
return `${certificate.parentKey}:${certificate.signature}`;
|
|
511
|
+
}
|
|
512
|
+
|
|
399
513
|
function writeDelegationCertificate(
|
|
400
514
|
delegation: BoundAggregate<typeof SignerDelegation.id, typeof SignerDelegation.fields>,
|
|
401
|
-
|
|
515
|
+
evidence: SignerDelegationRecoveryEvidence,
|
|
402
516
|
marker?: "ensures",
|
|
403
517
|
): PlannedOp[] {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
marked(set(delegation, "certificateParentId", certificate.parentId)),
|
|
411
|
-
marked(set(delegation, "certificateEpoch", certificate.epoch)),
|
|
412
|
-
marked(set(delegation, "certificateSignature", certificate.signature)),
|
|
413
|
-
...(certificate.verifiedVia ? [marked(set(delegation, "certificateVerifiedVia", certificate.verifiedVia))] : []),
|
|
414
|
-
...(certificate.idpToken ? [marked(set(delegation, "certificateIdpToken", certificate.idpToken))] : []),
|
|
415
|
-
...(certificate.capturedClock === undefined ? [] : [marked(set(delegation, "certificateCapturedClock", certificate.capturedClock))]),
|
|
416
|
-
...(certificate.authProviderIssuer ? [marked(set(delegation, "certificateAuthProviderIssuer", certificate.authProviderIssuer))] : []),
|
|
417
|
-
...(certificate.authProviderJwks ? [marked(set(delegation, "certificateAuthProviderJwks", certificate.authProviderJwks))] : []),
|
|
418
|
-
...(certificate.authProviderAudience ? [marked(set(delegation, "certificateAuthProviderAudience", certificate.authProviderAudience))] : []),
|
|
419
|
-
];
|
|
518
|
+
if (evidence.user !== evidence.certificate.founder) {
|
|
519
|
+
throw new Error("signer-delegation-evidence-must-bind-its-founder");
|
|
520
|
+
}
|
|
521
|
+
const operation = setEntry(delegation, "certificates",
|
|
522
|
+
signerDelegationCertificateId(evidence.certificate), evidence);
|
|
523
|
+
return [marker ? withMarker(operation, marker) : operation];
|
|
420
524
|
}
|
|
421
525
|
|
|
422
526
|
/** Materialise the exact typed delegation disclosed by a parent during device
|
|
@@ -432,9 +536,7 @@ export function writeSignerDelegationRecoveryEvidence(
|
|
|
432
536
|
);
|
|
433
537
|
return [
|
|
434
538
|
set(delegation, "user", evidence.user),
|
|
435
|
-
|
|
436
|
-
set(delegation, "enrolledAt", evidence.enrolledAt),
|
|
437
|
-
...writeDelegationCertificate(delegation, evidence.certificate),
|
|
539
|
+
...writeDelegationCertificate(delegation, evidence),
|
|
438
540
|
];
|
|
439
541
|
}
|
|
440
542
|
|
|
@@ -472,9 +574,6 @@ export const enrollFirstDevice = directive("enrollFirstDevice")
|
|
|
472
574
|
/** THE CAPTURED PREMISE: the IdP's signed identity assertion (JWT), re-verified on every lane against
|
|
473
575
|
* the on-chain AuthProvider keys. */
|
|
474
576
|
idpToken: z.string().min(1),
|
|
475
|
-
/** Legacy replay input for callers predating `delegationCertificate`. New callers must provide the
|
|
476
|
-
* typed certificate below. */
|
|
477
|
-
delegationCert: z.string().min(1).optional(),
|
|
478
577
|
/** THE KEYLESS DELEGATION CERT — the self-serve sibling of `enrollDelegation.certificate`. Built
|
|
479
578
|
* CLIENT-SIDE via the typed kernel sign operation: DEVICE-self-signed by this
|
|
480
579
|
* same device key `P_U` (parent_key == P_U), founder == `user:<uid>`, carrying `verifiedVia` +
|
|
@@ -506,28 +605,19 @@ export const enrollFirstDevice = directive("enrollFirstDevice")
|
|
|
506
605
|
});
|
|
507
606
|
// (2) fold the KEYLESS delegation cert as the user's `SignerDelegation` singleton (a SECONDARY ensures,
|
|
508
607
|
// per-op marked — the id is deterministic-by-user, an upsert). This is the seed `birthHome` reads to
|
|
509
|
-
// assemble the warranted chain; without it the home is born
|
|
608
|
+
// assemble the warranted chain; without it the home is born without its founder's device seat (co2's block).
|
|
510
609
|
// The delegation is keyed by the AUTHENTICATED principal — EXACTLY the `founder` the kernel's birth
|
|
511
610
|
// offer-effect reads it back by (`read_signer_delegation(state, &founder)`, founder == captured principal
|
|
512
611
|
// == this `rawPrincipal`, already in `user:<uid>` form). Do NOT re-prefix.
|
|
513
612
|
// Absent certificate ⇒ enroll-only (no delegation seeded), byte-identical to a pre-B enroll.
|
|
514
|
-
if (!p.
|
|
613
|
+
if (!p.delegationCertificate) return tupleOps;
|
|
515
614
|
const d = instance(SignerDelegation, signerDelegationInstanceId(rawPrincipal));
|
|
516
615
|
const delegationOps: PlannedOp[] = [
|
|
517
616
|
withMarker(set(d, "user", rawPrincipal), "ensures"),
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
...writeDelegationCertificate(d, p.delegationCertificate, "ensures"),
|
|
523
|
-
]
|
|
524
|
-
: [
|
|
525
|
-
// Exact legacy event order is replay identity, even though Lww folding would commute these
|
|
526
|
-
// two writes. Historical enrollFirstDevice intents wrote certJson before enrolledAt; retain
|
|
527
|
-
// that byte-level outcome when re-authoring an old payload under a candidate law.
|
|
528
|
-
withMarker(set(d, "certJson", p.delegationCert!), "ensures"),
|
|
529
|
-
withMarker(set(d, "enrolledAt", p.grantedAt), "ensures"),
|
|
530
|
-
]),
|
|
617
|
+
...writeDelegationCertificate(d, {
|
|
618
|
+
user: rawPrincipal, deviceKeySubject: "key:" + p.keyHash,
|
|
619
|
+
enrolledAt: p.grantedAt, certificate: p.delegationCertificate,
|
|
620
|
+
}, "ensures"),
|
|
531
621
|
];
|
|
532
622
|
return [...tupleOps, ...delegationOps];
|
|
533
623
|
})
|
|
@@ -590,7 +680,7 @@ export const enrollDelegation = directive("enrollDelegation")
|
|
|
590
680
|
.payload(
|
|
591
681
|
z.object({
|
|
592
682
|
user: z.string().min(1), // the user:<uid> the delegation enrolls
|
|
593
|
-
deviceKey: z.string().
|
|
683
|
+
deviceKey: z.string().regex(/^key:[0-9a-f]{64}$/), // enrolled public-key fingerprint
|
|
594
684
|
certificate: birthCertificatePayload,
|
|
595
685
|
enrolledAt: z.string().min(1), // ISO-8601, caller-stamped
|
|
596
686
|
}),
|
|
@@ -599,19 +689,62 @@ export const enrollDelegation = directive("enrollDelegation")
|
|
|
599
689
|
const d = instance(SignerDelegation, signerDelegationInstanceId(p.user));
|
|
600
690
|
return [
|
|
601
691
|
set(d, "user", p.user),
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
692
|
+
...writeDelegationCertificate(d, {
|
|
693
|
+
user: p.user, deviceKeySubject: p.deviceKey,
|
|
694
|
+
enrolledAt: p.enrolledAt, certificate: p.certificate,
|
|
695
|
+
}),
|
|
605
696
|
];
|
|
606
697
|
});
|
|
607
698
|
|
|
699
|
+
/** Retention is a law invariant, not a promise made by a scalar merge driver. */
|
|
700
|
+
function sameEvidenceValue(left: unknown, right: unknown): boolean {
|
|
701
|
+
if (left === right) return true;
|
|
702
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
|
|
703
|
+
const a = left as Record<string, unknown>, b = right as Record<string, unknown>;
|
|
704
|
+
const keys = Object.keys(a);
|
|
705
|
+
return keys.length === Object.keys(b).length && keys.every((key) =>
|
|
706
|
+
Object.prototype.hasOwnProperty.call(b, key) && sameEvidenceValue(a[key], b[key]));
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
export function signerDelegationEvidenceIsRetained(
|
|
710
|
+
name: string, directiveId: string, userPayloadPath: string,
|
|
711
|
+
) {
|
|
712
|
+
return workspaceInvariant(name).on(directiveId).reads(readSet(
|
|
713
|
+
readRef("before", SignerDelegation).pre().fromOptionalPayload(userPayloadPath, { prefix: SIGNER_DELEGATION_PREFIX }),
|
|
714
|
+
readRef("after", SignerDelegation).post().fromOptionalPayload(userPayloadPath, { prefix: SIGNER_DELEGATION_PREFIX }),
|
|
715
|
+
)).assert(({ before, after }) => {
|
|
716
|
+
const oldEntries = (before?.certificates ?? {}) as Record<string, unknown>;
|
|
717
|
+
const newEntries = (after?.certificates ?? {}) as Record<string, unknown>;
|
|
718
|
+
if (!Object.entries(oldEntries).every(([id, value]) => sameEvidenceValue(value, newEntries[id]))) {
|
|
719
|
+
return { reject: "signer-delegation-evidence-is-immutable" };
|
|
720
|
+
}
|
|
721
|
+
for (const [id, value] of Object.entries(newEntries)) {
|
|
722
|
+
const parsed = signerDelegationRecoveryEvidence.safeParse(value);
|
|
723
|
+
if (!parsed.success || parsed.data.user !== after?.user ||
|
|
724
|
+
parsed.data.certificate.founder !== parsed.data.user ||
|
|
725
|
+
signerDelegationCertificateId(parsed.data.certificate) !== id) {
|
|
726
|
+
return { reject: "signer-delegation-evidence-must-bind-its-identity" };
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return { accept: true };
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export const enrollDelegationRetainsEvidence = signerDelegationEvidenceIsRetained(
|
|
734
|
+
"EnrollDelegationRetainsEvidence", "enrollDelegation", "user",
|
|
735
|
+
);
|
|
736
|
+
export const enrollFirstDeviceRetainsEvidence = signerDelegationEvidenceIsRetained(
|
|
737
|
+
"EnrollFirstDeviceRetainsEvidence", "enrollFirstDevice", "delegationCertificate.founder",
|
|
738
|
+
);
|
|
739
|
+
|
|
608
740
|
/** Audit: a user's signer delegation (the enroll-once cert binding their device to the tenant lineage). */
|
|
609
741
|
export const signerDelegationByUser = query("signerDelegationByUser").key("user").returns(SignerDelegation);
|
|
610
742
|
|
|
611
|
-
/** Read
|
|
612
|
-
*
|
|
743
|
+
/** Read the exact device's evidence through the law's declared user index. A caller that
|
|
744
|
+
* omits the device is accepted only for a single-device collection; never pick another device. */
|
|
613
745
|
export function readSignerDelegationRecoveryEvidence(
|
|
614
746
|
user: string,
|
|
747
|
+
deviceKeySubject?: string,
|
|
615
748
|
): SignerDelegationRecoveryEvidence | undefined {
|
|
616
749
|
const rows = readFrameworkDeclaredRows(
|
|
617
750
|
signerDelegationByUser,
|
|
@@ -625,7 +758,10 @@ export function readSignerDelegationRecoveryEvidence(
|
|
|
625
758
|
);
|
|
626
759
|
}
|
|
627
760
|
const row = rows[0]!;
|
|
628
|
-
|
|
761
|
+
const candidates = Object.values(row.certificates ?? {}).map((value) =>
|
|
762
|
+
signerDelegationRecoveryEvidence.parse(value));
|
|
763
|
+
// Old typed fields are a read-only predecessor. New law never writes them again.
|
|
764
|
+
if (row.certificateSignature !== undefined) candidates.push(signerDelegationRecoveryEvidence.parse({
|
|
629
765
|
user: row.user,
|
|
630
766
|
deviceKeySubject: row.deviceKey,
|
|
631
767
|
enrolledAt: row.enrolledAt,
|
|
@@ -650,7 +786,18 @@ export function readSignerDelegationRecoveryEvidence(
|
|
|
650
786
|
? {}
|
|
651
787
|
: { authProviderAudience: row.certificateAuthProviderAudience }),
|
|
652
788
|
},
|
|
653
|
-
});
|
|
789
|
+
}));
|
|
790
|
+
const matching = candidates.filter((candidate) => candidate.user === user &&
|
|
791
|
+
(deviceKeySubject === undefined || candidate.deviceKeySubject === deviceKeySubject));
|
|
792
|
+
if (new Set(matching.map((candidate) => candidate.deviceKeySubject)).size > 1) {
|
|
793
|
+
throw new Error(`signerDelegationByUser('${user}') requires an exact device key`);
|
|
794
|
+
}
|
|
795
|
+
// Renewal selection is explicit and deterministic; unrelated devices cannot influence it.
|
|
796
|
+
matching.sort((a, b) => b.certificate.epoch - a.certificate.epoch ||
|
|
797
|
+
(b.certificate.capturedClock ?? 0) - (a.certificate.capturedClock ?? 0) ||
|
|
798
|
+
(a.certificate.signature < b.certificate.signature ? -1 :
|
|
799
|
+
a.certificate.signature > b.certificate.signature ? 1 : 0));
|
|
800
|
+
return matching[0];
|
|
654
801
|
}
|
|
655
802
|
|
|
656
803
|
/** Audit: the signer tuples for an identity (the raw relation aggregate). */
|
|
@@ -28,6 +28,8 @@ import { directive, type Directive } from "../directive.js";
|
|
|
28
28
|
import { set } from "../ops.js";
|
|
29
29
|
import { read } from "../read.js";
|
|
30
30
|
import { foreignQuery } from "../attested_core.js";
|
|
31
|
+
import { valueObject } from "../value_object.js";
|
|
32
|
+
import { RelationshipSubjectValue } from "./relations.js";
|
|
31
33
|
|
|
32
34
|
/** The framework-reserved roster instance-id prefix (kernel mirror:
|
|
33
35
|
* `nomos_executor::SOURCE_ROSTER_PREFIX`). */
|
|
@@ -43,16 +45,20 @@ export const ATTESTOR_ROSTER_QUERY_ID = "attestorRoster";
|
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
* NomosSourceRoster (§1.5) — the reader's folded mirror of one source's read-attestor
|
|
46
|
-
* roster, keyed by the source's TRUE identity (`source-roster:<genesis>`). `
|
|
48
|
+
* roster, keyed by the source's TRUE identity (`source-roster:<genesis>`). `roster` is
|
|
47
49
|
* the `keyHash → leaf` map exactly as the source's signer chain folds it (a key is ACCEPTED
|
|
48
50
|
* iff its leaf's `status == "active"` — the position-aware revocation mirror); the kernel's
|
|
49
51
|
* `accepted_keys` reads THIS under `roster: "tracked"`.
|
|
50
52
|
*/
|
|
53
|
+
export const SourceSignerRoster = valueObject("SourceSignerRoster", {
|
|
54
|
+
keys: z.record(z.string(), RelationshipSubjectValue.zod),
|
|
55
|
+
});
|
|
56
|
+
|
|
51
57
|
export const NomosSourceRoster = aggregate("NomosSourceRoster", {
|
|
52
58
|
/** The routable source name the edge was declared under (diagnostics). */
|
|
53
59
|
source: t.string().merge(Lww),
|
|
54
|
-
/** The `keyHash → enrollment leaf` map
|
|
55
|
-
|
|
60
|
+
/** The typed `keyHash → enrollment leaf` map attested by the source's own chain. */
|
|
61
|
+
roster: SourceSignerRoster.field().merge(Lww),
|
|
56
62
|
/** The source's `SignerEpoch` at the attested sync. */
|
|
57
63
|
currentEpoch: t.int().merge(Lww),
|
|
58
64
|
}).public();
|
|
@@ -63,7 +69,7 @@ interface AttestorRosterRow {
|
|
|
63
69
|
data: {
|
|
64
70
|
genesis: string;
|
|
65
71
|
currentEpoch: number;
|
|
66
|
-
keys: Record<string,
|
|
72
|
+
keys: Record<string, z.infer<typeof RelationshipSubjectValue.zod>>;
|
|
67
73
|
};
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -88,7 +94,7 @@ export function sourceRosterSync(sourceWs: string, directiveId = "syncSourceRost
|
|
|
88
94
|
const r = instance(NomosSourceRoster, sourceRosterId(d.genesis));
|
|
89
95
|
return [
|
|
90
96
|
set(r, "source", sourceWs),
|
|
91
|
-
set(r, "
|
|
97
|
+
set(r, "roster", { keys: d.keys ?? {} }),
|
|
92
98
|
set(r, "currentEpoch", d.currentEpoch ?? 0),
|
|
93
99
|
];
|
|
94
100
|
})
|