@githolon/dsl 0.108.7 → 0.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +1994 -1189
- 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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upgrade existing children from the former `child` epoch gate to the explicit
|
|
3
|
+
* epoch-consumer seat. New framework births already write that seat.
|
|
4
|
+
*
|
|
5
|
+
* This is an operator-authorised parent-local change, not device recovery and
|
|
6
|
+
* not a kernel exception. It uses canonical birth/lifecycle custody, never a
|
|
7
|
+
* tenant catalogue or a caller-supplied certificate. An explicit revocation is
|
|
8
|
+
* not a missing migration: neither it nor a withdrawn child may be resurrected.
|
|
9
|
+
* Remove this compatibility operation only after existing children are migrated.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { directive, nomosImplementationDetail } from "../../directive.js";
|
|
13
|
+
import { authenticatesWithEnrolledDevice, intent } from "../../intent.js";
|
|
14
|
+
import { readFrameworkDeclaredRows } from "../../read.js";
|
|
15
|
+
import { AUTH_PROVIDER_EPOCH_CONSUMER } from "../auth_provider_law.js";
|
|
16
|
+
import {
|
|
17
|
+
NomosBirthRecord,
|
|
18
|
+
NomosChildLifecycle,
|
|
19
|
+
birthRecordsByPrincipal,
|
|
20
|
+
childLifecyclesByPrincipal,
|
|
21
|
+
} from "../birth_meter.js";
|
|
22
|
+
import { MODEL_LIFECYCLE_AUTHORITY } from "../domain_lifecycle.js";
|
|
23
|
+
import { RelationTuple, relationTuplesByObject, writeTuple } from "../relations.js";
|
|
24
|
+
|
|
25
|
+
export const REPAIR_CHILD_AUTH_PROVIDER_EPOCH_CONSUMER_ID =
|
|
26
|
+
"nomosInternalRepairChildAuthProviderEpochConsumer7c1f5e";
|
|
27
|
+
|
|
28
|
+
const repairMissingChildEpochConsumer = nomosImplementationDetail(
|
|
29
|
+
directive("nomosInternalRepairMissingChildEpochConsumer7c1f5e")
|
|
30
|
+
.ensures(RelationTuple)
|
|
31
|
+
.payload(z.object({ child: z.string().min(1), principal: z.string().min(1) }).strict())
|
|
32
|
+
.plan(({ child, principal }, ctx) => {
|
|
33
|
+
const births = readFrameworkDeclaredRows(birthRecordsByPrincipal, NomosBirthRecord, { principal })
|
|
34
|
+
.filter((birth) => birth.child === child && birth.principal === principal);
|
|
35
|
+
if (births.length !== 1 || !/^[0-9a-f]{64}$/.test(births[0]!.childGenesisHash ?? "")) {
|
|
36
|
+
throw new Error("epoch-consumer repair requires one canonical birth for the exact child and principal");
|
|
37
|
+
}
|
|
38
|
+
const lifecycles = readFrameworkDeclaredRows(childLifecyclesByPrincipal, NomosChildLifecycle, { principal })
|
|
39
|
+
.filter((lifecycle) => lifecycle.child === child);
|
|
40
|
+
if (lifecycles.some((lifecycle) => lifecycle.status !== "active")) {
|
|
41
|
+
throw new Error("epoch-consumer repair cannot restore a retired or reclaimed child");
|
|
42
|
+
}
|
|
43
|
+
const subject = `workspace:${births[0]!.child}`;
|
|
44
|
+
const tuples = readFrameworkDeclaredRows(relationTuplesByObject, RelationTuple, { object: "workspace:self" })
|
|
45
|
+
.filter((tuple) => tuple.object === "workspace:self");
|
|
46
|
+
const childGrants = tuples.filter((tuple) => tuple.relation === "child")
|
|
47
|
+
.flatMap((tuple) => tuple.subjects?.[subject] ? [tuple.subjects[subject]!] : []);
|
|
48
|
+
const epochGrants = tuples.filter((tuple) => tuple.relation === AUTH_PROVIDER_EPOCH_CONSUMER)
|
|
49
|
+
.flatMap((tuple) => tuple.subjects?.[subject] ? [tuple.subjects[subject]!] : []);
|
|
50
|
+
if ([...childGrants, ...epochGrants].some((grant) => grant.status !== "active")) {
|
|
51
|
+
throw new Error("epoch-consumer repair cannot overwrite an explicit child or consumer revocation");
|
|
52
|
+
}
|
|
53
|
+
// A repeat is a real no-op: preserve original grant provenance as well as
|
|
54
|
+
// every other child's seat. Never replace the entire subjects map.
|
|
55
|
+
if (epochGrants.length > 0) return [];
|
|
56
|
+
return writeTuple({
|
|
57
|
+
object: "workspace:self",
|
|
58
|
+
relation: AUTH_PROVIDER_EPOCH_CONSUMER,
|
|
59
|
+
subject,
|
|
60
|
+
grantedBy: ctx.principal().asAttribution(),
|
|
61
|
+
grantedAt: String(ctx.clock().physical),
|
|
62
|
+
});
|
|
63
|
+
})
|
|
64
|
+
.reads(birthRecordsByPrincipal, childLifecyclesByPrincipal, relationTuplesByObject)
|
|
65
|
+
// Gate the step itself too: presentation-internal is not an authority boundary.
|
|
66
|
+
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" }),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
export const repairChildAuthProviderEpochConsumer = nomosImplementationDetail(
|
|
70
|
+
authenticatesWithEnrolledDevice(
|
|
71
|
+
intent(REPAIR_CHILD_AUTH_PROVIDER_EPOCH_CONSUMER_ID).performs(repairMissingChildEpochConsumer),
|
|
72
|
+
),
|
|
73
|
+
);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
|
|
4
|
+
// If a file isn't this / hosting this / authoring for this / proving this — it's gone.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* THE MODEL-AUTHORITY SELF-SEAT — a workspace's own model-lifecycle holder names who else may install
|
|
8
|
+
* law here. ONE directive, one gate, no handshake.
|
|
9
|
+
*
|
|
10
|
+
* Design: `architecture/parent_directed_model_transition.md` §9. Lane A seats the platform's holders in
|
|
11
|
+
* a child's GENESIS (`model_authority_seed.ts`); lane B seats them when the platform DIRECTS a
|
|
12
|
+
* transition (`model_transition.ts`). Both are forward-looking, and lane B's entry needs an operator who
|
|
13
|
+
* can author on the platform. The population neither reaches is the one that already exists: a workspace
|
|
14
|
+
* born before lane A whose platform never directed it, holding exactly one `workspace:self#installDomain`
|
|
15
|
+
* subject — its founder, the signed-in person. As the product owner put it: a platform whose every act
|
|
16
|
+
* still needs that person's device key does not hold authority, it holds an ask with a stamp.
|
|
17
|
+
*
|
|
18
|
+
* WHY THIS IS ONE DIRECTIVE AND NOT A HANDSHAKE. It was built first as a child-asks / parent-answers /
|
|
19
|
+
* child-accepts round trip, so the holders could be derived by parent law rather than supplied. That
|
|
20
|
+
* defends against a threat that does not exist: the founder already holds `installDomain` here and can
|
|
21
|
+
* therefore install ANY law into this workspace, including law that seats anyone. A directive that lets
|
|
22
|
+
* that same founder seat a named subject grants no privilege they do not already hold — the "attacker"
|
|
23
|
+
* would be the sole owner of the thing attacked. Every mechanism added to route around that was cost
|
|
24
|
+
* with no security return, and it was precisely what excluded the workspaces this exists to reach: the
|
|
25
|
+
* parent's `workspace:self#child` tuple is only written by births after 2026-09-11, and both live
|
|
26
|
+
* production receiving groups check `allowed: false` for it.
|
|
27
|
+
*
|
|
28
|
+
* THE GATE IS THE WHOLE ARGUMENT: {@link MODEL_LIFECYCLE_AUTHORITY} on `workspace:self`, and nothing
|
|
29
|
+
* else. Whoever may install law here may say who else may.
|
|
30
|
+
*
|
|
31
|
+
* THE EFFECT IS THE BIRTH SEED'S. {@link seatInheritedModelAuthorityOps} is shared by the genesis seed,
|
|
32
|
+
* the parent-directed acceptor and this directive, so a seeded, a directed and a self-seated workspace
|
|
33
|
+
* are indistinguishable afterwards. Idempotent by construction: `writeTuple`'s first op is `ensures` and
|
|
34
|
+
* the subject rides a `setEntry` on the same subject key, so a re-run writes the same tuple; the record
|
|
35
|
+
* is one constant-id row whose fields are Lww/AddWins. Only `grantedAt`/`seatedAt` move, which is what
|
|
36
|
+
* re-attesting a fact does.
|
|
37
|
+
*/
|
|
38
|
+
import { aggregate, instance } from "../aggregate.js";
|
|
39
|
+
import { directive, nomosImplementationDetail } from "../directive.js";
|
|
40
|
+
import { AddWins, Lww } from "../drivers.js";
|
|
41
|
+
import { t } from "../fields.js";
|
|
42
|
+
import { intent } from "../intent.js";
|
|
43
|
+
import { addToSet, set, withMarker, type PlannedOp } from "../ops.js";
|
|
44
|
+
import { MODEL_LIFECYCLE_AUTHORITY } from "./domain_lifecycle.js";
|
|
45
|
+
import {
|
|
46
|
+
inheritedModelAuthorityEvidence,
|
|
47
|
+
seatInheritedModelAuthorityOps,
|
|
48
|
+
type InheritedModelAuthorityEvidence,
|
|
49
|
+
} from "./model_authority_seed.js";
|
|
50
|
+
import { RelationTuple } from "./relations.js";
|
|
51
|
+
|
|
52
|
+
/** Wire ids — framework-internal, obscure on purpose (the 7c1f5e suffix convention). */
|
|
53
|
+
export const SEAT_MODEL_AUTHORITY_ID = "nomosInternalSeatModelAuthority7c1f5e";
|
|
54
|
+
export const SEAT_DECLARED_MODEL_AUTHORITY_ID = "nomosInternalSeatDeclaredModelAuthority7c1f5e";
|
|
55
|
+
export const RECORD_MODEL_AUTHORITY_SEAT_ID = "nomosInternalRecordModelAuthoritySeat7c1f5e";
|
|
56
|
+
export const PARENT_MODEL_AUTHORITY_SEAT_AGGREGATE = "nomos_internal_ParentModelAuthoritySeat_7c1f5e";
|
|
57
|
+
/** A CONSTANT instance id (the RelationTuple discipline — a literal, never minted), so every peer reads
|
|
58
|
+
* the same address and a client can ask for it by id with nothing compiled in but this string. */
|
|
59
|
+
export const PARENT_MODEL_AUTHORITY_SEAT_ID = "nomos-parent-model-authority-seat";
|
|
60
|
+
|
|
61
|
+
/** THE IN-HISTORY RECORD that this workspace has seated model-lifecycle authority beyond its founder,
|
|
62
|
+
* and the peer-readable answer to "has this workspace been seated yet". One row, one constant id. */
|
|
63
|
+
export const ParentModelAuthoritySeat = aggregate(PARENT_MODEL_AUTHORITY_SEAT_AGGREGATE, {
|
|
64
|
+
/** The model-lifecycle holders seated (`user:`/`key:` subjects). */
|
|
65
|
+
holders: t.set(t.string()).merge(AddWins),
|
|
66
|
+
seatedAt: t.string().merge(Lww),
|
|
67
|
+
}).public();
|
|
68
|
+
|
|
69
|
+
/** Seat the named holders — THE VERY OPS the genesis seed writes, so a seeded, a directed and a
|
|
70
|
+
* self-seated workspace are indistinguishable afterwards. */
|
|
71
|
+
export const seatDeclaredModelAuthority = nomosImplementationDetail(
|
|
72
|
+
directive(SEAT_DECLARED_MODEL_AUTHORITY_ID)
|
|
73
|
+
.ensures(RelationTuple)
|
|
74
|
+
.payload(inheritedModelAuthorityEvidence)
|
|
75
|
+
.plan((evidence, ctx) => seatInheritedModelAuthorityOps(evidence, ctx.principal().asAttribution())),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/** Record the seat, so a peer can tell "already seated" from one by-id read. */
|
|
79
|
+
export const recordModelAuthoritySeat = nomosImplementationDetail(
|
|
80
|
+
directive(RECORD_MODEL_AUTHORITY_SEAT_ID)
|
|
81
|
+
.ensures(ParentModelAuthoritySeat)
|
|
82
|
+
.payload(inheritedModelAuthorityEvidence)
|
|
83
|
+
.plan((evidence: InheritedModelAuthorityEvidence): PlannedOp[] => {
|
|
84
|
+
const row = instance(ParentModelAuthoritySeat, PARENT_MODEL_AUTHORITY_SEAT_ID);
|
|
85
|
+
return [
|
|
86
|
+
withMarker(set(row, "seatedAt", evidence.seatedAt), "ensures"),
|
|
87
|
+
addToSet(row, "holders", evidence.holders.map((holder) => holder.principal)),
|
|
88
|
+
];
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* THE SELF-SEAT. Gated on THIS workspace's own model-lifecycle seat — the founder/admin principal
|
|
94
|
+
* `seedGenesis` wrote — and on nothing else. Seat and record are ONE atomic transition.
|
|
95
|
+
*/
|
|
96
|
+
export const seatModelAuthority = nomosImplementationDetail(
|
|
97
|
+
intent(SEAT_MODEL_AUTHORITY_ID)
|
|
98
|
+
.contract({ accepts: inheritedModelAuthorityEvidence })
|
|
99
|
+
.directs(seatDeclaredModelAuthority, (evidence) => evidence)
|
|
100
|
+
.directs(recordModelAuthoritySeat, (evidence) => evidence)
|
|
101
|
+
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" })
|
|
102
|
+
.atomically(),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The export map the application-home policy law composes (keys are the obscure `__nomos_*` slots;
|
|
107
|
+
* values are THE singletons — the law graph folds an intent's step closure by object identity). The two
|
|
108
|
+
* consequences ride only the intent's step closure, as `child_authentication.ts` carries its gated
|
|
109
|
+
* acceptor effect.
|
|
110
|
+
*/
|
|
111
|
+
export function modelAuthorityRepairExports(): Readonly<Record<string, object>> {
|
|
112
|
+
return {
|
|
113
|
+
__nomos_ParentModelAuthoritySeat: ParentModelAuthoritySeat,
|
|
114
|
+
__nomos_seatModelAuthority: seatModelAuthority,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
|
|
4
|
+
// If a file isn't this / hosting this / authoring for this / proving this — it's gone.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* INHERITED MODEL AUTHORITY — the platform's own signer holds `installDomain` on every descendant.
|
|
8
|
+
*
|
|
9
|
+
* The authority model (`architecture/parent_directed_model_transition.md` §2): the framework holds no
|
|
10
|
+
* seat in a tenant workspace and the host holds none; the TENANT PLATFORM holds full model authority
|
|
11
|
+
* over its subtree; a user holds their own device seat. Until this module every child was born with
|
|
12
|
+
* exactly one `workspace:self#installDomain` holder — its founder (`seedGenesis`) — so the platform's
|
|
13
|
+
* operator could not lawfully install law into a child it had birthed.
|
|
14
|
+
*
|
|
15
|
+
* THE SEED. Every framework birth recipe (the receiving-group birth and the business `birth()`) reads
|
|
16
|
+
* the parent's committed model-lifecycle holders — the active `user:`/`key:` subjects of
|
|
17
|
+
* `workspace:self#installDomain` — and, for each `user:` holder, its active signer keys from
|
|
18
|
+
* `identity:<holder>#signer`, and embeds ONE genesis step that seats the same facts in the child. The
|
|
19
|
+
* read is the declared `relationTuplesByObject` query (captured, CAS-re-derived on every lane), the
|
|
20
|
+
* same idiom by which a birth copies the parent's verifier (`seedAuthProvider`). Authority therefore
|
|
21
|
+
* DESCENDS BY INDUCTION: a home inherits the platform's holders, an estate inherits the home's (the
|
|
22
|
+
* owner and the platform's operators). Root → platform is deliberately outside this lane.
|
|
23
|
+
*
|
|
24
|
+
* THE POSTURE. The seed carries no `.requires` (it runs on the warrant-free genesis prefix, like every
|
|
25
|
+
* child-genesis primitive) and is refused once the GenesisWarrant exists
|
|
26
|
+
* ({@link modelAuthoritySeedBeforeWarrant}, the `authProviderSeedBeforeWarrant` shape). After birth
|
|
27
|
+
* the ONLY way these seats enter a child is the parent-directed transition acceptor
|
|
28
|
+
* (`model_transition.ts`), admitted on the attested parent's cert chain.
|
|
29
|
+
*
|
|
30
|
+
* Signer leaves are carried VERBATIM (`grantedAt`, `verifiedVia`, `validFromEpoch`): provenance is
|
|
31
|
+
* preserved, never re-vouched. The relation schema already admits `user:`/`key:` subjects on
|
|
32
|
+
* `installDomain` (`modelLifecycleRelations`) and `key:` subjects on `signer` (`identityRelations`);
|
|
33
|
+
* nothing here changes a schema.
|
|
34
|
+
*/
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
import { directive, nomosImplementationDetail } from "../directive.js";
|
|
37
|
+
import type { PlannedOp } from "../ops.js";
|
|
38
|
+
import type { AttributionPrincipal } from "../principal_evidence.js";
|
|
39
|
+
import { readFrameworkDeclaredRows } from "../read.js";
|
|
40
|
+
import { MODEL_LIFECYCLE_AUTHORITY } from "./domain_lifecycle.js";
|
|
41
|
+
import { GENESIS_WARRANT_ID } from "./genesis.js";
|
|
42
|
+
import { RelationTuple, relationTuplesByObject, writeTuple } from "./relations.js";
|
|
43
|
+
import { SIGNER_RELATION, signerObject } from "./signing_identity.js";
|
|
44
|
+
import { readRef, readSet, workspaceInvariant } from "./workspace_invariant.js";
|
|
45
|
+
|
|
46
|
+
/** Wire id — framework-internal, obscure on purpose (the 7c1f5e suffix convention). */
|
|
47
|
+
export const SEAT_INHERITED_MODEL_AUTHORITY_ID = "nomosInternalSeatInheritedModelAuthority7c1f5e";
|
|
48
|
+
|
|
49
|
+
const principalSubject = z.string().regex(/^(user|key):[A-Za-z0-9][A-Za-z0-9_.:/-]*$/);
|
|
50
|
+
const deviceKeySubject = z.string().regex(/^key:[0-9a-f]{64}$/);
|
|
51
|
+
|
|
52
|
+
/** One active signer leaf of a holder, carried verbatim from the parent's committed signer relation. */
|
|
53
|
+
export const inheritedSignerLeaf = z
|
|
54
|
+
.object({
|
|
55
|
+
deviceKeySubject,
|
|
56
|
+
grantedAt: z.string().min(1),
|
|
57
|
+
verifiedVia: z.string().min(1).optional(),
|
|
58
|
+
validFromEpoch: z.number().int().nonnegative().optional(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
|
|
62
|
+
/** One model-lifecycle holder of the parent and the devices that may author as it. A `key:` holder
|
|
63
|
+
* self-authenticates and carries no signer leaves. */
|
|
64
|
+
export const inheritedModelAuthorityHolder = z
|
|
65
|
+
.object({
|
|
66
|
+
principal: principalSubject,
|
|
67
|
+
signers: z.array(inheritedSignerLeaf),
|
|
68
|
+
})
|
|
69
|
+
.strict();
|
|
70
|
+
|
|
71
|
+
/** The exact typed evidence a parent discloses: its current holders, and when it read them. */
|
|
72
|
+
export const inheritedModelAuthorityEvidence = z
|
|
73
|
+
.object({
|
|
74
|
+
holders: z.array(inheritedModelAuthorityHolder).min(1),
|
|
75
|
+
/** Caller-stamped (ISO-8601 or epoch string) — the timestamp doctrine; the plan reads no clock. */
|
|
76
|
+
seatedAt: z.string().min(1),
|
|
77
|
+
})
|
|
78
|
+
.strict();
|
|
79
|
+
export type InheritedModelAuthorityEvidence = z.infer<typeof inheritedModelAuthorityEvidence>;
|
|
80
|
+
export type InheritedModelAuthorityHolder = z.infer<typeof inheritedModelAuthorityHolder>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Read THIS workspace's current model-lifecycle holders and their active devices. Returns `undefined`
|
|
84
|
+
* when the workspace holds no `installDomain` tuple row (a chain born before the seat existed), so a
|
|
85
|
+
* birth plan can omit the step rather than seed an empty list. Deterministic: both reads are declared
|
|
86
|
+
* captured reads and the output is sorted by subject.
|
|
87
|
+
*/
|
|
88
|
+
export function readInheritedModelAuthority(): InheritedModelAuthorityHolder[] | undefined {
|
|
89
|
+
const rows = readFrameworkDeclaredRows(relationTuplesByObject, RelationTuple, { object: "workspace:self" });
|
|
90
|
+
const authority = rows.find(
|
|
91
|
+
(row) => row.object === "workspace:self" && row.relation === MODEL_LIFECYCLE_AUTHORITY,
|
|
92
|
+
);
|
|
93
|
+
if (authority === undefined) return undefined;
|
|
94
|
+
const principals = Object.entries(authority.subjects ?? {})
|
|
95
|
+
.filter(([subject, leaf]) => /^(user|key):/.test(subject) && leaf.status === "active")
|
|
96
|
+
.map(([subject]) => subject)
|
|
97
|
+
.sort();
|
|
98
|
+
if (principals.length === 0) return undefined;
|
|
99
|
+
return principals.map((principal) => {
|
|
100
|
+
if (!principal.startsWith("user:")) return { principal, signers: [] };
|
|
101
|
+
const signerRows = readFrameworkDeclaredRows(relationTuplesByObject, RelationTuple, {
|
|
102
|
+
object: signerObject(principal),
|
|
103
|
+
});
|
|
104
|
+
const signers = signerRows
|
|
105
|
+
.filter((row) => row.object === signerObject(principal) && row.relation === SIGNER_RELATION)
|
|
106
|
+
.flatMap((row) =>
|
|
107
|
+
Object.entries(row.subjects ?? {})
|
|
108
|
+
.filter(([subject, leaf]) => subject.startsWith("key:") && leaf.status === "active")
|
|
109
|
+
.map(([subject, leaf]) =>
|
|
110
|
+
inheritedSignerLeaf.parse({
|
|
111
|
+
deviceKeySubject: subject,
|
|
112
|
+
grantedAt: leaf.grantedAt,
|
|
113
|
+
...(leaf.verifiedVia === undefined ? {} : { verifiedVia: leaf.verifiedVia }),
|
|
114
|
+
...(leaf.validFromEpoch === undefined ? {} : { validFromEpoch: leaf.validFromEpoch }),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
.sort((left, right) => left.deviceKeySubject.localeCompare(right.deviceKeySubject));
|
|
119
|
+
return { principal, signers };
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The ops that seat one disclosed holder set: `workspace:self#installDomain@<holder>` and, per device,
|
|
124
|
+
* `identity:<holder>#signer@key:…`. Shared by the genesis seed and the parent-directed acceptor. */
|
|
125
|
+
export function seatInheritedModelAuthorityOps(
|
|
126
|
+
evidence: InheritedModelAuthorityEvidence,
|
|
127
|
+
grantedBy: string | AttributionPrincipal,
|
|
128
|
+
): PlannedOp[] {
|
|
129
|
+
return evidence.holders.flatMap((holder) => [
|
|
130
|
+
...writeTuple({
|
|
131
|
+
object: "workspace:self",
|
|
132
|
+
relation: MODEL_LIFECYCLE_AUTHORITY,
|
|
133
|
+
subject: holder.principal,
|
|
134
|
+
grantedBy,
|
|
135
|
+
grantedAt: evidence.seatedAt,
|
|
136
|
+
}),
|
|
137
|
+
...holder.signers.flatMap((leaf) =>
|
|
138
|
+
writeTuple({
|
|
139
|
+
object: signerObject(holder.principal),
|
|
140
|
+
relation: SIGNER_RELATION,
|
|
141
|
+
subject: leaf.deviceKeySubject,
|
|
142
|
+
grantedBy,
|
|
143
|
+
grantedAt: leaf.grantedAt,
|
|
144
|
+
...(leaf.verifiedVia === undefined ? {} : { verifiedVia: leaf.verifiedVia }),
|
|
145
|
+
...(leaf.validFromEpoch === undefined ? {} : { validFromEpoch: leaf.validFromEpoch }),
|
|
146
|
+
}),
|
|
147
|
+
),
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** THE GENESIS SEED: the child seats its parent's model-lifecycle holders as its own. */
|
|
152
|
+
export const seatInheritedModelAuthority = nomosImplementationDetail(
|
|
153
|
+
directive(SEAT_INHERITED_MODEL_AUTHORITY_ID)
|
|
154
|
+
.ensures(RelationTuple)
|
|
155
|
+
.payload(inheritedModelAuthorityEvidence)
|
|
156
|
+
.plan((evidence, ctx) => seatInheritedModelAuthorityOps(evidence, ctx.principal().asAttribution())),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
/** A child-genesis primitive: refused once the GenesisWarrant exists. After birth the seats enter only
|
|
160
|
+
* through the parent-directed transition acceptor, admitted on the attested parent's cert chain. */
|
|
161
|
+
export const modelAuthoritySeedBeforeWarrant = workspaceInvariant("ModelAuthoritySeedBeforeWarrant")
|
|
162
|
+
.on(SEAT_INHERITED_MODEL_AUTHORITY_ID)
|
|
163
|
+
.reads(readSet(readRef("warrant", "GenesisWarrant").at(GENESIS_WARRANT_ID)))
|
|
164
|
+
.assert(({ warrant }) => {
|
|
165
|
+
const principal = (warrant as { principal?: unknown } | null | undefined)?.principal;
|
|
166
|
+
return typeof principal !== "string" || principal.length === 0
|
|
167
|
+
? { accept: true }
|
|
168
|
+
: { reject: "inherited model authority is seated at genesis or by the parent-directed transition, never by a bare offer" };
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
/** Build the genesis step a birth recipe embeds, or `undefined` when the parent holds no seat to pass on. */
|
|
172
|
+
export function inheritedModelAuthorityGenesisStep(
|
|
173
|
+
domain: string,
|
|
174
|
+
actor: string,
|
|
175
|
+
seatedAt: string,
|
|
176
|
+
): { domain: string; directiveId: string; actor: string; payload: Record<string, unknown> } | undefined {
|
|
177
|
+
const holders = readInheritedModelAuthority();
|
|
178
|
+
if (holders === undefined) return undefined;
|
|
179
|
+
return {
|
|
180
|
+
domain,
|
|
181
|
+
directiveId: SEAT_INHERITED_MODEL_AUTHORITY_ID,
|
|
182
|
+
actor,
|
|
183
|
+
payload: inheritedModelAuthorityEvidence.parse({ holders, seatedAt }),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
|
|
4
|
+
// If a file isn't this / hosting this / authoring for this / proving this — it's gone.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PARENT-DIRECTED MODEL TRANSITION — the platform adopts once and DIRECTS the transition into each
|
|
8
|
+
* child; the child accepts it on the strength of its attested parent, with no device signature.
|
|
9
|
+
*
|
|
10
|
+
* Design: `architecture/parent_directed_model_transition.md`. Two halves, both framework law composed
|
|
11
|
+
* into the application-home domain whenever a package births children (the same era rule as child
|
|
12
|
+
* authentication), so the parent door and the child acceptor are one exported pair in one module:
|
|
13
|
+
*
|
|
14
|
+
* THE CHILD ACCEPTOR ({@link acceptParentModelTransition}) — a recovery-door intent
|
|
15
|
+
* (`acceptsVerifiedBirthParentRecoveryEffect`): the kernel admits the carried effect only when the
|
|
16
|
+
* verified source workspace IS this workspace's persisted birth parent. It atomically records the
|
|
17
|
+
* direction in the child's own chain ({@link ParentModelTransition}: "my parent directed model X for
|
|
18
|
+
* domain D") and seats the parent's current model-lifecycle holders + their devices exactly as the
|
|
19
|
+
* birth seed does — so a child born before the seat existed heals itself on its first direction.
|
|
20
|
+
*
|
|
21
|
+
* THE PLATFORM DIRECTION ({@link directChildModelTransition}) — gated on the platform's own
|
|
22
|
+
* `workspace:self#installDomain` (whoever may install law here may direct it into a child); it
|
|
23
|
+
* addresses ONE exact child from its typed input and derives every payload member from the
|
|
24
|
+
* platform's committed state, so no caller can make a child seat a subject the platform does not hold.
|
|
25
|
+
*
|
|
26
|
+
* WHY THE ACCEPTOR DOES NOT STAGE/PROMOTE ITSELF (yet): the kernel keys the model lifecycle by the
|
|
27
|
+
* nominal labels `nomos/stageDomain` / `nomos/promoteDomain`, needs the closure bytes at the author
|
|
28
|
+
* lane and a frontier transition proved at the child's own head — none of which a carried effect
|
|
29
|
+
* supplies today (design §3, §7 scopes the kernel lane). Until then the direction is the RECORD and the
|
|
30
|
+
* SEAT; the platform's signer — now lawful on the child — stages and promotes through the ordinary
|
|
31
|
+
* signed lifecycle offers (`githolon model adopt <platform> --children`).
|
|
32
|
+
*/
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
import { aggregate, instance } from "../aggregate.js";
|
|
35
|
+
import { directive, nomosImplementationDetail } from "../directive.js";
|
|
36
|
+
import { AddWins, Lww } from "../drivers.js";
|
|
37
|
+
import { sha256Digest, t } from "../fields.js";
|
|
38
|
+
import { intent } from "../intent.js";
|
|
39
|
+
import { addToSet, set, withMarker, type PlannedOp } from "../ops.js";
|
|
40
|
+
import { readFrameworkDeclaredRows } from "../read.js";
|
|
41
|
+
import { MODEL_LIFECYCLE_AUTHORITY } from "./domain_lifecycle.js";
|
|
42
|
+
import { verifiedBirthAttestation } from "./child_authentication.js";
|
|
43
|
+
import {
|
|
44
|
+
inheritedModelAuthorityEvidence,
|
|
45
|
+
readInheritedModelAuthority,
|
|
46
|
+
seatInheritedModelAuthorityOps,
|
|
47
|
+
} from "./model_authority_seed.js";
|
|
48
|
+
import { BirthAttestation } from "./receiving_group.js";
|
|
49
|
+
import { RelationTuple, relationTuplesByObject } from "./relations.js";
|
|
50
|
+
|
|
51
|
+
/** Wire ids — framework-internal, obscure on purpose (the 7c1f5e suffix convention). */
|
|
52
|
+
export const ACCEPT_PARENT_MODEL_TRANSITION_ID = "nomosInternalAcceptParentModelTransition7c1f5e";
|
|
53
|
+
export const DIRECT_CHILD_MODEL_TRANSITION_ID = "nomosInternalDirectChildModelTransition7c1f5e";
|
|
54
|
+
export const RECORD_PARENT_MODEL_TRANSITION_ID = "nomosInternalRecordParentModelTransition7c1f5e";
|
|
55
|
+
export const SEAT_PARENT_MODEL_AUTHORITY_ID = "nomosInternalSeatParentModelAuthority7c1f5e";
|
|
56
|
+
export const PARENT_MODEL_TRANSITION_AGGREGATE = "nomos_internal_ParentModelTransition_7c1f5e";
|
|
57
|
+
export const PARENT_MODEL_TRANSITION_PREFIX = "nomos-parent-model-transition:";
|
|
58
|
+
|
|
59
|
+
/** The transition a parent directs: which package, for which domain key, and why. The executing
|
|
60
|
+
* transition (stage + promote) is kernel-judged in full on the child; this names it. */
|
|
61
|
+
export const modelTransitionDirection = z
|
|
62
|
+
.object({
|
|
63
|
+
domain: z.string().min(1),
|
|
64
|
+
candidateHash: sha256Digest(),
|
|
65
|
+
supersedes: sha256Digest().optional(),
|
|
66
|
+
acceptanceReason: z.string().trim().min(1),
|
|
67
|
+
/** Caller-stamped (ISO-8601) — the timestamp doctrine. */
|
|
68
|
+
directedAt: z.string().min(1),
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
71
|
+
export type ModelTransitionDirection = z.infer<typeof modelTransitionDirection>;
|
|
72
|
+
|
|
73
|
+
/** What the child accepts: the direction plus the parent's current model-lifecycle holders. Every
|
|
74
|
+
* member is derived by parent law from committed parent state, never supplied by a caller. */
|
|
75
|
+
export const parentModelTransitionEvidence = z
|
|
76
|
+
.object({
|
|
77
|
+
direction: modelTransitionDirection,
|
|
78
|
+
authority: inheritedModelAuthorityEvidence,
|
|
79
|
+
})
|
|
80
|
+
.strict();
|
|
81
|
+
export type ParentModelTransitionEvidence = z.infer<typeof parentModelTransitionEvidence>;
|
|
82
|
+
|
|
83
|
+
/** The platform's typed call: the exact child and the transition to direct. */
|
|
84
|
+
export const directChildModelTransitionInput = z
|
|
85
|
+
.object({
|
|
86
|
+
workspace: z.string().min(1),
|
|
87
|
+
domain: z.string().min(1),
|
|
88
|
+
candidateHash: sha256Digest(),
|
|
89
|
+
supersedes: sha256Digest().optional(),
|
|
90
|
+
acceptanceReason: z.string().trim().min(1),
|
|
91
|
+
directedAt: z.string().min(1),
|
|
92
|
+
})
|
|
93
|
+
.strict();
|
|
94
|
+
export type DirectChildModelTransitionInput = z.infer<typeof directChildModelTransitionInput>;
|
|
95
|
+
|
|
96
|
+
/** The child's in-history record of a direction: one row per (domain, candidate). */
|
|
97
|
+
export const ParentModelTransition = aggregate(PARENT_MODEL_TRANSITION_AGGREGATE, {
|
|
98
|
+
domain: t.string().merge(Lww),
|
|
99
|
+
candidateHash: t.sha256Digest().merge(Lww),
|
|
100
|
+
supersedes: t.sha256Digest().merge(Lww).optional(),
|
|
101
|
+
acceptanceReason: t.string().merge(Lww),
|
|
102
|
+
/** The verified birth parent that directed it (`workspace:<parent>`), derived from this workspace's
|
|
103
|
+
* own BirthAttestation — never from the payload. */
|
|
104
|
+
directedBy: t.string().merge(Lww),
|
|
105
|
+
directedAt: t.string().merge(Lww),
|
|
106
|
+
/** The model-lifecycle holders seated alongside the record. */
|
|
107
|
+
seatedHolders: t.set(t.string()).merge(AddWins),
|
|
108
|
+
}).public();
|
|
109
|
+
|
|
110
|
+
export function parentModelTransitionAggregateId(domain: string, candidateHash: string): string {
|
|
111
|
+
return `${PARENT_MODEL_TRANSITION_PREFIX}${domain}:${candidateHash}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function verifiedBirthParentId(): string {
|
|
115
|
+
const births = readFrameworkDeclaredRows(verifiedBirthAttestation, BirthAttestation, { verified: "true" });
|
|
116
|
+
if (births.length !== 1) {
|
|
117
|
+
throw new Error(`a parent-directed model transition requires exactly one verified BirthAttestation; found ${births.length}`);
|
|
118
|
+
}
|
|
119
|
+
const birth = births[0]!;
|
|
120
|
+
if (birth.verified !== "true" || !birth.parentId?.trim()) {
|
|
121
|
+
throw new Error("a parent-directed model transition requires a verified non-root parent");
|
|
122
|
+
}
|
|
123
|
+
return birth.parentId;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── CHILD SIDE ─────────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/** Record the direction under the verified birth parent's name. */
|
|
129
|
+
export const recordParentModelTransition = nomosImplementationDetail(
|
|
130
|
+
directive(RECORD_PARENT_MODEL_TRANSITION_ID)
|
|
131
|
+
.ensures(ParentModelTransition)
|
|
132
|
+
.payload(parentModelTransitionEvidence)
|
|
133
|
+
.plan(({ direction, authority }): PlannedOp[] => {
|
|
134
|
+
const row = instance(ParentModelTransition, parentModelTransitionAggregateId(direction.domain, direction.candidateHash));
|
|
135
|
+
return [
|
|
136
|
+
withMarker(set(row, "domain", direction.domain), "ensures"),
|
|
137
|
+
set(row, "candidateHash", direction.candidateHash),
|
|
138
|
+
...(direction.supersedes === undefined ? [] : [set(row, "supersedes", direction.supersedes)]),
|
|
139
|
+
set(row, "acceptanceReason", direction.acceptanceReason),
|
|
140
|
+
set(row, "directedBy", `workspace:${verifiedBirthParentId()}`),
|
|
141
|
+
set(row, "directedAt", direction.directedAt),
|
|
142
|
+
addToSet(row, "seatedHolders", authority.holders.map((holder) => holder.principal)),
|
|
143
|
+
];
|
|
144
|
+
})
|
|
145
|
+
.reads(verifiedBirthAttestation),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
/** Seat the parent's disclosed holders — the same ops the genesis seed writes. */
|
|
149
|
+
export const seatParentModelAuthority = nomosImplementationDetail(
|
|
150
|
+
directive(SEAT_PARENT_MODEL_AUTHORITY_ID)
|
|
151
|
+
.ensures(RelationTuple)
|
|
152
|
+
.payload(parentModelTransitionEvidence)
|
|
153
|
+
.plan(({ authority }, ctx) => seatInheritedModelAuthorityOps(authority, ctx.principal().asAttribution())),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
/** THE ACCEPTOR: admitted through the verified-birth-parent door; record + seat are ONE transition. */
|
|
157
|
+
export const acceptParentModelTransition = nomosImplementationDetail(
|
|
158
|
+
intent(ACCEPT_PARENT_MODEL_TRANSITION_ID)
|
|
159
|
+
.contract({ accepts: parentModelTransitionEvidence })
|
|
160
|
+
.directs(recordParentModelTransition, (evidence) => evidence)
|
|
161
|
+
.directs(seatParentModelAuthority, (evidence) => evidence)
|
|
162
|
+
.acceptsVerifiedBirthParentRecoveryEffect()
|
|
163
|
+
.atomically(),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// ── PLATFORM SIDE ──────────────────────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
function currentParentModelTransitionEvidence(input: DirectChildModelTransitionInput): ParentModelTransitionEvidence {
|
|
169
|
+
const holders = readInheritedModelAuthority();
|
|
170
|
+
if (holders === undefined) {
|
|
171
|
+
throw new Error("directing a model transition requires this workspace to hold at least one active installDomain seat");
|
|
172
|
+
}
|
|
173
|
+
return parentModelTransitionEvidence.parse({
|
|
174
|
+
direction: modelTransitionDirection.parse({
|
|
175
|
+
domain: input.domain,
|
|
176
|
+
candidateHash: input.candidateHash,
|
|
177
|
+
...(input.supersedes === undefined ? {} : { supersedes: input.supersedes }),
|
|
178
|
+
acceptanceReason: input.acceptanceReason,
|
|
179
|
+
directedAt: input.directedAt,
|
|
180
|
+
}),
|
|
181
|
+
authority: { holders, seatedAt: input.directedAt },
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** THE DIRECTION: one exact child, addressed from the typed input; the evidence comes from committed
|
|
186
|
+
* platform state. Gated on the platform's own model-lifecycle authority. */
|
|
187
|
+
export const directChildModelTransition = nomosImplementationDetail(
|
|
188
|
+
intent(DIRECT_CHILD_MODEL_TRANSITION_ID)
|
|
189
|
+
.contract({ accepts: directChildModelTransitionInput })
|
|
190
|
+
.reads(relationTuplesByObject)
|
|
191
|
+
.directsWorkspace(acceptParentModelTransition, {
|
|
192
|
+
workspace: ({ workspace }) => workspace,
|
|
193
|
+
payload: currentParentModelTransitionEvidence,
|
|
194
|
+
})
|
|
195
|
+
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" })
|
|
196
|
+
.atomically(),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The export map the application-home policy law composes (keys are the obscure `__nomos_*` slots;
|
|
201
|
+
* values are THE singletons — the law graph folds an intent's step closure by object identity).
|
|
202
|
+
* The two child-side consequences ride only the acceptor's step closure, as `child_authentication.ts`
|
|
203
|
+
* carries its gated acceptor effect.
|
|
204
|
+
*/
|
|
205
|
+
export function modelTransitionExports(): Readonly<Record<string, object>> {
|
|
206
|
+
return {
|
|
207
|
+
__nomos_ParentModelTransition: ParentModelTransition,
|
|
208
|
+
__nomos_acceptParentModelTransition: acceptParentModelTransition,
|
|
209
|
+
__nomos_directChildModelTransition: directChildModelTransition,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
writeWrap,
|
|
20
20
|
} from "./encryption_scope.js";
|
|
21
21
|
import { GenesisWarrant, seedGenesis } from "./genesis.js";
|
|
22
|
-
import { BirthAttestation, BirthAttestationLink, seedBirthAttestation } from "./
|
|
22
|
+
import { BirthAttestation, BirthAttestationLink, seedBirthAttestation } from "./receiving_group.js";
|
|
23
23
|
import { RelationTuple, __qualifyAggregateType, removeTuple, writeTuple } from "./relations.js";
|
|
24
24
|
import { SIGNER_RELATION, enrollSigner, signerObject, identityRelations } from "./signing_identity.js";
|
|
25
25
|
import { impureCapability, impureCapabilityAggregate } from "./impure_capability.js";
|