@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
|
@@ -0,0 +1,857 @@
|
|
|
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
|
+
* framework/receiving_group.ts — THE RECEIVING GROUP (the identity role; `architecture/
|
|
8
|
+
* receiving_group_lifecycle.md`, `home_holons_and_cross_custody.md` §4). One holon, three roles —
|
|
9
|
+
* "workspace," "platform," "receiving group" are the SAME githolon wearing the role its installed LAW
|
|
10
|
+
* gives it. A receiving group runs THIS law: an identity's subject-side root — the one small workspace
|
|
11
|
+
* an enrolled device can always clone first. "Home" is a TENANT's word for the business law it composes
|
|
12
|
+
* into this anchor; the framework never uses it (this file was `home.ts` until 2026-09-11; that path
|
|
13
|
+
* remains a deprecated re-export shim for one release).
|
|
14
|
+
*
|
|
15
|
+
* It holds only PUBLIC material + signed facts (private keys never leave the client, so the cloud hosting
|
|
16
|
+
* it stays operator-blind):
|
|
17
|
+
* - KEY DIRECTORY — the identity's public device keys: the `signer` relation
|
|
18
|
+
* (`identity:<principal>#signer@key:<hash>`, `framework/signing_identity.ts`). Re-exported here so a
|
|
19
|
+
* home holon's law carries it: "wrap the scope key to U's device" becomes "read U's home key roster."
|
|
20
|
+
* - SHARED-WITH-ME index (§5) — one record per verified cross-custody attestation delivered here, so
|
|
21
|
+
* "show me everything shared with me" is a LOCAL query, no fan-out.
|
|
22
|
+
* - INBOX — grants/invites/notifications/dead-letters addressed to the identity.
|
|
23
|
+
*
|
|
24
|
+
* THE FORWARD-CHECK DOCTRINE (§5): the index is a DISCOVERY HINT, not the authority. When the identity
|
|
25
|
+
* actually opens the asserting workspace, THAT workspace's gate does the live `Check` — so a stale share
|
|
26
|
+
* (the asserter revoked it) is fail-safe: the identity sees a workspace it can no longer open, the forward
|
|
27
|
+
* check denies, the hint is pruned. (The cross-custody ATTESTATION that proves a delivered share is signed
|
|
28
|
+
* + verifiable is §3; the host DELIVERY lane that fills the index is §5. This file is the law that holds
|
|
29
|
+
* the result, and the owner-authored lane to maintain it.)
|
|
30
|
+
*/
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
import type { ApplicationHomeGenesis } from "../application_home_genesis.js";
|
|
33
|
+
import { aggregate, instance, OWNED_READ_RELATION } from "../aggregate.js";
|
|
34
|
+
import { sha256Digest, t } from "../fields.js";
|
|
35
|
+
import { Lww } from "../drivers.js";
|
|
36
|
+
import { directive } from "../directive.js";
|
|
37
|
+
import { ensure } from "../authoring.js";
|
|
38
|
+
import { set, withMarker, type PlannedOp } from "../ops.js";
|
|
39
|
+
import { query } from "../query.js";
|
|
40
|
+
import { AUTH_PROVIDER_EPOCH_CONSUMER, birth, type WorkspaceRole } from "../births.js";
|
|
41
|
+
import {
|
|
42
|
+
authProvider as authProviderQuery,
|
|
43
|
+
authProviderCertificates as authProviderCertificatesQuery,
|
|
44
|
+
authProviderKeys as authProviderKeysQuery,
|
|
45
|
+
readAuthProviderVerifyingConfiguration,
|
|
46
|
+
seedAuthProvider,
|
|
47
|
+
} from "./auth_provider_law.js";
|
|
48
|
+
import { readRef, readSet, workspaceInvariant, refAs } from "./workspace_invariant.js";
|
|
49
|
+
import {
|
|
50
|
+
identityRelations,
|
|
51
|
+
} from "./signing_identity.js";
|
|
52
|
+
import {
|
|
53
|
+
RelationTuple,
|
|
54
|
+
relationTuplesByObject,
|
|
55
|
+
writeTuple,
|
|
56
|
+
} from "./relations.js";
|
|
57
|
+
import { inheritedModelAuthorityGenesisStep } from "./model_authority_seed.js";
|
|
58
|
+
import { relationType, self, user, selfObject } from "../relation_schema.js";
|
|
59
|
+
import { requireParentAttestedBirth, type ParentAttestedBirthMarker } from "../births.js";
|
|
60
|
+
import { GenesisWarrant, seedGenesis } from "./genesis.js";
|
|
61
|
+
import { idOf } from "../value_object.js";
|
|
62
|
+
import {
|
|
63
|
+
DomainInstallExecutor,
|
|
64
|
+
domainInstallGenesisOffer,
|
|
65
|
+
} from "./domain_lifecycle.js";
|
|
66
|
+
import { birthCertificatePayload } from "./birth_certificate_law.js";
|
|
67
|
+
import { recordReceivedWorkspaceAccess } from "./workspace_access.js";
|
|
68
|
+
import { readInheritedOperators, seedFoundingPrincipal } from "./founding_genesis.js";
|
|
69
|
+
import { readInheritedEnrollers, relationTuplesByRelation } from "./principal_enrollment.js";
|
|
70
|
+
import { inheritedGovernanceStep } from "./console_governance.js";
|
|
71
|
+
import { delegatedRolesSteps, RECEIVING_GROUP_CHILD_KIND } from "./delegated_roles.js";
|
|
72
|
+
export { recordReceivedWorkspaceAccess, receivedWorkspaceAccess } from "./workspace_access.js";
|
|
73
|
+
|
|
74
|
+
// `nomos.sha256` — the TRUSTLESS, pure hash computed IN the githolon (Rust sha2 bound in the sealed engine;
|
|
75
|
+
// see deterministic-rquickjs). Available as a global to plans + invariant predicates (like `nomos.mint`); it
|
|
76
|
+
// lets the home law re-derive `home-<hash(owner)>` itself, so the SQUAT invariant runs in-law on every peer.
|
|
77
|
+
declare const nomos: { sha256(input: string): string };
|
|
78
|
+
|
|
79
|
+
/** The KEY DIRECTORY is the `signer` relation — re-exported so a home holon's law composes it (the
|
|
80
|
+
* identity's public device keys, enrolled via `enrollSigner`, checked by the gate). */
|
|
81
|
+
export { identityRelations };
|
|
82
|
+
|
|
83
|
+
/** THE GENESIS WARRANT primitives — re-exported so a HOME law carries them in the `home` domain key. The
|
|
84
|
+
* birthReceivingGroup genesis chain runs `home/seedGenesis` to record the home's unique self-id (== homeName), so
|
|
85
|
+
* `workspace_self_id` resolves on the home and BOTH the signed-author regime + the parent-attested-birth
|
|
86
|
+
* cert door FIRE on every home (BREAK 1). Without this the home is unwarranted and both doors are inert. */
|
|
87
|
+
export { GenesisWarrant, seedGenesis };
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* THE RECEIVING GROUP — one sovereign framework identity whose enrolled devices clone this same holon.
|
|
91
|
+
* It is not a tenant Person, an IdP account or a host-owned account. The native global
|
|
92
|
+
* `identity:<principal>#signer@key:<hash>` relation is its device roster; this singleton binds the group's
|
|
93
|
+
* stable principal to the workspace that carries that roster and its access manifest.
|
|
94
|
+
*
|
|
95
|
+
* The wire identifiers are deliberately obscure framework names. Tenant business packages may model their
|
|
96
|
+
* own `ReceivingGroup`, `home` or `group` concepts without silently colliding with this implementation.
|
|
97
|
+
*/
|
|
98
|
+
export const RECEIVING_GROUP_ID = "nomos-internal-receiving-group-7c1f5e:self";
|
|
99
|
+
/** A receiving group's self-committed workspace-name prefix. */
|
|
100
|
+
export const RECEIVING_GROUP_NAME_PREFIX = "nomos-rg-";
|
|
101
|
+
export const RECEIVING_GROUP_NAME_HASH_LEN = 40;
|
|
102
|
+
export const RECEIVING_GROUP_POLICY_ID = "nomos-internal-receiving-group-policy-7c1f5e:self";
|
|
103
|
+
export const ReceivingGroup = aggregate("NomosInternalReceivingGroup7c1f5e", {
|
|
104
|
+
/** The stable Zanzibar principal represented by this group. Credentials may locate it but do not own it. */
|
|
105
|
+
principal: t.string().merge(Lww),
|
|
106
|
+
/** The group's self-committed workspace name, deterministically bound to its stable principal. */
|
|
107
|
+
workspaceName: t.string().merge(Lww),
|
|
108
|
+
/** ISO-8601, caller-stamped (determinism — no clock in the plan). */
|
|
109
|
+
bornAt: t.string().merge(Lww),
|
|
110
|
+
// The group's genesis seats its controller in OWNED_READ_RELATION below. Declaring that same capability
|
|
111
|
+
// here makes the law's coarse pack gate recognise the seat, so a newly enrolled device can clone its own
|
|
112
|
+
// receiving group before any tenant records or tenant-authored grants exist. This remains ordinary,
|
|
113
|
+
// replayable Zanzibar state: revocation removes the seat; there is no kernel-level founder bypass.
|
|
114
|
+
}, { cap: 1 }).readableBy(OWNED_READ_RELATION);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The framework-owned device lifecycle policy for one receiving group. This is deliberately data, not a
|
|
118
|
+
* client-side branch: every peer replays the same rule. The first supported posture is intentionally small
|
|
119
|
+
* and secure: a live, verified device may add or revoke another device; keyless IdP enrolment is only the
|
|
120
|
+
* pre-group bootstrap lane.
|
|
121
|
+
*
|
|
122
|
+
* ALL-DEVICE-LOSS RECOVERY — CO2 already recovers through its platform AuthProvider, but the implementation
|
|
123
|
+
* reaches that result by repeating birth after this home's direct enrollment policy refuses. The corrected
|
|
124
|
+
* design (`architecture/guardian_device_recovery.md`, reconciled 2026-08-10) makes recovery authority an
|
|
125
|
+
* explicit tenant-selected policy: platform identity provider or guardian quorum. The missing work is to
|
|
126
|
+
* evaluate that choice at the existing child's frontier and delete the client-side birth-repeat bypass.
|
|
127
|
+
*/
|
|
128
|
+
export const ReceivingGroupPolicy = aggregate("NomosInternalReceivingGroupPolicy7c1f5e", {
|
|
129
|
+
additionalDevice: t.string().merge(Lww),
|
|
130
|
+
revocation: t.string().merge(Lww),
|
|
131
|
+
recovery: t.string().merge(Lww),
|
|
132
|
+
guardianQuorum: t.int().merge(Lww),
|
|
133
|
+
setAt: t.string().merge(Lww),
|
|
134
|
+
}, { cap: 1 }).public();
|
|
135
|
+
|
|
136
|
+
export const DEFAULT_RECEIVING_GROUP_POLICY = {
|
|
137
|
+
additionalDevice: "current-device",
|
|
138
|
+
revocation: "current-device",
|
|
139
|
+
recovery: "guardian-quorum",
|
|
140
|
+
guardianQuorum: 1,
|
|
141
|
+
} as const;
|
|
142
|
+
|
|
143
|
+
/** The recovery authorities a receiving group may explicitly select. The default remains guardian
|
|
144
|
+
* quorum; an application must deliberately choose its attested parent identity provider. */
|
|
145
|
+
export const RECEIVING_GROUP_RECOVERY_POLICIES = [
|
|
146
|
+
"guardian-quorum",
|
|
147
|
+
"platform-identity-provider",
|
|
148
|
+
] as const;
|
|
149
|
+
export const receivingGroupRecoveryPolicy = z.enum(RECEIVING_GROUP_RECOVERY_POLICIES);
|
|
150
|
+
export type ReceivingGroupRecoveryPolicy = z.infer<typeof receivingGroupRecoveryPolicy>;
|
|
151
|
+
export const PLATFORM_IDENTITY_PROVIDER_RECOVERY: ReceivingGroupRecoveryPolicy =
|
|
152
|
+
"platform-identity-provider";
|
|
153
|
+
|
|
154
|
+
/** The one policy write: genesis seeds it, and the framework's recovery acceptor re-seats it for a
|
|
155
|
+
* receiving group born under the untouched framework default (`receiving_group_recovery.ts`). */
|
|
156
|
+
export function writeReceivingGroupPolicy(p: {
|
|
157
|
+
setAt: string;
|
|
158
|
+
recovery?: ReceivingGroupRecoveryPolicy | undefined;
|
|
159
|
+
}): PlannedOp[] {
|
|
160
|
+
const policy = instance(ReceivingGroupPolicy, RECEIVING_GROUP_POLICY_ID);
|
|
161
|
+
return [
|
|
162
|
+
set(policy, "additionalDevice", DEFAULT_RECEIVING_GROUP_POLICY.additionalDevice),
|
|
163
|
+
set(policy, "revocation", DEFAULT_RECEIVING_GROUP_POLICY.revocation),
|
|
164
|
+
set(policy, "recovery", p.recovery ?? DEFAULT_RECEIVING_GROUP_POLICY.recovery),
|
|
165
|
+
set(policy, "guardianQuorum", DEFAULT_RECEIVING_GROUP_POLICY.guardianQuorum),
|
|
166
|
+
set(policy, "setAt", p.setAt),
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Genesis-only default policy. Policy changes become a separate, guardian-aware ceremony. */
|
|
171
|
+
export const seedReceivingGroupPolicy = directive("nomosInternalSeedReceivingGroupPolicy7c1f5e")
|
|
172
|
+
.ensures(ReceivingGroupPolicy)
|
|
173
|
+
.payload(z.object({
|
|
174
|
+
setAt: z.string().min(1),
|
|
175
|
+
recovery: receivingGroupRecoveryPolicy.optional(),
|
|
176
|
+
}))
|
|
177
|
+
.plan((p) => writeReceivingGroupPolicy(p));
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The group's controller relation binds its stable principal inside the same genesis. Business representation
|
|
181
|
+
* remains separate Zanzibar edges; this relation only establishes who controls this receiving-group holon.
|
|
182
|
+
*/
|
|
183
|
+
export const receivingGroupRelations = relationType("nomos.internal.receiving-group.7c1f5e").relation("controller", {
|
|
184
|
+
rewrite: self(),
|
|
185
|
+
allow: [user()],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/** Stamp the receiving group and atomically seat its controller relation. */
|
|
189
|
+
export function writeReceivingGroup(p: { principal: string; workspaceName: string; bornAt: string }): PlannedOp[] {
|
|
190
|
+
const h = instance(ReceivingGroup, RECEIVING_GROUP_ID);
|
|
191
|
+
return [
|
|
192
|
+
set(h, "principal", p.principal),
|
|
193
|
+
set(h, "workspaceName", p.workspaceName),
|
|
194
|
+
set(h, "bornAt", p.bornAt),
|
|
195
|
+
...writeTuple({
|
|
196
|
+
object: selfObject(receivingGroupRelations),
|
|
197
|
+
relation: "controller",
|
|
198
|
+
subject: p.principal,
|
|
199
|
+
grantedBy: p.principal,
|
|
200
|
+
grantedAt: p.bornAt,
|
|
201
|
+
}),
|
|
202
|
+
// A device clones the encrypted workspace before record-level ownership can be evaluated locally.
|
|
203
|
+
// Seat the group's controller for that ciphertext transport in the same genesis; individual owned
|
|
204
|
+
// records still require their own per-record reader edge before they decrypt or appear in reads.
|
|
205
|
+
...writeTuple({
|
|
206
|
+
object: "workspace:self",
|
|
207
|
+
relation: OWNED_READ_RELATION,
|
|
208
|
+
subject: p.principal,
|
|
209
|
+
grantedBy: p.principal,
|
|
210
|
+
grantedAt: p.bornAt,
|
|
211
|
+
}),
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Genesis-only receiving-group stamp. The name is caller-supplied but the invariant below re-derives it.
|
|
217
|
+
*/
|
|
218
|
+
export const seedReceivingGroup = directive("nomosInternalSeedReceivingGroup7c1f5e")
|
|
219
|
+
.ensures(ReceivingGroup)
|
|
220
|
+
.payload(z.object({ principal: z.string().min(1), workspaceName: z.string().min(1), bornAt: z.string().min(1) }))
|
|
221
|
+
.plan((p) => writeReceivingGroup(p))
|
|
222
|
+
.alsoEnsures(RelationTuple);
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* THE SQUAT INVARIANT (home_holons §5) — a home holon's self-committed `homeName` MUST be the hash of its
|
|
226
|
+
* owner: `homeName == home-<sha256(principal)>`. Declared by the home law, it EXECUTES at the one gate — and
|
|
227
|
+
* crucially at `verify_chain` (the bailiff's upload-birth): a squatter's chain (claiming `home-<hash(bob)>`
|
|
228
|
+
* but owned by `mallory`) FAILS the home law's OWN gate during adoption — refused IN THE GITHOLON, BY THE LAW,
|
|
229
|
+
* never by a host check. The host only routes the chain to the name it commits to (custody). `nomos.sha256`
|
|
230
|
+
* re-derives the hash in-law on every peer, so the verdict is identical everywhere.
|
|
231
|
+
*/
|
|
232
|
+
export const receivingGroupNameBindsPrincipal = workspaceInvariant("ReceivingGroupNameBindsPrincipal")
|
|
233
|
+
.on("nomosInternalSeedReceivingGroup7c1f5e")
|
|
234
|
+
.reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
|
|
235
|
+
.declassify("the receiving-group address is a public hash of its stable principal; refusing a mismatch reveals no private field")
|
|
236
|
+
.assert(({ group }) => {
|
|
237
|
+
const g = (group ?? {}) as { principal?: string; workspaceName?: string };
|
|
238
|
+
const expected = RECEIVING_GROUP_NAME_PREFIX
|
|
239
|
+
+ nomos.sha256(String(g.principal ?? "")).slice(0, RECEIVING_GROUP_NAME_HASH_LEN);
|
|
240
|
+
return g.workspaceName === expected
|
|
241
|
+
? { accept: true }
|
|
242
|
+
: { reject: "a receiving group's workspace name must hash its stable principal" };
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const receivingGroupPolicyRefs = readSet(
|
|
246
|
+
readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID),
|
|
247
|
+
readRef("policy", ReceivingGroupPolicy).at(RECEIVING_GROUP_POLICY_ID),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
/** An IdP token may bootstrap the parent before the group exists; it may never bypass an existing group's
|
|
251
|
+
* device policy. An existing group chooses whether recovery is delegated to its platform AuthProvider or to
|
|
252
|
+
* guardians; the current default below says guardian, while CO2's current product behaviour is platform IdP.
|
|
253
|
+
*
|
|
254
|
+
* KNOWN GAP (`architecture/guardian_device_recovery.md`): this invariant is declared `.on("enrollFirstDevice")`
|
|
255
|
+
* and only fires when that directive targets THIS home directly. It is NOT re-applied when the birth
|
|
256
|
+
* offer-effect (`run_birth_effect`, repeating `nomosInternalBirthReceivingGroup` against an ALREADY-founded
|
|
257
|
+
* home) folds a signer seat via the SAME underlying mechanism. That is how CO2 recovery works today, but its
|
|
258
|
+
* home frontier still says `guardian-quorum`. The fix is not to ban platform recovery: add the explicit
|
|
259
|
+
* platform-identity-provider policy, make CO2 choose it, and have the child gate verify the chosen evidence. */
|
|
260
|
+
export const receivingGroupRejectsKeylessAdditionalDevice = workspaceInvariant("ReceivingGroupRejectsKeylessAdditionalDevice")
|
|
261
|
+
.on("enrollFirstDevice")
|
|
262
|
+
.reads(receivingGroupPolicyRefs)
|
|
263
|
+
.declassify("device enrollment deliberately reveals that this receiving group has already completed first-device bootstrap")
|
|
264
|
+
.assert(({ group }) => typeof group?.principal !== "string" || group.principal.length === 0
|
|
265
|
+
? { accept: true }
|
|
266
|
+
: { reject: "receiving-group-additional-device-requires-current-device-or-guardian" });
|
|
267
|
+
|
|
268
|
+
/** The shipped additional-device policy: an already enrolled, verified device signs the self-bound enrol. */
|
|
269
|
+
export const receivingGroupAdditionalDeviceFollowsPolicy = workspaceInvariant("ReceivingGroupAdditionalDeviceFollowsPolicy")
|
|
270
|
+
.on("enrollSignerSelf")
|
|
271
|
+
.reads(receivingGroupPolicyRefs)
|
|
272
|
+
.declassify("device lifecycle policy intentionally branches on principal_verified")
|
|
273
|
+
.assert(({ group, policy }, ctx) => {
|
|
274
|
+
if (typeof group?.principal !== "string" || group.principal.length === 0) return { accept: true };
|
|
275
|
+
const p = policy as { additionalDevice?: string } | undefined;
|
|
276
|
+
return p?.additionalDevice === "current-device" && ctx?.principalVerified === true
|
|
277
|
+
? { accept: true }
|
|
278
|
+
: { reject: "receiving-group-additional-device-policy-refused" };
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
/** The shipped revocation policy: a live verified group device authors the remove-wins signer change. */
|
|
282
|
+
export const receivingGroupRevocationFollowsPolicy = workspaceInvariant("ReceivingGroupRevocationFollowsPolicy")
|
|
283
|
+
.on("revokeSigner")
|
|
284
|
+
.reads(receivingGroupPolicyRefs)
|
|
285
|
+
.declassify("device lifecycle policy intentionally branches on principal_verified")
|
|
286
|
+
.assert(({ group, policy }, ctx) => {
|
|
287
|
+
if (typeof group?.principal !== "string" || group.principal.length === 0) return { accept: true };
|
|
288
|
+
const p = policy as { revocation?: string } | undefined;
|
|
289
|
+
return p?.revocation === "current-device" && ctx?.principalVerified === true
|
|
290
|
+
? { accept: true }
|
|
291
|
+
: { reject: "receiving-group-revocation-policy-refused" };
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
/** Re-derive a receiving group's workspace name in-law. */
|
|
295
|
+
export function receivingGroupNameOf(principal: string): string {
|
|
296
|
+
return RECEIVING_GROUP_NAME_PREFIX
|
|
297
|
+
+ nomos.sha256(String(principal)).slice(0, RECEIVING_GROUP_NAME_HASH_LEN);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** The speaker is established by the ordinary carried-offer gate. This law binds
|
|
301
|
+
* its otherwise source-scoped access claim to the actual recipient of this Home. */
|
|
302
|
+
export const receivedWorkspaceAccessIsForOwner = workspaceInvariant("ReceivedWorkspaceAccessIsForOwner")
|
|
303
|
+
.on(recordReceivedWorkspaceAccess.id)
|
|
304
|
+
.reads(readSet(readRef("group", ReceivingGroup).at(RECEIVING_GROUP_ID)))
|
|
305
|
+
.declassify("acceptance discloses only that this access claim was addressed to the receiving principal")
|
|
306
|
+
.assert(({ group }, ctx) => group?.principal === ctx?.intent.recipient
|
|
307
|
+
? { accept: true }
|
|
308
|
+
: { reject: "workspace-access-wrong-recipient" });
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* `birthReceivingGroup` (née `birthHome`) — THE FRAMEWORK BIRTH PRIMITIVE for a receiving group. This is
|
|
312
|
+
* the MECHANICS (OURS) so a tenant never hand-builds a home genesis: the parent (the platform whose law includes this directive)
|
|
313
|
+
* ORDERS the birth of an identity's home holon, and the kernel's offer-effect spawns it + self-validates it
|
|
314
|
+
* through the child's OWN gate from intent 0 (the `.births()` declared consequence; `architecture/
|
|
315
|
+
* home_holons_and_cross_custody.md` + `sovereign_adapter.md` "birth is a kernel offer-effect").
|
|
316
|
+
*
|
|
317
|
+
* SELF-BOUND — squatting is structurally impossible (no separate grant needed; the policy gate is the
|
|
318
|
+
* tenant's `setAuthProvider` auth scoping, which decides whether the offering principal is verified at all):
|
|
319
|
+
* 1. The home OWNER rides the payload (`owner`, a `user:<uid>` subject). `ctx.principal()` is opaque
|
|
320
|
+
* PrincipalEvidence — it CANNOT be read as a raw string in-plan to compute the slot name — so the owner
|
|
321
|
+
* is payload-carried, but the binding below makes it sound.
|
|
322
|
+
* 2. The workspace name + the seeded `homeName` are BOTH derived from THE SAME payload `owner` via
|
|
323
|
+
* {@link homeNameOf} — they cannot diverge in this plan.
|
|
324
|
+
* 3. The child's genesis includes `seedHomeOwner(owner, homeNameOf(owner), bornAt)`, and the home law's
|
|
325
|
+
* {@link homeNameBindsOwner} invariant RE-DERIVES `home-<sha256(owner)>` at the child's gate (author AND
|
|
326
|
+
* verify_chain). So a chain that claims `home-<hash(alice)>` but seeds owner `bob` FAILS the child's own
|
|
327
|
+
* law — refused in the githolon, by the law, never by a host. A verified principal is required upstream
|
|
328
|
+
* (no anonymous home birth); the offer principal IS the auth boundary (the tenant's setAuthProvider).
|
|
329
|
+
*
|
|
330
|
+
* The genesis chain it authors (reusing the order-declares-the-law install shape — bytes resolved by hash
|
|
331
|
+
* from the parent's typed USDA closure custody):
|
|
332
|
+
* [ bootstrap/installDomain(frameworkHash), nomos/home installDomain(lawHash), seedHomeOwner(owner, …) ]
|
|
333
|
+
* `frameworkHash` (the bootstrap controller hash) + `lawHash` (the HOME law package domainHash) are
|
|
334
|
+
* tenant-PINNED at their governance compile (mirroring `birthChild`) — the end-user app supplies neither;
|
|
335
|
+
* the tenant's HomeBirth.viaCoordinator buildBody / their governance fills them. `seedHomeOwner` is INCLUDED
|
|
336
|
+
* so HomeOwner is seeded and `homeNameBindsOwner` passes (a birth without it would be an invalid, un-owned
|
|
337
|
+
* home).
|
|
338
|
+
*/
|
|
339
|
+
// Standalone framework law explicitly declares `home`; compiler-composed business applications explicitly
|
|
340
|
+
// declare the obscure APPLICATION_HOME_DOMAIN. The kernel refuses law with no attestation-seed registrar.
|
|
341
|
+
export const RECEIVING_GROUP_LAW_DOMAIN = "home";
|
|
342
|
+
/**
|
|
343
|
+
* Build the private home-birth primitive for the domain key that carries the home law.
|
|
344
|
+
*
|
|
345
|
+
* Most framework-authored laws still use the historic public `home` key through
|
|
346
|
+
* {@link birthReceivingGroup}. Business packages use an obscure compiler-owned key instead, so a
|
|
347
|
+
* tenant remains free to model a business concept called "home". The key is closed over
|
|
348
|
+
* here and written into every child-genesis step; it is never a tenant payload field.
|
|
349
|
+
*/
|
|
350
|
+
export function birthReceivingGroupForDomain(
|
|
351
|
+
groupLawDomain: string,
|
|
352
|
+
maxChildWorkspacesPerUser: number,
|
|
353
|
+
birthPolicyDomains: readonly string[],
|
|
354
|
+
homeGenesis?: ApplicationHomeGenesis,
|
|
355
|
+
parentWorkspaceRoles: readonly WorkspaceRole[] = [],
|
|
356
|
+
recoveryPosture?: ReceivingGroupRecoveryPolicy,
|
|
357
|
+
) {
|
|
358
|
+
if (groupLawDomain.length === 0) {
|
|
359
|
+
throw new Error("birthReceivingGroupForDomain requires a non-empty receiving-group law domain");
|
|
360
|
+
}
|
|
361
|
+
if (recoveryPosture !== undefined && !RECEIVING_GROUP_RECOVERY_POLICIES.includes(recoveryPosture)) {
|
|
362
|
+
throw new Error(
|
|
363
|
+
`birthReceivingGroupForDomain: unknown receiving-group recovery posture '${String(recoveryPosture)}'`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
if (!Number.isInteger(maxChildWorkspacesPerUser) || maxChildWorkspacesPerUser < 0) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
"birthReceivingGroupForDomain requires an explicit non-negative maxChildWorkspacesPerUser; " +
|
|
369
|
+
"Nomos has no compiled birth-policy fallback",
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const policyDomains = [...new Set(birthPolicyDomains)];
|
|
373
|
+
if (
|
|
374
|
+
policyDomains.length === 0 ||
|
|
375
|
+
policyDomains.some((domain) => domain.trim().length === 0) ||
|
|
376
|
+
!policyDomains.includes(groupLawDomain)
|
|
377
|
+
) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
"birthReceivingGroupForDomain requires every co-resident birth-capable law domain, " +
|
|
380
|
+
"including its own domain; Nomos will not birth partially configured law",
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
const birthDirective = directive("nomosInternalBirthReceivingGroup7c1f5e")
|
|
384
|
+
.creates(ReceivingGroup)
|
|
385
|
+
.payload(
|
|
386
|
+
z.object({
|
|
387
|
+
/** The identity this home belongs to (a `user:<uid>` subject). The workspace name + seeded homeName are
|
|
388
|
+
* both `home-<sha256(owner)>` — self-bound, so you can only birth YOUR OWN home slot. */
|
|
389
|
+
owner: z.string().min(1),
|
|
390
|
+
/** The bootstrap framework controller content hash — tenant-pinned at governance compile (the child
|
|
391
|
+
* installs it IN-CHAIN as intent 0; the kernel resolves the bytes by hash from the parent's custody). */
|
|
392
|
+
frameworkHash: sha256Digest(),
|
|
393
|
+
/** The HOME law package domainHash — tenant-pinned at governance compile (the child's second install). */
|
|
394
|
+
lawHash: sha256Digest(),
|
|
395
|
+
/** ISO-8601 birth time, caller-stamped (the timestamp doctrine — the plan reads no clock). */
|
|
396
|
+
bornAt: z.string().min(1),
|
|
397
|
+
/** AUTHPROVIDER INHERITANCE — DEFAULT ON. Birth copies the current verifying material once;
|
|
398
|
+
* later rotations arrive through the ordinary lazy provider-adoption offer. */
|
|
399
|
+
inheritAuthProvider: z.boolean().optional(),
|
|
400
|
+
/** TOKEN-FREE FOUNDING (identity target model D22): sha256hex of the owner's device public key.
|
|
401
|
+
* Present ⇒ the owner FOUNDS their own group — `seedFoundingPrincipal` seats this key as the
|
|
402
|
+
* owner's signer and the owner as admin, with the parent's operators and enrollers — and the
|
|
403
|
+
* recipe carries no provider verifier, certificate or parent role, so the kernel's birth effect
|
|
404
|
+
* takes the founded lane. The birth must then be authored by the owner (see
|
|
405
|
+
* {@link receivingGroupFoundedBirthBindsOwner}). Absent ⇒ the historical recipe. */
|
|
406
|
+
foundingKeyHash: sha256Digest().optional(),
|
|
407
|
+
}),
|
|
408
|
+
)
|
|
409
|
+
.plan((p) => {
|
|
410
|
+
const founder = p.owner; // the actor for the child's own genesis steps (its self-bound owner subject)
|
|
411
|
+
const groupName = receivingGroupNameOf(p.owner);
|
|
412
|
+
const founded = p.foundingKeyHash !== undefined;
|
|
413
|
+
const inheritedProvider = founded || p.inheritAuthProvider === false
|
|
414
|
+
? undefined
|
|
415
|
+
: readAuthProviderVerifyingConfiguration();
|
|
416
|
+
const bornAtMs = Date.parse(p.bornAt);
|
|
417
|
+
if (founded && !Number.isFinite(bornAtMs)) {
|
|
418
|
+
throw new Error("a founded receiving-group birth needs bornAt as an ISO-8601 instant");
|
|
419
|
+
}
|
|
420
|
+
// The owner founds the group: its device key, the owner as admin, and the parent's operators and
|
|
421
|
+
// enrollers (D15/D22), in the one requires-free founding step on the warrant-free prefix. It closes
|
|
422
|
+
// the recipe, in the group's own law domain: the kernel appends the birth origin and the warrant to
|
|
423
|
+
// the LAST step's domain, so the recipe must end in the law that declares them (a tenant
|
|
424
|
+
// home-genesis step last would route them to a tenant domain that has neither).
|
|
425
|
+
const inheritedEnrollers = founded ? readInheritedEnrollers() : [];
|
|
426
|
+
const inheritedOperators = founded ? readInheritedOperators() : [];
|
|
427
|
+
const foundingSteps = founded
|
|
428
|
+
? [{
|
|
429
|
+
domain: groupLawDomain,
|
|
430
|
+
directiveId: seedFoundingPrincipal.id,
|
|
431
|
+
actor: founder,
|
|
432
|
+
payload: seedFoundingPrincipal.payloadSchema.parse({
|
|
433
|
+
principal: p.owner,
|
|
434
|
+
keyHash: p.foundingKeyHash,
|
|
435
|
+
operators: [...inheritedOperators],
|
|
436
|
+
enrollers: inheritedEnrollers.map((enroller) => ({ scope: enroller.scope, subject: enroller.subject })),
|
|
437
|
+
grantedAt: bornAtMs,
|
|
438
|
+
}) as Record<string, unknown>,
|
|
439
|
+
},
|
|
440
|
+
// Each inherited enroller's keys, so it can act here (enrol the owner's next device): keys live
|
|
441
|
+
// on every workspace, which is what keeps each one independently replayable.
|
|
442
|
+
// The inherited operators' and enrollers' keys, and installDomain for the operators, so the
|
|
443
|
+
// jurisdiction can enrol the owner's next device and roll out law here without a re-found.
|
|
444
|
+
...inheritedGovernanceStep(groupLawDomain, founder, inheritedOperators, inheritedEnrollers, bornAtMs,
|
|
445
|
+
{ principal: p.owner, keyHash: p.foundingKeyHash as string })]
|
|
446
|
+
: [];
|
|
447
|
+
// The roles the parent delegates into every group its owner's scope founds (formerly a per-intent
|
|
448
|
+
// delegation certificate), as state tuples for the owner — each seated in the domain whose law
|
|
449
|
+
// declares it. They precede the founding steps, which must close the recipe (see above).
|
|
450
|
+
const delegatedRoleSteps = founded
|
|
451
|
+
? delegatedRolesSteps(founder, p.owner, RECEIVING_GROUP_CHILD_KIND, bornAtMs)
|
|
452
|
+
: [];
|
|
453
|
+
const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string }> = [
|
|
454
|
+
// THE ORDER-DECLARED LAW INSTALL: two typed closure identities. The kernel resolves each
|
|
455
|
+
// closure from the parent's custody when it folds the birth; law bytes never ride the intent.
|
|
456
|
+
domainInstallGenesisOffer(
|
|
457
|
+
DomainInstallExecutor.StageZero,
|
|
458
|
+
p.frameworkHash,
|
|
459
|
+
founder,
|
|
460
|
+
),
|
|
461
|
+
domainInstallGenesisOffer(
|
|
462
|
+
DomainInstallExecutor.LifecycleController,
|
|
463
|
+
p.lawHash,
|
|
464
|
+
founder,
|
|
465
|
+
),
|
|
466
|
+
// THE POSTURE IS BORN, NOT MIGRATED. The application's receiving-group declaration names the
|
|
467
|
+
// recovery authority its anchors are born with; an application that declares none seeds the
|
|
468
|
+
// framework default exactly as before (the field is omitted, so the recipe is byte-identical).
|
|
469
|
+
{
|
|
470
|
+
domain: groupLawDomain,
|
|
471
|
+
directiveId: "nomosInternalSeedReceivingGroupPolicy7c1f5e",
|
|
472
|
+
actor: founder,
|
|
473
|
+
payload: recoveryPosture === undefined
|
|
474
|
+
? { setAt: p.bornAt }
|
|
475
|
+
: { setAt: p.bornAt, recovery: recoveryPosture },
|
|
476
|
+
},
|
|
477
|
+
// SEED THE OWNER — the home is born OWNED; the homeNameBindsOwner invariant in the child's law proves
|
|
478
|
+
// homeName == home-<sha256(owner)> at the child's own gate, so the slot can ONLY be its rightful owner's.
|
|
479
|
+
{
|
|
480
|
+
domain: groupLawDomain,
|
|
481
|
+
directiveId: "nomosInternalSeedReceivingGroup7c1f5e",
|
|
482
|
+
actor: founder,
|
|
483
|
+
payload: { principal: p.owner, workspaceName: groupName, bornAt: p.bornAt },
|
|
484
|
+
},
|
|
485
|
+
// The receiving group is born with its child-workspace allowance as explicit law-state. The
|
|
486
|
+
// value is closed over by the package's application policy, so neither the host nor the end
|
|
487
|
+
// user can substitute it in the offer payload.
|
|
488
|
+
...policyDomains.map((domain) => ({
|
|
489
|
+
domain,
|
|
490
|
+
directiveId: "seedBirthPolicy",
|
|
491
|
+
actor: founder,
|
|
492
|
+
payload: {
|
|
493
|
+
maxBirthsPerPrincipal: maxChildWorkspacesPerUser,
|
|
494
|
+
setBy: p.owner,
|
|
495
|
+
setAt: p.bornAt,
|
|
496
|
+
},
|
|
497
|
+
})),
|
|
498
|
+
// Tenant founding law is an ordered list of ordinary offers. Values derive solely from the
|
|
499
|
+
// canonical home facts; every payload is parsed by the target directive before it reaches
|
|
500
|
+
// the child's author gate. There is no host append point and no framework-selected fallback.
|
|
501
|
+
...(homeGenesis?.steps ?? []).map((step) => ({
|
|
502
|
+
domain: step.domain,
|
|
503
|
+
directiveId: step.directive.id,
|
|
504
|
+
actor: founder,
|
|
505
|
+
payload: step.directive.payloadSchema.parse(step.payload({
|
|
506
|
+
owner: p.owner,
|
|
507
|
+
workspaceName: groupName,
|
|
508
|
+
bornAt: p.bornAt,
|
|
509
|
+
})),
|
|
510
|
+
})),
|
|
511
|
+
...delegatedRoleSteps,
|
|
512
|
+
...foundingSteps,
|
|
513
|
+
];
|
|
514
|
+
if (!founded && p.inheritAuthProvider !== false) {
|
|
515
|
+
if (inheritedProvider !== undefined) {
|
|
516
|
+
genesisChain.push({
|
|
517
|
+
domain: groupLawDomain,
|
|
518
|
+
directiveId: "seedAuthProvider",
|
|
519
|
+
actor: founder,
|
|
520
|
+
payload: {
|
|
521
|
+
...inheritedProvider,
|
|
522
|
+
label: "inherited-from-parent",
|
|
523
|
+
setBy: p.owner,
|
|
524
|
+
setAt: 1,
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// MODEL AUTHORITY DESCENDS (architecture/parent_directed_model_transition.md §4, lane A): the anchor is
|
|
530
|
+
// born holding its platform's current installDomain holders + their devices, read from THIS
|
|
531
|
+
// workspace's committed relation state, so the platform's own signer may lawfully stage and promote
|
|
532
|
+
// law in every receiving group it births. A parent holding no seat (a chain born before the seat
|
|
533
|
+
// existed) embeds nothing; the parent-directed transition seats it later.
|
|
534
|
+
// A founded group inherits law authority as `operator` tuples in its founding step instead.
|
|
535
|
+
const inheritedAuthority = founded
|
|
536
|
+
? undefined
|
|
537
|
+
: inheritedModelAuthorityGenesisStep(groupLawDomain, founder, p.bornAt);
|
|
538
|
+
if (inheritedAuthority !== undefined) genesisChain.push(inheritedAuthority);
|
|
539
|
+
// THE DECLARED BIRTH EFFECT (the unix model): the recipe rides the committed intent as a declared output
|
|
540
|
+
// (captured_ports.births). The kernel — reading the `.births()` declaration off the law manifest — takes
|
|
541
|
+
// the recipe and spawns + self-validates the child through the child's OWN gate (each step self-verifies).
|
|
542
|
+
// A founded birth declares no parent role: parent roles ride the certificate lane's attestation.
|
|
543
|
+
birth({
|
|
544
|
+
workspace: groupName,
|
|
545
|
+
genesisChain,
|
|
546
|
+
...(!founded && parentWorkspaceRoles.length > 0 ? { parentWorkspaceRoles } : {}),
|
|
547
|
+
});
|
|
548
|
+
// Parent/child lineage is an ordinary global Zanzibar relation in parent custody. The exact child
|
|
549
|
+
// address comes from the same plan which births it, so neither the host nor the caller can redirect
|
|
550
|
+
// this seat. Application law can then authorize upward workspace effects with the ordinary relation.
|
|
551
|
+
// THE INVERSE EPOCH EDGE rides the same plan: the born receiving group may ask THIS workspace for its
|
|
552
|
+
// current verifier epoch (`nomosInternalServeAuthProviderEpoch7c1f5e` gates on it), so a group whose
|
|
553
|
+
// verifier went stale refreshes it through the one framework lane instead of a tenant copy.
|
|
554
|
+
return [
|
|
555
|
+
...writeTuple({
|
|
556
|
+
object: "workspace:self",
|
|
557
|
+
relation: "child",
|
|
558
|
+
subject: `workspace:${groupName}`,
|
|
559
|
+
grantedBy: p.owner,
|
|
560
|
+
grantedAt: p.bornAt,
|
|
561
|
+
}),
|
|
562
|
+
// A founded group has no provider verifier, so it never asks for an epoch.
|
|
563
|
+
...(founded ? [] : writeTuple({
|
|
564
|
+
object: "workspace:self",
|
|
565
|
+
relation: AUTH_PROVIDER_EPOCH_CONSUMER,
|
|
566
|
+
subject: `workspace:${groupName}`,
|
|
567
|
+
grantedBy: p.owner,
|
|
568
|
+
grantedAt: p.bornAt,
|
|
569
|
+
})),
|
|
570
|
+
];
|
|
571
|
+
})
|
|
572
|
+
.reads(authProviderQuery)
|
|
573
|
+
.reads(authProviderKeysQuery)
|
|
574
|
+
.reads(authProviderCertificatesQuery)
|
|
575
|
+
.reads(relationTuplesByObject)
|
|
576
|
+
// The parent's enroller tuples a founded group inherits (D22) — readInheritedEnrollers's one read.
|
|
577
|
+
.reads(relationTuplesByRelation)
|
|
578
|
+
.alsoEnsures(RelationTuple)
|
|
579
|
+
.births(); // BIRTH IS A DECLARED KERNEL PRIMITIVE — emits the `nomosBirthGate` era key; the kernel folds the recipe
|
|
580
|
+
return {
|
|
581
|
+
...birthDirective,
|
|
582
|
+
...(homeGenesis === undefined || homeGenesis.steps.length === 0
|
|
583
|
+
? {}
|
|
584
|
+
: {
|
|
585
|
+
birthGenesisRequirements: homeGenesis.steps.map((step) => ({
|
|
586
|
+
domain: step.domain,
|
|
587
|
+
directiveId: step.directive.id,
|
|
588
|
+
})),
|
|
589
|
+
}),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* A FOUNDED receiving-group birth (D22, `foundingKeyHash` present) carries no certificate or token to
|
|
595
|
+
* prove who the owner is, so the parent's own admission proves it: the birth is admitted only when its
|
|
596
|
+
* author — the principal the kernel authenticated from a signed key — IS the owner it founds. Nobody can
|
|
597
|
+
* found someone else's group, and the owner is authenticated exactly as for any other write (for an
|
|
598
|
+
* application, a `user:<uid>` key its enroller enrolled). The historical recipe is unchanged.
|
|
599
|
+
*/
|
|
600
|
+
export const receivingGroupFoundedBirthBindsOwner = workspaceInvariant("ReceivingGroupFoundedBirthBindsOwner")
|
|
601
|
+
.on("nomosInternalBirthReceivingGroup7c1f5e")
|
|
602
|
+
.reads(readSet())
|
|
603
|
+
.declassify("the founded-birth refusal reveals only whether the author is the owner it names")
|
|
604
|
+
.assert((_snapshots, ctx) => {
|
|
605
|
+
const intent = (ctx?.intent ?? {}) as { owner?: string; foundingKeyHash?: string };
|
|
606
|
+
if (intent.foundingKeyHash === undefined) return { accept: true };
|
|
607
|
+
return typeof intent.owner === "string" && intent.owner.length > 0 && ctx?.principal === intent.owner
|
|
608
|
+
? { accept: true }
|
|
609
|
+
: { reject: "founded-receiving-group-birth-must-be-authored-by-its-owner" };
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
/** Build the standalone framework receiving-group law with an explicit allowance. */
|
|
613
|
+
export const birthReceivingGroup = (maxChildWorkspacesPerUser: number) =>
|
|
614
|
+
birthReceivingGroupForDomain(
|
|
615
|
+
RECEIVING_GROUP_LAW_DOMAIN,
|
|
616
|
+
maxChildWorkspacesPerUser,
|
|
617
|
+
[RECEIVING_GROUP_LAW_DOMAIN],
|
|
618
|
+
);
|
|
619
|
+
/** @deprecated the old name of {@link birthReceivingGroup}; kept as an alias for one release. */
|
|
620
|
+
export const birthHome = birthReceivingGroup;
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* THE SHARED-WITH-ME INDEX (§5) — one record per cross-custody attestation delivered to this home holon:
|
|
624
|
+
* a signed edge `<asserterWorkspace> : <object> # <relation> @ this-identity`, indexed LOCALLY so the
|
|
625
|
+
* access list is a local query (no fan-out across every resource holon). A DISCOVERY HINT — the live authz
|
|
626
|
+
* stays the forward `Check` on the asserting holon when the identity opens it.
|
|
627
|
+
*/
|
|
628
|
+
export const SharedWithMe = aggregate("SharedWithMe", {
|
|
629
|
+
/** The holon that signed + delivered this edge — WHERE the live forward `Check` runs when opened. */
|
|
630
|
+
asserterWorkspace: t.string().merge(Lww),
|
|
631
|
+
/** The shared OBJECT — the whole workspace (`workspace:self`) or a specific record (`Entry:<id>`). */
|
|
632
|
+
object: t.string().merge(Lww),
|
|
633
|
+
/** The relation granted (`reader` / `editor` / a read cap). */
|
|
634
|
+
relation: t.string().merge(Lww),
|
|
635
|
+
/** The asserter's OWNER principal — whose key signed the attestation (verified at §3 admission). */
|
|
636
|
+
asserterPrincipal: t.string().merge(Lww),
|
|
637
|
+
/** ISO-8601, caller-stamped (determinism — no clock in the plan). */
|
|
638
|
+
attestedAt: t.string().merge(Lww),
|
|
639
|
+
/** PRUNED (forward-check fail-safe): set when the live `Check` on the asserter denies — the hint is
|
|
640
|
+
* dropped from the list but the audit row stays. `""` = live. */
|
|
641
|
+
prunedAt: t.string().merge(Lww),
|
|
642
|
+
}).public();
|
|
643
|
+
|
|
644
|
+
/** One idempotent access-manifest row per source edge in this home. Delivery is at-least-once: a retry,
|
|
645
|
+
* reconnect or source outbox replay must update the same bookmark rather than manufacture another thing the
|
|
646
|
+
* recipient appears to have access to. The home itself scopes the recipient, so the edge coordinate is the
|
|
647
|
+
* source workspace + protected object + granted relation. */
|
|
648
|
+
export function sharedWithMeId(asserterWorkspace: string, object: string, relation: string): string {
|
|
649
|
+
const coordinate = JSON.stringify([asserterWorkspace, object, relation]);
|
|
650
|
+
return `SharedWithMe_${nomos.sha256(coordinate)}`;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* `recordShare` — index a cross-custody share into the home holon's access list. AUTHORED on the home
|
|
655
|
+
* holon (the identity's own subject-side root). The payload carries the §3 CROSS-CUSTODY ATTESTATION — a
|
|
656
|
+
* SIGNED QUOTE: the asserter's device `publicKey` + a `signature` over the edge
|
|
657
|
+
* `<asserterWorkspace>:<object>#<relation>@<this-home-owner>`. The home holon's OWN gate verifies it
|
|
658
|
+
* (`executor::record_share_attestation_violation`, bound to the warrant owner), so the BAILIFF can deliver
|
|
659
|
+
* it HOST-FREE (the host carries bytes, vouches nothing — the gate vouches). The owner lane authors the
|
|
660
|
+
* SAME shape (the owner holds the quote the asserter gave it). The signature is a verification input, not
|
|
661
|
+
* stored — only the edge facts fold into the index. The live authority stays the forward `Check` on the
|
|
662
|
+
* asserter when the identity opens it (§5 fail-safe); a stale/forged quote can't enter the list at all.
|
|
663
|
+
*/
|
|
664
|
+
export const recordShare = directive("recordShare")
|
|
665
|
+
.ensures(SharedWithMe)
|
|
666
|
+
.payload(
|
|
667
|
+
z.object({
|
|
668
|
+
asserterWorkspace: z.string().min(1),
|
|
669
|
+
object: z.string().min(1),
|
|
670
|
+
relation: z.string().min(1),
|
|
671
|
+
asserterPrincipal: z.string().min(1),
|
|
672
|
+
attestedAt: z.string().min(1),
|
|
673
|
+
/** The asserter's device PUBLIC KEY (base64) — what the §3 gate verifies the quote against. */
|
|
674
|
+
publicKey: z.string().min(1),
|
|
675
|
+
/** The asserter's SIGNATURE (base64) over the edge bytes, subject = this home's owner. */
|
|
676
|
+
signature: z.string().min(1),
|
|
677
|
+
}),
|
|
678
|
+
)
|
|
679
|
+
.plan((p) => {
|
|
680
|
+
// ENSURE, never CREATE: signed delivery is retryable and therefore at-least-once. Re-delivery (including
|
|
681
|
+
// a later re-grant after prune) refreshes this exact access edge and makes it live again.
|
|
682
|
+
ensure(SharedWithMe, sharedWithMeId(p.asserterWorkspace, p.object, p.relation))
|
|
683
|
+
.set("asserterWorkspace", p.asserterWorkspace)
|
|
684
|
+
.set("object", p.object)
|
|
685
|
+
.set("relation", p.relation)
|
|
686
|
+
.set("asserterPrincipal", p.asserterPrincipal)
|
|
687
|
+
.set("attestedAt", p.attestedAt)
|
|
688
|
+
.set("prunedAt", "");
|
|
689
|
+
return [];
|
|
690
|
+
})
|
|
691
|
+
// CROSS-CUSTODY ATTESTATION-AUTHORIZED (§3/§5): the payload's signed quote IS the authorization (lowered
|
|
692
|
+
// into the `nomosAttestationGate` era key) — the one gate verifies it against the home's owner (the
|
|
693
|
+
// LAW-DECLARED owner-source below, NOT a kernel-hardcoded id) + pins the principal to the attestation key,
|
|
694
|
+
// so the bailiff delivers it host-free to an OFFLINE subject (no device key, no author block).
|
|
695
|
+
.attestationAuthorized({ ownerObject: RECEIVING_GROUP_ID, ownerField: "principal" });
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* `pruneShare` — fail-safe drop of a hint whose forward `Check` denied (the asserter revoked it). The row
|
|
699
|
+
* stays for audit; `prunedAt` removes it from the live list. (The forward check itself is the resource
|
|
700
|
+
* holon's job — this records the verdict locally.)
|
|
701
|
+
*/
|
|
702
|
+
export const pruneShare = directive("pruneShare")
|
|
703
|
+
.mutates(SharedWithMe)
|
|
704
|
+
.payload(z.object({ shareId: idOf(SharedWithMe), prunedAt: z.string().min(1) }))
|
|
705
|
+
.plan((p) => [set(instance(SharedWithMe, p.shareId), "prunedAt", p.prunedAt)]);
|
|
706
|
+
|
|
707
|
+
/** "Show me everything <asserter> shared with me" — an indexed probe over the local access list. */
|
|
708
|
+
export const sharedByWorkspace = query("sharedByWorkspace").key("asserterWorkspace").returns(SharedWithMe);
|
|
709
|
+
|
|
710
|
+
/** "Show me every share for this protected object" — the indexed access-list probe used by generated
|
|
711
|
+
* application discovery. A home commonly has a very large installed law, so discovery must never fall back
|
|
712
|
+
* to the generic `aggregatesByType(SharedWithMe)` scan merely to find whole-workspace shares. */
|
|
713
|
+
export const sharedByObject = query("sharedByObject").key("object").returns(SharedWithMe);
|
|
714
|
+
|
|
715
|
+
// ─────────────────── THE PEER-SOVEREIGN ROOT-OF-TRUST (the BirthAttestation cert chain) ───────────────────
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* THE BIRTH ATTESTATION (the cert, on-chain for AUDIT). When a parent P births this home via the offer-
|
|
719
|
+
* effect, the kernel seeds a SIGNED [executor::birth_cert::BirthCert] {child, founder, verified, parentKey,
|
|
720
|
+
* parentId, epoch} into the home's genesis (it travels with the home's `GenesisWarrant.bornUnder` so a peer
|
|
721
|
+
* verifies it OFFLINE), AND folds this singleton record so the home's projection surfaces "who attested my
|
|
722
|
+
* birth, and was my founder verified". `verified` + `founder` are the parent's GATE verdict (captured-port,
|
|
723
|
+
* CAS-as-law), never the offer payload. A peer's TRUST decision uses the travelling signed chain (the kernel
|
|
724
|
+
* verify doors); this aggregate is the human-readable mirror.
|
|
725
|
+
*/
|
|
726
|
+
export const BIRTH_ATTESTATION_ID = "birth-attestation:self";
|
|
727
|
+
export const BIRTH_ATTESTATION_LINK_PREFIX = "birth-attestation-link:";
|
|
728
|
+
export const BirthAttestation = aggregate("BirthAttestation", {
|
|
729
|
+
/** This home's self-id (`home-<sha256(founder)>`) — the cert's `child`. */
|
|
730
|
+
child: t.string().merge(Lww),
|
|
731
|
+
/** The founding principal the parent attested (== {@link HomeOwner} `principal`). */
|
|
732
|
+
founder: t.string().merge(Lww),
|
|
733
|
+
/** THE PARENT'S GATE VERDICT: was `founder` VERIFIED at the parent's offer gate (an IdP vouched)? */
|
|
734
|
+
verified: t.string().merge(Lww), // "true"/"false" (string for the Lww scalar projection)
|
|
735
|
+
/** The parent's workspace self-id (lineage). */
|
|
736
|
+
parentId: t.string().merge(Lww),
|
|
737
|
+
/** The parent's enrolled signer PUBLIC KEY (base64) — `K_P`, the cert's signer. */
|
|
738
|
+
parentKey: t.string().merge(Lww),
|
|
739
|
+
/** The parent's SignerEpoch at the attestation (informational; the cert is judged at birth-time validity). */
|
|
740
|
+
epoch: t.int().merge(Lww),
|
|
741
|
+
/** Legacy replay slot for histories authored before `BirthAttestationLink`. New histories write the
|
|
742
|
+
* travelling chain only as ordered, typed link records below. */
|
|
743
|
+
certChain: t.string().merge(Lww).optional(),
|
|
744
|
+
}).public();
|
|
745
|
+
|
|
746
|
+
/** One exact link in the travelling certificate chain. The zero-padded index is custody order; every
|
|
747
|
+
* certificate member is a separate typed USDA property. `certChain` above remains only as a replay fallback
|
|
748
|
+
* for histories authored before this aggregate existed. */
|
|
749
|
+
export const BirthAttestationLink = aggregate("BirthAttestationLink", {
|
|
750
|
+
index: t.int().merge(Lww),
|
|
751
|
+
child: t.string().merge(Lww),
|
|
752
|
+
founder: t.string().merge(Lww),
|
|
753
|
+
verified: t.bool().merge(Lww),
|
|
754
|
+
parentKey: t.string().merge(Lww),
|
|
755
|
+
parentId: t.string().merge(Lww),
|
|
756
|
+
epoch: t.int().merge(Lww),
|
|
757
|
+
signature: t.string().merge(Lww),
|
|
758
|
+
verifiedVia: t.string().merge(Lww).optional(),
|
|
759
|
+
idpToken: t.string().merge(Lww).optional(),
|
|
760
|
+
capturedClock: t.int().merge(Lww).optional(),
|
|
761
|
+
authProviderIssuer: t.string().merge(Lww).optional(),
|
|
762
|
+
authProviderJwks: t.string().merge(Lww).optional(),
|
|
763
|
+
authProviderAudience: t.string().merge(Lww).optional(),
|
|
764
|
+
}).public();
|
|
765
|
+
|
|
766
|
+
/** Audit read: this home's birth attestation (who attested it + the verified verdict). */
|
|
767
|
+
export const birthAttestation = query("birthAttestation").key("child").returns(BirthAttestation);
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* `seedBirthAttestation` — THE GENESIS-DOOR step the parent's offer-effect INJECTS into the home's genesis
|
|
771
|
+
* chain (the kernel appends it in `run_birth_effect` carrying the assembled + signed cert chain). It folds
|
|
772
|
+
* the [`BirthAttestation`] singleton so the home's own state carries its travelling cert. A CONTROL-PLANE
|
|
773
|
+
* write seeded once at birth (the open session lane structurally refuses it by directive id). The values
|
|
774
|
+
* are the parent's GATE VERDICT (`founder`/`verified` lowered from the captured port — CAS-as-law, never
|
|
775
|
+
* the offer payload) + the typed signed links; the home's verify doors re-derive trust from the chain.
|
|
776
|
+
*/
|
|
777
|
+
export const seedBirthAttestation = directive("seedBirthAttestation")
|
|
778
|
+
.ensures(BirthAttestation)
|
|
779
|
+
.payload(
|
|
780
|
+
z.object({
|
|
781
|
+
child: z.string().min(1),
|
|
782
|
+
founder: z.string().min(1),
|
|
783
|
+
verified: z.string().min(1), // "true"/"false"
|
|
784
|
+
/** The parent's workspace self-id (lineage). EMPTY for a root-DIRECT birth (root carries no self-id
|
|
785
|
+
* binding in the unwarranted lane) — the cert's terminal link is then signed by K_root directly. */
|
|
786
|
+
parentId: z.string(),
|
|
787
|
+
parentKey: z.string().min(1), // base64 K_P
|
|
788
|
+
epoch: z.number().int().nonnegative(),
|
|
789
|
+
links: z.array(birthCertificatePayload).min(1),
|
|
790
|
+
/** Law-declared target roles for the verified immediate parent workspace. The kernel injects this
|
|
791
|
+
* value from the admitted birth recipe and supplies `parentId`; no host chooses either fact. */
|
|
792
|
+
parentWorkspaceRoles: z.array(z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/)).optional(),
|
|
793
|
+
}).superRefine((payload, ctx) => {
|
|
794
|
+
if ((payload.parentWorkspaceRoles?.length ?? 0) > 0 && payload.parentId.length === 0) {
|
|
795
|
+
ctx.addIssue({ code: "custom", message: "parent workspace roles require a verified non-root parentId" });
|
|
796
|
+
}
|
|
797
|
+
}),
|
|
798
|
+
)
|
|
799
|
+
.plan((p) => {
|
|
800
|
+
const h = instance(BirthAttestation, BIRTH_ATTESTATION_ID);
|
|
801
|
+
const header = [
|
|
802
|
+
set(h, "child", p.child),
|
|
803
|
+
set(h, "founder", p.founder),
|
|
804
|
+
set(h, "verified", p.verified),
|
|
805
|
+
set(h, "parentId", p.parentId),
|
|
806
|
+
set(h, "parentKey", p.parentKey),
|
|
807
|
+
set(h, "epoch", p.epoch),
|
|
808
|
+
];
|
|
809
|
+
const links = p.links.flatMap((certificate, index) => {
|
|
810
|
+
const link = instance(BirthAttestationLink, `${BIRTH_ATTESTATION_LINK_PREFIX}${String(index).padStart(8, "0")}`);
|
|
811
|
+
return [
|
|
812
|
+
withMarker(set(link, "index", index), "ensures"),
|
|
813
|
+
withMarker(set(link, "child", certificate.child), "ensures"),
|
|
814
|
+
withMarker(set(link, "founder", certificate.founder), "ensures"),
|
|
815
|
+
withMarker(set(link, "verified", certificate.verified), "ensures"),
|
|
816
|
+
withMarker(set(link, "parentKey", certificate.parentKey), "ensures"),
|
|
817
|
+
withMarker(set(link, "parentId", certificate.parentId), "ensures"),
|
|
818
|
+
withMarker(set(link, "epoch", certificate.epoch), "ensures"),
|
|
819
|
+
withMarker(set(link, "signature", certificate.signature), "ensures"),
|
|
820
|
+
...(certificate.verifiedVia ? [withMarker(set(link, "verifiedVia", certificate.verifiedVia), "ensures")] : []),
|
|
821
|
+
...(certificate.idpToken ? [withMarker(set(link, "idpToken", certificate.idpToken), "ensures")] : []),
|
|
822
|
+
...(certificate.capturedClock === undefined ? [] : [withMarker(set(link, "capturedClock", certificate.capturedClock), "ensures")]),
|
|
823
|
+
...(certificate.authProviderIssuer ? [withMarker(set(link, "authProviderIssuer", certificate.authProviderIssuer), "ensures")] : []),
|
|
824
|
+
...(certificate.authProviderJwks ? [withMarker(set(link, "authProviderJwks", certificate.authProviderJwks), "ensures")] : []),
|
|
825
|
+
...(certificate.authProviderAudience ? [withMarker(set(link, "authProviderAudience", certificate.authProviderAudience), "ensures")] : []),
|
|
826
|
+
];
|
|
827
|
+
});
|
|
828
|
+
const parentAuthority = (p.parentWorkspaceRoles ?? []).flatMap((relation) => writeTuple({
|
|
829
|
+
object: "workspace:self",
|
|
830
|
+
relation,
|
|
831
|
+
subject: `workspace:${p.parentId}`,
|
|
832
|
+
grantedBy: `workspace:${p.parentId}`,
|
|
833
|
+
grantedAt: String(p.epoch),
|
|
834
|
+
}));
|
|
835
|
+
return [...header, ...links, ...parentAuthority];
|
|
836
|
+
})
|
|
837
|
+
.alsoEnsures(BirthAttestationLink)
|
|
838
|
+
.alsoEnsures(RelationTuple);
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* THE ERA DECLARATION (the `nomosParentAttestedBirth` USD key). Exporting this sentinel from the home law
|
|
842
|
+
* tells the kernel's verify doors to VERIFY THE TRAVELLING CERT CHAIN to the pinned `K_root` on every lane
|
|
843
|
+
* (author / receive / verify_chain) — so a warranted home that self-asserts a verified founder WITHOUT a
|
|
844
|
+
* parent-signed chain rooting at root is refused (`ParentDidNotAttest` / `CertChainBroken` /
|
|
845
|
+
* `NotRootedAtRoot`). Era-ruled: a home law that does NOT export this carries no key and replays byte-
|
|
846
|
+
* identical (the squat-only `homeNameBindsOwner` check still runs; the cert check is simply inert).
|
|
847
|
+
*/
|
|
848
|
+
export const parentAttestedBirth: ParentAttestedBirthMarker = {
|
|
849
|
+
...requireParentAttestedBirth,
|
|
850
|
+
// DECLARE the owner + cert locators so the KERNEL reads them (it names neither): the home owner lives at
|
|
851
|
+
// the `HomeOwner` singleton's `principal`. `certObject`/`certField` retain the legacy chain locator for
|
|
852
|
+
// old histories; current histories are discovered through the typed `BirthAttestationLink` aggregate.
|
|
853
|
+
ownerObject: RECEIVING_GROUP_ID,
|
|
854
|
+
ownerField: "principal",
|
|
855
|
+
certObject: BIRTH_ATTESTATION_ID,
|
|
856
|
+
certField: "certChain",
|
|
857
|
+
};
|