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