From 9c71e769c62f1fadf6b5ea1fd181d2be262450b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Sun, 16 Aug 2026 03:36:02 -0300 Subject: [PATCH 01/11] feat: distribute recovery custody across holders --- provenance/hermes-agent-0.19.0.json | 2 +- src/daimon_matrix/identity.py | 107 +- src/daimon_matrix/operator_rebirth.py | 1117 ++++++++++++++++- tests/test_dm078_recovery_rebirth.py | 523 +++++++- vectors/hermes/v1/index.json | 2 +- vectors/hermes/v1/valid/launch-receipt.json | 2 +- vectors/hermes/v1/valid/profile-manifest.json | 2 +- 7 files changed, 1722 insertions(+), 33 deletions(-) diff --git a/provenance/hermes-agent-0.19.0.json b/provenance/hermes-agent-0.19.0.json index cbccb17..45156b2 100644 --- a/provenance/hermes-agent-0.19.0.json +++ b/provenance/hermes-agent-0.19.0.json @@ -1 +1 @@ -{"audited_at":"2026-08-05","commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","contract_digests":{"agent/agent_init.py":"6b4aa7a877d25e7065af35fd5a5e99dc0b85ef0ede349d95c5bd74818a70b89f","agent/memory_manager.py":"35e4e313f08e7529d7407f2d36b1639681acbc347eedfe9f2e38c2610973761c","agent/memory_provider.py":"7a86b453edbe3dae6ea02f3af406fac7e39fe7ee7b61bfc84b8c8e4b4a0ce8cd","agent/plugin_llm.py":"4158b0ed2be2140eb99f09e1a488daed04da496052fe415cb84e412e4b71fb30","agent/system_prompt.py":"261481c471ddee92ced3fe381d63acbbe9136bedb6420f613983225007e2bb9a","agent/turn_context.py":"726ebf615b90237cca98d8f5d2d4e04f4507690d66917eb88f5d478c0b1ecaa2","agent/turn_finalizer.py":"a6c91daefaa805ef71604ddf9e9e08399825c1448bff2982bf72099da35957ac","hermes_cli/_parser.py":"82db5bb23c4619bf11536f44b56ea72c2b87cafcd062660af3593d2a4e08db0b","hermes_cli/config.py":"038040b0c0bbdd5f740b39c64d2e9ea1d7c78ae3d582cb337dbe8c145b4c8b03","hermes_cli/main.py":"55333e3fb37bec97b12c404760968716a3e81f3ae373896fc628df8dee3fb416","hermes_cli/plugins.py":"0f6c28614bebb7444392625a63c2b3186039f04238fe6ca79ad62d4849b0551c","model_tools.py":"db74ee29c8d335d80f3c18cc31f8c441956af956b2ed08e5113ced249fad32b4","plugins/memory/__init__.py":"f6bc37128d23f931ea1db52fc60cf25cca448f41105ed37d5b46f3843ab71b3a","plugins/memory/config_schema.py":"b6f58adffb2fd01a9605f10b891d905b8dfa72a509d1577499b82ad66ff7a937","pyproject.toml":"35462080afc8177258babd430dcdc2ac654fdf69332cc4370fec555295f7eaba"},"copy_policy":"No Hermes source or private profile bytes are distributed.","git_archive_sha256":"860a664f622e1099a095cb6cf06b04bcfe78b2fd3affc7192da9cf7ccefcdd63","license":"MIT","matrix_payload":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","package_tree_sha256":"702cdd0512bd5dc2a7377e3a4b1f5415903dd3c5e80d2618884b3444b8f51a10","plugin_manifest_sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92","plugin_sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5","provider_name":"daimon-matrix","skill_sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275","soul_sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a","tools":["matrix_scope","matrix_propose_observation"]},"python_interval":">=3.11,<3.14","repository":"https://github.com/nicoechaniz/hermes-agent","schema":"dm.hermes-body.provenance/v1","supported_surfaces":["external-memory-provider","profile-plugin-discovery","memory-manager","current-user-api-content-sidecar","session-lifecycle-hooks","static-system-prompt-block"],"tree":"ac7dec02ca029e895963402788bd1cdc3afb36f8","version":"0.19.0"} +{"audited_at":"2026-08-05","commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","contract_digests":{"agent/agent_init.py":"6b4aa7a877d25e7065af35fd5a5e99dc0b85ef0ede349d95c5bd74818a70b89f","agent/memory_manager.py":"35e4e313f08e7529d7407f2d36b1639681acbc347eedfe9f2e38c2610973761c","agent/memory_provider.py":"7a86b453edbe3dae6ea02f3af406fac7e39fe7ee7b61bfc84b8c8e4b4a0ce8cd","agent/plugin_llm.py":"4158b0ed2be2140eb99f09e1a488daed04da496052fe415cb84e412e4b71fb30","agent/system_prompt.py":"261481c471ddee92ced3fe381d63acbbe9136bedb6420f613983225007e2bb9a","agent/turn_context.py":"726ebf615b90237cca98d8f5d2d4e04f4507690d66917eb88f5d478c0b1ecaa2","agent/turn_finalizer.py":"a6c91daefaa805ef71604ddf9e9e08399825c1448bff2982bf72099da35957ac","hermes_cli/_parser.py":"82db5bb23c4619bf11536f44b56ea72c2b87cafcd062660af3593d2a4e08db0b","hermes_cli/config.py":"038040b0c0bbdd5f740b39c64d2e9ea1d7c78ae3d582cb337dbe8c145b4c8b03","hermes_cli/main.py":"55333e3fb37bec97b12c404760968716a3e81f3ae373896fc628df8dee3fb416","hermes_cli/plugins.py":"0f6c28614bebb7444392625a63c2b3186039f04238fe6ca79ad62d4849b0551c","model_tools.py":"db74ee29c8d335d80f3c18cc31f8c441956af956b2ed08e5113ced249fad32b4","plugins/memory/__init__.py":"f6bc37128d23f931ea1db52fc60cf25cca448f41105ed37d5b46f3843ab71b3a","plugins/memory/config_schema.py":"b6f58adffb2fd01a9605f10b891d905b8dfa72a509d1577499b82ad66ff7a937","pyproject.toml":"35462080afc8177258babd430dcdc2ac654fdf69332cc4370fec555295f7eaba"},"copy_policy":"No Hermes source or private profile bytes are distributed.","git_archive_sha256":"860a664f622e1099a095cb6cf06b04bcfe78b2fd3affc7192da9cf7ccefcdd63","license":"MIT","matrix_payload":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","package_tree_sha256":"9d5f2839096289dc4648afa0ba75d8f00b8ad611fd53601d9bb56302c7bceb4a","plugin_manifest_sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92","plugin_sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5","provider_name":"daimon-matrix","skill_sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275","soul_sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a","tools":["matrix_scope","matrix_propose_observation"]},"python_interval":">=3.11,<3.14","repository":"https://github.com/nicoechaniz/hermes-agent","schema":"dm.hermes-body.provenance/v1","supported_surfaces":["external-memory-provider","profile-plugin-discovery","memory-manager","current-user-api-content-sidecar","session-lifecycle-hooks","static-system-prompt-block"],"tree":"ac7dec02ca029e895963402788bd1cdc3afb36f8","version":"0.19.0"} diff --git a/src/daimon_matrix/identity.py b/src/daimon_matrix/identity.py index 94f573c..be3ce4c 100644 --- a/src/daimon_matrix/identity.py +++ b/src/daimon_matrix/identity.py @@ -539,13 +539,39 @@ def create_recovery( ) -> Artifact: """Recover a unique head while naming every currently known branch head.""" + prepared = prepare_recovery( + states, + threshold_policy(replacement_root_seeds, replacement_threshold), + revoke_embodiments=revoke_embodiments, + ) + body = prepared["body"] + signatures = [ + create_recovery_authorization_share(prepared, states, seed) + for seed in current_recovery_seeds + ] + signatures.extend( + create_recovery_possession_share(prepared, states, seed) + for seed in replacement_root_seeds + ) + return _artifact("recovery", body, signatures) + + +def prepare_recovery( + states: Sequence[ControlState], + replacement_root_policy: Mapping[str, Any], + *, + revoke_embodiments: Sequence[str], +) -> Artifact: + """Freeze an unsigned recovery artifact for independent threshold holders.""" + if not states: raise IdentityError("recovery needs at least one known control head") being_refs = {state.being_ref for state in states} recovery_policies = {canonical_bytes(state.recovery_policy) for state in states} if len(being_refs) != 1 or len(recovery_policies) != 1: raise IdentityError("recovery heads do not share being/recovery authority") - replacement_policy = threshold_policy(replacement_root_seeds, replacement_threshold) + replacement_policy = copy.deepcopy(dict(replacement_root_policy)) + _validate_threshold_policy(replacement_policy) _require_separate_policies(replacement_policy, states[0].recovery_policy) body = { "being_ref": states[0].being_ref, @@ -555,13 +581,82 @@ def create_recovery( "replacement_root": replacement_policy, "revoked_embodiments": sorted(set(revoke_embodiments)), } - return _sign_control( - "recovery", - body, - _seed_map(current_recovery_seeds), + return _artifact("recovery", body, []) + + +def _require_policy_seed(seed: bytes, policy: Mapping[str, Any], *, role: str) -> None: + kid = signing_descriptor(seed)["key_id"] + if kid not in _public_map(policy): + raise IdentityError(f"seed is not authorized for {role}") + + +def _prepared_recovery( + artifact: Mapping[str, Any], states: Sequence[ControlState] +) -> Artifact: + body, _ = _verify_wrapper(artifact, "recovery") + if artifact["signatures"]: + raise IdentityError("prepared recovery already contains signatures") + expected = prepare_recovery( + states, + body.get("replacement_root", {}), + revoke_embodiments=body.get("revoked_embodiments", []), + ) + if canonical_bytes(expected) != canonical_bytes(artifact): + raise IdentityError("prepared recovery does not match known heads") + return expected + + +def create_recovery_authorization_share( + prepared: Mapping[str, Any], + states: Sequence[ControlState], + recovery_seed: bytes, +) -> dict[str, str]: + """Sign one recovery authorization without exposing any other holder key.""" + + expected = _prepared_recovery(prepared, states) + _require_policy_seed( + recovery_seed, states[0].recovery_policy, role="recovery-authorization" + ) + return _signature( + recovery_seed, "recovery-authorization", - _seed_map(replacement_root_seeds), + domain_bytes(DOMAINS["recovery"], expected["body"]), + ) + + +def create_recovery_possession_share( + prepared: Mapping[str, Any], + states: Sequence[ControlState], + replacement_root_seed: bytes, +) -> dict[str, str]: + """Prove possession of one replacement root without sharing its seed.""" + + expected = _prepared_recovery(prepared, states) + policy = expected["body"]["replacement_root"] + _require_policy_seed(replacement_root_seed, policy, role="new-root-possession") + raw_hash = digest(DOMAINS["recovery"], expected["body"]) + return _signature( + replacement_root_seed, + "new-root-possession", + DOMAINS["recovery"].encode("ascii") + b"/possession\x00" + raw_hash, + ) + + +def aggregate_recovery( + prepared: Mapping[str, Any], + states: Sequence[ControlState], + shares: Sequence[Mapping[str, Any]], +) -> Artifact: + """Keyless aggregation of independently produced recovery shares.""" + + expected = _prepared_recovery(prepared, states) + artifact = _artifact( + "recovery", + expected["body"], + [copy.deepcopy(dict(share)) for share in shares], ) + verify_recovery(artifact, states) + return artifact def _verify_position(body: Mapping[str, Any], state: ControlState) -> None: diff --git a/src/daimon_matrix/operator_rebirth.py b/src/daimon_matrix/operator_rebirth.py index 9b82b40..bcff048 100644 --- a/src/daimon_matrix/operator_rebirth.py +++ b/src/daimon_matrix/operator_rebirth.py @@ -46,14 +46,18 @@ DOMAINS, ControlChain, ControlState, + aggregate_recovery, create_embodiment_credential, create_incarnation_authorization, create_recovery, + create_recovery_authorization_share, + create_recovery_possession_share, ed25519_public, generate_ed25519_seed, generate_x25519_private, key_descriptor, key_id, + prepare_recovery, signing_descriptor, verify_embodiment_credential, verify_incarnation_authorization, @@ -78,6 +82,26 @@ RECOVERY_ACTIVATION_SCHEMA: Final = "dm.operator.recovery-activation/v1" RECOVERY_ACTIVATION_DOMAIN: Final = "dm.operator.recovery-activation/v1" RECOVERY_ACTIVATION_ID_PREFIX: Final = "dm:recovery-activation:v1:" +DISTRIBUTED_RECOVERY_INTENT_SCHEMA: Final = "dm.operator.distributed-recovery-intent/v1" +DISTRIBUTED_RECOVERY_INTENT_DOMAIN: Final = "dm.operator.distributed-recovery-intent/v1" +DISTRIBUTED_RECOVERY_INTENT_ID_PREFIX: Final = "dm:recovery-intent:v1:" +DISTRIBUTED_RECOVERY_SHARE_SCHEMA: Final = "dm.operator.distributed-recovery-share/v1" +DISTRIBUTED_RECOVERY_SHARE_DOMAIN: Final = "dm.operator.distributed-recovery-share/v1" +DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_SCHEMA: Final = ( + "dm.operator.distributed-recovery-authorization-intent/v1" +) +DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_DOMAIN: Final = ( + "dm.operator.distributed-recovery-authorization-intent/v1" +) +DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_ID_PREFIX: Final = ( + "dm:recovery-authorization-intent:v1:" +) +DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_SCHEMA: Final = ( + "dm.operator.distributed-recovery-authorization-share/v1" +) +DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_DOMAIN: Final = ( + "dm.operator.distributed-recovery-authorization-share/v1" +) TRANSPORT_SCHEME: Final = "dm-peer-v1" MAX_TIME: Final = 2**53 - 1 MAX_ARTIFACT_BYTES: Final = 1024 * 1024 @@ -177,6 +201,59 @@ def _identity_signature(seed: bytes, role: str, preimage: bytes) -> dict[str, st } +def _holder_attestation( + seed: bytes, domain: str, body: Mapping[str, Any] +) -> dict[str, str]: + public = ed25519_public(seed) + return { + "alg": "Ed25519", + "kid": key_id("Ed25519", public), + "value": b64url( + Ed25519PrivateKey.from_private_bytes(seed).sign(domain_bytes(domain, body)) + ), + } + + +def _verify_holder_attestation( + value: Any, + domain: str, + body: Mapping[str, Any], + allowed_descriptors: Sequence[Mapping[str, Any]], + *, + code: str, +) -> str: + attestation = _closed(value, {"alg", "kid", "value"}, code) + public_by_id: dict[str, bytes] = {} + try: + for descriptor in allowed_descriptors: + row = _closed( + descriptor, + {"algorithm", "key_id", "public"}, + code, + ) + public = unb64url(str(row["public"]), length=32) + kid = str(row["key_id"]) + if ( + row["algorithm"] != "Ed25519" + or kid != key_id("Ed25519", public) + or kid in public_by_id + ): + raise RebirthError(code) + public_by_id[kid] = public + kid = str(attestation["kid"]) + if attestation["alg"] != "Ed25519" or kid not in public_by_id: + raise RebirthError(code) + Ed25519PublicKey.from_public_bytes(public_by_id[kid]).verify( + unb64url(str(attestation["value"]), length=64), + domain_bytes(domain, body), + ) + return kid + except RebirthError: + raise + except (InvalidSignature, TypeError, ValueError) as exception: + raise RebirthError(code) from exception + + def _origin(value: Any) -> dict[str, str]: row = _closed( value, @@ -801,6 +878,363 @@ def authorize_recovery_enrollment_request( return activation +def _distributed_recovery_authorization_material( + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + *, + issued_at_ms: int, +) -> dict[str, Any]: + issued = _uint(issued_at_ms, "invalid_rebirth_time") + request_base = recovery_request_base(previous, recovery_artifact) + verified = validate_enrollment_request(request, request_base, observed_at_ms=issued) + credential = copy.deepcopy(verified["body"]["credential"]) + incarnation = copy.deepcopy(verified["body"]["incarnation"]) + origin = _origin(verified["body"]["origin"]) + manifest = BeingManifest.from_value( + { + "schema": "being-manifest/v2", + "being_ref": previous.manifest.being_ref, + "control_head": request_base.state.head, + "history_binding_id": previous.manifest.value["history_binding_id"], + "revision": previous.manifest.value["revision"] + 1, + "embodiments": [ + { + "body_ref": origin["body_ref"], + "embodiment_credential_id": credential["artifact_id"], + "embodiment_id": origin["embodiment_id"], + "incarnation_authorization_id": incarnation["artifact_id"], + "incarnation_id": origin["incarnation_id"], + "status": "active", + } + ], + } + ) + transition = create_recovery_rebirth( + previous.manifest, + manifest, + recovery_artifact=recovery_artifact, + body_ref=origin["body_ref"], + embodiment_id=origin["embodiment_id"], + incarnation_id=origin["incarnation_id"], + embodiment_credential_id=credential["artifact_id"], + incarnation_authorization_id=incarnation["artifact_id"], + principal_id=origin["principal_id"], + root_seeds=[], + issued_at_ms=issued, + ) + if transition["signatures"]: + raise AssertionError("unsigned recovery transition contains signatures") + return { + "request_id": verified["request_id"], + "being_ref": previous.manifest.being_ref, + "previous_control_head": previous.state.head, + "previous_manifest_hash": previous.manifest.digest, + "recovery_artifact": copy.deepcopy(dict(recovery_artifact)), + "recovered_control_head": request_base.state.head, + "successor_manifest": manifest.value, + "credential": credential, + "incarnation": incarnation, + "origin": origin, + "transition": transition, + "issued_at_ms": issued, + } + + +def create_distributed_recovery_authorization_intent( + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + *, + issued_at_ms: int, + expires_at_ms: int, + nonce: bytes, +) -> dict[str, Any]: + """Freeze target enrollment and transition before root holders sign.""" + + material = _distributed_recovery_authorization_material( + request, + previous, + recovery_artifact, + issued_at_ms=issued_at_ms, + ) + expires = _uint(expires_at_ms, "invalid_rebirth_recovery_authorization_intent_time") + if ( + expires <= material["issued_at_ms"] + or expires - material["issued_at_ms"] > 24 * 60 * 60 * 1000 + ): + raise RebirthError("invalid_rebirth_recovery_authorization_intent_time") + if len(nonce) != 32: + raise RebirthError("invalid_rebirth_recovery_authorization_intent_nonce") + body = { + "activation_body": material, + "expires_at_ms": expires, + "nonce": b64url(nonce), + } + result = { + "schema": DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_SCHEMA, + "intent_id": DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_ID_PREFIX + + b64url(digest(DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_DOMAIN, body)), + "body": body, + } + _canonical(result, "invalid_rebirth_recovery_authorization_intent") + return result + + +def validate_distributed_recovery_authorization_intent( + value: Any, + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Rebuild every public byte before a replacement-root holder signs.""" + + row = _closed( + value, + {"schema", "intent_id", "body"}, + "invalid_rebirth_recovery_authorization_intent", + ) + if row["schema"] != DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_SCHEMA: + raise RebirthError("unsupported_rebirth_recovery_authorization_intent") + body = _closed( + row["body"], + {"activation_body", "expires_at_ms", "nonce"}, + "invalid_rebirth_recovery_authorization_intent", + ) + observed = _uint( + observed_at_ms, "invalid_rebirth_recovery_authorization_intent_time" + ) + expires = _uint( + body["expires_at_ms"], + "invalid_rebirth_recovery_authorization_intent_time", + ) + try: + nonce = b64url(unb64url(str(body["nonce"]), length=32)) + except (TypeError, ValueError) as exception: + raise RebirthError( + "invalid_rebirth_recovery_authorization_intent_nonce" + ) from exception + activation_body = body["activation_body"] + if not isinstance(activation_body, Mapping): + raise RebirthError("invalid_rebirth_recovery_authorization_intent") + issued = _uint( + activation_body.get("issued_at_ms"), + "invalid_rebirth_recovery_authorization_intent_time", + ) + if ( + nonce != body["nonce"] + or expires <= issued + or expires - issued > 24 * 60 * 60 * 1000 + or not issued <= observed < expires + ): + raise RebirthError("rebirth_recovery_authorization_intent_not_timely") + expected = _distributed_recovery_authorization_material( + request, + previous, + recovery_artifact, + issued_at_ms=issued, + ) + if canonical_bytes(expected) != canonical_bytes(activation_body): + raise RebirthError("rebirth_recovery_authorization_intent_mismatch") + expected_id = DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_ID_PREFIX + b64url( + digest(DISTRIBUTED_RECOVERY_AUTHORIZATION_INTENT_DOMAIN, body) + ) + normalized = copy.deepcopy(dict(row)) + if row["intent_id"] != expected_id or _canonical( + normalized, "invalid_rebirth_recovery_authorization_intent" + ) != _canonical(value, "invalid_rebirth_recovery_authorization_intent"): + raise RebirthError("invalid_rebirth_recovery_authorization_intent") + return normalized + + +def create_distributed_recovery_authorization_share( + intent: Any, + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + replacement_root_seed: bytes, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Sign credential and transition with exactly one replacement-root key.""" + + verified = validate_distributed_recovery_authorization_intent( + intent, + request, + previous, + recovery_artifact, + observed_at_ms=observed_at_ms, + ) + activation_body = verified["body"]["activation_body"] + credential = activation_body["credential"] + transition = activation_body["transition"] + recovered = recovery_request_base(previous, recovery_artifact) + kid = key_id("Ed25519", ed25519_public(replacement_root_seed)) + allowed = {row["key_id"] for row in recovered.state.root_policy["keys"]} + if kid not in allowed: + raise RebirthError("rebirth_recovered_root_holder_not_authorized") + credential_signature = _identity_signature( + replacement_root_seed, + "root-authorization", + domain_bytes(DOMAINS["embodiment-credential"], credential["body"]), + ) + signed_transition = create_recovery_rebirth( + previous.manifest, + BeingManifest.from_value(activation_body["successor_manifest"]), + recovery_artifact=recovery_artifact, + body_ref=activation_body["origin"]["body_ref"], + embodiment_id=activation_body["origin"]["embodiment_id"], + incarnation_id=activation_body["origin"]["incarnation_id"], + embodiment_credential_id=credential["artifact_id"], + incarnation_authorization_id=activation_body["incarnation"]["artifact_id"], + principal_id=activation_body["origin"]["principal_id"], + root_seeds=[replacement_root_seed], + issued_at_ms=activation_body["issued_at_ms"], + ) + if canonical_bytes( + {key: value for key, value in signed_transition.items() if key != "signatures"} + ) != canonical_bytes( + {key: value for key, value in transition.items() if key != "signatures"} + ): + raise RebirthError("rebirth_recovery_authorization_intent_mismatch") + share_body = { + "intent_id": verified["intent_id"], + "credential_artifact_id": credential["artifact_id"], + "transition_content_hash": transition["content_hash"], + "credential_signature": credential_signature, + "transition_signature": signed_transition["signatures"][0], + } + return { + "schema": DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_SCHEMA, + **share_body, + "attestation": _holder_attestation( + replacement_root_seed, + DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_DOMAIN, + share_body, + ), + } + + +def aggregate_distributed_recovery_authorization( + intent: Any, + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + shares: Sequence[Any], + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Build and verify a recovery activation without access to root seeds.""" + + verified = validate_distributed_recovery_authorization_intent( + intent, + request, + previous, + recovery_artifact, + observed_at_ms=observed_at_ms, + ) + body = copy.deepcopy(verified["body"]["activation_body"]) + credential = body["credential"] + transition = body["transition"] + request_base = recovery_request_base(previous, recovery_artifact) + credential_signatures: list[Mapping[str, Any]] = [] + transition_signatures: list[Mapping[str, Any]] = [] + paired_ids: set[str] = set() + for value in shares: + share = _closed( + value, + { + "schema", + "intent_id", + "credential_artifact_id", + "transition_content_hash", + "credential_signature", + "transition_signature", + "attestation", + }, + "invalid_rebirth_recovery_authorization_share", + ) + credential_signature = share["credential_signature"] + transition_signature = share["transition_signature"] + if ( + share["schema"] != DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_SCHEMA + or share["intent_id"] != verified["intent_id"] + or share["credential_artifact_id"] != credential["artifact_id"] + or share["transition_content_hash"] != transition["content_hash"] + or not isinstance(credential_signature, Mapping) + or not isinstance(transition_signature, Mapping) + or credential_signature.get("key_id") != transition_signature.get("kid") + or credential_signature.get("key_id") in paired_ids + ): + raise RebirthError("invalid_rebirth_recovery_authorization_share") + share_body = { + "intent_id": share["intent_id"], + "credential_artifact_id": share["credential_artifact_id"], + "transition_content_hash": share["transition_content_hash"], + "credential_signature": credential_signature, + "transition_signature": transition_signature, + } + attested_kid = _verify_holder_attestation( + share["attestation"], + DISTRIBUTED_RECOVERY_AUTHORIZATION_SHARE_DOMAIN, + share_body, + request_base.state.root_policy["keys"], + code="invalid_rebirth_recovery_authorization_share", + ) + if credential_signature.get("key_id") != attested_kid: + raise RebirthError("invalid_rebirth_recovery_authorization_share") + paired_ids.add(str(credential_signature["key_id"])) + credential_signatures.append(credential_signature) + transition_signatures.append(transition_signature) + credential["signatures"].extend( + copy.deepcopy(dict(signature)) for signature in credential_signatures + ) + credential["signatures"].sort(key=lambda row: (row["key_id"], row["role"])) + transition["signatures"] = sorted( + (copy.deepcopy(dict(signature)) for signature in transition_signatures), + key=lambda row: row["kid"], + ) + try: + verify_embodiment_credential( + credential, + request_base.state, + at_ms=body["issued_at_ms"], + ) + verify_incarnation_authorization( + body["incarnation"], + credential, + request_base.state, + at_ms=body["issued_at_ms"], + ) + successor = RootAuthority( + BeingManifest.from_value(body["successor_manifest"]), + request_base.state, + {credential["artifact_id"]: credential}, + {body["incarnation"]["artifact_id"]: body["incarnation"]}, + ) + verify_recovery_rebirth( + transition, + previous, + successor, + recovery_artifact, + ) + RootHistoryAuthority(successor, [previous], [transition]) + except (TypeError, ValueError) as exception: + raise RebirthError( + "rebirth_recovery_authorization_share_threshold_rejected" + ) from exception + activation = { + "schema": RECOVERY_ACTIVATION_SCHEMA, + "activation_id": RECOVERY_ACTIVATION_ID_PREFIX + + b64url(digest(RECOVERY_ACTIVATION_DOMAIN, body)), + "body": body, + } + validate_recovery_activation(activation, previous, request=request) + return activation + + def validate_recovery_activation( value: Any, previous: RootAuthority, @@ -1266,6 +1700,48 @@ def _fsync_directory(path: Path) -> None: os.close(descriptor) +def _write_new_document(path: Path, value: Mapping[str, Any]) -> None: + output = Path(os.path.abspath(path)) + parent = _owner_directory(output.parent, "rebirth_output_parent_rejected") + raw = canonical_bytes(value) + if output.exists() or output.is_symlink(): + if canonical_bytes(_safe_document(output, "rebirth_output_exists")) == raw: + return + raise RebirthError("rebirth_output_exists") + staging = parent / (f".{output.name}.{hashlib.sha256(raw).hexdigest()}.staging") + if staging.exists() or staging.is_symlink(): + try: + if ( + canonical_bytes( + _safe_document(staging, "rebirth_output_staging_rejected") + ) + != raw + ): + raise RebirthError("rebirth_output_staging_rejected") from None + except RebirthError: + info = staging.lstat() + if ( + stat.S_ISLNK(info.st_mode) + or not stat.S_ISREG(info.st_mode) + or info.st_uid != os.geteuid() + ): + raise RebirthError("rebirth_output_staging_rejected") from None + staging.unlink() + _fsync_directory(parent) + _private_write(staging, value) + else: + _private_write(staging, value) + try: + os.link(staging, output, follow_symlinks=False) + except FileExistsError: + if canonical_bytes(_safe_document(output, "rebirth_output_exists")) != raw: + raise RebirthError("rebirth_output_exists") from None + finally: + if staging.exists() and not staging.is_symlink(): + staging.unlink() + _fsync_directory(parent) + + def _password(descriptor: int) -> bytearray: if descriptor < 0: raise RebirthError("invalid_rebirth_password_descriptor") @@ -2101,14 +2577,395 @@ def restore_recovery_ledger( } -def create_recovery_custody( +def create_distributed_recovery_intent( + previous: RootAuthority, + replacement_root_descriptors: Sequence[Mapping[str, Any]], + replacement_threshold: int, + *, + created_at_ms: int, + expires_at_ms: int, + nonce: bytes, + known_states: Sequence[ControlState] | None = None, +) -> dict[str, Any]: + """Freeze public recovery material before any independent holder signs.""" + + created = _uint(created_at_ms, "invalid_rebirth_recovery_intent_time") + expires = _uint(expires_at_ms, "invalid_rebirth_recovery_intent_time") + if expires <= created or expires - created > 24 * 60 * 60 * 1000: + raise RebirthError("invalid_rebirth_recovery_intent_time") + if len(nonce) != 32: + raise RebirthError("invalid_rebirth_recovery_intent_nonce") + states = tuple(known_states or (previous.state,)) + if ( + not states + or previous.state.head not in {state.head for state in states} + or any(state.being_ref != previous.state.being_ref for state in states) + ): + raise RebirthError("rebirth_recovery_intent_authority_mismatch") + policy = { + "keys": sorted( + (copy.deepcopy(dict(row)) for row in replacement_root_descriptors), + key=lambda row: str(row.get("key_id", "")), + ), + "threshold": replacement_threshold, + } + revoked = sorted( + { + row["embodiment_id"] + for row in previous.manifest.value["embodiments"] + if row["status"] == "active" + } + ) + try: + prepared = prepare_recovery( + states, + policy, + revoke_embodiments=revoked, + ) + except (TypeError, ValueError) as exception: + raise RebirthError("rebirth_recovery_intent_policy_rejected") from exception + body = { + "being_ref": previous.state.being_ref, + "created_at_ms": created, + "expires_at_ms": expires, + "known_control_heads": sorted({state.head for state in states}), + "nonce": b64url(nonce), + "prepared_recovery": prepared, + "previous_manifest_hash": previous.manifest.digest, + } + result = { + "schema": DISTRIBUTED_RECOVERY_INTENT_SCHEMA, + "intent_id": DISTRIBUTED_RECOVERY_INTENT_ID_PREFIX + + b64url(digest(DISTRIBUTED_RECOVERY_INTENT_DOMAIN, body)), + "body": body, + } + _canonical(result, "invalid_rebirth_recovery_intent") + return result + + +def validate_distributed_recovery_intent( + value: Any, + previous: RootAuthority, + *, + observed_at_ms: int, + known_states: Sequence[ControlState] | None = None, +) -> dict[str, Any]: + """Validate the closed recovery intent independently at every holder.""" + + row = _closed( + value, + {"schema", "intent_id", "body"}, + "invalid_rebirth_recovery_intent", + ) + if row["schema"] != DISTRIBUTED_RECOVERY_INTENT_SCHEMA: + raise RebirthError("unsupported_rebirth_recovery_intent") + body = _closed( + row["body"], + { + "being_ref", + "created_at_ms", + "expires_at_ms", + "known_control_heads", + "nonce", + "prepared_recovery", + "previous_manifest_hash", + }, + "invalid_rebirth_recovery_intent", + ) + observed = _uint(observed_at_ms, "invalid_rebirth_recovery_intent_time") + created = _uint(body["created_at_ms"], "invalid_rebirth_recovery_intent_time") + expires = _uint(body["expires_at_ms"], "invalid_rebirth_recovery_intent_time") + if ( + expires <= created + or expires - created > 24 * 60 * 60 * 1000 + or not created <= observed < expires + ): + raise RebirthError("rebirth_recovery_intent_not_timely") + try: + nonce = b64url(unb64url(str(body["nonce"]), length=32)) + except (TypeError, ValueError) as exception: + raise RebirthError("invalid_rebirth_recovery_intent_nonce") from exception + states = tuple(known_states or (previous.state,)) + heads = sorted({state.head for state in states}) + if ( + nonce != body["nonce"] + or body["being_ref"] != previous.state.being_ref + or body["previous_manifest_hash"] != previous.manifest.digest + or body["known_control_heads"] != heads + or previous.state.head not in heads + ): + raise RebirthError("rebirth_recovery_intent_authority_mismatch") + prepared = body["prepared_recovery"] + if not isinstance(prepared, Mapping): + raise RebirthError("invalid_rebirth_recovery_intent") + prepared_body = prepared.get("body") + if not isinstance(prepared_body, Mapping): + raise RebirthError("invalid_rebirth_recovery_intent") + expected_revocations = sorted( + { + item["embodiment_id"] + for item in previous.manifest.value["embodiments"] + if item["status"] == "active" + } + ) + try: + expected = prepare_recovery( + states, + prepared_body.get("replacement_root", {}), + revoke_embodiments=expected_revocations, + ) + except (TypeError, ValueError) as exception: + raise RebirthError("invalid_rebirth_recovery_intent") from exception + if canonical_bytes(prepared) != canonical_bytes(expected): + raise RebirthError("rebirth_recovery_intent_authority_mismatch") + expected_id = DISTRIBUTED_RECOVERY_INTENT_ID_PREFIX + b64url( + digest(DISTRIBUTED_RECOVERY_INTENT_DOMAIN, body) + ) + normalized = copy.deepcopy(dict(row)) + if row["intent_id"] != expected_id or _canonical( + normalized, "invalid_rebirth_recovery_intent" + ) != _canonical(value, "invalid_rebirth_recovery_intent"): + raise RebirthError("invalid_rebirth_recovery_intent") + return normalized + + +def create_distributed_recovery_share( + intent: Any, + previous: RootAuthority, + holder_seed: bytes, + *, + observed_at_ms: int, + known_states: Sequence[ControlState] | None = None, +) -> dict[str, Any]: + """Produce one role-bound share in a process holding exactly one seed.""" + + states = tuple(known_states or (previous.state,)) + verified = validate_distributed_recovery_intent( + intent, + previous, + observed_at_ms=observed_at_ms, + known_states=states, + ) + prepared = verified["body"]["prepared_recovery"] + kid = key_id("Ed25519", ed25519_public(holder_seed)) + recovery_ids = {row["key_id"] for row in states[0].recovery_policy["keys"]} + replacement_ids = { + row["key_id"] for row in prepared["body"]["replacement_root"]["keys"] + } + try: + if kid in recovery_ids and kid not in replacement_ids: + signature = create_recovery_authorization_share( + prepared, states, holder_seed + ) + elif kid in replacement_ids and kid not in recovery_ids: + signature = create_recovery_possession_share(prepared, states, holder_seed) + else: + raise RebirthError("rebirth_recovery_holder_not_authorized") + except (TypeError, ValueError) as exception: + raise RebirthError("rebirth_recovery_holder_not_authorized") from exception + share_body = { + "intent_id": verified["intent_id"], + "recovery_artifact_id": prepared["artifact_id"], + "signature": signature, + } + return { + "schema": DISTRIBUTED_RECOVERY_SHARE_SCHEMA, + **share_body, + "attestation": _holder_attestation( + holder_seed, + DISTRIBUTED_RECOVERY_SHARE_DOMAIN, + share_body, + ), + } + + +def aggregate_distributed_recovery( + intent: Any, + previous: RootAuthority, + shares: Sequence[Any], + *, + observed_at_ms: int, + known_states: Sequence[ControlState] | None = None, +) -> dict[str, Any]: + """Aggregate public shares without opening any holder keystore.""" + + states = tuple(known_states or (previous.state,)) + verified = validate_distributed_recovery_intent( + intent, + previous, + observed_at_ms=observed_at_ms, + known_states=states, + ) + prepared = verified["body"]["prepared_recovery"] + signatures: list[Mapping[str, Any]] = [] + for value in shares: + share = _closed( + value, + { + "schema", + "intent_id", + "recovery_artifact_id", + "signature", + "attestation", + }, + "invalid_rebirth_recovery_share", + ) + if ( + share["schema"] != DISTRIBUTED_RECOVERY_SHARE_SCHEMA + or share["intent_id"] != verified["intent_id"] + or share["recovery_artifact_id"] != prepared["artifact_id"] + or not isinstance(share["signature"], Mapping) + ): + raise RebirthError("invalid_rebirth_recovery_share") + signature = share["signature"] + role = signature.get("role") + allowed_descriptors = ( + states[0].recovery_policy["keys"] + if role == "recovery-authorization" + else prepared["body"]["replacement_root"]["keys"] + if role == "new-root-possession" + else [] + ) + share_body = { + "intent_id": share["intent_id"], + "recovery_artifact_id": share["recovery_artifact_id"], + "signature": signature, + } + attested_kid = _verify_holder_attestation( + share["attestation"], + DISTRIBUTED_RECOVERY_SHARE_DOMAIN, + share_body, + allowed_descriptors, + code="invalid_rebirth_recovery_share", + ) + if signature.get("key_id") != attested_kid: + raise RebirthError("invalid_rebirth_recovery_share") + signatures.append(share["signature"]) + try: + artifact = aggregate_recovery(prepared, states, signatures) + recovery_request_base(previous, artifact) + except (TypeError, ValueError) as exception: + raise RebirthError("rebirth_recovery_share_threshold_rejected") from exception + return artifact + + +def create_replacement_root_holder( + output: Path, + previous: RootAuthority, + password_reader: PasswordReader, +) -> dict[str, Any]: + """Generate one replacement root in one owner-only holder store.""" + + target = Path(os.path.abspath(output)) + _owner_directory(target.parent, "rebirth_holder_parent_rejected") + if target.exists() or target.is_symlink(): + raise RebirthError("rebirth_holder_exists") + holder_seed = generate_ed25519_seed() + descriptor = signing_descriptor(holder_seed) + try: + EncryptedKeystore.create( + target, + password_reader, + control_head=previous.state.head, + secrets={"root.signing.v1:holder": holder_seed}, + ) + except KeystoreError as exception: + raise RebirthError("rebirth_holder_create_rejected") from exception + _fsync_directory(target.parent) + return { + "schema": "dm.operator.replacement-root-holder/v1", + "being_ref": previous.state.being_ref, + "custody_control_head": previous.state.head, + "key": descriptor, + } + + +def _single_holder_seed( + path: Path, + previous: RootAuthority, + password_reader: PasswordReader, + *, + allowed_prefixes: tuple[str, ...], + code: str, +) -> bytes: + try: + contents = EncryptedKeystore(path).open( + password_reader, + minimum_counter=1, + required_control_head=previous.state.head, + ) + except KeystoreError as exception: + raise RebirthError(code) from exception + if len(contents.secrets) != 1: + raise RebirthError(code) + slot, holder_seed = next(iter(contents.secrets.items())) + if not slot.startswith(allowed_prefixes): + raise RebirthError(code) + return holder_seed + + +def create_distributed_recovery_share_from_holder( + intent: Any, + previous: RootAuthority, + holder_path: Path, + password_reader: PasswordReader, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Open exactly one holder store and emit only its public recovery share.""" + + holder_seed = _single_holder_seed( + holder_path, + previous, + password_reader, + allowed_prefixes=("root.signing.v1:", "recovery.signing.v1:"), + code="rebirth_recovery_holder_store_rejected", + ) + return create_distributed_recovery_share( + intent, + previous, + holder_seed, + observed_at_ms=observed_at_ms, + ) + + +def create_distributed_recovery_authorization_share_from_holder( + intent: Any, + request: Any, + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + holder_path: Path, + password_reader: PasswordReader, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Open one replacement-root store and emit one paired authorization share.""" + + holder_seed = _single_holder_seed( + holder_path, + previous, + password_reader, + allowed_prefixes=("root.signing.v1:",), + code="rebirth_recovered_root_holder_store_rejected", + ) + return create_distributed_recovery_authorization_share( + intent, + request, + previous, + recovery_artifact, + holder_seed, + observed_at_ms=observed_at_ms, + ) + + +def create_synthetic_single_store_recovery_custody( output: Path, previous: RootAuthority, custody_path: Path, current_password_reader: PasswordReader, replacement_password_reader: PasswordReader, ) -> dict[str, Any]: - """Rotate a recovery quorum into fresh root custody without a body key.""" + """Synthetic fixture: centralize a quorum for deterministic local tests.""" try: contents = EncryptedKeystore(custody_path).open( @@ -2207,7 +3064,7 @@ def create_recovery_custody( shutil.rmtree(staging) -def authorize_recovery_from_root_custody( +def authorize_synthetic_single_store_recovery( request: Any, previous: RootAuthority, recovery_artifact: Mapping[str, Any], @@ -2316,13 +3173,83 @@ def parser() -> argparse.ArgumentParser: activate.add_argument("--output", type=Path, required=True) activate.add_argument("--password-fd", type=int, required=True) recover = commands.add_parser( - "recover", help="rotate a recovery quorum into fresh root custody" + "synthetic-single-store-recover", + help="test fixture: centralize recovery and replacement-root seeds", ) recover.add_argument("--authority", type=Path, required=True) recover.add_argument("--root-custody", type=Path, required=True) recover.add_argument("--current-password-fd", type=int, required=True) recover.add_argument("--replacement-password-fd", type=int, required=True) recover.add_argument("--output", type=Path, required=True) + create_holder = commands.add_parser( + "create-replacement-root-holder", + help="generate one isolated replacement-root holder store", + ) + create_holder.add_argument("--authority", type=Path, required=True) + create_holder.add_argument("--holder", type=Path, required=True) + create_holder.add_argument("--password-fd", type=int, required=True) + create_holder.add_argument("--output", type=Path, required=True) + recovery_intent = commands.add_parser( + "create-recovery-intent", + help="freeze one recovery intent from public holder descriptors", + ) + recovery_intent.add_argument("--authority", type=Path, required=True) + recovery_intent.add_argument( + "--holder-descriptor", type=Path, action="append", required=True + ) + recovery_intent.add_argument("--threshold", type=int, required=True) + recovery_intent.add_argument("--ttl-seconds", type=int, default=3600) + recovery_intent.add_argument("--output", type=Path, required=True) + recovery_share = commands.add_parser( + "recovery-share", help="emit one share from one isolated holder store" + ) + recovery_share.add_argument("--authority", type=Path, required=True) + recovery_share.add_argument("--intent", type=Path, required=True) + recovery_share.add_argument("--holder", type=Path, required=True) + recovery_share.add_argument("--password-fd", type=int, required=True) + recovery_share.add_argument("--output", type=Path, required=True) + aggregate_recovery_command = commands.add_parser( + "aggregate-recovery", + help="aggregate recovery shares without opening holder stores", + ) + aggregate_recovery_command.add_argument("--authority", type=Path, required=True) + aggregate_recovery_command.add_argument("--intent", type=Path, required=True) + aggregate_recovery_command.add_argument( + "--share", type=Path, action="append", required=True + ) + aggregate_recovery_command.add_argument("--output", type=Path, required=True) + authorization_intent = commands.add_parser( + "create-recovery-authorization-intent", + help="freeze target credential and transition for root holders", + ) + authorization_intent.add_argument("--authority", type=Path, required=True) + authorization_intent.add_argument("--recovery", type=Path, required=True) + authorization_intent.add_argument("--request", type=Path, required=True) + authorization_intent.add_argument("--ttl-seconds", type=int, default=3600) + authorization_intent.add_argument("--output", type=Path, required=True) + authorization_share = commands.add_parser( + "recovery-authorization-share", + help="emit one target authorization share from one root holder", + ) + authorization_share.add_argument("--authority", type=Path, required=True) + authorization_share.add_argument("--recovery", type=Path, required=True) + authorization_share.add_argument("--request", type=Path, required=True) + authorization_share.add_argument("--intent", type=Path, required=True) + authorization_share.add_argument("--holder", type=Path, required=True) + authorization_share.add_argument("--password-fd", type=int, required=True) + authorization_share.add_argument("--output", type=Path, required=True) + aggregate_authorization = commands.add_parser( + "aggregate-recovery-authorization", + help="aggregate target authorization shares without holder stores", + ) + aggregate_authorization.add_argument("--authority", type=Path, required=True) + aggregate_authorization.add_argument("--recovery", type=Path, required=True) + aggregate_authorization.add_argument("--request", type=Path, required=True) + aggregate_authorization.add_argument("--intent", type=Path, required=True) + aggregate_authorization.add_argument( + "--share", type=Path, action="append", required=True + ) + aggregate_authorization.add_argument("--output", type=Path, required=True) prepare_recovery = commands.add_parser( "prepare-recovery", help="generate target custody after recovery" ) @@ -2333,7 +3260,8 @@ def parser() -> argparse.ArgumentParser: prepare_recovery.add_argument("--password-fd", type=int, required=True) prepare_recovery.add_argument("--ttl-seconds", type=int, default=3600) authorize_recovery = commands.add_parser( - "authorize-recovery", help="authorize a target with recovered root custody" + "synthetic-single-store-authorize-recovery", + help="test fixture: authorize from centralized recovered root custody", ) authorize_recovery.add_argument("--authority", type=Path, required=True) authorize_recovery.add_argument("--recovery", type=Path, required=True) @@ -2359,7 +3287,167 @@ def main(argv: Sequence[str] | None = None) -> int: try: arguments = parser().parse_args(argv) now = time.time_ns() // 1_000_000 - if arguments.command in {"prepare", "prepare-recovery"}: + if arguments.command == "create-replacement-root-holder": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + password = _password(arguments.password_fd) + try: + receipt = create_replacement_root_holder( + arguments.holder, + authority, + _password_reader(password), + ) + _write_new_document(arguments.output, receipt) + finally: + password[:] = b"\x00" * len(password) + elif arguments.command == "create-recovery-intent": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + ttl = arguments.ttl_seconds + if ( + not isinstance(ttl, int) + or isinstance(ttl, bool) + or not 60 <= ttl <= 86_400 + ): + raise RebirthError("invalid_rebirth_ttl") + descriptors: list[Mapping[str, Any]] = [] + for path in arguments.holder_descriptor: + holder = _closed( + _safe_document(path, "rebirth_holder_descriptor_unavailable"), + {"schema", "being_ref", "custody_control_head", "key"}, + "invalid_rebirth_holder_descriptor", + ) + if ( + holder["schema"] != "dm.operator.replacement-root-holder/v1" + or holder["being_ref"] != authority.state.being_ref + or holder["custody_control_head"] != authority.state.head + or not isinstance(holder["key"], Mapping) + ): + raise RebirthError("invalid_rebirth_holder_descriptor") + descriptors.append(holder["key"]) + receipt = create_distributed_recovery_intent( + authority, + descriptors, + arguments.threshold, + created_at_ms=now, + expires_at_ms=now + ttl * 1000, + nonce=secrets.token_bytes(32), + ) + _write_new_document(arguments.output, receipt) + elif arguments.command == "recovery-share": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + password = _password(arguments.password_fd) + try: + receipt = create_distributed_recovery_share_from_holder( + _safe_document( + arguments.intent, + "rebirth_recovery_intent_unavailable", + ), + authority, + arguments.holder, + _password_reader(password), + observed_at_ms=now, + ) + _write_new_document(arguments.output, receipt) + finally: + password[:] = b"\x00" * len(password) + elif arguments.command == "aggregate-recovery": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + receipt = aggregate_distributed_recovery( + _safe_document( + arguments.intent, + "rebirth_recovery_intent_unavailable", + ), + authority, + [ + _safe_document(path, "rebirth_recovery_share_unavailable") + for path in arguments.share + ], + observed_at_ms=now, + ) + _write_new_document(arguments.output, receipt) + elif arguments.command == "create-recovery-authorization-intent": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + ttl = arguments.ttl_seconds + if ( + not isinstance(ttl, int) + or isinstance(ttl, bool) + or not 60 <= ttl <= 86_400 + ): + raise RebirthError("invalid_rebirth_ttl") + receipt = create_distributed_recovery_authorization_intent( + _safe_document(arguments.request, "rebirth_request_unavailable"), + authority, + _safe_document( + arguments.recovery, + "rebirth_recovery_artifact_unavailable", + ), + issued_at_ms=now, + expires_at_ms=now + ttl * 1000, + nonce=secrets.token_bytes(32), + ) + _write_new_document(arguments.output, receipt) + elif arguments.command == "recovery-authorization-share": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + password = _password(arguments.password_fd) + try: + receipt = create_distributed_recovery_authorization_share_from_holder( + _safe_document( + arguments.intent, + "rebirth_recovery_authorization_intent_unavailable", + ), + _safe_document( + arguments.request, + "rebirth_request_unavailable", + ), + authority, + _safe_document( + arguments.recovery, + "rebirth_recovery_artifact_unavailable", + ), + arguments.holder, + _password_reader(password), + observed_at_ms=now, + ) + _write_new_document(arguments.output, receipt) + finally: + password[:] = b"\x00" * len(password) + elif arguments.command == "aggregate-recovery-authorization": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + receipt = aggregate_distributed_recovery_authorization( + _safe_document( + arguments.intent, + "rebirth_recovery_authorization_intent_unavailable", + ), + _safe_document(arguments.request, "rebirth_request_unavailable"), + authority, + _safe_document( + arguments.recovery, + "rebirth_recovery_artifact_unavailable", + ), + [ + _safe_document( + path, + "rebirth_recovery_authorization_share_unavailable", + ) + for path in arguments.share + ], + observed_at_ms=now, + ) + _write_new_document(arguments.output, receipt) + elif arguments.command in {"prepare", "prepare-recovery"}: authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) @@ -2400,7 +3488,10 @@ def main(argv: Sequence[str] | None = None) -> int: ) finally: password[:] = b"\x00" * len(password) - elif arguments.command in {"authorize", "authorize-recovery"}: + elif arguments.command in { + "authorize", + "synthetic-single-store-authorize-recovery", + }: authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) @@ -2410,7 +3501,7 @@ def main(argv: Sequence[str] | None = None) -> int: arguments.request, "rebirth_request_unavailable" ) receipt = ( - authorize_recovery_from_root_custody( + authorize_synthetic_single_store_recovery( request, authority, _safe_document( @@ -2421,7 +3512,7 @@ def main(argv: Sequence[str] | None = None) -> int: _password_reader(password), issued_at_ms=now, ) - if arguments.command == "authorize-recovery" + if arguments.command == "synthetic-single-store-authorize-recovery" else authorize_from_root_custody( request, authority, @@ -2438,14 +3529,14 @@ def main(argv: Sequence[str] | None = None) -> int: _fsync_directory(output.parent) finally: password[:] = b"\x00" * len(password) - elif arguments.command == "recover": + elif arguments.command == "synthetic-single-store-recover": authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) current_password = _password(arguments.current_password_fd) replacement_password = _password(arguments.replacement_password_fd) try: - receipt = create_recovery_custody( + receipt = create_synthetic_single_store_recovery_custody( arguments.output, authority, arguments.root_custody, @@ -2513,10 +3604,10 @@ def main(argv: Sequence[str] | None = None) -> int: "authorize_enrollment_request", "authorize_from_root_custody", "authorize_recovery_enrollment_request", - "authorize_recovery_from_root_custody", + "authorize_synthetic_single_store_recovery", "create_enrollment_request", - "create_recovery_custody", "create_recovery_target_preparation", + "create_synthetic_single_store_recovery_custody", "create_target_preparation", "main", "parser", diff --git a/tests/test_dm078_recovery_rebirth.py b/tests/test_dm078_recovery_rebirth.py index 80da629..d7ddfd5 100644 --- a/tests/test_dm078_recovery_rebirth.py +++ b/tests/test_dm078_recovery_rebirth.py @@ -6,6 +6,7 @@ import os import subprocess import sys +import time from pathlib import Path from tempfile import TemporaryDirectory from typing import Any @@ -38,13 +39,19 @@ from daimon_matrix.operator_rebirth import ( RebirthError, activate_recovery_target_runtime, + aggregate_distributed_recovery, + aggregate_distributed_recovery_authorization, authority_from_runtime_bundle, authorize_enrollment_request, authorize_recovery_enrollment_request, - authorize_recovery_from_root_custody, + authorize_synthetic_single_store_recovery, + create_distributed_recovery_authorization_intent, + create_distributed_recovery_authorization_share, + create_distributed_recovery_intent, + create_distributed_recovery_share, create_enrollment_request, - create_recovery_custody, create_recovery_target_preparation, + create_synthetic_single_store_recovery_custody, recovery_request_base, restore_recovery_ledger, validate_activation, @@ -115,6 +122,43 @@ def _base_runtime_bundle(self) -> dict[str, Any]: }, } + def _invoke_rebirth_cli( + self, + arguments: list[str], + passwords: dict[str, bytes] | None = None, + ) -> subprocess.CompletedProcess[bytes]: + descriptors: dict[str, int] = {} + readers: list[int] = [] + for marker, password in (passwords or {}).items(): + reader, writer = os.pipe() + os.write(writer, password) + os.close(writer) + descriptors[marker] = reader + readers.append(reader) + try: + return subprocess.run( + [ + sys.executable, + "-m", + "daimon_matrix.operator_rebirth", + *[ + str(descriptors[value]) if value in descriptors else value + for value in arguments + ], + ], + cwd=Path(__file__).resolve().parents[1], + env={ + **os.environ, + "PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src"), + }, + pass_fds=tuple(descriptors.values()), + capture_output=True, + check=False, + ) + finally: + for descriptor in readers: + os.close(descriptor) + def _successor( self, *, @@ -839,7 +883,7 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( }, }, ) - receipt = create_recovery_custody( + receipt = create_synthetic_single_store_recovery_custody( ceremony / "successor", self.authority, old_store, @@ -876,7 +920,7 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( }, ) with self.assertRaisesRegex(RebirthError, "custody_rejected"): - create_recovery_custody( + create_synthetic_single_store_recovery_custody( ceremony / "wrong-successor", self.authority, wrong_store, @@ -894,7 +938,7 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( }, ) with self.assertRaisesRegex(RebirthError, "custody_rejected"): - create_recovery_custody( + create_synthetic_single_store_recovery_custody( ceremony / "extra-successor", self.authority, extra_store, @@ -916,7 +960,7 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( expires_at_ms=NOW + 60_010, nonce=seed("custody-target-nonce"), ) - activation = authorize_recovery_from_root_custody( + activation = authorize_synthetic_single_store_recovery( request, self.authority, recovery, @@ -936,7 +980,7 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( }, ) with self.assertRaisesRegex(RebirthError, "custody_rejected"): - authorize_recovery_from_root_custody( + authorize_synthetic_single_store_recovery( request, self.authority, recovery, @@ -950,7 +994,466 @@ def test_offline_recovery_custody_drops_old_roots_and_authorizes_target( assert old_password.decode() not in public assert new_password.decode() not in public - def test_recovery_cli_runs_each_custody_role_in_a_distinct_process(self) -> None: + def test_distributed_recovery_aggregates_disjoint_holder_shares(self) -> None: + recovery_seeds = [ + seed("recovery-a"), + seed("recovery-b"), + seed("recovery-c"), + ] + replacement_roots = [ + seed("distributed-root-a"), + seed("distributed-root-b"), + seed("distributed-root-c"), + ] + intent = create_distributed_recovery_intent( + self.authority, + [signing_descriptor(value) for value in replacement_roots], + 2, + created_at_ms=NOW, + expires_at_ms=NOW + 60_000, + nonce=seed("distributed-recovery-intent"), + ) + shares = [ + create_distributed_recovery_share( + intent, + self.authority, + value, + observed_at_ms=NOW + 1, + ) + for value in ( + recovery_seeds[0], + replacement_roots[0], + recovery_seeds[1], + replacement_roots[1], + ) + ] + recovery = aggregate_distributed_recovery( + intent, + self.authority, + shares, + observed_at_ms=NOW + 2, + ) + recovered = recovery_request_base(self.authority, recovery) + self.assertEqual(recovered.state.head, recovery["artifact_id"]) + self.assertEqual( + {row["key_id"] for row in recovered.state.root_policy["keys"]}, + {signing_descriptor(value)["key_id"] for value in replacement_roots}, + ) + public_exchange = canonical_bytes( + {"intent": intent, "shares": shares, "recovery": recovery} + ) + for private_seed in recovery_seeds + replacement_roots: + self.assertNotIn(private_seed, public_exchange) + + def test_distributed_recovery_rejects_replay_substitution_and_shortfall( + self, + ) -> None: + recovery_seeds = [ + seed("recovery-a"), + seed("recovery-b"), + seed("recovery-c"), + ] + replacement_roots = [ + seed("distributed-hostile-root-a"), + seed("distributed-hostile-root-b"), + seed("distributed-hostile-root-c"), + ] + intent = create_distributed_recovery_intent( + self.authority, + [signing_descriptor(value) for value in replacement_roots], + 2, + created_at_ms=NOW, + expires_at_ms=NOW + 60_000, + nonce=seed("distributed-hostile-intent"), + ) + recovery_share = create_distributed_recovery_share( + intent, + self.authority, + recovery_seeds[0], + observed_at_ms=NOW + 1, + ) + root_share = create_distributed_recovery_share( + intent, + self.authority, + replacement_roots[0], + observed_at_ms=NOW + 1, + ) + with self.assertRaisesRegex(RebirthError, "threshold_rejected"): + aggregate_distributed_recovery( + intent, + self.authority, + [recovery_share, root_share], + observed_at_ms=NOW + 2, + ) + with self.assertRaisesRegex(RebirthError, "threshold_rejected"): + aggregate_distributed_recovery( + intent, + self.authority, + [recovery_share, recovery_share, root_share, root_share], + observed_at_ms=NOW + 2, + ) + substituted = copy.deepcopy(root_share) + substituted["intent_id"] = "dm:recovery-intent:v1:substituted" + with self.assertRaisesRegex(RebirthError, "invalid_rebirth_recovery_share"): + aggregate_distributed_recovery( + intent, + self.authority, + [recovery_share, substituted], + observed_at_ms=NOW + 2, + ) + second_intent = create_distributed_recovery_intent( + self.authority, + [signing_descriptor(value) for value in replacement_roots], + 2, + created_at_ms=NOW, + expires_at_ms=NOW + 60_000, + nonce=seed("distributed-hostile-second-intent"), + ) + transplanted = copy.deepcopy(recovery_share) + transplanted["intent_id"] = second_intent["intent_id"] + with self.assertRaisesRegex(RebirthError, "invalid_rebirth_recovery_share"): + aggregate_distributed_recovery( + second_intent, + self.authority, + [transplanted], + observed_at_ms=NOW + 2, + ) + with self.assertRaisesRegex(RebirthError, "holder_not_authorized"): + create_distributed_recovery_share( + intent, + self.authority, + seed("hostile-holder"), + observed_at_ms=NOW + 1, + ) + with self.assertRaisesRegex(RebirthError, "not_timely"): + create_distributed_recovery_share( + intent, + self.authority, + recovery_seeds[0], + observed_at_ms=NOW + 60_000, + ) + + def test_distributed_root_holders_authorize_target_without_seed_aggregation( + self, + ) -> None: + recovery_seeds = [ + seed("recovery-a"), + seed("recovery-b"), + seed("recovery-c"), + ] + replacement_roots = [ + seed("distributed-authorization-root-a"), + seed("distributed-authorization-root-b"), + seed("distributed-authorization-root-c"), + ] + recovery_intent = create_distributed_recovery_intent( + self.authority, + [signing_descriptor(value) for value in replacement_roots], + 2, + created_at_ms=NOW, + expires_at_ms=NOW + 60_000, + nonce=seed("distributed-authorization-recovery-intent"), + ) + recovery_shares = [ + create_distributed_recovery_share( + recovery_intent, + self.authority, + value, + observed_at_ms=NOW + 1, + ) + for value in ( + recovery_seeds[0], + recovery_seeds[1], + replacement_roots[0], + replacement_roots[1], + ) + ] + recovery = aggregate_distributed_recovery( + recovery_intent, + self.authority, + recovery_shares, + observed_at_ms=NOW + 2, + ) + request = create_enrollment_request( + recovery_request_base(self.authority, recovery), + signing_seed=seed("distributed-target-signing"), + encryption_private=seed("distributed-target-encryption"), + transport_seed=seed("distributed-target-transport"), + body_ref="cluster:distributed-target:compaii", + embodiment_id="embodiment:distributed-target", + incarnation_id="incarnation:distributed-target:0", + principal_id="compaii@distributed-target", + created_at_ms=NOW + 10, + expires_at_ms=NOW + 60_010, + nonce=seed("distributed-target-request"), + ) + authorization_intent = create_distributed_recovery_authorization_intent( + request, + self.authority, + recovery, + issued_at_ms=NOW + 20, + expires_at_ms=NOW + 60_020, + nonce=seed("distributed-authorization-intent"), + ) + authorization_shares = [ + create_distributed_recovery_authorization_share( + authorization_intent, + request, + self.authority, + recovery, + value, + observed_at_ms=NOW + 21, + ) + for value in replacement_roots[:2] + ] + activation = aggregate_distributed_recovery_authorization( + authorization_intent, + request, + self.authority, + recovery, + authorization_shares, + observed_at_ms=NOW + 22, + ) + validate_recovery_activation(activation, self.authority, request=request) + public_exchange = canonical_bytes( + { + "recovery_intent": recovery_intent, + "recovery_shares": recovery_shares, + "recovery": recovery, + "authorization_intent": authorization_intent, + "authorization_shares": authorization_shares, + "activation": activation, + } + ) + for private_seed in recovery_seeds + replacement_roots: + self.assertNotIn(private_seed, public_exchange) + with self.assertRaisesRegex(RebirthError, "threshold_rejected"): + aggregate_distributed_recovery_authorization( + authorization_intent, + request, + self.authority, + recovery, + authorization_shares[:1], + observed_at_ms=NOW + 22, + ) + + def test_distributed_custody_cli_isolates_every_holder_and_keyless_aggregator( + self, + ) -> None: + root = self.root_path / "distributed-custody-cli" + root.mkdir(mode=0o700) + authority_path = root / "authority.json" + authority_path.write_bytes(canonical_bytes(self._authority_document())) + authority_path.chmod(0o600) + recovery_passwords = [ + f"distributed-recovery-holder-{index}".encode() for index in range(3) + ] + recovery_holders: list[Path] = [] + recovery_seeds = [ + seed("recovery-a"), + seed("recovery-b"), + seed("recovery-c"), + ] + for index, (password, recovery_seed) in enumerate( + zip(recovery_passwords, recovery_seeds, strict=True) + ): + path = root / f"recovery-holder-{index}.json" + EncryptedKeystore.create( + path, + lambda value=password: bytearray(value), + control_head=self.state.head, + secrets={"recovery.signing.v1:holder": recovery_seed}, + ) + recovery_holders.append(path) + + root_passwords = [ + f"distributed-root-holder-{index}".encode() for index in range(3) + ] + root_holders: list[Path] = [] + descriptors: list[Path] = [] + for index, password in enumerate(root_passwords): + holder = root / f"root-holder-{index}.json" + descriptor = root / f"root-holder-{index}-descriptor.json" + result = self._invoke_rebirth_cli( + [ + "create-replacement-root-holder", + "--authority", + str(authority_path), + "--holder", + str(holder), + "--password-fd", + "{password}", + "--output", + str(descriptor), + ], + {"{password}": password}, + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + root_holders.append(holder) + descriptors.append(descriptor) + + intent_path = root / "recovery-intent.json" + arguments = [ + "create-recovery-intent", + "--authority", + str(authority_path), + "--threshold", + "2", + "--output", + str(intent_path), + ] + for descriptor in descriptors: + arguments.extend(["--holder-descriptor", str(descriptor)]) + result = self._invoke_rebirth_cli(arguments) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + + recovery_share_paths: list[Path] = [] + for role, holders, passwords in ( + ("recovery", recovery_holders[:2], recovery_passwords[:2]), + ("root", root_holders[:2], root_passwords[:2]), + ): + for index, (holder, password) in enumerate( + zip(holders, passwords, strict=True) + ): + share_path = root / f"{role}-share-{index}.json" + result = self._invoke_rebirth_cli( + [ + "recovery-share", + "--authority", + str(authority_path), + "--intent", + str(intent_path), + "--holder", + str(holder), + "--password-fd", + "{password}", + "--output", + str(share_path), + ], + {"{password}": password}, + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + recovery_share_paths.append(share_path) + + recovery_path = root / "recovery.json" + arguments = [ + "aggregate-recovery", + "--authority", + str(authority_path), + "--intent", + str(intent_path), + "--output", + str(recovery_path), + ] + for share_path in recovery_share_paths: + arguments.extend(["--share", str(share_path)]) + result = self._invoke_rebirth_cli(arguments) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + retry = self._invoke_rebirth_cli(arguments) + self.assertEqual(retry.returncode, 0, retry.stderr.decode()) + recovery = json.loads(recovery_path.read_bytes()) + request_base = recovery_request_base(self.authority, recovery) + wall_ms = time.time_ns() // 1_000_000 + request = create_enrollment_request( + request_base, + signing_seed=seed("distributed-cli-target-signing"), + encryption_private=seed("distributed-cli-target-encryption"), + transport_seed=seed("distributed-cli-target-transport"), + body_ref="cluster:distributed-cli-target:compaii", + embodiment_id="embodiment:distributed-cli-target", + incarnation_id="incarnation:distributed-cli-target:0", + principal_id="compaii@distributed-cli-target", + created_at_ms=wall_ms - 1_000, + expires_at_ms=wall_ms + 600_000, + nonce=seed("distributed-cli-target-request"), + ) + request_path = root / "request.json" + request_path.write_bytes(canonical_bytes(request)) + request_path.chmod(0o600) + authorization_intent_path = root / "authorization-intent.json" + result = self._invoke_rebirth_cli( + [ + "create-recovery-authorization-intent", + "--authority", + str(authority_path), + "--recovery", + str(recovery_path), + "--request", + str(request_path), + "--output", + str(authorization_intent_path), + ] + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + + authorization_share_paths: list[Path] = [] + for index, (holder, password) in enumerate( + zip(root_holders[:2], root_passwords[:2], strict=True) + ): + share_path = root / f"authorization-share-{index}.json" + result = self._invoke_rebirth_cli( + [ + "recovery-authorization-share", + "--authority", + str(authority_path), + "--recovery", + str(recovery_path), + "--request", + str(request_path), + "--intent", + str(authorization_intent_path), + "--holder", + str(holder), + "--password-fd", + "{password}", + "--output", + str(share_path), + ], + {"{password}": password}, + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + authorization_share_paths.append(share_path) + + activation_path = root / "activation.json" + arguments = [ + "aggregate-recovery-authorization", + "--authority", + str(authority_path), + "--recovery", + str(recovery_path), + "--request", + str(request_path), + "--intent", + str(authorization_intent_path), + "--output", + str(activation_path), + ] + for share_path in authorization_share_paths: + arguments.extend(["--share", str(share_path)]) + result = self._invoke_rebirth_cli(arguments) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + retry = self._invoke_rebirth_cli(arguments) + self.assertEqual(retry.returncode, 0, retry.stderr.decode()) + activation = json.loads(activation_path.read_bytes()) + validate_recovery_activation(activation, self.authority, request=request) + + public_paths = [ + *descriptors, + intent_path, + *recovery_share_paths, + recovery_path, + request_path, + authorization_intent_path, + *authorization_share_paths, + activation_path, + ] + public_exchange = b"".join(path.read_bytes() for path in public_paths) + for private_seed in recovery_seeds: + self.assertNotIn(private_seed, public_exchange) + for holder in (*recovery_holders, *root_holders): + self.assertNotIn(str(holder).encode(), result.stdout) + + def test_synthetic_recovery_cli_runs_each_stage_in_a_distinct_process(self) -> None: root = self.root_path / "recovery-cli" root.mkdir(mode=0o700) authority_path = root / "authority.json" @@ -1031,7 +1534,7 @@ def invoke( recovered = invoke( [ - "recover", + "synthetic-single-store-recover", "--authority", str(authority_path), "--root-custody", @@ -1068,7 +1571,7 @@ def invoke( activation_path = root / "activation.json" authorized = invoke( [ - "authorize-recovery", + "synthetic-single-store-authorize-recovery", "--authority", str(authority_path), "--recovery", diff --git a/vectors/hermes/v1/index.json b/vectors/hermes/v1/index.json index 3f8de92..a8e80df 100644 --- a/vectors/hermes/v1/index.json +++ b/vectors/hermes/v1/index.json @@ -1 +1 @@ -{"files":[{"name":"negative/context-id-tampered.json","sha256":"542687c5286ba74d4157bca175ac6c98f591a9c73ad3eec90ecb30861a3a7c6c","valid":false},{"name":"negative/park-presence-not-relinquished.json","sha256":"08da5c7d7e4bcfe080ef3bc5a1ee785dd26f277902e63021ef03b139b3a279b5","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"7c8b6b6d196ba088fc3417d0191bd9e1200f207205874d5c5d2b85bb86eab541","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"e4318d28ae7bf72d0685eb48acf3244ba09e9f20d2d4ac069e3ab0fff5fd725b","valid":true},{"name":"valid/context.json","sha256":"ab55dbcd81211b009fc797095368e389ad24639b19578017fa8e5c334f8fcf3a","valid":true},{"name":"valid/current-memory-projection.json","sha256":"ab9b4d897da7fd12c89ec782486378884867bd2afbb8327f67b61c0a10bb6c55","valid":true},{"name":"valid/effect-receipt.json","sha256":"f5aa1b7d7a97a2c818b66d7a9b672e91b224b52b3203de419042f0a0927c7b46","valid":true},{"name":"valid/launch-receipt.json","sha256":"55d2405fd4e0520962d58da1112894364d8f2d692f682be894eb63ed1111d2d4","valid":true},{"name":"valid/park-receipt.json","sha256":"764774f4b4993668ff4f8d8272b4dc1c0b468e5df38148c9d1e261fdbe03069c","valid":true},{"name":"valid/park-request.json","sha256":"1c1c1b06c162371edfaaae7bd5562f9e5acb2853efbc12ccdd523ac2431ea2cf","valid":true},{"name":"valid/plan.json","sha256":"d711657a4fa972cfbdf83ddcb78b30c0703dd0674697641d7dd3239efc549878","valid":true},{"name":"valid/profile-manifest.json","sha256":"ff3350fa1d57ffbb5110b537f0bbea4c05c9dc84723539abe41045796dfc055e","valid":true},{"name":"valid/provider-config.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5","valid":true},{"name":"valid/provider-ready.json","sha256":"d9f9684474222e2339e8da122570bd8fb4266f81ff5499952049b64d48f7cc5e","valid":true},{"name":"valid/runtime-active.json","sha256":"12c9f5e7516adf8aa58bb7ef90c3965db557612d50b1618e252fe13f629c4b8b","valid":true},{"name":"valid/runtime-parking.json","sha256":"bb2adcfef7a7b31ba3c2b79a62369c20ab6ab612e1d0b6bc33a88c59685ccba9","valid":true},{"name":"valid/runtime-starting.json","sha256":"52f448d212d230cda8dce0f88b946592c5ccbe5a462046f0a7f39a3dc4e8ffb6","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_version":"0.19.0","schema":"dm.hermes-body.vector-index/v1"} +{"files":[{"name":"negative/context-id-tampered.json","sha256":"542687c5286ba74d4157bca175ac6c98f591a9c73ad3eec90ecb30861a3a7c6c","valid":false},{"name":"negative/park-presence-not-relinquished.json","sha256":"08da5c7d7e4bcfe080ef3bc5a1ee785dd26f277902e63021ef03b139b3a279b5","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"7c8b6b6d196ba088fc3417d0191bd9e1200f207205874d5c5d2b85bb86eab541","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"e4318d28ae7bf72d0685eb48acf3244ba09e9f20d2d4ac069e3ab0fff5fd725b","valid":true},{"name":"valid/context.json","sha256":"ab55dbcd81211b009fc797095368e389ad24639b19578017fa8e5c334f8fcf3a","valid":true},{"name":"valid/current-memory-projection.json","sha256":"ab9b4d897da7fd12c89ec782486378884867bd2afbb8327f67b61c0a10bb6c55","valid":true},{"name":"valid/effect-receipt.json","sha256":"f5aa1b7d7a97a2c818b66d7a9b672e91b224b52b3203de419042f0a0927c7b46","valid":true},{"name":"valid/launch-receipt.json","sha256":"12a66944a02b875e0d6d4c6cd60650ec13886c8a21bab1a515907d9b5d9a78b1","valid":true},{"name":"valid/park-receipt.json","sha256":"764774f4b4993668ff4f8d8272b4dc1c0b468e5df38148c9d1e261fdbe03069c","valid":true},{"name":"valid/park-request.json","sha256":"1c1c1b06c162371edfaaae7bd5562f9e5acb2853efbc12ccdd523ac2431ea2cf","valid":true},{"name":"valid/plan.json","sha256":"d711657a4fa972cfbdf83ddcb78b30c0703dd0674697641d7dd3239efc549878","valid":true},{"name":"valid/profile-manifest.json","sha256":"2962c9f8f2613abbce671582fbd7513dc64bb98d3aba7048a20f35566c06da0f","valid":true},{"name":"valid/provider-config.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5","valid":true},{"name":"valid/provider-ready.json","sha256":"d9f9684474222e2339e8da122570bd8fb4266f81ff5499952049b64d48f7cc5e","valid":true},{"name":"valid/runtime-active.json","sha256":"12c9f5e7516adf8aa58bb7ef90c3965db557612d50b1618e252fe13f629c4b8b","valid":true},{"name":"valid/runtime-parking.json","sha256":"bb2adcfef7a7b31ba3c2b79a62369c20ab6ab612e1d0b6bc33a88c59685ccba9","valid":true},{"name":"valid/runtime-starting.json","sha256":"52f448d212d230cda8dce0f88b946592c5ccbe5a462046f0a7f39a3dc4e8ffb6","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_version":"0.19.0","schema":"dm.hermes-body.vector-index/v1"} diff --git a/vectors/hermes/v1/valid/launch-receipt.json b/vectors/hermes/v1/valid/launch-receipt.json index b57dde4..48f83d1 100644 --- a/vectors/hermes/v1/valid/launch-receipt.json +++ b/vectors/hermes/v1/valid/launch-receipt.json @@ -1 +1 @@ -{"active_handle_id":"dm:hermes-handle:v1:7qD764Q4xNYnguH0C2VESNXWHnCaL-XjR1RM3gdZ_rg","deployment":"synthetic-isolated","hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_session_id":"hermes-session-vector","hermes_version":"0.19.0","launch_receipt_id":"dm:hermes-launch:v1:LIdfLcUtz9Fgc9l1oJQn0dPzNbZVQoZ3gc-gywZkL24","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"6c7909d6e453d7d955c88c552c0a30fe8ce2c479f93008fd866caad1fb8b9412"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"a71e68b984c3aa6b1ab6533bfce29709b05f36da04d37aaf16d49e74952027b2"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"702cdd0512bd5dc2a7377e3a4b1f5415903dd3c5e80d2618884b3444b8f51a10"},"plan_id":"dm:hermes-plan:v1:O3YR1x4fwBrTHF-QO-fbGii0f_D0O-kSzsZbMcuL0vM","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:UHiM5_UrowyOAwjwZxkOXdsvbLGRjHVSRZN69JTU0O4","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:L9ZIK64wzJ-zg-l9QVw_b1btW7o6SOxOhQL_6TET0rM"} +{"active_handle_id":"dm:hermes-handle:v1:7qD764Q4xNYnguH0C2VESNXWHnCaL-XjR1RM3gdZ_rg","deployment":"synthetic-isolated","hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_session_id":"hermes-session-vector","hermes_version":"0.19.0","launch_receipt_id":"dm:hermes-launch:v1:LMNaiUi14LYi1kLjXZJNwqCt0rYB2to5BY3mhHXUz7c","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"aa8563b4bcb2a000d044419e620a91b8d153620cfd571f8d1a466f1bd1455083"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"28a91fa173eafb5309d9143ce22574c5dbe7fddf5b1fcf5fcf4a9b65f76daf29"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"9d5f2839096289dc4648afa0ba75d8f00b8ad611fd53601d9bb56302c7bceb4a"},"plan_id":"dm:hermes-plan:v1:O3YR1x4fwBrTHF-QO-fbGii0f_D0O-kSzsZbMcuL0vM","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:UHiM5_UrowyOAwjwZxkOXdsvbLGRjHVSRZN69JTU0O4","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:L9ZIK64wzJ-zg-l9QVw_b1btW7o6SOxOhQL_6TET0rM"} diff --git a/vectors/hermes/v1/valid/profile-manifest.json b/vectors/hermes/v1/valid/profile-manifest.json index 5af8efe..c47feeb 100644 --- a/vectors/hermes/v1/valid/profile-manifest.json +++ b/vectors/hermes/v1/valid/profile-manifest.json @@ -1 +1 @@ -{"adapter_version":"1.0.0","being_ref":"dm:being:v1:c020x2AEpKTIXF0hnAal0Q64Ia--MtxTfGGk_e4itVQ","body_ref":"dm:body:v1:B0iU3se3iyz0g6tPShEM8_Z-oX7BkspNY4KGQzFQEn0","embodiment_id":"dm:embodiment:v1:c6daXNxOWxfE9qM-j85N8YbFN35Q6Kl7g6AYGf7bbZE","files":[{"name":"SOUL.md","sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a"},{"name":"config.yaml","sha256":"47a67b881a4530c67c8d609d7bf3f758717acb9a266a5a55fc65049ae4ee38ab"},{"name":"plugins/daimon-matrix/__init__.py","sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5"},{"name":"plugins/daimon-matrix/matrix.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_version":"0.19.0","incarnation_id":"dm:incarnation:v1:VMS4o9jcMXzf6bmAuMHSsZUMK_g_W9N46SK-LLD_7G8","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"6c7909d6e453d7d955c88c552c0a30fe8ce2c479f93008fd866caad1fb8b9412"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"a71e68b984c3aa6b1ab6533bfce29709b05f36da04d37aaf16d49e74952027b2"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"702cdd0512bd5dc2a7377e3a4b1f5415903dd3c5e80d2618884b3444b8f51a10"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"3b7611d71e1fc01ad31c5f903be7db1a28b47ff0f43be912cec65b31cb8bd2f3","profile_id":"dm:hermes-profile:v1:G1CEu4pjBY3xtZGnOziUvdr2RK2D7Uwz7sUTdaTER94","schema":"dm.hermes-body.profile-manifest/v1","workspace_ref":"dm:workspace:v1:1g9p07RgFRlBvtWJcvb92rOP7KNe9mB8v3TeueVtERM"} +{"adapter_version":"1.0.0","being_ref":"dm:being:v1:c020x2AEpKTIXF0hnAal0Q64Ia--MtxTfGGk_e4itVQ","body_ref":"dm:body:v1:B0iU3se3iyz0g6tPShEM8_Z-oX7BkspNY4KGQzFQEn0","embodiment_id":"dm:embodiment:v1:c6daXNxOWxfE9qM-j85N8YbFN35Q6Kl7g6AYGf7bbZE","files":[{"name":"SOUL.md","sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a"},{"name":"config.yaml","sha256":"47a67b881a4530c67c8d609d7bf3f758717acb9a266a5a55fc65049ae4ee38ab"},{"name":"plugins/daimon-matrix/__init__.py","sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5"},{"name":"plugins/daimon-matrix/matrix.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_version":"0.19.0","incarnation_id":"dm:incarnation:v1:VMS4o9jcMXzf6bmAuMHSsZUMK_g_W9N46SK-LLD_7G8","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"aa8563b4bcb2a000d044419e620a91b8d153620cfd571f8d1a466f1bd1455083"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"28a91fa173eafb5309d9143ce22574c5dbe7fddf5b1fcf5fcf4a9b65f76daf29"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"9d5f2839096289dc4648afa0ba75d8f00b8ad611fd53601d9bb56302c7bceb4a"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"3b7611d71e1fc01ad31c5f903be7db1a28b47ff0f43be912cec65b31cb8bd2f3","profile_id":"dm:hermes-profile:v1:ZQwyiKcM6ToEothNwp0MIivicGw-F5LEampTCIHGWxg","schema":"dm.hermes-body.profile-manifest/v1","workspace_ref":"dm:workspace:v1:1g9p07RgFRlBvtWJcvb92rOP7KNe9mB8v3TeueVtERM"} From 417649641b3af9ea00a2b5283a5111fb98bf5215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Sun, 16 Aug 2026 04:31:55 -0300 Subject: [PATCH 02/11] fix: close distributed recovery custody gaps --- docs/dm024-hosted-runtime.md | 5 +- docs/dm083-two-host-dogfood.md | 5 +- docs/runbooks/operator-bootstrap.md | 46 +- provenance/hermes-agent-0.19.0.json | 2 +- pyproject.toml | 3 +- schemas/identity/v1/artifact.schema.json | 14 +- src/daimon_matrix/authority_epochs.py | 17 +- src/daimon_matrix/identity.py | 258 ++++++++++-- src/daimon_matrix/operator_bootstrap.py | 14 +- src/daimon_matrix/operator_genesis.py | 393 ++++++++++++++++++ src/daimon_matrix/operator_rebirth.py | 222 ++++++++-- src/daimon_matrix/synthetic_birth.py | 6 +- src/daimon_matrix/synthetic_multihost.py | 4 +- src/daimon_matrix/synthetic_relationships.py | 4 +- src/daimon_matrix/synthetic_sources.py | 4 +- tests/test_dm021_identity.py | 18 +- tests/test_dm021_process.py | 6 +- tests/test_dm022_ledger.py | 4 +- tests/test_dm041_hermes_body.py | 6 +- tests/test_dm060_synthetic_birth.py | 6 +- tests/test_dm078_recovery_rebirth.py | 250 ++++++++++- tests/test_operator_genesis.py | 259 ++++++++++++ tests/test_package_scaffold.py | 5 +- tools/check_distribution.py | 7 +- tools/generate_dm021_vectors.py | 8 +- tools/generate_dm078_recovery_vectors.py | 11 +- tools/reproducible_build.py | 1 + vectors/hermes/v1/index.json | 2 +- vectors/hermes/v1/valid/launch-receipt.json | 2 +- vectors/hermes/v1/valid/profile-manifest.json | 2 +- .../recovery-omits-recovery-threshold.json | 2 +- vectors/identity/v1/valid/recovery.json | 2 +- .../weave/v1/recovery-rebirth/activation.json | 2 +- .../embodiment-credential.json | 2 +- .../recovery-rebirth/enrollment-request.json | 2 +- .../incarnation-authorization.json | 2 +- vectors/weave/v1/recovery-rebirth/index.json | 2 +- .../negative/recovery-signature-tampered.json | 2 +- .../negative/transition-hash-tampered.json | 2 +- .../recovery-rebirth/recovery-artifact.json | 2 +- .../v1/recovery-rebirth/recovery-rebirth.json | 2 +- .../recovery-rebirth/successor-manifest.json | 2 +- 42 files changed, 1449 insertions(+), 159 deletions(-) create mode 100644 src/daimon_matrix/operator_genesis.py create mode 100644 tests/test_operator_genesis.py diff --git a/docs/dm024-hosted-runtime.md b/docs/dm024-hosted-runtime.md index 7ee50b1..f208e64 100644 --- a/docs/dm024-hosted-runtime.md +++ b/docs/dm024-hosted-runtime.md @@ -50,8 +50,9 @@ sorted, exact native-peer HTTP(S) target and timeout. The endpoint is fixed at startup rather than accepted from a sync call. `we.sync.peer-pull` reuses the DM-023 request frozen by `scope.we.sync-plan`, performs the encrypted peer round trip inside the daemon's custody boundary and atomically imports one -bounded page. The installed `daimon-bootstrap` ceremony and operational steps -are documented in `docs/runbooks/operator-bootstrap.md`. +bounded page. Distributed `daimon-genesis` provisioning and the explicitly +synthetic single-store bootstrap fixture are documented in +`docs/runbooks/operator-bootstrap.md`. ## Local protocol diff --git a/docs/dm083-two-host-dogfood.md b/docs/dm083-two-host-dogfood.md index e00bf1b..3064432 100644 --- a/docs/dm083-two-host-dogfood.md +++ b/docs/dm083-two-host-dogfood.md @@ -36,8 +36,9 @@ that must be refreshed: 3. Read-only host inventory found no current root-bound Matrix being on either host. The only remnants are matching all-zero historical fixture manifests in trash; they are not migration authority. Matrix therefore now owns the - fresh plural-being `daimon-bootstrap` ceremony instead of asking Cluster to - invent identity. + fresh plural-being distributed `daimon-genesis` ceremony instead of asking + Cluster to invent identity. The former one-process bootstrap is now exposed + only as `daimon-synthetic-bootstrap` and cannot prove separated custody. 4. V6 configured only a local peer listener, forcing an operator to inject a remote URL outside the runtime contract. V7 adds exact per-embodiment peer targets and `we.sync.peer-pull`, so the ordinary CLI can execute the frozen diff --git a/docs/runbooks/operator-bootstrap.md b/docs/runbooks/operator-bootstrap.md index 5f3f950..73e4ec4 100644 --- a/docs/runbooks/operator-bootstrap.md +++ b/docs/runbooks/operator-bootstrap.md @@ -1,10 +1,16 @@ -# Root-bound plural-being bootstrap +# Distributed genesis and synthetic bootstrap fixture -`daimon-bootstrap` is the Matrix-owned operator ceremony used when no current -root-bound being exists to migrate. It creates one fresh self-certifying root, -an encrypted offline root/recovery custody and two or more active embodiment -runtimes. Cluster installs the resulting runtime directories but never creates -or interprets their identity. +`daimon-genesis` is the production-shaped first stage for a new being. Each +root and recovery holder runs `create-holder` separately, retains one encrypted +seed in one owner-only package, and publishes only `descriptor.json`. An +operator freezes those public descriptors with `create-intent`; each holder +then runs `sign`, and the keyless `aggregate` step emits the genesis artifact. +Threshold shortfall, duplicate shares, role substitution and key substitution +fail closed. + +`daimon-synthetic-bootstrap` is retained only as a local deterministic fixture. +It centralizes every root and recovery seed in one process and one store and is +not an operational custody procedure. The ceremony is one shot: the output path must not exist, all files are created owner-only, and publication is an fsynced atomic directory rename. Passwords @@ -46,12 +52,32 @@ embodiments use. The sample addresses are documentation-only. Do not publish a real private endpoint in repository evidence. -## Ceremony +## Distributed genesis ceremony + +Run each `create-holder` and `sign` invocation in its holder's independent +process and custody boundary. The following abbreviated example shows the file +flow; passwords enter through inherited descriptors and every output path must +be new: + +```bash +daimon-genesis create-holder --role root --password-fd 3 --output root-a 3=3.11,<3.14","repository":"https://github.com/nicoechaniz/hermes-agent","schema":"dm.hermes-body.provenance/v1","supported_surfaces":["external-memory-provider","profile-plugin-discovery","memory-manager","current-user-api-content-sidecar","session-lifecycle-hooks","static-system-prompt-block"],"tree":"ac7dec02ca029e895963402788bd1cdc3afb36f8","version":"0.19.0"} +{"audited_at":"2026-08-05","commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","contract_digests":{"agent/agent_init.py":"6b4aa7a877d25e7065af35fd5a5e99dc0b85ef0ede349d95c5bd74818a70b89f","agent/memory_manager.py":"35e4e313f08e7529d7407f2d36b1639681acbc347eedfe9f2e38c2610973761c","agent/memory_provider.py":"7a86b453edbe3dae6ea02f3af406fac7e39fe7ee7b61bfc84b8c8e4b4a0ce8cd","agent/plugin_llm.py":"4158b0ed2be2140eb99f09e1a488daed04da496052fe415cb84e412e4b71fb30","agent/system_prompt.py":"261481c471ddee92ced3fe381d63acbbe9136bedb6420f613983225007e2bb9a","agent/turn_context.py":"726ebf615b90237cca98d8f5d2d4e04f4507690d66917eb88f5d478c0b1ecaa2","agent/turn_finalizer.py":"a6c91daefaa805ef71604ddf9e9e08399825c1448bff2982bf72099da35957ac","hermes_cli/_parser.py":"82db5bb23c4619bf11536f44b56ea72c2b87cafcd062660af3593d2a4e08db0b","hermes_cli/config.py":"038040b0c0bbdd5f740b39c64d2e9ea1d7c78ae3d582cb337dbe8c145b4c8b03","hermes_cli/main.py":"55333e3fb37bec97b12c404760968716a3e81f3ae373896fc628df8dee3fb416","hermes_cli/plugins.py":"0f6c28614bebb7444392625a63c2b3186039f04238fe6ca79ad62d4849b0551c","model_tools.py":"db74ee29c8d335d80f3c18cc31f8c441956af956b2ed08e5113ced249fad32b4","plugins/memory/__init__.py":"f6bc37128d23f931ea1db52fc60cf25cca448f41105ed37d5b46f3843ab71b3a","plugins/memory/config_schema.py":"b6f58adffb2fd01a9605f10b891d905b8dfa72a509d1577499b82ad66ff7a937","pyproject.toml":"35462080afc8177258babd430dcdc2ac654fdf69332cc4370fec555295f7eaba"},"copy_policy":"No Hermes source or private profile bytes are distributed.","git_archive_sha256":"860a664f622e1099a095cb6cf06b04bcfe78b2fd3affc7192da9cf7ccefcdd63","license":"MIT","matrix_payload":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","package_tree_sha256":"6d0c6681eb4d748595adbcceb9aaa9e75fe28722b0c7c96c0d1e711e445cb59f","plugin_manifest_sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92","plugin_sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5","provider_name":"daimon-matrix","skill_sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275","soul_sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a","tools":["matrix_scope","matrix_propose_observation"]},"python_interval":">=3.11,<3.14","repository":"https://github.com/nicoechaniz/hermes-agent","schema":"dm.hermes-body.provenance/v1","supported_surfaces":["external-memory-provider","profile-plugin-discovery","memory-manager","current-user-api-content-sidecar","session-lifecycle-hooks","static-system-prompt-block"],"tree":"ac7dec02ca029e895963402788bd1cdc3afb36f8","version":"0.19.0"} diff --git a/pyproject.toml b/pyproject.toml index ad4ac29..5e41216 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,10 +31,11 @@ daimon-curator-worker = "daimon_matrix.curator_worker_process:main" daimon-hermes-body = "daimon_matrix.hermes_body:main" daimon-mcp = "daimon_matrix.mcp_server:main" daimon-matrixd = "daimon_matrix.daemon:main" -daimon-bootstrap = "daimon_matrix.operator_bootstrap:main" +daimon-genesis = "daimon_matrix.operator_genesis:main" daimon-rebirth = "daimon_matrix.operator_rebirth:main" daimon-reviewer = "daimon_matrix.reviewer_cli:main" daimon-synthetic-birth = "daimon_matrix.synthetic_birth:main" +daimon-synthetic-bootstrap = "daimon_matrix.operator_bootstrap:main" daimon-synthetic-multihost = "daimon_matrix.synthetic_multihost:main" daimon-synthetic-relationships = "daimon_matrix.synthetic_relationships:main" daimon-synthetic-species = "daimon_matrix.synthetic_species:main" diff --git a/schemas/identity/v1/artifact.schema.json b/schemas/identity/v1/artifact.schema.json index 753e6e4..efd37c2 100644 --- a/schemas/identity/v1/artifact.schema.json +++ b/schemas/identity/v1/artifact.schema.json @@ -179,12 +179,24 @@ "recovery": { "type": "object", "additionalProperties": false, - "required": ["being_ref", "competing_control_heads", "control_generation", "control_sequence", "replacement_root", "revoked_embodiments"], + "required": ["being_ref", "competing_control_heads", "control_generation", "control_sequence", "revocation_high_water", "replacement_root", "revoked_embodiments"], "properties": { "being_ref": {"type": "string", "pattern": "^dm:being:v1:"}, "competing_control_heads": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^dm:identity:v1:"}}, "control_generation": {"type": "integer", "minimum": 1}, "control_sequence": {"const": 0}, + "revocation_high_water": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "required": ["cutoff_incarnation_sequence", "revocation_generation"], + "properties": { + "cutoff_incarnation_sequence": {"type": "integer", "minimum": 0}, + "revocation_generation": {"type": "integer", "minimum": 0} + } + } + }, "replacement_root": {"$ref": "#/$defs/threshold"}, "revoked_embodiments": {"type": "array", "uniqueItems": true, "items": {"type": "string"}} } diff --git a/src/daimon_matrix/authority_epochs.py b/src/daimon_matrix/authority_epochs.py index 6eb2b31..a6d96a3 100644 --- a/src/daimon_matrix/authority_epochs.py +++ b/src/daimon_matrix/authority_epochs.py @@ -29,7 +29,7 @@ key_id, verify_embodiment_credential, verify_incarnation_authorization, - verify_recovery, + verify_recovery_from_anchor, ) from .weave import BeingManifest, RootAuthority, WeaveProtocolError @@ -600,6 +600,13 @@ def _recovery_rebirth_core( recovery_body = recovery_artifact.get("body") if not isinstance(recovery_body, Mapping): raise AuthorityEpochError("recovery_rebirth_artifact_invalid") + revoked = recovery_body.get("revoked_embodiments") + if ( + not isinstance(revoked, list) + or not all(isinstance(item, str) and item for item in revoked) + or revoked != sorted(set(revoked)) + ): + raise AuthorityEpochError("recovery_rebirth_artifact_invalid") return { "schema": RECOVERY_REBIRTH_SCHEMA, "being_ref": previous.being_ref, @@ -717,7 +724,7 @@ def verify_recovery_rebirth( raise AuthorityEpochError("recovery_rebirth_artifact_invalid") recovery_artifact = embedded try: - recovered_state = verify_recovery(recovery_artifact, [previous.state]) + recovered_state = verify_recovery_from_anchor(recovery_artifact, previous.state) except VerificationError as exception: raise AuthorityEpochError("recovery_rebirth_artifact_invalid") from exception previous_manifest = previous.manifest @@ -750,9 +757,9 @@ def verify_recovery_rebirth( or successor_manifest.value["history_binding_id"] != previous_manifest.value["history_binding_id"] or value.get("recovery_artifact_id") != recovery_artifact.get("artifact_id") - or value.get("revoked_embodiment_ids") != active_predecessors - or recovery_artifact.get("body", {}).get("revoked_embodiments") - != active_predecessors + or value.get("revoked_embodiment_ids") + != recovery_artifact.get("body", {}).get("revoked_embodiments") + or not set(active_predecessors).issubset(value["revoked_embodiment_ids"]) or previous_root_ids & recovered_root_ids ): raise AuthorityEpochError("recovery_rebirth_lineage_mismatch") diff --git a/src/daimon_matrix/identity.py b/src/daimon_matrix/identity.py index be3ce4c..1685ee6 100644 --- a/src/daimon_matrix/identity.py +++ b/src/daimon_matrix/identity.py @@ -340,49 +340,132 @@ class ControlState: activated_binding: str | None = None -def create_genesis( - root_seeds: Sequence[bytes], - root_threshold: int, - recovery_seeds: Sequence[bytes], - recovery_threshold: int, +def prepare_genesis( + root_policy: Mapping[str, Any], + recovery_policy: Mapping[str, Any], *, created_at_ms: int, - nonce: bytes | None = None, + nonce: bytes, ) -> Artifact: - """Create a self-certifying being genesis with independent thresholds.""" + """Freeze public genesis material before isolated holders sign it.""" - root = threshold_policy(root_seeds, root_threshold) - recovery = threshold_policy(recovery_seeds, recovery_threshold) + root = copy.deepcopy(dict(root_policy)) + recovery = copy.deepcopy(dict(recovery_policy)) + _validate_threshold_policy(root) + _validate_threshold_policy(recovery) _require_separate_policies(root, recovery) + if len(nonce) != 32: + raise IdentityError("genesis nonce must be 32 bytes") core = { - "nonce": b64url(nonce if nonce is not None else secrets.token_bytes(32)), + "nonce": b64url(nonce), "protocol": "daimon-matrix", "recovery": recovery, "root": root, "version": 1, } being_ref = "dm:being:v1:" + b64url(digest("dm.identity.being/v1", core)) - body = { - "being_ref": being_ref, - "control_generation": 0, - "control_sequence": 0, - "core": core, - "created_at_ms": created_at_ms, - } - signatures = _signatures( - _seed_map(root_seeds), - "root-authorization", - domain_bytes(DOMAINS["genesis"], body), + return _artifact( + "genesis", + { + "being_ref": being_ref, + "control_generation": 0, + "control_sequence": 0, + "core": core, + "created_at_ms": created_at_ms, + }, + [], ) - raw_hash = digest(DOMAINS["genesis"], body) - signatures.extend( - _signatures( - _seed_map(recovery_seeds), - "recovery-possession", - DOMAINS["genesis"].encode("ascii") + b"/possession\x00" + raw_hash, + + +def _prepared_genesis(artifact: Mapping[str, Any]) -> Artifact: + body, _ = _verify_wrapper(artifact, "genesis") + if artifact["signatures"]: + raise IdentityError("prepared genesis already contains signatures") + core = body.get("core") + if not isinstance(core, Mapping): + raise IdentityError("prepared genesis is invalid") + from .canonical import unb64url + + try: + expected = prepare_genesis( + core.get("root", {}), + core.get("recovery", {}), + created_at_ms=body["created_at_ms"], + nonce=unb64url(str(core.get("nonce")), length=32), + ) + except (KeyError, TypeError, ValueError) as exception: + raise IdentityError("prepared genesis is invalid") from exception + if canonical_bytes(expected) != canonical_bytes(artifact): + raise IdentityError("prepared genesis is invalid") + return expected + + +def create_genesis_holder_share( + prepared: Mapping[str, Any], seed: bytes, *, role: Literal["root", "recovery"] +) -> dict[str, str]: + """Sign genesis while holding exactly one root or recovery seed.""" + + expected = _prepared_genesis(prepared) + body = expected["body"] + policy = body["core"][role] + _require_policy_seed(seed, policy, role=f"genesis-{role}") + if role == "root": + return _signature( + seed, "root-authorization", domain_bytes(DOMAINS["genesis"], body) ) + raw_hash = digest(DOMAINS["genesis"], body) + return _signature( + seed, + "recovery-possession", + DOMAINS["genesis"].encode("ascii") + b"/possession\x00" + raw_hash, + ) + + +def aggregate_genesis( + prepared: Mapping[str, Any], shares: Sequence[Mapping[str, Any]] +) -> Artifact: + """Aggregate public genesis shares without access to any holder store.""" + + expected = _prepared_genesis(prepared) + artifact = _artifact( + "genesis", + expected["body"], + [copy.deepcopy(dict(share)) for share in shares], + ) + verify_genesis(artifact) + return artifact + + +def create_synthetic_genesis_in_process( + root_seeds: Sequence[bytes], + root_threshold: int, + recovery_seeds: Sequence[bytes], + recovery_threshold: int, + *, + created_at_ms: int, + nonce: bytes | None = None, +) -> Artifact: + """Synthetic fixture that centralizes every genesis seed in-process.""" + + prepared = prepare_genesis( + threshold_policy(root_seeds, root_threshold), + threshold_policy(recovery_seeds, recovery_threshold), + created_at_ms=created_at_ms, + nonce=nonce if nonce is not None else secrets.token_bytes(32), + ) + return aggregate_genesis( + prepared, + [ + *( + create_genesis_holder_share(prepared, seed, role="root") + for seed in root_seeds + ), + *( + create_genesis_holder_share(prepared, seed, role="recovery") + for seed in recovery_seeds + ), + ], ) - return _artifact("genesis", body, signatures) def verify_genesis(artifact: Mapping[str, Any]) -> ControlState: @@ -529,7 +612,7 @@ def create_revocation( ) -def create_recovery( +def create_synthetic_recovery_in_process( states: Sequence[ControlState], current_recovery_seeds: Sequence[bytes], replacement_root_seeds: Sequence[bytes], @@ -537,7 +620,7 @@ def create_recovery( *, revoke_embodiments: Sequence[str], ) -> Artifact: - """Recover a unique head while naming every currently known branch head.""" + """Synthetic fixture that centralizes every recovery/root seed in-process.""" prepared = prepare_recovery( states, @@ -578,6 +661,7 @@ def prepare_recovery( "competing_control_heads": sorted({state.head for state in states}), "control_generation": max(state.generation for state in states) + 1, "control_sequence": 0, + "revocation_high_water": _merge_revocations(states), "replacement_root": replacement_policy, "revoked_embodiments": sorted(set(revoke_embodiments)), } @@ -786,6 +870,7 @@ def verify_recovery( "competing_control_heads", "control_generation", "control_sequence", + "revocation_high_water", "replacement_root", "revoked_embodiments", }, @@ -821,7 +906,9 @@ def verify_recovery( "new-root-possession", DOMAINS["recovery"].encode("ascii") + b"/possession\x00" + raw_hash, ) - revoked = _merge_revocations(states) + if body["revocation_high_water"] != _merge_revocations(states): + raise VerificationError("recovery revocation high-water mismatch") + revoked = copy.deepcopy(body["revocation_high_water"]) for embodiment_id in revoked_embodiments: previous = revoked.get(embodiment_id, {}) revoked[embodiment_id] = { @@ -845,6 +932,109 @@ def verify_recovery( ) +def verify_recovery_from_anchor( + artifact: Mapping[str, Any], anchor: ControlState +) -> ControlState: + """Verify a quorum-attested fork recovery from any cited verified head. + + Exact fork discovery belongs to the recovery holders. The signed artifact + carries the complete head set and the conservative revocation high-water; + a later verifier only needs one already-verified cited head as its trust + anchor. Ceremony aggregation still uses :func:`verify_recovery` with every + known state and therefore checks the holder-attested values exactly. + """ + + body, raw_hash = _verify_wrapper(artifact, "recovery") + _closed( + body, + { + "being_ref", + "competing_control_heads", + "control_generation", + "control_sequence", + "revocation_high_water", + "replacement_root", + "revoked_embodiments", + }, + "recovery body", + ) + heads = body["competing_control_heads"] + if ( + body["being_ref"] != anchor.being_ref + or not isinstance(heads, list) + or not all(isinstance(head, str) and head for head in heads) + or heads != sorted(set(heads)) + or anchor.head not in heads + or not isinstance(body["control_generation"], int) + or isinstance(body["control_generation"], bool) + or body["control_generation"] <= anchor.generation + or body["control_sequence"] != 0 + ): + raise VerificationError("recovery anchor mismatch") + high_water = body["revocation_high_water"] + if not isinstance(high_water, Mapping) or any( + not isinstance(embodiment_id, str) + or not embodiment_id + or not isinstance(value, Mapping) + or set(value) != {"cutoff_incarnation_sequence", "revocation_generation"} + or any( + not isinstance(value[field], int) + or isinstance(value[field], bool) + or value[field] < 0 + for field in ("cutoff_incarnation_sequence", "revocation_generation") + ) + for embodiment_id, value in high_water.items() + ): + raise VerificationError("recovery revocation high-water mismatch") + for embodiment_id, value in anchor.revocations.items(): + merged = high_water.get(embodiment_id) + if ( + not isinstance(merged, Mapping) + or merged.get("revocation_generation", -1) < value["revocation_generation"] + or merged.get("cutoff_incarnation_sequence", 2**63 - 1) + > value["cutoff_incarnation_sequence"] + ): + raise VerificationError("recovery revocation high-water mismatch") + revoked_embodiments = body["revoked_embodiments"] + if revoked_embodiments != sorted(set(revoked_embodiments)): + raise VerificationError("recovery revocations must be sorted and unique") + _require_separate_policies(body["replacement_root"], anchor.recovery_policy) + _verify_threshold( + artifact, + anchor.recovery_policy, + "recovery-authorization", + domain_bytes(DOMAINS["recovery"], body), + ) + _verify_threshold( + artifact, + body["replacement_root"], + "new-root-possession", + DOMAINS["recovery"].encode("ascii") + b"/possession\x00" + raw_hash, + ) + revoked = copy.deepcopy(dict(high_water)) + for embodiment_id in revoked_embodiments: + previous = revoked.get(embodiment_id, {}) + revoked[embodiment_id] = { + "cutoff_incarnation_sequence": previous.get( + "cutoff_incarnation_sequence", 0 + ), + "revocation_generation": previous.get("revocation_generation", 0) + 1, + } + return ControlState( + being_ref=anchor.being_ref, + head=artifact["artifact_id"], + generation=body["control_generation"], + sequence=0, + root_policy=copy.deepcopy(body["replacement_root"]), + recovery_policy=copy.deepcopy(anchor.recovery_policy), + revocations=revoked, + credential_authorities={ + artifact["artifact_id"]: copy.deepcopy(body["replacement_root"]) + }, + carried_credential_authorities={}, + ) + + def _merge_revocations( states: Sequence[ControlState], ) -> dict[str, dict[str, int]]: @@ -1320,12 +1510,12 @@ def require_trust_mode(state: ControlState, requested: str) -> None: "ControlState", "IdentityError", "VerificationError", + "aggregate_genesis", "create_binding_activation", "create_embodiment_credential", - "create_genesis", + "create_genesis_holder_share", "create_history_binding", "create_incarnation_authorization", - "create_recovery", "create_recovery_policy_change", "create_revocation", "create_root_rotation", @@ -1335,6 +1525,7 @@ def require_trust_mode(state: ControlState, requested: str) -> None: "generate_x25519_private", "key_descriptor", "key_id", + "prepare_genesis", "require_trust_mode", "signing_descriptor", "threshold_policy", @@ -1344,6 +1535,7 @@ def require_trust_mode(state: ControlState, requested: str) -> None: "verify_history_binding", "verify_incarnation_authorization", "verify_recovery", + "verify_recovery_from_anchor", "verify_successor", "x25519_public", ] diff --git a/src/daimon_matrix/operator_bootstrap.py b/src/daimon_matrix/operator_bootstrap.py index 0f240e9..a2b85e6 100644 --- a/src/daimon_matrix/operator_bootstrap.py +++ b/src/daimon_matrix/operator_bootstrap.py @@ -1,4 +1,8 @@ -"""One-shot operator ceremony for a new plural root-bound hosted being.""" +"""Synthetic single-store bootstrap fixture for local deterministic tests. + +Production provisioning starts with ``daimon-genesis`` so every recovery key +is created and used by an isolated holder process/store. +""" from __future__ import annotations @@ -23,8 +27,8 @@ from .client import CLIENT_CONFIG_SCHEMA, load_json_document from .identity import ( create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, generate_ed25519_seed, generate_x25519_private, @@ -229,7 +233,7 @@ def _create( created_at_ms = time.time_ns() // 1_000_000 root_seeds = [generate_ed25519_seed() for _ in range(3)] recovery_seeds = [generate_ed25519_seed() for _ in range(3)] - genesis = create_genesis( + genesis = create_synthetic_genesis_in_process( root_seeds, 2, recovery_seeds, @@ -516,7 +520,9 @@ def _create( def parser() -> argparse.ArgumentParser: - result = argparse.ArgumentParser(prog="daimon-bootstrap", description=__doc__) + result = argparse.ArgumentParser( + prog="daimon-synthetic-bootstrap", description=__doc__ + ) result.add_argument("--output", type=Path, required=True) result.add_argument("--profile", type=Path, required=True) result.add_argument("--root-password-fd", type=int, required=True) diff --git a/src/daimon_matrix/operator_genesis.py b/src/daimon_matrix/operator_genesis.py new file mode 100644 index 0000000..d26a82c --- /dev/null +++ b/src/daimon_matrix/operator_genesis.py @@ -0,0 +1,393 @@ +"""Distributed genesis ceremony with one encrypted store per key holder.""" + +from __future__ import annotations + +import argparse +import copy +import json +import os +import shutil +import stat +import sys +import tempfile +import time +from collections.abc import Callable, Mapping, Sequence +from contextlib import suppress +from pathlib import Path +from typing import Any, Final, Literal + +from .canonical import canonical_bytes +from .identity import ( + aggregate_genesis, + create_genesis_holder_share, + generate_ed25519_seed, + prepare_genesis, + signing_descriptor, + verify_genesis, +) +from .keystore import EncryptedKeystore, KeystoreError + +HOLDER_SCHEMA: Final = "dm.operator.genesis-holder/v1" +INTENT_SCHEMA: Final = "dm.operator.genesis-intent/v1" +PENDING_CONTROL_HEAD: Final = "dm:identity:genesis-pending:v1" +MAX_DOCUMENT_BYTES: Final = 4 * 1024 * 1024 + + +class GenesisError(RuntimeError): + """Distributed genesis input or custody failed closed.""" + + +def _owner_directory(path: Path, code: str) -> Path: + target = Path(os.path.abspath(path)) + try: + info = target.lstat() + except OSError as exception: + raise GenesisError(code) from exception + if ( + target.is_symlink() + or not stat.S_ISDIR(info.st_mode) + or info.st_uid != os.geteuid() + or stat.S_IMODE(info.st_mode) & 0o077 + ): + raise GenesisError(code) + return target + + +def _fsync_directory(path: Path) -> None: + descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)) + try: + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def _private_write(path: Path, value: Mapping[str, Any]) -> None: + raw = canonical_bytes(value) + descriptor = os.open( + path, + os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0), + 0o600, + ) + try: + offset = 0 + while offset < len(raw): + offset += os.write(descriptor, raw[offset:]) + os.fsync(descriptor) + finally: + os.close(descriptor) + + +def _document(path: Path, code: str) -> Any: + descriptor = -1 + try: + descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)) + info = os.fstat(descriptor) + if ( + not stat.S_ISREG(info.st_mode) + or info.st_uid != os.geteuid() + or not 1 <= info.st_size <= MAX_DOCUMENT_BYTES + ): + raise GenesisError(code) + chunks: list[bytes] = [] + size = 0 + while chunk := os.read(descriptor, 1024 * 1024): + size += len(chunk) + if size > MAX_DOCUMENT_BYTES: + raise GenesisError(code) + chunks.append(chunk) + raw = b"".join(chunks) + value = json.loads(raw) + if canonical_bytes(value) != raw.rstrip(b"\n"): + raise GenesisError(code) + return value + except GenesisError: + raise + except (OSError, ValueError) as exception: + raise GenesisError(code) from exception + finally: + if descriptor >= 0: + os.close(descriptor) + + +def _write_new(path: Path, value: Mapping[str, Any]) -> None: + target = Path(os.path.abspath(path)) + _owner_directory(target.parent, "genesis_output_parent_rejected") + if target.exists() or target.is_symlink(): + if target.is_file() and canonical_bytes( + _document(target, "genesis_output_exists") + ) == canonical_bytes(value): + return + raise GenesisError("genesis_output_exists") + _private_write(target, value) + _fsync_directory(target.parent) + + +def _password(descriptor: int) -> bytearray: + if descriptor < 0: + raise GenesisError("invalid_genesis_password_descriptor") + try: + raw = os.read(descriptor, 1025) + except OSError as exception: + raise GenesisError("genesis_password_unavailable") from exception + finally: + with suppress(OSError): + os.close(descriptor) + if not 12 <= len(raw) <= 1024: + raise GenesisError("invalid_genesis_password_length") + return bytearray(raw) + + +def _reader(password: bytearray) -> Callable[[], bytearray]: + return lambda: bytearray(password) + + +def create_holder_package( + output: Path, + role: Literal["root", "recovery"], + password_reader: Callable[[], bytes | bytearray], +) -> dict[str, Any]: + """Atomically create one package containing exactly one private seed.""" + + if role not in {"root", "recovery"}: + raise GenesisError("invalid_genesis_holder_role") + target = Path(os.path.abspath(output)) + parent = _owner_directory(target.parent, "genesis_holder_parent_rejected") + if target.exists() or target.is_symlink(): + raise GenesisError("genesis_holder_exists") + seed = generate_ed25519_seed() + descriptor = { + "schema": HOLDER_SCHEMA, + "role": role, + "key": signing_descriptor(seed), + } + staging: Path | None = None + try: + staging = Path(tempfile.mkdtemp(prefix=f".{target.name}.tmp-", dir=parent)) + staging.chmod(0o700) + EncryptedKeystore.create( + staging / "holder.json", + password_reader, + control_head=PENDING_CONTROL_HEAD, + secrets={f"genesis.{role}.v1:holder": seed}, + ) + _private_write(staging / "descriptor.json", descriptor) + _fsync_directory(staging) + os.replace(staging, target) + _fsync_directory(parent) + staging = None + return descriptor + except KeystoreError as exception: + raise GenesisError("genesis_holder_create_rejected") from exception + finally: + if staging is not None and staging.exists(): + shutil.rmtree(staging) + + +def create_intent( + descriptors: Sequence[Any], + *, + root_threshold: int, + recovery_threshold: int, + created_at_ms: int, + nonce: bytes, +) -> dict[str, Any]: + """Freeze a genesis body using only public holder descriptors.""" + + policies: dict[str, list[Mapping[str, Any]]] = {"root": [], "recovery": []} + seen: set[str] = set() + for value in descriptors: + if ( + not isinstance(value, Mapping) + or set(value) != {"schema", "role", "key"} + or value.get("schema") != HOLDER_SCHEMA + or value.get("role") not in policies + or not isinstance(value.get("key"), Mapping) + ): + raise GenesisError("invalid_genesis_holder_descriptor") + key_id = value["key"].get("key_id") + if not isinstance(key_id, str) or key_id in seen: + raise GenesisError("invalid_genesis_holder_descriptor") + seen.add(key_id) + policies[str(value["role"])].append(copy.deepcopy(dict(value["key"]))) + for values in policies.values(): + values.sort(key=lambda row: str(row["key_id"])) + try: + prepared = prepare_genesis( + {"keys": policies["root"], "threshold": root_threshold}, + {"keys": policies["recovery"], "threshold": recovery_threshold}, + created_at_ms=created_at_ms, + nonce=nonce, + ) + except (TypeError, ValueError) as exception: + raise GenesisError("genesis_intent_policy_rejected") from exception + return {"schema": INTENT_SCHEMA, "prepared_genesis": prepared} + + +def create_holder_share( + intent: Any, + holder_package: Path, + password_reader: Callable[[], bytes | bytearray], +) -> dict[str, Any]: + """Open one holder package and emit its one public genesis signature.""" + + if ( + not isinstance(intent, Mapping) + or set(intent) != {"schema", "prepared_genesis"} + or intent.get("schema") != INTENT_SCHEMA + or not isinstance(intent.get("prepared_genesis"), Mapping) + ): + raise GenesisError("invalid_genesis_intent") + package = _owner_directory(holder_package, "genesis_holder_package_rejected") + descriptor = _document( + package / "descriptor.json", "genesis_holder_descriptor_unavailable" + ) + if ( + not isinstance(descriptor, Mapping) + or set(descriptor) != {"schema", "role", "key"} + or descriptor.get("schema") != HOLDER_SCHEMA + or descriptor.get("role") not in {"root", "recovery"} + ): + raise GenesisError("invalid_genesis_holder_descriptor") + role = str(descriptor["role"]) + try: + contents = EncryptedKeystore(package / "holder.json").open( + password_reader, + minimum_counter=1, + required_control_head=PENDING_CONTROL_HEAD, + ) + except KeystoreError as exception: + raise GenesisError("genesis_holder_store_rejected") from exception + if len(contents.secrets) != 1: + raise GenesisError("genesis_holder_store_rejected") + slot, seed = next(iter(contents.secrets.items())) + if ( + slot != f"genesis.{role}.v1:holder" + or signing_descriptor(seed) != descriptor["key"] + ): + raise GenesisError("genesis_holder_store_rejected") + try: + signature = create_genesis_holder_share( + intent["prepared_genesis"], + seed, + role=role, # type: ignore[arg-type] + ) + except (TypeError, ValueError) as exception: + raise GenesisError("genesis_holder_not_authorized") from exception + return {"schema": "dm.operator.genesis-share/v1", "signature": signature} + + +def aggregate_intent(intent: Any, shares: Sequence[Any]) -> dict[str, Any]: + """Keyless aggregation of holder-produced public signatures.""" + + if ( + not isinstance(intent, Mapping) + or set(intent) != {"schema", "prepared_genesis"} + or intent.get("schema") != INTENT_SCHEMA + or not isinstance(intent.get("prepared_genesis"), Mapping) + ): + raise GenesisError("invalid_genesis_intent") + signatures: list[Mapping[str, Any]] = [] + for share in shares: + if ( + not isinstance(share, Mapping) + or set(share) != {"schema", "signature"} + or share.get("schema") != "dm.operator.genesis-share/v1" + or not isinstance(share.get("signature"), Mapping) + ): + raise GenesisError("invalid_genesis_share") + signatures.append(share["signature"]) + try: + genesis = aggregate_genesis(intent["prepared_genesis"], signatures) + verify_genesis(genesis) + return genesis + except (TypeError, ValueError) as exception: + raise GenesisError("genesis_share_threshold_rejected") from exception + + +def parser() -> argparse.ArgumentParser: + result = argparse.ArgumentParser(prog="daimon-genesis", description=__doc__) + commands = result.add_subparsers(dest="command", required=True) + holder = commands.add_parser("create-holder") + holder.add_argument("--role", choices=("root", "recovery"), required=True) + holder.add_argument("--password-fd", type=int, required=True) + holder.add_argument("--output", type=Path, required=True) + intent = commands.add_parser("create-intent") + intent.add_argument("--descriptor", type=Path, action="append", required=True) + intent.add_argument("--root-threshold", type=int, required=True) + intent.add_argument("--recovery-threshold", type=int, required=True) + intent.add_argument("--output", type=Path, required=True) + sign = commands.add_parser("sign") + sign.add_argument("--intent", type=Path, required=True) + sign.add_argument("--holder", type=Path, required=True) + sign.add_argument("--password-fd", type=int, required=True) + sign.add_argument("--output", type=Path, required=True) + aggregate = commands.add_parser("aggregate") + aggregate.add_argument("--intent", type=Path, required=True) + aggregate.add_argument("--share", type=Path, action="append", required=True) + aggregate.add_argument("--output", type=Path, required=True) + return result + + +def main(argv: Sequence[str] | None = None) -> int: + password = bytearray() + try: + arguments = parser().parse_args(argv) + if arguments.command == "create-holder": + password = _password(arguments.password_fd) + receipt = create_holder_package( + arguments.output, arguments.role, _reader(password) + ) + elif arguments.command == "create-intent": + receipt = create_intent( + [ + _document(path, "genesis_holder_descriptor_unavailable") + for path in arguments.descriptor + ], + root_threshold=arguments.root_threshold, + recovery_threshold=arguments.recovery_threshold, + created_at_ms=time.time_ns() // 1_000_000, + nonce=os.urandom(32), + ) + _write_new(arguments.output, receipt) + elif arguments.command == "sign": + password = _password(arguments.password_fd) + receipt = create_holder_share( + _document(arguments.intent, "genesis_intent_unavailable"), + arguments.holder, + _reader(password), + ) + _write_new(arguments.output, receipt) + else: + receipt = aggregate_intent( + _document(arguments.intent, "genesis_intent_unavailable"), + [ + _document(path, "genesis_share_unavailable") + for path in arguments.share + ], + ) + _write_new(arguments.output, receipt) + sys.stdout.buffer.write(canonical_bytes(receipt) + b"\n") + return 0 + except GenesisError as exception: + print(str(exception), file=sys.stderr) + return 2 + except (OSError, TypeError, ValueError): + print("genesis_failed", file=sys.stderr) + return 1 + finally: + password[:] = b"\x00" * len(password) + + +if __name__ == "__main__": + raise SystemExit(main()) + + +__all__ = [ + "GenesisError", + "aggregate_intent", + "create_holder_package", + "create_holder_share", + "create_intent", + "main", + "parser", +] diff --git a/src/daimon_matrix/operator_rebirth.py b/src/daimon_matrix/operator_rebirth.py index bcff048..ffa127a 100644 --- a/src/daimon_matrix/operator_rebirth.py +++ b/src/daimon_matrix/operator_rebirth.py @@ -49,9 +49,9 @@ aggregate_recovery, create_embodiment_credential, create_incarnation_authorization, - create_recovery, create_recovery_authorization_share, create_recovery_possession_share, + create_synthetic_recovery_in_process, ed25519_public, generate_ed25519_seed, generate_x25519_private, @@ -62,11 +62,14 @@ verify_embodiment_credential, verify_incarnation_authorization, verify_recovery, + verify_recovery_from_anchor, x25519_public, ) from .keystore import EncryptedKeystore, KeystoreError, PasswordReader from .ledger import Ledger from .local_api import LocalCapability, create_capability +from .operator_genesis import HOLDER_SCHEMA as GENESIS_HOLDER_SCHEMA +from .operator_genesis import PENDING_CONTROL_HEAD from .peer_transport import PeerTransportError, http_peer_round_trip from .runtime import load_runtime from .service import SERVICE_METHODS @@ -567,12 +570,19 @@ def _exact_custody_role_seeds( def recovery_request_base( - previous: RootAuthority, recovery_artifact: Mapping[str, Any] + previous: RootAuthority, + recovery_artifact: Mapping[str, Any], + *, + known_states: Sequence[ControlState] | None = None, ) -> RecoveryRequestBase: """Verify a recovery quorum artifact before target custody is generated.""" try: - recovered = verify_recovery(recovery_artifact, [previous.state]) + recovered = ( + verify_recovery(recovery_artifact, known_states) + if known_states is not None + else verify_recovery_from_anchor(recovery_artifact, previous.state) + ) except (TypeError, ValueError) as exception: raise RebirthError("rebirth_recovery_artifact_invalid") from exception expected_revocations = sorted( @@ -585,9 +595,8 @@ def recovery_request_base( body = recovery_artifact.get("body") previous_root_ids = {row["key_id"] for row in previous.state.root_policy["keys"]} recovered_root_ids = {row["key_id"] for row in recovered.root_policy["keys"]} - if ( - not isinstance(body, Mapping) - or body.get("revoked_embodiments") != expected_revocations + if not isinstance(body, Mapping) or not set(expected_revocations).issubset( + body.get("revoked_embodiments", []) ): raise RebirthError("rebirth_recovery_incomplete_revocation") if previous_root_ids & recovered_root_ids: @@ -773,7 +782,7 @@ def validate_activation( return normalized, successor, history -def authorize_recovery_enrollment_request( +def authorize_synthetic_recovery_enrollment_in_process( request: Any, previous: RootAuthority, recovery_artifact: Mapping[str, Any], @@ -781,7 +790,7 @@ def authorize_recovery_enrollment_request( replacement_root_seeds: Sequence[bytes], issued_at_ms: int, ) -> dict[str, Any]: - """Authorize the first fresh body directly under a recovered root.""" + """Synthetic fixture that centralizes a replacement-root threshold.""" issued = _uint(issued_at_ms, "invalid_rebirth_time") request_base = recovery_request_base(previous, recovery_artifact) @@ -1474,6 +1483,28 @@ def authority_from_document(value: Any) -> RootAuthority: return authority +def _known_control_authorities( + previous: RootAuthority, documents: Sequence[Any] +) -> tuple[RootAuthority, ...]: + """Verify independently supplied fork documents for a recovery ceremony.""" + + authorities = [previous, *(authority_from_document(value) for value in documents)] + by_head: dict[str, RootAuthority] = {} + recovery_policy = canonical_bytes(previous.state.recovery_policy) + for authority in authorities: + state = authority.state + if ( + state.being_ref != previous.state.being_ref + or canonical_bytes(state.recovery_policy) != recovery_policy + ): + raise RebirthError("rebirth_recovery_branch_authority_mismatch") + existing = by_head.get(state.head) + if existing is not None and existing != authority: + raise RebirthError("rebirth_recovery_branch_authority_mismatch") + by_head[state.head] = authority + return tuple(by_head[head] for head in sorted(by_head)) + + def authority_from_runtime_bundle(value: Any) -> RootAuthority: """Verify the active root authority and signed history in one V7 bundle.""" @@ -2586,6 +2617,7 @@ def create_distributed_recovery_intent( expires_at_ms: int, nonce: bytes, known_states: Sequence[ControlState] | None = None, + known_manifests: Sequence[BeingManifest] | None = None, ) -> dict[str, Any]: """Freeze public recovery material before any independent holder signs.""" @@ -2596,10 +2628,19 @@ def create_distributed_recovery_intent( if len(nonce) != 32: raise RebirthError("invalid_rebirth_recovery_intent_nonce") states = tuple(known_states or (previous.state,)) + manifests = tuple(known_manifests or (previous.manifest,)) + state_heads = {state.head for state in states} if ( not states - or previous.state.head not in {state.head for state in states} + or not manifests + or previous.state.head not in state_heads or any(state.being_ref != previous.state.being_ref for state in states) + or previous.manifest.digest not in {manifest.digest for manifest in manifests} + or any( + manifest.being_ref != previous.state.being_ref + or manifest.value["control_head"] not in state_heads + for manifest in manifests + ) ): raise RebirthError("rebirth_recovery_intent_authority_mismatch") policy = { @@ -2612,7 +2653,8 @@ def create_distributed_recovery_intent( revoked = sorted( { row["embodiment_id"] - for row in previous.manifest.value["embodiments"] + for manifest in manifests + for row in manifest.value["embodiments"] if row["status"] == "active" } ) @@ -2629,6 +2671,7 @@ def create_distributed_recovery_intent( "created_at_ms": created, "expires_at_ms": expires, "known_control_heads": sorted({state.head for state in states}), + "known_manifest_hashes": sorted({manifest.digest for manifest in manifests}), "nonce": b64url(nonce), "prepared_recovery": prepared, "previous_manifest_hash": previous.manifest.digest, @@ -2649,6 +2692,7 @@ def validate_distributed_recovery_intent( *, observed_at_ms: int, known_states: Sequence[ControlState] | None = None, + known_manifests: Sequence[BeingManifest] | None = None, ) -> dict[str, Any]: """Validate the closed recovery intent independently at every holder.""" @@ -2666,6 +2710,7 @@ def validate_distributed_recovery_intent( "created_at_ms", "expires_at_ms", "known_control_heads", + "known_manifest_hashes", "nonce", "prepared_recovery", "previous_manifest_hash", @@ -2686,13 +2731,22 @@ def validate_distributed_recovery_intent( except (TypeError, ValueError) as exception: raise RebirthError("invalid_rebirth_recovery_intent_nonce") from exception states = tuple(known_states or (previous.state,)) + manifests = tuple(known_manifests or (previous.manifest,)) heads = sorted({state.head for state in states}) + manifest_hashes = sorted({manifest.digest for manifest in manifests}) if ( nonce != body["nonce"] or body["being_ref"] != previous.state.being_ref or body["previous_manifest_hash"] != previous.manifest.digest or body["known_control_heads"] != heads + or body["known_manifest_hashes"] != manifest_hashes or previous.state.head not in heads + or previous.manifest.digest not in manifest_hashes + or any( + manifest.being_ref != previous.state.being_ref + or manifest.value["control_head"] not in heads + for manifest in manifests + ) ): raise RebirthError("rebirth_recovery_intent_authority_mismatch") prepared = body["prepared_recovery"] @@ -2704,7 +2758,8 @@ def validate_distributed_recovery_intent( expected_revocations = sorted( { item["embodiment_id"] - for item in previous.manifest.value["embodiments"] + for manifest in manifests + for item in manifest.value["embodiments"] if item["status"] == "active" } ) @@ -2736,6 +2791,7 @@ def create_distributed_recovery_share( *, observed_at_ms: int, known_states: Sequence[ControlState] | None = None, + known_manifests: Sequence[BeingManifest] | None = None, ) -> dict[str, Any]: """Produce one role-bound share in a process holding exactly one seed.""" @@ -2745,6 +2801,7 @@ def create_distributed_recovery_share( previous, observed_at_ms=observed_at_ms, known_states=states, + known_manifests=known_manifests, ) prepared = verified["body"]["prepared_recovery"] kid = key_id("Ed25519", ed25519_public(holder_seed)) @@ -2786,6 +2843,7 @@ def aggregate_distributed_recovery( *, observed_at_ms: int, known_states: Sequence[ControlState] | None = None, + known_manifests: Sequence[BeingManifest] | None = None, ) -> dict[str, Any]: """Aggregate public shares without opening any holder keystore.""" @@ -2795,6 +2853,7 @@ def aggregate_distributed_recovery( previous, observed_at_ms=observed_at_ms, known_states=states, + known_manifests=known_manifests, ) prepared = verified["body"]["prepared_recovery"] signatures: list[Mapping[str, Any]] = [] @@ -2854,30 +2913,41 @@ def create_replacement_root_holder( previous: RootAuthority, password_reader: PasswordReader, ) -> dict[str, Any]: - """Generate one replacement root in one owner-only holder store.""" + """Atomically generate one isolated holder package and public descriptor.""" target = Path(os.path.abspath(output)) - _owner_directory(target.parent, "rebirth_holder_parent_rejected") + parent = _owner_directory(target.parent, "rebirth_holder_parent_rejected") if target.exists() or target.is_symlink(): raise RebirthError("rebirth_holder_exists") holder_seed = generate_ed25519_seed() descriptor = signing_descriptor(holder_seed) + receipt = { + "schema": "dm.operator.replacement-root-holder/v1", + "being_ref": previous.state.being_ref, + "custody_control_head": previous.state.head, + "key": descriptor, + } + staging: Path | None = None try: + staging = Path(tempfile.mkdtemp(prefix=f".{target.name}.tmp-", dir=parent)) + staging.chmod(0o700) EncryptedKeystore.create( - target, + staging / "holder.json", password_reader, control_head=previous.state.head, secrets={"root.signing.v1:holder": holder_seed}, ) + _private_write(staging / "descriptor.json", receipt) + _fsync_directory(staging) + os.replace(staging, target) + _fsync_directory(parent) + staging = None except KeystoreError as exception: raise RebirthError("rebirth_holder_create_rejected") from exception - _fsync_directory(target.parent) - return { - "schema": "dm.operator.replacement-root-holder/v1", - "being_ref": previous.state.being_ref, - "custody_control_head": previous.state.head, - "key": descriptor, - } + finally: + if staging is not None and staging.exists(): + shutil.rmtree(staging) + return receipt def _single_holder_seed( @@ -2888,18 +2958,65 @@ def _single_holder_seed( allowed_prefixes: tuple[str, ...], code: str, ) -> bytes: + target = path + required_control_head = previous.state.head + exact_slot: str | None = None + expected_descriptor: Mapping[str, Any] | None = None + if path.is_dir(): + descriptor = _safe_document(path / "descriptor.json", code) + if descriptor.get("schema") == GENESIS_HOLDER_SCHEMA: + row = _closed( + descriptor, + {"schema", "role", "key"}, + code, + ) + role = row["role"] + if ( + role not in {"root", "recovery"} + or not any( + prefix.startswith(f"{role}.signing.v1:") + for prefix in allowed_prefixes + ) + or not isinstance(row["key"], Mapping) + ): + raise RebirthError(code) + required_control_head = PENDING_CONTROL_HEAD + exact_slot = f"genesis.{role}.v1:holder" + expected_descriptor = row["key"] + else: + row = _closed( + descriptor, + {"schema", "being_ref", "custody_control_head", "key"}, + code, + ) + if ( + row["schema"] != "dm.operator.replacement-root-holder/v1" + or row["being_ref"] != previous.state.being_ref + or row["custody_control_head"] != previous.state.head + or not isinstance(row["key"], Mapping) + ): + raise RebirthError(code) + expected_descriptor = row["key"] + target = path / "holder.json" try: - contents = EncryptedKeystore(path).open( + contents = EncryptedKeystore(target).open( password_reader, minimum_counter=1, - required_control_head=previous.state.head, + required_control_head=required_control_head, ) except KeystoreError as exception: raise RebirthError(code) from exception if len(contents.secrets) != 1: raise RebirthError(code) slot, holder_seed = next(iter(contents.secrets.items())) - if not slot.startswith(allowed_prefixes): + if ( + (exact_slot is not None and slot != exact_slot) + or (exact_slot is None and not slot.startswith(allowed_prefixes)) + or ( + expected_descriptor is not None + and signing_descriptor(holder_seed) != expected_descriptor + ) + ): raise RebirthError(code) return holder_seed @@ -2911,6 +3028,8 @@ def create_distributed_recovery_share_from_holder( password_reader: PasswordReader, *, observed_at_ms: int, + known_states: Sequence[ControlState] | None = None, + known_manifests: Sequence[BeingManifest] | None = None, ) -> dict[str, Any]: """Open exactly one holder store and emit only its public recovery share.""" @@ -2926,6 +3045,8 @@ def create_distributed_recovery_share_from_holder( previous, holder_seed, observed_at_ms=observed_at_ms, + known_states=known_states, + known_manifests=known_manifests, ) @@ -3005,7 +3126,7 @@ def create_synthetic_single_store_recovery_custody( if row["status"] == "active" } ) - recovery_artifact = create_recovery( + recovery_artifact = create_synthetic_recovery_in_process( [previous.state], recovery_seeds[:threshold], replacement_roots, @@ -3104,7 +3225,7 @@ def authorize_synthetic_single_store_recovery( threshold = request_base.state.root_policy["threshold"] if len(roots) < threshold: raise RebirthError("rebirth_root_threshold_shortfall") - return authorize_recovery_enrollment_request( + return authorize_synthetic_recovery_enrollment_in_process( request, previous, recovery_artifact, @@ -3183,10 +3304,9 @@ def parser() -> argparse.ArgumentParser: recover.add_argument("--output", type=Path, required=True) create_holder = commands.add_parser( "create-replacement-root-holder", - help="generate one isolated replacement-root holder store", + help="atomically generate an isolated replacement-root holder package", ) create_holder.add_argument("--authority", type=Path, required=True) - create_holder.add_argument("--holder", type=Path, required=True) create_holder.add_argument("--password-fd", type=int, required=True) create_holder.add_argument("--output", type=Path, required=True) recovery_intent = commands.add_parser( @@ -3194,6 +3314,9 @@ def parser() -> argparse.ArgumentParser: help="freeze one recovery intent from public holder descriptors", ) recovery_intent.add_argument("--authority", type=Path, required=True) + recovery_intent.add_argument( + "--known-authority", type=Path, action="append", default=[] + ) recovery_intent.add_argument( "--holder-descriptor", type=Path, action="append", required=True ) @@ -3204,6 +3327,9 @@ def parser() -> argparse.ArgumentParser: "recovery-share", help="emit one share from one isolated holder store" ) recovery_share.add_argument("--authority", type=Path, required=True) + recovery_share.add_argument( + "--known-authority", type=Path, action="append", default=[] + ) recovery_share.add_argument("--intent", type=Path, required=True) recovery_share.add_argument("--holder", type=Path, required=True) recovery_share.add_argument("--password-fd", type=int, required=True) @@ -3213,6 +3339,9 @@ def parser() -> argparse.ArgumentParser: help="aggregate recovery shares without opening holder stores", ) aggregate_recovery_command.add_argument("--authority", type=Path, required=True) + aggregate_recovery_command.add_argument( + "--known-authority", type=Path, action="append", default=[] + ) aggregate_recovery_command.add_argument("--intent", type=Path, required=True) aggregate_recovery_command.add_argument( "--share", type=Path, action="append", required=True @@ -3294,17 +3423,25 @@ def main(argv: Sequence[str] | None = None) -> int: password = _password(arguments.password_fd) try: receipt = create_replacement_root_holder( - arguments.holder, + arguments.output, authority, _password_reader(password), ) - _write_new_document(arguments.output, receipt) finally: password[:] = b"\x00" * len(password) elif arguments.command == "create-recovery-intent": authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) + known_authorities = _known_control_authorities( + authority, + [ + _safe_document(path, "rebirth_known_authority_unavailable") + for path in arguments.known_authority + ], + ) + known_states = tuple(item.state for item in known_authorities) + known_manifests = tuple(item.manifest for item in known_authorities) ttl = arguments.ttl_seconds if ( not isinstance(ttl, int) @@ -3334,12 +3471,23 @@ def main(argv: Sequence[str] | None = None) -> int: created_at_ms=now, expires_at_ms=now + ttl * 1000, nonce=secrets.token_bytes(32), + known_states=known_states, + known_manifests=known_manifests, ) _write_new_document(arguments.output, receipt) elif arguments.command == "recovery-share": authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) + known_authorities = _known_control_authorities( + authority, + [ + _safe_document(path, "rebirth_known_authority_unavailable") + for path in arguments.known_authority + ], + ) + known_states = tuple(item.state for item in known_authorities) + known_manifests = tuple(item.manifest for item in known_authorities) password = _password(arguments.password_fd) try: receipt = create_distributed_recovery_share_from_holder( @@ -3351,6 +3499,8 @@ def main(argv: Sequence[str] | None = None) -> int: arguments.holder, _password_reader(password), observed_at_ms=now, + known_states=known_states, + known_manifests=known_manifests, ) _write_new_document(arguments.output, receipt) finally: @@ -3359,6 +3509,15 @@ def main(argv: Sequence[str] | None = None) -> int: authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") ) + known_authorities = _known_control_authorities( + authority, + [ + _safe_document(path, "rebirth_known_authority_unavailable") + for path in arguments.known_authority + ], + ) + known_states = tuple(item.state for item in known_authorities) + known_manifests = tuple(item.manifest for item in known_authorities) receipt = aggregate_distributed_recovery( _safe_document( arguments.intent, @@ -3370,6 +3529,8 @@ def main(argv: Sequence[str] | None = None) -> int: for path in arguments.share ], observed_at_ms=now, + known_states=known_states, + known_manifests=known_manifests, ) _write_new_document(arguments.output, receipt) elif arguments.command == "create-recovery-authorization-intent": @@ -3603,7 +3764,6 @@ def main(argv: Sequence[str] | None = None) -> int: "authority_from_runtime_bundle", "authorize_enrollment_request", "authorize_from_root_custody", - "authorize_recovery_enrollment_request", "authorize_synthetic_single_store_recovery", "create_enrollment_request", "create_recovery_target_preparation", diff --git a/src/daimon_matrix/synthetic_birth.py b/src/daimon_matrix/synthetic_birth.py index a02bd27..6a480c4 100644 --- a/src/daimon_matrix/synthetic_birth.py +++ b/src/daimon_matrix/synthetic_birth.py @@ -40,8 +40,8 @@ from .identity import ( ControlState, create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, generate_ed25519_seed, generate_x25519_private, @@ -261,7 +261,7 @@ def _create_identity( else (generate_ed25519_seed(), generate_ed25519_seed()) ) actual_genesis = ( - create_genesis( + create_synthetic_genesis_in_process( actual_roots, 2, actual_recovery, @@ -649,7 +649,7 @@ def run_synthetic_birth( # operational keys deliberately do not. newborn_roots = (generate_ed25519_seed(), generate_ed25519_seed()) newborn_recovery = (generate_ed25519_seed(), generate_ed25519_seed()) - newborn_genesis = create_genesis( + newborn_genesis = create_synthetic_genesis_in_process( newborn_roots, 2, newborn_recovery, diff --git a/src/daimon_matrix/synthetic_multihost.py b/src/daimon_matrix/synthetic_multihost.py index 6c5044f..9050049 100644 --- a/src/daimon_matrix/synthetic_multihost.py +++ b/src/daimon_matrix/synthetic_multihost.py @@ -38,8 +38,8 @@ from .identity import ( ControlState, create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, key_descriptor, verify_genesis, @@ -186,7 +186,7 @@ class _RuntimeState: def _create_identity() -> _Identity: root_seeds = tuple(_seed(f"root:{index}") for index in range(3)) recovery_seeds = tuple(_seed(f"recovery:{index}") for index in range(3)) - genesis = create_genesis( + genesis = create_synthetic_genesis_in_process( root_seeds, 2, recovery_seeds, diff --git a/src/daimon_matrix/synthetic_relationships.py b/src/daimon_matrix/synthetic_relationships.py index c34910e..9818a87 100644 --- a/src/daimon_matrix/synthetic_relationships.py +++ b/src/daimon_matrix/synthetic_relationships.py @@ -24,8 +24,8 @@ from .identity import ( ControlState, create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, key_descriptor, signing_descriptor, @@ -141,7 +141,7 @@ class _Identity: def _identity(label: str) -> _Identity: root_seeds = tuple(_seed(f"{label}:root:{index}") for index in range(3)) recovery_seeds = tuple(_seed(f"{label}:recovery:{index}") for index in range(3)) - genesis = create_genesis( + genesis = create_synthetic_genesis_in_process( root_seeds, 2, recovery_seeds, diff --git a/src/daimon_matrix/synthetic_sources.py b/src/daimon_matrix/synthetic_sources.py index caf3651..94681aa 100644 --- a/src/daimon_matrix/synthetic_sources.py +++ b/src/daimon_matrix/synthetic_sources.py @@ -23,8 +23,8 @@ from .identity import ( ControlState, create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, key_descriptor, signing_descriptor, @@ -113,7 +113,7 @@ class _Identity: def _identity(label: str) -> _Identity: root_seeds = tuple(_seed(f"{label}:root:{index}") for index in range(3)) recovery_seeds = tuple(_seed(f"{label}:recovery:{index}") for index in range(3)) - genesis = create_genesis( + genesis = create_synthetic_genesis_in_process( root_seeds, 2, recovery_seeds, diff --git a/tests/test_dm021_identity.py b/tests/test_dm021_identity.py index 63a249e..8a93746 100644 --- a/tests/test_dm021_identity.py +++ b/tests/test_dm021_identity.py @@ -17,12 +17,12 @@ VerificationError, create_binding_activation, create_embodiment_credential, - create_genesis, create_history_binding, create_incarnation_authorization, - create_recovery, create_revocation, create_root_rotation, + create_synthetic_genesis_in_process, + create_synthetic_recovery_in_process, ed25519_public, key_descriptor, require_trust_mode, @@ -60,7 +60,7 @@ def setUp(self) -> None: seed("recovery-b"), seed("recovery-c"), ] - self.genesis = create_genesis( + self.genesis = create_synthetic_genesis_in_process( self.root, 2, self.recovery, @@ -143,7 +143,7 @@ def test_body_key_and_principal_substitution_fail_closed(self) -> None: def test_root_recovery_and_embodiment_keys_are_purpose_separated(self) -> None: with self.assertRaises(VerificationError): - create_genesis( + create_synthetic_genesis_in_process( self.root, 2, [self.root[0], seed("other-rec-a"), seed("other-rec-b")], @@ -294,7 +294,7 @@ def test_competing_successors_quarantine_until_recovery_names_every_head( _ = chain.state incomplete_root = [seed("incomplete-1"), seed("incomplete-2")] - incomplete = create_recovery( + incomplete = create_synthetic_recovery_in_process( [chain.states()[0]], self.recovery, incomplete_root, @@ -305,7 +305,7 @@ def test_competing_successors_quarantine_until_recovery_names_every_head( chain.add(incomplete) recovered_root = [seed("recovered-1"), seed("recovered-2")] - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( chain.states(), self.recovery, recovered_root, @@ -320,7 +320,7 @@ def test_competing_successors_quarantine_until_recovery_names_every_head( def test_loss_of_both_thresholds_cannot_recreate_the_being(self) -> None: replacement = [seed("lost-replacement-a"), seed("lost-replacement-b")] - unauthorized = create_recovery( + unauthorized = create_synthetic_recovery_in_process( [self.state], [], replacement, @@ -330,7 +330,7 @@ def test_loss_of_both_thresholds_cannot_recreate_the_being(self) -> None: with self.assertRaises(VerificationError): verify_recovery(unauthorized, [self.state]) - successor = create_genesis( + successor = create_synthetic_genesis_in_process( replacement, 2, [seed("successor-rec-a"), seed("successor-rec-b")], @@ -377,7 +377,7 @@ def test_recovery_conservatively_merges_every_branch_revocation(self) -> None: legion_strict, ) replacement = [seed("merged-root-a"), seed("merged-root-b")] - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( [legion_wide, peer_state], self.recovery, replacement, diff --git a/tests/test_dm021_process.py b/tests/test_dm021_process.py index f7acbd8..40f738f 100644 --- a/tests/test_dm021_process.py +++ b/tests/test_dm021_process.py @@ -18,7 +18,7 @@ import sys from pathlib import Path -from daimon_matrix.identity import create_genesis, verify_genesis +from daimon_matrix.identity import create_synthetic_genesis_in_process, verify_genesis from daimon_matrix.keystore import EncryptedKeystore payload = json.loads(sys.stdin.buffer.read()) @@ -28,7 +28,9 @@ password = decode(payload["password"]) first = Path(payload["first"]) second = Path(payload["second"]) -genesis = create_genesis(root, 2, recovery, 2, created_at_ms=1800000000000) +genesis = create_synthetic_genesis_in_process( + root, 2, recovery, 2, created_at_ms=1800000000000 +) state = verify_genesis(genesis) store = EncryptedKeystore.create( first / "custody.json", diff --git a/tests/test_dm022_ledger.py b/tests/test_dm022_ledger.py index 8151dbe..69e7e10 100644 --- a/tests/test_dm022_ledger.py +++ b/tests/test_dm022_ledger.py @@ -21,10 +21,10 @@ from daimon_matrix.identity import ( create_binding_activation, create_embodiment_credential, - create_genesis, create_history_binding, create_incarnation_authorization, create_revocation, + create_synthetic_genesis_in_process, ed25519_public, key_descriptor, signing_descriptor, @@ -76,7 +76,7 @@ def setUp(self) -> None: self.root_path.chmod(0o700) self.root_seeds = [seed("root-a"), seed("root-b"), seed("root-c")] recovery = [seed("recovery-a"), seed("recovery-b"), seed("recovery-c")] - self.genesis = create_genesis( + self.genesis = create_synthetic_genesis_in_process( self.root_seeds, 2, recovery, diff --git a/tests/test_dm041_hermes_body.py b/tests/test_dm041_hermes_body.py index 0f74a85..76e7dac 100644 --- a/tests/test_dm041_hermes_body.py +++ b/tests/test_dm041_hermes_body.py @@ -502,7 +502,7 @@ def test_python_venv_launcher_is_preserved_and_target_is_bound(self) -> None: def test_profile_is_deterministic_exclusive_and_native_memory_free(self) -> None: manifest = self.create() self.assertEqual(manifest, verify_profile(self.plan)) - self.assertEqual(len(manifest["matrix_package"]["modules"]), 49) + self.assertEqual(len(manifest["matrix_package"]["modules"]), 50) self.assertIn( "collective_memory.py", {item["name"] for item in manifest["matrix_package"]["modules"]}, @@ -511,6 +511,10 @@ def test_profile_is_deterministic_exclusive_and_native_memory_free(self) -> None "operator_rebirth.py", {item["name"] for item in manifest["matrix_package"]["modules"]}, ) + self.assertIn( + "operator_genesis.py", + {item["name"] for item in manifest["matrix_package"]["modules"]}, + ) self.assertEqual( len(manifest["matrix_package"]["tree_sha256"]), 64, diff --git a/tests/test_dm060_synthetic_birth.py b/tests/test_dm060_synthetic_birth.py index e89ec21..ad79fec 100644 --- a/tests/test_dm060_synthetic_birth.py +++ b/tests/test_dm060_synthetic_birth.py @@ -27,8 +27,8 @@ from daimon_matrix.canonical import b64url from daimon_matrix.identity import ( create_embodiment_credential, - create_genesis, create_incarnation_authorization, + create_synthetic_genesis_in_process, ed25519_public, key_descriptor, verify_genesis, @@ -61,7 +61,7 @@ def __init__(self, label: str, purposes: list[str]) -> None: seed(f"{label}:recovery:a"), seed(f"{label}:recovery:b"), ] - self.genesis = create_genesis( + self.genesis = create_synthetic_genesis_in_process( self.root_seeds, 2, self.recovery_seeds, @@ -350,7 +350,7 @@ def test_parent_offer_cannot_precommit_newborn_or_reuse_key_roles(self) -> None: offer_nonce=seed("alias-offer"), ) - parent_controlled_genesis = create_genesis( + parent_controlled_genesis = create_synthetic_genesis_in_process( self.parent.root_seeds, 2, self.parent.recovery_seeds, diff --git a/tests/test_dm078_recovery_rebirth.py b/tests/test_dm078_recovery_rebirth.py index d7ddfd5..6c83e59 100644 --- a/tests/test_dm078_recovery_rebirth.py +++ b/tests/test_dm078_recovery_rebirth.py @@ -7,6 +7,7 @@ import subprocess import sys import time +from collections.abc import Callable from pathlib import Path from tempfile import TemporaryDirectory from typing import Any @@ -25,17 +26,20 @@ from daimon_matrix.identity import ( create_embodiment_credential, create_incarnation_authorization, - create_recovery, + create_revocation, + create_synthetic_recovery_in_process, ed25519_public, key_descriptor, signing_descriptor, verify_genesis, verify_recovery, + verify_successor, x25519_public, ) from daimon_matrix.keystore import EncryptedKeystore from daimon_matrix.ledger import Ledger from daimon_matrix.local_api import create_capability +from daimon_matrix.operator_genesis import PENDING_CONTROL_HEAD from daimon_matrix.operator_rebirth import ( RebirthError, activate_recovery_target_runtime, @@ -43,7 +47,7 @@ aggregate_distributed_recovery_authorization, authority_from_runtime_bundle, authorize_enrollment_request, - authorize_recovery_enrollment_request, + authorize_synthetic_recovery_enrollment_in_process, authorize_synthetic_single_store_recovery, create_distributed_recovery_authorization_intent, create_distributed_recovery_authorization_share, @@ -51,6 +55,7 @@ create_distributed_recovery_share, create_enrollment_request, create_recovery_target_preparation, + create_replacement_root_holder, create_synthetic_single_store_recovery_custody, recovery_request_base, restore_recovery_ledger, @@ -65,6 +70,10 @@ from tools.generate_dm078_recovery_vectors import generate as generate_vectors +def _test_password_reader(value: bytes) -> Callable[[], bytearray]: + return lambda: bytearray(value) + + class TestRecoveryRebirthAuthority(RootLedgerFixture): def _authority_document(self) -> dict[str, Any]: return { @@ -187,7 +196,7 @@ def _successor( for row in previous.manifest.value["embodiments"] if row["status"] == "active" ) - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( [previous.state], recovery_seeds, replacement_roots, @@ -606,7 +615,7 @@ def test_target_request_is_signed_only_after_verified_recovery(self) -> None: for row in self.manifest.value["embodiments"] if row["status"] == "active" ) - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( [self.state], recovery_seeds, replacement_roots, @@ -630,7 +639,7 @@ def test_target_request_is_signed_only_after_verified_recovery(self) -> None: ) assert request["body"]["control_head"] == request_base.state.head assert request["body"]["base_manifest_hash"] == self.manifest.digest - activation = authorize_recovery_enrollment_request( + activation = authorize_synthetic_recovery_enrollment_in_process( request, self.authority, recovery, @@ -647,7 +656,7 @@ def test_target_request_is_signed_only_after_verified_recovery(self) -> None: "embodiment:recovered-request-target" ) - incomplete = create_recovery( + incomplete = create_synthetic_recovery_in_process( [self.state], recovery_seeds, replacement_roots, @@ -678,7 +687,7 @@ def test_recovery_preparation_builds_loadable_target_only_runtime(self) -> None: for row in self.manifest.value["embodiments"] if row["status"] == "active" ) - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( [self.state], recovery_seeds, replacement_roots, @@ -707,7 +716,7 @@ def test_recovery_preparation_builds_loadable_target_only_runtime(self) -> None: expires_at_ms=NOW + 60_010, ) request = json.loads((ceremony / "preparation/request.json").read_bytes()) - activation = authorize_recovery_enrollment_request( + activation = authorize_synthetic_recovery_enrollment_in_process( request, self.authority, recovery, @@ -1237,6 +1246,201 @@ def test_distributed_root_holders_authorize_target_without_seed_aggregation( observed_at_ms=NOW + 22, ) + def test_multi_head_recovery_runs_through_cli_and_authority_epoch(self) -> None: + branch_artifacts = [ + create_revocation( + self.state, + self.root_seeds, + embodiment_id=f"embodiment:retired-{index}", + cutoff_incarnation_sequence=index, + revocation_generation=1, + ) + for index in (1, 2) + ] + branch_states = [ + verify_successor(artifact, self.state) for artifact in branch_artifacts + ] + branch_authorities: list[RootAuthority] = [] + branch_documents: list[dict[str, Any]] = [] + for branch_index, (artifact, state) in enumerate( + zip(branch_artifacts, branch_states, strict=True) + ): + rows = copy.deepcopy(self.manifest.value["embodiments"]) + if branch_index == 0: + rows[0]["status"] = "retired" + manifest = BeingManifest.from_value( + { + **self.manifest.value, + "control_head": state.head, + "embodiments": rows, + } + ) + authority = RootAuthority( + manifest, state, self.credentials, self.incarnations + ) + branch_authorities.append(authority) + branch_documents.append( + { + "schema": "dm.operator.authority/v1", + "control_artifacts": [self.genesis, artifact], + "control_head": state.head, + "manifest": manifest.value, + "credentials": list(self.credentials.values()), + "incarnations": list(self.incarnations.values()), + } + ) + + root = self.root_path / "multi-head-cli" + root.mkdir(mode=0o700) + authority_paths: list[Path] = [] + for index, document in enumerate(branch_documents): + path = root / f"branch-{index}.json" + path.write_bytes(canonical_bytes(document)) + path.chmod(0o600) + authority_paths.append(path) + replacement_roots = [ + seed("multi-head-root-a"), + seed("multi-head-root-b"), + seed("multi-head-root-c"), + ] + descriptors: list[Path] = [] + for index, replacement_seed in enumerate(replacement_roots): + package = root / f"replacement-{index}" + create_replacement_root_holder( + package, + branch_authorities[0], + _test_password_reader(f"multi-head-password-{index}".encode()), + ) + # Bind the CLI intent to deterministic test keys so direct holder + # shares below can prove the same exact multi-head artifact. + descriptor = json.loads((package / "descriptor.json").read_bytes()) + descriptor["key"] = signing_descriptor(replacement_seed) + path = root / f"descriptor-{index}.json" + path.write_bytes(canonical_bytes(descriptor)) + path.chmod(0o600) + descriptors.append(path) + intent_path = root / "intent.json" + result = self._invoke_rebirth_cli( + [ + "create-recovery-intent", + "--authority", + str(authority_paths[0]), + "--known-authority", + str(authority_paths[1]), + *( + item + for path in descriptors + for item in ("--holder-descriptor", str(path)) + ), + "--threshold", + "2", + "--output", + str(intent_path), + ] + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + intent = json.loads(intent_path.read_bytes()) + self.assertEqual( + intent["body"]["known_control_heads"], + sorted(state.head for state in branch_states), + ) + self.assertEqual( + intent["body"]["prepared_recovery"]["body"]["revoked_embodiments"], + sorted(row["embodiment_id"] for row in self.manifest.value["embodiments"]), + ) + recovery_seeds = [seed("recovery-a"), seed("recovery-b")] + shares = [ + create_distributed_recovery_share( + intent, + branch_authorities[0], + value, + observed_at_ms=intent["body"]["created_at_ms"], + known_states=branch_states, + known_manifests=[ + authority.manifest for authority in branch_authorities + ], + ) + for value in [*recovery_seeds, *replacement_roots[:2]] + ] + recovery = aggregate_distributed_recovery( + intent, + branch_authorities[0], + shares, + observed_at_ms=intent["body"]["created_at_ms"], + known_states=branch_states, + known_manifests=[authority.manifest for authority in branch_authorities], + ) + request_base = recovery_request_base(branch_authorities[0], recovery) + self.assertEqual(request_base.state.generation, 1) + self.assertIn("embodiment:retired-2", request_base.state.revocations) + signing_seed = seed("multi-head-target-signing") + transport_seed = seed("multi-head-target-transport") + credential = create_embodiment_credential( + request_base.state, + replacement_roots[:2], + signing_seed, + x25519_public(seed("multi-head-target-encryption")), + embodiment_id="embodiment:multi-head-target", + body_ref="cluster:multi-head-target:compaii", + purposes=["dm.we", "messages"], + valid_from_ms=NOW, + valid_until_ms=NOW + 100_000, + transport_principals=[ + { + "scheme": "dm-peer-v1", + "principal_id": "compaii@multi-head-target", + "key": key_descriptor("Ed25519", ed25519_public(transport_seed)), + } + ], + ) + incarnation = create_incarnation_authorization( + credential, + signing_seed, + incarnation_id="incarnation:multi-head-target:0", + incarnation_sequence=0, + started_at_ms=NOW, + ) + manifest = BeingManifest.from_value( + { + "schema": "being-manifest/v2", + "being_ref": self.state.being_ref, + "control_head": request_base.state.head, + "history_binding_id": self.manifest.value["history_binding_id"], + "revision": self.manifest.value["revision"] + 1, + "embodiments": [ + { + "body_ref": "cluster:multi-head-target:compaii", + "embodiment_credential_id": credential["artifact_id"], + "embodiment_id": "embodiment:multi-head-target", + "incarnation_authorization_id": incarnation["artifact_id"], + "incarnation_id": "incarnation:multi-head-target:0", + "status": "active", + } + ], + } + ) + successor = RootAuthority( + manifest, + request_base.state, + {credential["artifact_id"]: credential}, + {incarnation["artifact_id"]: incarnation}, + ) + transition = create_recovery_rebirth( + branch_authorities[0].manifest, + successor.manifest, + recovery_artifact=recovery, + body_ref="cluster:multi-head-target:compaii", + embodiment_id="embodiment:multi-head-target", + incarnation_id="incarnation:multi-head-target:0", + embodiment_credential_id=credential["artifact_id"], + incarnation_authorization_id=incarnation["artifact_id"], + principal_id="compaii@multi-head-target", + root_seeds=replacement_roots[:2], + issued_at_ms=NOW + 1, + ) + verify_recovery_rebirth(transition, branch_authorities[0], successor, recovery) + RootHistoryAuthority(successor, [branch_authorities[0]], [transition]) + def test_distributed_custody_cli_isolates_every_holder_and_keyless_aggregator( self, ) -> None: @@ -1257,13 +1461,25 @@ def test_distributed_custody_cli_isolates_every_holder_and_keyless_aggregator( for index, (password, recovery_seed) in enumerate( zip(recovery_passwords, recovery_seeds, strict=True) ): - path = root / f"recovery-holder-{index}.json" + path = root / f"recovery-holder-{index}" + path.mkdir(mode=0o700) EncryptedKeystore.create( - path, - lambda value=password: bytearray(value), - control_head=self.state.head, - secrets={"recovery.signing.v1:holder": recovery_seed}, + path / "holder.json", + _test_password_reader(password), + control_head=PENDING_CONTROL_HEAD, + secrets={"genesis.recovery.v1:holder": recovery_seed}, ) + descriptor = path / "descriptor.json" + descriptor.write_bytes( + canonical_bytes( + { + "schema": "dm.operator.genesis-holder/v1", + "role": "recovery", + "key": signing_descriptor(recovery_seed), + } + ) + ) + descriptor.chmod(0o600) recovery_holders.append(path) root_passwords = [ @@ -1272,19 +1488,17 @@ def test_distributed_custody_cli_isolates_every_holder_and_keyless_aggregator( root_holders: list[Path] = [] descriptors: list[Path] = [] for index, password in enumerate(root_passwords): - holder = root / f"root-holder-{index}.json" - descriptor = root / f"root-holder-{index}-descriptor.json" + holder = root / f"root-holder-{index}" + descriptor = holder / "descriptor.json" result = self._invoke_rebirth_cli( [ "create-replacement-root-holder", "--authority", str(authority_path), - "--holder", - str(holder), "--password-fd", "{password}", "--output", - str(descriptor), + str(holder), ], {"{password}": password}, ) diff --git a/tests/test_operator_genesis.py b/tests/test_operator_genesis.py new file mode 100644 index 0000000..9ece4e9 --- /dev/null +++ b/tests/test_operator_genesis.py @@ -0,0 +1,259 @@ +from __future__ import annotations + +import json +import os +import subprocess +import sys +from collections.abc import Sequence +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest import TestCase + +from daimon_matrix.canonical import canonical_bytes +from daimon_matrix.keystore import EncryptedKeystore +from daimon_matrix.operator_genesis import ( + PENDING_CONTROL_HEAD, + GenesisError, + aggregate_intent, + create_holder_package, +) +from daimon_matrix.operator_rebirth import create_replacement_root_holder +from tests.test_dm022_ledger import RootLedgerFixture + + +def _reader(value: bytes): # type: ignore[no-untyped-def] + return lambda: bytearray(value) + + +class DistributedGenesisTests(TestCase): + def setUp(self) -> None: + self.temporary = TemporaryDirectory(prefix="dm-genesis-test-") + self.root = Path(self.temporary.name) + self.root.chmod(0o700) + + def tearDown(self) -> None: + self.temporary.cleanup() + + def _invoke( + self, arguments: Sequence[str], password: bytes | None = None + ) -> subprocess.CompletedProcess[bytes]: + descriptors: tuple[int, ...] = () + values = list(arguments) + reader = -1 + if password is not None: + reader, writer = os.pipe() + os.write(writer, password) + os.close(writer) + descriptors = (reader,) + values = [ + str(reader) if value == "{password}" else value for value in values + ] + try: + return subprocess.run( + [sys.executable, "-m", "daimon_matrix.operator_genesis", *values], + cwd=Path(__file__).resolve().parents[1], + env={ + **os.environ, + "PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src"), + }, + pass_fds=descriptors, + capture_output=True, + check=False, + ) + finally: + if reader >= 0: + os.close(reader) + + def test_cli_uses_six_isolated_holder_processes_and_keyless_aggregation( + self, + ) -> None: + holders: list[tuple[Path, bytes]] = [] + descriptors: list[Path] = [] + for role in ("root", "recovery"): + for index in range(3): + package = self.root / f"{role}-{index}" + password = f"genesis-{role}-{index}-password".encode() + result = self._invoke( + [ + "create-holder", + "--role", + role, + "--password-fd", + "{password}", + "--output", + str(package), + ], + password, + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + holders.append((package, password)) + descriptors.append(package / "descriptor.json") + contents = EncryptedKeystore(package / "holder.json").open( + _reader(password), + minimum_counter=1, + required_control_head=PENDING_CONTROL_HEAD, + ) + self.assertEqual(len(contents.secrets), 1) + + intent_path = self.root / "intent.json" + result = self._invoke( + [ + "create-intent", + *(item for path in descriptors for item in ("--descriptor", str(path))), + "--root-threshold", + "2", + "--recovery-threshold", + "2", + "--output", + str(intent_path), + ] + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + + share_paths: list[Path] = [] + for index in (0, 1, 3, 4): + package, password = holders[index] + share = self.root / f"share-{index}.json" + result = self._invoke( + [ + "sign", + "--intent", + str(intent_path), + "--holder", + str(package), + "--password-fd", + "{password}", + "--output", + str(share), + ], + password, + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + share_paths.append(share) + + genesis_path = self.root / "genesis.json" + result = self._invoke( + [ + "aggregate", + "--intent", + str(intent_path), + *(item for path in share_paths for item in ("--share", str(path))), + "--output", + str(genesis_path), + ] + ) + self.assertEqual(result.returncode, 0, result.stderr.decode()) + public = b"".join( + path.read_bytes() + for path in [intent_path, *descriptors, *share_paths, genesis_path] + ) + for package, _password in holders: + self.assertNotIn((package / "holder.json").read_bytes(), public) + + intent = json.loads(intent_path.read_bytes()) + shares = [json.loads(path.read_bytes()) for path in share_paths] + with self.assertRaisesRegex(GenesisError, "threshold_rejected"): + aggregate_intent(intent, shares[:1] + shares[2:3]) + with self.assertRaisesRegex(GenesisError, "threshold_rejected"): + aggregate_intent(intent, [shares[0], shares[0], shares[2], shares[3]]) + + def test_holder_package_is_atomic_across_sigkill_at_commit(self) -> None: + target = self.root / "recovery-holder" + password = b"atomic-genesis-holder-password" + result = subprocess.run( + [ + sys.executable, + "-c", + """ +import os +import sys +from pathlib import Path +from unittest import mock +from daimon_matrix.operator_genesis import create_holder_package +with mock.patch( + "daimon_matrix.operator_genesis.os.replace", + side_effect=lambda *_args: os.kill(os.getpid(), 9), +): + create_holder_package( + Path(sys.argv[1]), "recovery", lambda: bytearray.fromhex(sys.argv[2]) + ) +""", + str(target), + password.hex(), + ], + cwd=Path(__file__).resolve().parents[1], + env={ + **os.environ, + "PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src"), + }, + capture_output=True, + check=False, + ) + self.assertEqual(result.returncode, -9) + self.assertFalse(target.exists()) + receipt = create_holder_package(target, "recovery", _reader(password)) + self.assertEqual(json.loads((target / "descriptor.json").read_bytes()), receipt) + + +class ReplacementHolderAtomicityTests(RootLedgerFixture): + def test_replacement_holder_package_retries_after_commit_kill(self) -> None: + target = self.root_path / "replacement-holder" + password = b"atomic-replacement-holder-password" + authority_path = self.root_path / "authority.json" + authority_path.write_bytes( + canonical_bytes( + { + "schema": "dm.operator.authority/v1", + "control_artifacts": [self.genesis], + "control_head": self.state.head, + "manifest": self.manifest.value, + "credentials": list(self.credentials.values()), + "incarnations": list(self.incarnations.values()), + } + ) + ) + authority_path.chmod(0o600) + result = subprocess.run( + [ + sys.executable, + "-c", + """ +import json +import os +import sys +from pathlib import Path +from unittest import mock +from daimon_matrix.operator_rebirth import ( + authority_from_document, + create_replacement_root_holder, +) +authority = authority_from_document(json.loads(Path(sys.argv[2]).read_bytes())) +with mock.patch( + "daimon_matrix.operator_rebirth.os.replace", + side_effect=lambda *_args: os.kill(os.getpid(), 9), +): + create_replacement_root_holder( + Path(sys.argv[1]), authority, lambda: bytearray.fromhex(sys.argv[3]) + ) +""", + str(target), + str(authority_path), + password.hex(), + ], + cwd=Path(__file__).resolve().parents[1], + env={ + **os.environ, + "PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src"), + }, + capture_output=True, + check=False, + ) + self.assertEqual(result.returncode, -9) + self.assertFalse(target.exists()) + receipt = create_replacement_root_holder( + target, self.authority, _reader(password) + ) + self.assertEqual( + canonical_bytes(json.loads((target / "descriptor.json").read_bytes())), + canonical_bytes(receipt), + ) diff --git a/tests/test_package_scaffold.py b/tests/test_package_scaffold.py index 1c902aa..124ce6d 100644 --- a/tests/test_package_scaffold.py +++ b/tests/test_package_scaffold.py @@ -46,7 +46,7 @@ def test_public_metadata_and_runtime_dependency_boundary(self) -> None: project["scripts"], { "daimon": "daimon_matrix.cli:main", - "daimon-bootstrap": "daimon_matrix.operator_bootstrap:main", + "daimon-genesis": "daimon_matrix.operator_genesis:main", "daimon-codex-body": "daimon_matrix.codex_body:main", "daimon-conformance": "daimon_matrix.conformance:main", "daimon-curator-worker": "daimon_matrix.curator_worker_process:main", @@ -56,6 +56,7 @@ def test_public_metadata_and_runtime_dependency_boundary(self) -> None: "daimon-rebirth": "daimon_matrix.operator_rebirth:main", "daimon-reviewer": "daimon_matrix.reviewer_cli:main", "daimon-synthetic-birth": "daimon_matrix.synthetic_birth:main", + "daimon-synthetic-bootstrap": ("daimon_matrix.operator_bootstrap:main"), "daimon-synthetic-multihost": ( "daimon_matrix.synthetic_multihost:main" ), @@ -179,6 +180,7 @@ def test_allowlists_are_frozen(self) -> None: "src/daimon_matrix/local_we.py", "src/daimon_matrix/mcp_server.py", "src/daimon_matrix/operator_bootstrap.py", + "src/daimon_matrix/operator_genesis.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -234,6 +236,7 @@ def test_allowlists_are_frozen(self) -> None: "daimon_matrix/local_we.py", "daimon_matrix/mcp_server.py", "daimon_matrix/operator_bootstrap.py", + "daimon_matrix/operator_genesis.py", "daimon_matrix/operator_rebirth.py", "daimon_matrix/memory_policy.py", "daimon_matrix/memory_projection.py", diff --git a/tools/check_distribution.py b/tools/check_distribution.py index b606518..8312c48 100644 --- a/tools/check_distribution.py +++ b/tools/check_distribution.py @@ -59,6 +59,7 @@ "src/daimon_matrix/local_we.py", "src/daimon_matrix/mcp_server.py", "src/daimon_matrix/operator_bootstrap.py", + "src/daimon_matrix/operator_genesis.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -113,6 +114,7 @@ "daimon_matrix/local_we.py", "daimon_matrix/mcp_server.py", "daimon_matrix/operator_bootstrap.py", + "daimon_matrix/operator_genesis.py", "daimon_matrix/operator_rebirth.py", "daimon_matrix/memory_policy.py", "daimon_matrix/memory_projection.py", @@ -323,6 +325,7 @@ def inspect_sdist(path: Path, source_root: Path) -> dict[str, object]: "src/daimon_matrix/local_we.py", "src/daimon_matrix/mcp_server.py", "src/daimon_matrix/operator_bootstrap.py", + "src/daimon_matrix/operator_genesis.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -396,16 +399,17 @@ def inspect_wheel(path: Path, source_root: Path) -> dict[str, object]: if files[f"{DIST_INFO}/entry_points.txt"] != ( b"[console_scripts]\n" b"daimon = daimon_matrix.cli:main\n" - b"daimon-bootstrap = daimon_matrix.operator_bootstrap:main\n" b"daimon-codex-body = daimon_matrix.codex_body:main\n" b"daimon-conformance = daimon_matrix.conformance:main\n" b"daimon-curator-worker = daimon_matrix.curator_worker_process:main\n" + b"daimon-genesis = daimon_matrix.operator_genesis:main\n" b"daimon-hermes-body = daimon_matrix.hermes_body:main\n" b"daimon-matrixd = daimon_matrix.daemon:main\n" b"daimon-mcp = daimon_matrix.mcp_server:main\n" b"daimon-rebirth = daimon_matrix.operator_rebirth:main\n" b"daimon-reviewer = daimon_matrix.reviewer_cli:main\n" b"daimon-synthetic-birth = daimon_matrix.synthetic_birth:main\n" + b"daimon-synthetic-bootstrap = daimon_matrix.operator_bootstrap:main\n" b"daimon-synthetic-multihost = daimon_matrix.synthetic_multihost:main\n" b"daimon-synthetic-relationships = " b"daimon_matrix.synthetic_relationships:main\n" @@ -468,6 +472,7 @@ def inspect_wheel(path: Path, source_root: Path) -> dict[str, object]: "daimon_matrix/operator_bootstrap.py": ( "src/daimon_matrix/operator_bootstrap.py" ), + "daimon_matrix/operator_genesis.py": "src/daimon_matrix/operator_genesis.py", "daimon_matrix/operator_rebirth.py": ("src/daimon_matrix/operator_rebirth.py"), "daimon_matrix/memory_policy.py": "src/daimon_matrix/memory_policy.py", "daimon_matrix/memory_projection.py": ( diff --git a/tools/generate_dm021_vectors.py b/tools/generate_dm021_vectors.py index 296ca7d..c4b650b 100644 --- a/tools/generate_dm021_vectors.py +++ b/tools/generate_dm021_vectors.py @@ -17,13 +17,13 @@ from daimon_matrix.identity import ( # noqa: E402 create_binding_activation, create_embodiment_credential, - create_genesis, create_history_binding, create_incarnation_authorization, - create_recovery, create_recovery_policy_change, create_revocation, create_root_rotation, + create_synthetic_genesis_in_process, + create_synthetic_recovery_in_process, ed25519_public, key_descriptor, verify_genesis, @@ -56,7 +56,7 @@ def without_role(artifact: dict[str, Any], role: str) -> dict[str, Any]: def generate() -> None: root = [seed("root-a"), seed("root-b"), seed("root-c")] recovery = [seed("recovery-a"), seed("recovery-b"), seed("recovery-c")] - genesis = create_genesis( + genesis = create_synthetic_genesis_in_process( root, 2, recovery, @@ -127,7 +127,7 @@ def generate() -> None: verify_successor(branch_b, state), ] recovered_root = [seed("recovered-root-a"), seed("recovered-root-b")] - recovery_artifact = create_recovery( + recovery_artifact = create_synthetic_recovery_in_process( branch_states, recovery, recovered_root, diff --git a/tools/generate_dm078_recovery_vectors.py b/tools/generate_dm078_recovery_vectors.py index 03f2059..4795190 100644 --- a/tools/generate_dm078_recovery_vectors.py +++ b/tools/generate_dm078_recovery_vectors.py @@ -17,9 +17,12 @@ sys.path.insert(0, str(ROOT / "src")) from daimon_matrix.canonical import canonical_bytes # noqa: E402 -from daimon_matrix.identity import create_recovery, verify_genesis # noqa: E402 +from daimon_matrix.identity import ( # noqa: E402 + create_synthetic_recovery_in_process, + verify_genesis, +) from daimon_matrix.operator_rebirth import ( # noqa: E402 - authorize_recovery_enrollment_request, + authorize_synthetic_recovery_enrollment_in_process, create_enrollment_request, recovery_request_base, ) @@ -63,7 +66,7 @@ def generate(output: Path) -> None: seed("dm078-recovered-root-a"), seed("dm078-recovered-root-b"), ] - recovery = create_recovery( + recovery = create_synthetic_recovery_in_process( [state], [seed("recovery-a"), seed("recovery-b")], replacement_roots, @@ -83,7 +86,7 @@ def generate(output: Path) -> None: expires_at_ms=NOW + 60_010, nonce=seed("dm078-recovery-request-nonce"), ) - activation = authorize_recovery_enrollment_request( + activation = authorize_synthetic_recovery_enrollment_in_process( request, base, recovery, diff --git a/tools/reproducible_build.py b/tools/reproducible_build.py index 76cabd1..ea3f5be 100644 --- a/tools/reproducible_build.py +++ b/tools/reproducible_build.py @@ -53,6 +53,7 @@ Path("src/daimon_matrix/local_we.py"), Path("src/daimon_matrix/mcp_server.py"), Path("src/daimon_matrix/operator_bootstrap.py"), + Path("src/daimon_matrix/operator_genesis.py"), Path("src/daimon_matrix/operator_rebirth.py"), Path("src/daimon_matrix/memory_policy.py"), Path("src/daimon_matrix/memory_projection.py"), diff --git a/vectors/hermes/v1/index.json b/vectors/hermes/v1/index.json index a8e80df..b05c006 100644 --- a/vectors/hermes/v1/index.json +++ b/vectors/hermes/v1/index.json @@ -1 +1 @@ -{"files":[{"name":"negative/context-id-tampered.json","sha256":"542687c5286ba74d4157bca175ac6c98f591a9c73ad3eec90ecb30861a3a7c6c","valid":false},{"name":"negative/park-presence-not-relinquished.json","sha256":"08da5c7d7e4bcfe080ef3bc5a1ee785dd26f277902e63021ef03b139b3a279b5","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"7c8b6b6d196ba088fc3417d0191bd9e1200f207205874d5c5d2b85bb86eab541","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"e4318d28ae7bf72d0685eb48acf3244ba09e9f20d2d4ac069e3ab0fff5fd725b","valid":true},{"name":"valid/context.json","sha256":"ab55dbcd81211b009fc797095368e389ad24639b19578017fa8e5c334f8fcf3a","valid":true},{"name":"valid/current-memory-projection.json","sha256":"ab9b4d897da7fd12c89ec782486378884867bd2afbb8327f67b61c0a10bb6c55","valid":true},{"name":"valid/effect-receipt.json","sha256":"f5aa1b7d7a97a2c818b66d7a9b672e91b224b52b3203de419042f0a0927c7b46","valid":true},{"name":"valid/launch-receipt.json","sha256":"12a66944a02b875e0d6d4c6cd60650ec13886c8a21bab1a515907d9b5d9a78b1","valid":true},{"name":"valid/park-receipt.json","sha256":"764774f4b4993668ff4f8d8272b4dc1c0b468e5df38148c9d1e261fdbe03069c","valid":true},{"name":"valid/park-request.json","sha256":"1c1c1b06c162371edfaaae7bd5562f9e5acb2853efbc12ccdd523ac2431ea2cf","valid":true},{"name":"valid/plan.json","sha256":"d711657a4fa972cfbdf83ddcb78b30c0703dd0674697641d7dd3239efc549878","valid":true},{"name":"valid/profile-manifest.json","sha256":"2962c9f8f2613abbce671582fbd7513dc64bb98d3aba7048a20f35566c06da0f","valid":true},{"name":"valid/provider-config.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5","valid":true},{"name":"valid/provider-ready.json","sha256":"d9f9684474222e2339e8da122570bd8fb4266f81ff5499952049b64d48f7cc5e","valid":true},{"name":"valid/runtime-active.json","sha256":"12c9f5e7516adf8aa58bb7ef90c3965db557612d50b1618e252fe13f629c4b8b","valid":true},{"name":"valid/runtime-parking.json","sha256":"bb2adcfef7a7b31ba3c2b79a62369c20ab6ab612e1d0b6bc33a88c59685ccba9","valid":true},{"name":"valid/runtime-starting.json","sha256":"52f448d212d230cda8dce0f88b946592c5ccbe5a462046f0a7f39a3dc4e8ffb6","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_version":"0.19.0","schema":"dm.hermes-body.vector-index/v1"} +{"files":[{"name":"negative/context-id-tampered.json","sha256":"542687c5286ba74d4157bca175ac6c98f591a9c73ad3eec90ecb30861a3a7c6c","valid":false},{"name":"negative/park-presence-not-relinquished.json","sha256":"08da5c7d7e4bcfe080ef3bc5a1ee785dd26f277902e63021ef03b139b3a279b5","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"7c8b6b6d196ba088fc3417d0191bd9e1200f207205874d5c5d2b85bb86eab541","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"e4318d28ae7bf72d0685eb48acf3244ba09e9f20d2d4ac069e3ab0fff5fd725b","valid":true},{"name":"valid/context.json","sha256":"ab55dbcd81211b009fc797095368e389ad24639b19578017fa8e5c334f8fcf3a","valid":true},{"name":"valid/current-memory-projection.json","sha256":"ab9b4d897da7fd12c89ec782486378884867bd2afbb8327f67b61c0a10bb6c55","valid":true},{"name":"valid/effect-receipt.json","sha256":"f5aa1b7d7a97a2c818b66d7a9b672e91b224b52b3203de419042f0a0927c7b46","valid":true},{"name":"valid/launch-receipt.json","sha256":"144c0d071db6b368c03ceb478c17738aa5ec1ceb2f2c82ca462fe7d4892b67fa","valid":true},{"name":"valid/park-receipt.json","sha256":"764774f4b4993668ff4f8d8272b4dc1c0b468e5df38148c9d1e261fdbe03069c","valid":true},{"name":"valid/park-request.json","sha256":"1c1c1b06c162371edfaaae7bd5562f9e5acb2853efbc12ccdd523ac2431ea2cf","valid":true},{"name":"valid/plan.json","sha256":"d711657a4fa972cfbdf83ddcb78b30c0703dd0674697641d7dd3239efc549878","valid":true},{"name":"valid/profile-manifest.json","sha256":"1bd027f2ad6bc6d94c2e9eaaf524d245f3136e6f9d43f19810af8766bc57a40a","valid":true},{"name":"valid/provider-config.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5","valid":true},{"name":"valid/provider-ready.json","sha256":"d9f9684474222e2339e8da122570bd8fb4266f81ff5499952049b64d48f7cc5e","valid":true},{"name":"valid/runtime-active.json","sha256":"12c9f5e7516adf8aa58bb7ef90c3965db557612d50b1618e252fe13f629c4b8b","valid":true},{"name":"valid/runtime-parking.json","sha256":"bb2adcfef7a7b31ba3c2b79a62369c20ab6ab612e1d0b6bc33a88c59685ccba9","valid":true},{"name":"valid/runtime-starting.json","sha256":"52f448d212d230cda8dce0f88b946592c5ccbe5a462046f0a7f39a3dc4e8ffb6","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_version":"0.19.0","schema":"dm.hermes-body.vector-index/v1"} diff --git a/vectors/hermes/v1/valid/launch-receipt.json b/vectors/hermes/v1/valid/launch-receipt.json index 48f83d1..8a2cee8 100644 --- a/vectors/hermes/v1/valid/launch-receipt.json +++ b/vectors/hermes/v1/valid/launch-receipt.json @@ -1 +1 @@ -{"active_handle_id":"dm:hermes-handle:v1:7qD764Q4xNYnguH0C2VESNXWHnCaL-XjR1RM3gdZ_rg","deployment":"synthetic-isolated","hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_session_id":"hermes-session-vector","hermes_version":"0.19.0","launch_receipt_id":"dm:hermes-launch:v1:LMNaiUi14LYi1kLjXZJNwqCt0rYB2to5BY3mhHXUz7c","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"aa8563b4bcb2a000d044419e620a91b8d153620cfd571f8d1a466f1bd1455083"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"28a91fa173eafb5309d9143ce22574c5dbe7fddf5b1fcf5fcf4a9b65f76daf29"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"9d5f2839096289dc4648afa0ba75d8f00b8ad611fd53601d9bb56302c7bceb4a"},"plan_id":"dm:hermes-plan:v1:O3YR1x4fwBrTHF-QO-fbGii0f_D0O-kSzsZbMcuL0vM","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:UHiM5_UrowyOAwjwZxkOXdsvbLGRjHVSRZN69JTU0O4","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:L9ZIK64wzJ-zg-l9QVw_b1btW7o6SOxOhQL_6TET0rM"} +{"active_handle_id":"dm:hermes-handle:v1:7qD764Q4xNYnguH0C2VESNXWHnCaL-XjR1RM3gdZ_rg","deployment":"synthetic-isolated","hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_session_id":"hermes-session-vector","hermes_version":"0.19.0","launch_receipt_id":"dm:hermes-launch:v1:rLT4DEope0V-k0VVaVoANf34BiObXco_44sldv03Zfw","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"9e0375eafa706951ec43ba58cb07afed8ece8498a388d969ac0f7c8f5eff8f1f"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"bedcfb9cbadc1a645ef280e62c7c47f320e48a0eff3cc817d6683cfdd6565f1e"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"8aace4c62039afe98ebf73820c3d0e6ff958c42a9cf02183158f703958ad399c"},{"name":"operator_genesis.py","sha256":"bc591260d7e53cbd61676a5b89893f9cb954606ba77239e5bcafda8e0548098c"},{"name":"operator_rebirth.py","sha256":"3e332e8bbf52834804e4ba28a0eb9a76b5bffb51587fe6f1aaa3bd6fd4a39537"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"abd89a0239f9b3694c644da64adb62579f5ba4773540d70c43de480fb20a865a"},{"name":"synthetic_multihost.py","sha256":"35eb14ed3c38d6cb1cf589d3156c559221f9934997665218a0aa39a2358aafd9"},{"name":"synthetic_relationships.py","sha256":"96a1f7aa96b43de2d0216ce179e4daf363ecc05371dba17d62e6aaf3c45b4c5b"},{"name":"synthetic_sources.py","sha256":"1fd93c61198ead8e3ea75da137e7e3813befbfc861557d333dd28a0aed5db8a1"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"6d0c6681eb4d748595adbcceb9aaa9e75fe28722b0c7c96c0d1e711e445cb59f"},"plan_id":"dm:hermes-plan:v1:O3YR1x4fwBrTHF-QO-fbGii0f_D0O-kSzsZbMcuL0vM","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:UHiM5_UrowyOAwjwZxkOXdsvbLGRjHVSRZN69JTU0O4","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:L9ZIK64wzJ-zg-l9QVw_b1btW7o6SOxOhQL_6TET0rM"} diff --git a/vectors/hermes/v1/valid/profile-manifest.json b/vectors/hermes/v1/valid/profile-manifest.json index c47feeb..9cc877f 100644 --- a/vectors/hermes/v1/valid/profile-manifest.json +++ b/vectors/hermes/v1/valid/profile-manifest.json @@ -1 +1 @@ -{"adapter_version":"1.0.0","being_ref":"dm:being:v1:c020x2AEpKTIXF0hnAal0Q64Ia--MtxTfGGk_e4itVQ","body_ref":"dm:body:v1:B0iU3se3iyz0g6tPShEM8_Z-oX7BkspNY4KGQzFQEn0","embodiment_id":"dm:embodiment:v1:c6daXNxOWxfE9qM-j85N8YbFN35Q6Kl7g6AYGf7bbZE","files":[{"name":"SOUL.md","sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a"},{"name":"config.yaml","sha256":"47a67b881a4530c67c8d609d7bf3f758717acb9a266a5a55fc65049ae4ee38ab"},{"name":"plugins/daimon-matrix/__init__.py","sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5"},{"name":"plugins/daimon-matrix/matrix.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_version":"0.19.0","incarnation_id":"dm:incarnation:v1:VMS4o9jcMXzf6bmAuMHSsZUMK_g_W9N46SK-LLD_7G8","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"c785d8e3c2d41d0cd66b5893e4f6deb82c1cee06e7b718ce03780f97cb17688b"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"aa8563b4bcb2a000d044419e620a91b8d153620cfd571f8d1a466f1bd1455083"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"23d0b19cb566df0e9a337c791fbde1fc74c75c6a21e3124c1c12ed0014532e9f"},{"name":"operator_rebirth.py","sha256":"28a91fa173eafb5309d9143ce22574c5dbe7fddf5b1fcf5fcf4a9b65f76daf29"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"5035bc0d57d8c927b774b2792575477ae9ecc426a2518e69def3ab2d0eda9ac4"},{"name":"synthetic_multihost.py","sha256":"c41ea0920de7e03d82e510045ec52be8b2a3df493087fe55ef5eaceafb7e91e1"},{"name":"synthetic_relationships.py","sha256":"393fd27a49a7a996ef9e433bf1472cfd9746a08336dc1e22882ce675e878d8ee"},{"name":"synthetic_sources.py","sha256":"d9090c4adf0189b023226357b5c544e461d674e86db7050057cfbeec37ec0c1d"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"9d5f2839096289dc4648afa0ba75d8f00b8ad611fd53601d9bb56302c7bceb4a"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"3b7611d71e1fc01ad31c5f903be7db1a28b47ff0f43be912cec65b31cb8bd2f3","profile_id":"dm:hermes-profile:v1:ZQwyiKcM6ToEothNwp0MIivicGw-F5LEampTCIHGWxg","schema":"dm.hermes-body.profile-manifest/v1","workspace_ref":"dm:workspace:v1:1g9p07RgFRlBvtWJcvb92rOP7KNe9mB8v3TeueVtERM"} +{"adapter_version":"1.0.0","being_ref":"dm:being:v1:c020x2AEpKTIXF0hnAal0Q64Ia--MtxTfGGk_e4itVQ","body_ref":"dm:body:v1:B0iU3se3iyz0g6tPShEM8_Z-oX7BkspNY4KGQzFQEn0","embodiment_id":"dm:embodiment:v1:c6daXNxOWxfE9qM-j85N8YbFN35Q6Kl7g6AYGf7bbZE","files":[{"name":"SOUL.md","sha256":"0fc541f40cedc23056c07011a9eb99138028968930074c0a0a88db37eb81f25a"},{"name":"config.yaml","sha256":"47a67b881a4530c67c8d609d7bf3f758717acb9a266a5a55fc65049ae4ee38ab"},{"name":"plugins/daimon-matrix/__init__.py","sha256":"c6ef73ae9adedf45a5bd628ca745c1ff958a961f4b8ae0125b134ecddf6d88f5"},{"name":"plugins/daimon-matrix/matrix.json","sha256":"3977a77b68b6d2d70f41540e00529cf88216da21654616e8bd8882bd49493dd5"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"0db1912911fafa384aa5ee0145929658a9d1dd33","hermes_python":{"executable_sha256":"a2fdb31eaf46a78ba3f0687f59b4c5a3bc318f7cd56dbbe6f68272c4af34cc71","implementation":"cpython","supported_interval":">=3.11,<3.14","version":"3.13.5"},"hermes_version":"0.19.0","incarnation_id":"dm:incarnation:v1:VMS4o9jcMXzf6bmAuMHSsZUMK_g_W9N46SK-LLD_7G8","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"975cc4fc1bc2628e42c1e24c972fbb458f7522378ba8c3cc720b248bfdf52de5"},{"name":"authority_epochs.py","sha256":"9e0375eafa706951ec43ba58cb07afed8ece8498a388d969ac0f7c8f5eff8f1f"},{"name":"birth.py","sha256":"8407f496592b0b84e5caf78b7384c379d41d40ce78ed61351be4e0dc02741749"},{"name":"canonical.py","sha256":"1abee4501a0621cf981cc247ac21ab825e278dc0a71ed98c2e57cfdc91efbd47"},{"name":"cli.py","sha256":"c2f0d634c2faa1619f280a76e714dbe963300705dd48630c2fa14dfd090bb603"},{"name":"client.py","sha256":"1fd3a27158035280a1ef37ed094305392a580896f21ce665a9d8c38f91148821"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"76eb0b27cecd06d89a220f172042bd94250e3c79dfee3cdd25737e9801565fd4"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"be6f873c196ab325b512fce06403eeada67f2f1dc98c6f83930577a630cb88d9"},{"name":"curator.py","sha256":"ff9718bdc1577657f3029b71fd35bad701063a185acddb797f12b196a04739ce"},{"name":"curator_worker.py","sha256":"3f717165540cb209c3545cab95bf846803a454c8bef73b3067904b65a4959edb"},{"name":"curator_worker_process.py","sha256":"bdbd6296072b27d1b42222924e5e0af95c5c1ac8d01cfa9c59fdad57fa2d5a2d"},{"name":"daemon.py","sha256":"b1938dfb5a44f3555607a36eb933c1f4f2dab0c8130e063de605630a852b4988"},{"name":"hermes_body.py","sha256":"9b32654d3093d3c1b7a33d3fd932a61fd0d21b32fa28a7c843c4e3f41605a8c9"},{"name":"human_review.py","sha256":"40a1ff0a6da8278c60383ceda0b2e1dfaf90d60929f08da77f3926d8ecd9169f"},{"name":"identity.py","sha256":"bedcfb9cbadc1a645ef280e62c7c47f320e48a0eff3cc817d6683cfdd6565f1e"},{"name":"keystore.py","sha256":"d9f9bebc247f4d613d04bece26f20ef877c375454d5aaabafd038ecfd89698a4"},{"name":"ledger.py","sha256":"9673f7e12d3d35ed6ec8043ddf9f729c6f20153f795e748e5e3f3670d68b1e96"},{"name":"local_api.py","sha256":"3f4a179815ecf62b6d60e5825eea836d511daa2371ebe25bfb3abdd40787d55b"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"1ba204a3b1c865b579f761e5e6792e5d80529983ad55106cb1c1a0b3b6bc48b4"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"37c824e30469aeebd8dcd0269c4249715a16ee5afa05c2b9d4a74b8e26e68664"},{"name":"operator_bootstrap.py","sha256":"8aace4c62039afe98ebf73820c3d0e6ff958c42a9cf02183158f703958ad399c"},{"name":"operator_genesis.py","sha256":"bc591260d7e53cbd61676a5b89893f9cb954606ba77239e5bcafda8e0548098c"},{"name":"operator_rebirth.py","sha256":"3e332e8bbf52834804e4ba28a0eb9a76b5bffb51587fe6f1aaa3bd6fd4a39537"},{"name":"peer_transport.py","sha256":"3c86895783e908165508158c17bc5d474b2effcc98c08c8d629aaeaafb2c11a4"},{"name":"projections.py","sha256":"dd31294f4d6dc2ff350ca05f57ed79d7f24f7699d903a9fc8d00cf9932f254b1"},{"name":"publication.py","sha256":"31d0623f4637d280f3f8b6f65fae6ffa961e592004b418d3cae7f4ca5eba0459"},{"name":"relationship_store.py","sha256":"08afda06469d7c89710de115095b6b06e094baa79db6e7f818626c9c76e4abd4"},{"name":"relationships.py","sha256":"d572abd90da60e0beb301fb34af11bdbfac52ecc13605aeb00bfe4ac368092e0"},{"name":"reviewer_cli.py","sha256":"abf92a9f43a8811a36affad7182de085736cb9bde4f55da78fbc064236ff1bc3"},{"name":"routes.py","sha256":"04d5b6b8ba44499d57689f9e97f520a2e309d86532cb4b658a8e168099fc7799"},{"name":"runtime.py","sha256":"6aaf2041af4ef842f8ca303ce93f9226dc149b4d49009afddaa94a735362aaf3"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"be549bce2055a86a8ef06f32c71e54c0dd6fb820a932284a6b5d6a064748401e"},{"name":"sources.py","sha256":"803aaba82c14f6d5d39e10a0e2e583da0641295ab8e910bc29a39a717aa92c38"},{"name":"species.py","sha256":"824978695997e1384614e0e77239eaae63baed1a64a502b29a414492551a8d40"},{"name":"species_runner.py","sha256":"3312dda2c13548503863698482bdd7c6d585415f445cbad765cce41a38ebdfc1"},{"name":"sync.py","sha256":"5836e80d6ea68cde799eb3bd66b4b3b3437db90ce55f8c7d77d7944b728585b2"},{"name":"synthetic_birth.py","sha256":"abd89a0239f9b3694c644da64adb62579f5ba4773540d70c43de480fb20a865a"},{"name":"synthetic_multihost.py","sha256":"35eb14ed3c38d6cb1cf589d3156c559221f9934997665218a0aa39a2358aafd9"},{"name":"synthetic_relationships.py","sha256":"96a1f7aa96b43de2d0216ce179e4daf363ecc05371dba17d62e6aaf3c45b4c5b"},{"name":"synthetic_sources.py","sha256":"1fd93c61198ead8e3ea75da137e7e3813befbfc861557d333dd28a0aed5db8a1"},{"name":"synthetic_species.py","sha256":"4a839e42820426ea3af33ef9f96551d51159e1b48dbb692259b6d521227bf256"},{"name":"weave.py","sha256":"68980eff472720dd3b3e2cdd01ea2381ded7b0b142f443fdcf85e24e81da7325"}],"tree_sha256":"6d0c6681eb4d748595adbcceb9aaa9e75fe28722b0c7c96c0d1e711e445cb59f"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"3b7611d71e1fc01ad31c5f903be7db1a28b47ff0f43be912cec65b31cb8bd2f3","profile_id":"dm:hermes-profile:v1:EY4rzO6iNGZ1f6VfSlNVcHiMH9EwKdvfZi1j82EhTJ0","schema":"dm.hermes-body.profile-manifest/v1","workspace_ref":"dm:workspace:v1:1g9p07RgFRlBvtWJcvb92rOP7KNe9mB8v3TeueVtERM"} diff --git a/vectors/identity/v1/negative/recovery-omits-recovery-threshold.json b/vectors/identity/v1/negative/recovery-omits-recovery-threshold.json index d43975d..82c0970 100644 --- a/vectors/identity/v1/negative/recovery-omits-recovery-threshold.json +++ b/vectors/identity/v1/negative/recovery-omits-recovery-threshold.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:rVsLg-tRBBdVU8E4QeATWuGhx17ltvEIJVkzhp_khFQ","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:TLUD14jYDxVuCWtcH71M3vbC3moGw-oDXJV9dTmC_rU","dm:identity:v1:YEfv-O6Mp7sdz9OGtyL1rxFOgQUjjBvt7TGDhm-5C2U"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","public":"bN0btUx-yepJlYqoiUu__gG26yKK0A2Xenjozdz_F40"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","public":"WVC7B8XEEmWM99Fx5aHpCKrXwn6lv_hqE8KZvBuJV-0"}],"threshold":2},"revoked_embodiments":["embodiment:vector:compromised"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","role":"new-root-possession","value":"RT1PYS6mofLZPPTJhuTCJNyGRrXBaNBvQdnlJcigLDMKluLglYFlNHknLS50C2Ijn0sprOMkbvko9KhnyWeTCg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","role":"new-root-possession","value":"z_EagZv9vH2-007BqHMaEoTizAxZgUqgnKv6WWwUDCLJO_D0AaEEHU_cEKgetJTp9yNqLny6OnTyeyM64dQOCw"}]} +{"artifact_id":"dm:identity:v1:dR0vK5t4-u2JM1mdJcuGGNHQsI_v7w5BdhRxXTTRrMg","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:TLUD14jYDxVuCWtcH71M3vbC3moGw-oDXJV9dTmC_rU","dm:identity:v1:YEfv-O6Mp7sdz9OGtyL1rxFOgQUjjBvt7TGDhm-5C2U"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","public":"bN0btUx-yepJlYqoiUu__gG26yKK0A2Xenjozdz_F40"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","public":"WVC7B8XEEmWM99Fx5aHpCKrXwn6lv_hqE8KZvBuJV-0"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:compromised"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","role":"new-root-possession","value":"aDsCH20tBth9zgFolvsPFO_kd328QxOAlRv4ovwd9z0hxFULmyqkQ8KwjHxeGuKR9fh7U8hlPJawfN-ALPYpCQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","role":"new-root-possession","value":"pyqXxQdusfvReYfXiGFz7ZkV2aOrniAJis4ugEsXt_F_s4S_fttTdqyJDaOMCKOLNkx9dmY5B5cFMIdpOyBzDg"}]} diff --git a/vectors/identity/v1/valid/recovery.json b/vectors/identity/v1/valid/recovery.json index 4468b5c..c7e28a9 100644 --- a/vectors/identity/v1/valid/recovery.json +++ b/vectors/identity/v1/valid/recovery.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:rVsLg-tRBBdVU8E4QeATWuGhx17ltvEIJVkzhp_khFQ","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:TLUD14jYDxVuCWtcH71M3vbC3moGw-oDXJV9dTmC_rU","dm:identity:v1:YEfv-O6Mp7sdz9OGtyL1rxFOgQUjjBvt7TGDhm-5C2U"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","public":"bN0btUx-yepJlYqoiUu__gG26yKK0A2Xenjozdz_F40"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","public":"WVC7B8XEEmWM99Fx5aHpCKrXwn6lv_hqE8KZvBuJV-0"}],"threshold":2},"revoked_embodiments":["embodiment:vector:compromised"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:2BAnM77TgiYBJBEJFH3VMvOR4xaNgBdt-QfNVdp2sxg","role":"recovery-authorization","value":"iUq4HYH_MUuGtw5BzYKcX3XPFwJJAf4LP8ttS9WITvbENoeSpTihdOyEfcfhqiiPMyqBU-XTmYxvt2VilhaQCA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"fgJK2RBRq5v7pDWuBbBMKUuW9H-t9YhoQ8NR9eWfbDldluCS4sF_Ub3CpwqREcofGjLRkijKCI5x8Yk3pWucDg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","role":"new-root-possession","value":"RT1PYS6mofLZPPTJhuTCJNyGRrXBaNBvQdnlJcigLDMKluLglYFlNHknLS50C2Ijn0sprOMkbvko9KhnyWeTCg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"Sj8xSs42CUAiTbh6v_8WaOLa07ZskecuWMbRn5FXTSZ2ODw-gCqZjj9rqlGxYNVIBjXeh4pIy1Uti2hOB7grDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","role":"new-root-possession","value":"z_EagZv9vH2-007BqHMaEoTizAxZgUqgnKv6WWwUDCLJO_D0AaEEHU_cEKgetJTp9yNqLny6OnTyeyM64dQOCw"}]} +{"artifact_id":"dm:identity:v1:dR0vK5t4-u2JM1mdJcuGGNHQsI_v7w5BdhRxXTTRrMg","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:TLUD14jYDxVuCWtcH71M3vbC3moGw-oDXJV9dTmC_rU","dm:identity:v1:YEfv-O6Mp7sdz9OGtyL1rxFOgQUjjBvt7TGDhm-5C2U"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","public":"bN0btUx-yepJlYqoiUu__gG26yKK0A2Xenjozdz_F40"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","public":"WVC7B8XEEmWM99Fx5aHpCKrXwn6lv_hqE8KZvBuJV-0"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:compromised"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:2BAnM77TgiYBJBEJFH3VMvOR4xaNgBdt-QfNVdp2sxg","role":"recovery-authorization","value":"VhhcNqjaxiWCBRTcvHnW1QEXPwi4d_n0ELoKO6TNbZ2Lr3_czxKxID_FCQecHrq5QJsQG15azO2sBRaj3zdjBw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"zMZv6duA2zRH1Bbwk5T-75ze65tZPrc7qMqa7Tok2SWNZ4EIHLnH9k_-n--K0ygkY1TZs9dZzxq_wWbi6DDjDg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:bBIhudKetb0dlNR2ViKkxGGHc9kcjGjNOcXmsFJ_2os","role":"new-root-possession","value":"aDsCH20tBth9zgFolvsPFO_kd328QxOAlRv4ovwd9z0hxFULmyqkQ8KwjHxeGuKR9fh7U8hlPJawfN-ALPYpCQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"6vpHoROMXfBKu7CncLlv1YqHaEgCa6ucRLVZiK3MkidadZi-LhXnHlqYgJE5IwQSW7HeGDYk6_gBi76hDq6-AA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:l0-WROFMFwvGseuMftFAugQw6piQ25XER_Zzz-7zvJQ","role":"new-root-possession","value":"pyqXxQdusfvReYfXiGFz7ZkV2aOrniAJis4ugEsXt_F_s4S_fttTdqyJDaOMCKOLNkx9dmY5B5cFMIdpOyBzDg"}]} diff --git a/vectors/weave/v1/recovery-rebirth/activation.json b/vectors/weave/v1/recovery-rebirth/activation.json index 89e64d1..6ddd12b 100644 --- a/vectors/weave/v1/recovery-rebirth/activation.json +++ b/vectors/weave/v1/recovery-rebirth/activation.json @@ -1 +1 @@ -{"activation_id":"dm:recovery-activation:v1:FfrjX4zRiJ-B8-qIffe8HpFF10tZPf6GjD0rEs9KHq8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","credential":{"artifact_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"GX3FxjhlHtRIWyhLPwCpuMn5Gl2eTYZEQxjzkW17s2VHir7pq44h9Ljy9vcCUKHWh5roJFvNzfkBdmth2JDSCg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"root-authorization","value":"e_oTM4qrl4IntKE_kmcB0hgCuypYL35V58KYmBT5YmzC7XU-IsnjQkePHeO6k4QVa8xc7-obNjNtq3KQbXI1Bw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"root-authorization","value":"rzy35vy7sl-zVdUmDFR_V7hIY3H_bcG4VeuB0VH8KvBVmAry60pRnJ-9OFLYsa5a2ABwErqCL_EJrODdGPzsAA"}]},"incarnation":{"artifact_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"tI7lF8SolThmbM-SfkhzEc_9GaMNvmx8foK9umt9BSao8O1yyq0qGglNTCNKlhMt9aF8SQ-wNvRuGqRCXLKrBA"}]},"issued_at_ms":1800000000020,"origin":{"body_ref":"cluster:vector:recovered","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","principal_id":"compaii@vector-recovered"},"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","recovered_control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","recovery_artifact":{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"4rjiqmYL27gmexc4H_wVP4DqPgE5B0OqSI7tSDRRXDmOqvj2CdgYNhTs-FdjQgvJ5aAbgXCk_7vQejTp-OZjBQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]},"request_id":"dm:embodiment-request:v1:2a1MMsTUXQxnkBWbj0U9Iv03CTRRgpDmL0bFZYT0T6c","successor_manifest":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","embodiments":[{"body_ref":"cluster:vector:recovered","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","incarnation_id":"incarnation:vector:recovered:0","status":"active"}],"history_binding_id":null,"revision":2,"schema":"being-manifest/v2"},"transition":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"a7b7b13b4d44c828b768b2e8c2f780481acafb21f1fdf6828c736940ee72628d","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"4rjiqmYL27gmexc4H_wVP4DqPgE5B0OqSI7tSDRRXDmOqvj2CdgYNhTs-FdjQgvJ5aAbgXCk_7vQejTp-OZjBQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]},"recovery_artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"dEY6R9l_QW97ZkplRPtltVmTE0Ttep6ksv2MTkeuOxSTGyyvzzbGlmODKkI-IHFvOOjV9DY-T8hBFT2lsG9iBg"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"Ga9rRMFdELmS2OYl0cHN0NS9V8CqESKuIwwCdXKgoXXQIIm5L9NN3BcIbQailOLHe1AilPU3WmCoA1C1XMxlBQ"}],"successor_control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","successor_manifest_hash":"7eb18806432749d1030da7f4ede5659679dbb68a3c0b06557fc04cce1a77b4f3","successor_revision":2}},"schema":"dm.operator.recovery-activation/v1"} +{"activation_id":"dm:recovery-activation:v1:r75ujA58w00LFP82tw6KUYuiwCVAm1pVvjMFBwwkBTg","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","credential":{"artifact_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"8SCPaKGdPaycsjk4N7MLW7NydBKue2j-Ehcgrnm351JdwbNSPiXF1AKgFAECpdjVq6FgiLaanpiZ2gNHox37CA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"root-authorization","value":"GSIal-JJpqBKxwnb8M28GEOn-mMFy1-rwLOkMrqNJkijM-Ivtl-HzxvWo_rU7JDsXqL4RPpajuC2fSZ9Mre0DA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"root-authorization","value":"4jtCDW6q7GwMVHAdDZVUvappyzBjNoft9O_gmVclVLFLHVgEjbHmjoo77zndHLxtHaj4kmyZH1g3JSRRqx4-Dw"}]},"incarnation":{"artifact_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"LwazWXVcGY6KtDEExbw4d-BzCElhoDGZmxsbhp1PPL1hNpo0S8fMQJc_UOz0YrU3biGDwixqLTQ7Qrx2UoVVBA"}]},"issued_at_ms":1800000000020,"origin":{"body_ref":"cluster:vector:recovered","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","principal_id":"compaii@vector-recovered"},"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","recovered_control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","recovery_artifact":{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"mkUSxIBlkgb4lYKj94e3ah8louDhENf71Y7vERS2uuXRLeLM8mVqx6WRxJUvPJa9d_BAAtTzz3FHb_DzPK7iDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]},"request_id":"dm:embodiment-request:v1:y6a0gdFbMKAHpmLtaK5mDaIjRV7WPEvRoj1cQ1iv5r0","successor_manifest":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","embodiments":[{"body_ref":"cluster:vector:recovered","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","incarnation_id":"incarnation:vector:recovered:0","status":"active"}],"history_binding_id":null,"revision":2,"schema":"being-manifest/v2"},"transition":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"c5b709999b3994e7bcf62d5d433e3fd651a9fed55aedbba8fabc046b8b3c0303","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"mkUSxIBlkgb4lYKj94e3ah8louDhENf71Y7vERS2uuXRLeLM8mVqx6WRxJUvPJa9d_BAAtTzz3FHb_DzPK7iDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]},"recovery_artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"J8oXKkFFDRB1cWcLl69RGlb38br0LKLSjlQrD9w1u2MD_eUnRLLZuHuwMYS8Ef9OoMj2wKezrjWDbRopDywSBA"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"i1ipHcIfNBQecxPCjADCBd6ytXh8nxeKV2FtX_mLEGCLixarRdJrR6mafHfR5ZFkTOwA95GtnQ2j5Td7yaFTAQ"}],"successor_control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","successor_manifest_hash":"7c5ea9a4f7f347b20bc4dcd3ded402abbaa2e9c2559855f0ece4f0b74c7f8d48","successor_revision":2}},"schema":"dm.operator.recovery-activation/v1"} diff --git a/vectors/weave/v1/recovery-rebirth/embodiment-credential.json b/vectors/weave/v1/recovery-rebirth/embodiment-credential.json index 92ca18e..ca77688 100644 --- a/vectors/weave/v1/recovery-rebirth/embodiment-credential.json +++ b/vectors/weave/v1/recovery-rebirth/embodiment-credential.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"GX3FxjhlHtRIWyhLPwCpuMn5Gl2eTYZEQxjzkW17s2VHir7pq44h9Ljy9vcCUKHWh5roJFvNzfkBdmth2JDSCg"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"root-authorization","value":"e_oTM4qrl4IntKE_kmcB0hgCuypYL35V58KYmBT5YmzC7XU-IsnjQkePHeO6k4QVa8xc7-obNjNtq3KQbXI1Bw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"root-authorization","value":"rzy35vy7sl-zVdUmDFR_V7hIY3H_bcG4VeuB0VH8KvBVmAry60pRnJ-9OFLYsa5a2ABwErqCL_EJrODdGPzsAA"}]} +{"artifact_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"8SCPaKGdPaycsjk4N7MLW7NydBKue2j-Ehcgrnm351JdwbNSPiXF1AKgFAECpdjVq6FgiLaanpiZ2gNHox37CA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"root-authorization","value":"GSIal-JJpqBKxwnb8M28GEOn-mMFy1-rwLOkMrqNJkijM-Ivtl-HzxvWo_rU7JDsXqL4RPpajuC2fSZ9Mre0DA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"root-authorization","value":"4jtCDW6q7GwMVHAdDZVUvappyzBjNoft9O_gmVclVLFLHVgEjbHmjoo77zndHLxtHaj4kmyZH1g3JSRRqx4-Dw"}]} diff --git a/vectors/weave/v1/recovery-rebirth/enrollment-request.json b/vectors/weave/v1/recovery-rebirth/enrollment-request.json index 02bafcd..ba8175b 100644 --- a/vectors/weave/v1/recovery-rebirth/enrollment-request.json +++ b/vectors/weave/v1/recovery-rebirth/enrollment-request.json @@ -1 +1 @@ -{"body":{"base_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","base_revision":1,"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","created_at_ms":1800000000010,"credential":{"artifact_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"GX3FxjhlHtRIWyhLPwCpuMn5Gl2eTYZEQxjzkW17s2VHir7pq44h9Ljy9vcCUKHWh5roJFvNzfkBdmth2JDSCg"}]},"expires_at_ms":1800000060010,"incarnation":{"artifact_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"tI7lF8SolThmbM-SfkhzEc_9GaMNvmx8foK9umt9BSao8O1yyq0qGglNTCNKlhMt9aF8SQ-wNvRuGqRCXLKrBA"}]},"nonce":"jAi7J01e3sJ0w2CWAffYwqACmYIHQzDioIMBj5dw7A0","origin":{"body_ref":"cluster:vector:recovered","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","principal_id":"compaii@vector-recovered"}},"request_id":"dm:embodiment-request:v1:2a1MMsTUXQxnkBWbj0U9Iv03CTRRgpDmL0bFZYT0T6c","schema":"dm.operator.embodiment-request/v1","signature":{"alg":"Ed25519","kid":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","value":"B-Agdj_4_Dq84utfL8IhS9TnfyezEy5aHvpSxt6_d2aXTK78mZikLCEDTKhJsv-luiwjcXwwxxNLQU4foyGXAw"},"transport_signature":{"alg":"Ed25519","kid":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","value":"3pTsu5GC2pqGNYac3iByWDWfNQ7voV22R6BfQGlC5N1gHeq4gmeqVVrDiFO2MQbLDEU6oXhMg6IA5klx07UPDg"}} +{"body":{"base_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","base_revision":1,"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","created_at_ms":1800000000010,"credential":{"artifact_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","embodiment_id":"embodiment:vector:recovered","encryption_key":{"algorithm":"X25519","key_id":"dm:key:v1:IPxMs15TNDqzsKqO-gYZzXetjjnPmteS4-l99hmzMZM","public":"QMkDf51iZPzJYWGQDfus-9mcTlWwfEI6qqduXmlyNVo"},"purposes":["dm.we","messages"],"revocation_generation":0,"signing_key":{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","public":"L364RhzgW9wo45rwnq58xAhl7kPsp_VCRAk_C_tnp6I"},"transport_principals":[{"key":{"algorithm":"Ed25519","key_id":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","public":"ORF4B-Vp2yLpc5r0EEj3VwUOgNmyJQBOMS0RYNoL4oY"},"principal_id":"compaii@vector-recovered","scheme":"dm-peer-v1"}],"valid_from_ms":1800000000010,"valid_until_ms":9007199254740991},"kind":"embodiment-credential","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"embodiment-acceptance","value":"8SCPaKGdPaycsjk4N7MLW7NydBKue2j-Ehcgrnm351JdwbNSPiXF1AKgFAECpdjVq6FgiLaanpiZ2gNHox37CA"}]},"expires_at_ms":1800000060010,"incarnation":{"artifact_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"LwazWXVcGY6KtDEExbw4d-BzCElhoDGZmxsbhp1PPL1hNpo0S8fMQJc_UOz0YrU3biGDwixqLTQ7Qrx2UoVVBA"}]},"nonce":"jAi7J01e3sJ0w2CWAffYwqACmYIHQzDioIMBj5dw7A0","origin":{"body_ref":"cluster:vector:recovered","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","principal_id":"compaii@vector-recovered"}},"request_id":"dm:embodiment-request:v1:y6a0gdFbMKAHpmLtaK5mDaIjRV7WPEvRoj1cQ1iv5r0","schema":"dm.operator.embodiment-request/v1","signature":{"alg":"Ed25519","kid":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","value":"wDvOgEoEUCgQ7LWFdOBhi8hXw5-dxytLMKFMUP_pl4wFDGiRacmLhqlU11i6VgI6li2fVTXAnlNwzO6oEBtJAw"},"transport_signature":{"alg":"Ed25519","kid":"dm:key:v1:vQcAZxmRZimCvxbccXsGHy9FPqOJiJ5MfZ56YVZ5p_M","value":"r5EwqX-QyjVHGtwKRjDUvgW_z7TeHAieJQvqgqLbOy0dSeJSVmBKtKO3BKi0XnlqSUUOYVsNY2BMgKnjAgdQBg"}} diff --git a/vectors/weave/v1/recovery-rebirth/incarnation-authorization.json b/vectors/weave/v1/recovery-rebirth/incarnation-authorization.json index fb41b54..aa49914 100644 --- a/vectors/weave/v1/recovery-rebirth/incarnation-authorization.json +++ b/vectors/weave/v1/recovery-rebirth/incarnation-authorization.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"tI7lF8SolThmbM-SfkhzEc_9GaMNvmx8foK9umt9BSao8O1yyq0qGglNTCNKlhMt9aF8SQ-wNvRuGqRCXLKrBA"}]} +{"artifact_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_id":"incarnation:vector:recovered:0","incarnation_sequence":0,"started_at_ms":1800000000010},"kind":"incarnation-authorization","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:O6RyYPPDkeYr5vJLftX3WASiis-Fb4x1srcejRzugtQ","role":"incarnation-authorization","value":"LwazWXVcGY6KtDEExbw4d-BzCElhoDGZmxsbhp1PPL1hNpo0S8fMQJc_UOz0YrU3biGDwixqLTQ7Qrx2UoVVBA"}]} diff --git a/vectors/weave/v1/recovery-rebirth/index.json b/vectors/weave/v1/recovery-rebirth/index.json index 9ea1c02..1a545df 100644 --- a/vectors/weave/v1/recovery-rebirth/index.json +++ b/vectors/weave/v1/recovery-rebirth/index.json @@ -1 +1 @@ -{"activation":"activation.json","credential":"embodiment-credential.json","identity":{"existing_embodiment_credential":"../../../identity/v1/valid/embodiment-credential.json","existing_incarnation_authorization":"../../../identity/v1/valid/incarnation-authorization.json","genesis":"../../../identity/v1/valid/genesis.json"},"incarnation":"incarnation-authorization.json","negative_recovery":"negative/recovery-signature-tampered.json","negative_transition":"negative/transition-hash-tampered.json","previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest":"previous-manifest.json","recovered_control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","recovery_artifact":"recovery-artifact.json","request":"enrollment-request.json","schema":"dm.we.recovery-rebirth-vectors/v1","successor_manifest":"successor-manifest.json","transition":"recovery-rebirth.json"} +{"activation":"activation.json","credential":"embodiment-credential.json","identity":{"existing_embodiment_credential":"../../../identity/v1/valid/embodiment-credential.json","existing_incarnation_authorization":"../../../identity/v1/valid/incarnation-authorization.json","genesis":"../../../identity/v1/valid/genesis.json"},"incarnation":"incarnation-authorization.json","negative_recovery":"negative/recovery-signature-tampered.json","negative_transition":"negative/transition-hash-tampered.json","previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest":"previous-manifest.json","recovered_control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","recovery_artifact":"recovery-artifact.json","request":"enrollment-request.json","schema":"dm.we.recovery-rebirth-vectors/v1","successor_manifest":"successor-manifest.json","transition":"recovery-rebirth.json"} diff --git a/vectors/weave/v1/recovery-rebirth/negative/recovery-signature-tampered.json b/vectors/weave/v1/recovery-rebirth/negative/recovery-signature-tampered.json index bd0ee01..2e920f7 100644 --- a/vectors/weave/v1/recovery-rebirth/negative/recovery-signature-tampered.json +++ b/vectors/weave/v1/recovery-rebirth/negative/recovery-signature-tampered.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]} +{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]} diff --git a/vectors/weave/v1/recovery-rebirth/negative/transition-hash-tampered.json b/vectors/weave/v1/recovery-rebirth/negative/transition-hash-tampered.json index fdd88e0..c9289c4 100644 --- a/vectors/weave/v1/recovery-rebirth/negative/transition-hash-tampered.json +++ b/vectors/weave/v1/recovery-rebirth/negative/transition-hash-tampered.json @@ -1 +1 @@ -{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"0000000000000000000000000000000000000000000000000000000000000000","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"4rjiqmYL27gmexc4H_wVP4DqPgE5B0OqSI7tSDRRXDmOqvj2CdgYNhTs-FdjQgvJ5aAbgXCk_7vQejTp-OZjBQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]},"recovery_artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"dEY6R9l_QW97ZkplRPtltVmTE0Ttep6ksv2MTkeuOxSTGyyvzzbGlmODKkI-IHFvOOjV9DY-T8hBFT2lsG9iBg"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"Ga9rRMFdELmS2OYl0cHN0NS9V8CqESKuIwwCdXKgoXXQIIm5L9NN3BcIbQailOLHe1AilPU3WmCoA1C1XMxlBQ"}],"successor_control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","successor_manifest_hash":"7eb18806432749d1030da7f4ede5659679dbb68a3c0b06557fc04cce1a77b4f3","successor_revision":2} +{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"0000000000000000000000000000000000000000000000000000000000000000","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"mkUSxIBlkgb4lYKj94e3ah8louDhENf71Y7vERS2uuXRLeLM8mVqx6WRxJUvPJa9d_BAAtTzz3FHb_DzPK7iDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]},"recovery_artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"J8oXKkFFDRB1cWcLl69RGlb38br0LKLSjlQrD9w1u2MD_eUnRLLZuHuwMYS8Ef9OoMj2wKezrjWDbRopDywSBA"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"i1ipHcIfNBQecxPCjADCBd6ytXh8nxeKV2FtX_mLEGCLixarRdJrR6mafHfR5ZFkTOwA95GtnQ2j5Td7yaFTAQ"}],"successor_control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","successor_manifest_hash":"7c5ea9a4f7f347b20bc4dcd3ded402abbaa2e9c2559855f0ece4f0b74c7f8d48","successor_revision":2} diff --git a/vectors/weave/v1/recovery-rebirth/recovery-artifact.json b/vectors/weave/v1/recovery-rebirth/recovery-artifact.json index 0860e56..88daab0 100644 --- a/vectors/weave/v1/recovery-rebirth/recovery-artifact.json +++ b/vectors/weave/v1/recovery-rebirth/recovery-artifact.json @@ -1 +1 @@ -{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"4rjiqmYL27gmexc4H_wVP4DqPgE5B0OqSI7tSDRRXDmOqvj2CdgYNhTs-FdjQgvJ5aAbgXCk_7vQejTp-OZjBQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]} +{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"mkUSxIBlkgb4lYKj94e3ah8louDhENf71Y7vERS2uuXRLeLM8mVqx6WRxJUvPJa9d_BAAtTzz3FHb_DzPK7iDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]} diff --git a/vectors/weave/v1/recovery-rebirth/recovery-rebirth.json b/vectors/weave/v1/recovery-rebirth/recovery-rebirth.json index 8def204..ebd0b8a 100644 --- a/vectors/weave/v1/recovery-rebirth/recovery-rebirth.json +++ b/vectors/weave/v1/recovery-rebirth/recovery-rebirth.json @@ -1 +1 @@ -{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"a7b7b13b4d44c828b768b2e8c2f780481acafb21f1fdf6828c736940ee72628d","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"4rjiqmYL27gmexc4H_wVP4DqPgE5B0OqSI7tSDRRXDmOqvj2CdgYNhTs-FdjQgvJ5aAbgXCk_7vQejTp-OZjBQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"E9w3Gx9Hx9JzGs7GKS8EBRcRYSsg5Ys9bNF6M4S07klNO3vhCmSG0DTvnuqxiWkPKoI_jQ4ovWFZK7bD8Hz3Ag"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"TL1HU2-Oazd8XuJGwEHoUhMUXjslFcZEM-AS2aoC_XQaKjrZJVLz8HzE0ELCzG1WTKHbEXqg_9ziag8iJV_aAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"sbFuGzwuY0cf0n0ifaZ7xnZNCU9aq_-mffq3wSOMHbji3FmSbnjC4kN5SjXtBpP8x1QUL2VnMEmCr2ocx4n_DQ"}]},"recovery_artifact_id":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"dEY6R9l_QW97ZkplRPtltVmTE0Ttep6ksv2MTkeuOxSTGyyvzzbGlmODKkI-IHFvOOjV9DY-T8hBFT2lsG9iBg"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"Ga9rRMFdELmS2OYl0cHN0NS9V8CqESKuIwwCdXKgoXXQIIm5L9NN3BcIbQailOLHe1AilPU3WmCoA1C1XMxlBQ"}],"successor_control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","successor_manifest_hash":"7eb18806432749d1030da7f4ede5659679dbb68a3c0b06557fc04cce1a77b4f3","successor_revision":2} +{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","body_ref":"cluster:vector:recovered","content_hash":"c5b709999b3994e7bcf62d5d433e3fd651a9fed55aedbba8fabc046b8b3c0303","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","incarnation_id":"incarnation:vector:recovered:0","issued_at_ms":1800000000020,"previous_control_head":"dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys","previous_manifest_hash":"cba12f829782f5d24564ca8749a624bdb6cc2f03c4293ad04cae6e1c8b2b4331","previous_revision":1,"principal_id":"compaii@vector-recovered","recovery_artifact":{"artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","body":{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","competing_control_heads":["dm:identity:v1:Nca5Hlx-dWYGgzJap1H82V4VJTwKDbGOufJ0_-8-Lys"],"control_generation":1,"control_sequence":0,"replacement_root":{"keys":[{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","public":"kOsCXTr2uQRqxEnk5oH_Lt_CMIMfSBCqvnExoGKkcUA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","public":"Om9KxhkrWPJrFauq9RAhO_V7v0vZQYSuXn92juy-bOE"}],"threshold":2},"revocation_high_water":{},"revoked_embodiments":["embodiment:vector:legion"]},"kind":"recovery","schema":"dm.identity.artifact/v1","signatures":[{"algorithm":"Ed25519","key_id":"dm:key:v1:LG5PTtt3Uc0syqP86SwpU_sQ6t6XbLqzBERV0dyiyWU","role":"recovery-authorization","value":"mkUSxIBlkgb4lYKj94e3ah8louDhENf71Y7vERS2uuXRLeLM8mVqx6WRxJUvPJa9d_BAAtTzz3FHb_DzPK7iDA"},{"algorithm":"Ed25519","key_id":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","role":"new-root-possession","value":"xNdvM7Ym1_qv96BmM7UoC8anNUwU8p-fr_FLIbsDtlF8z2EMPQQFcyWnqdS1Cva3w9stbt-PHbiK_jXpzhSQAw"},{"algorithm":"Ed25519","key_id":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","role":"new-root-possession","value":"G9pmbyYCqG_UmuHrmi66qKR2qDrUVZ1n-mmF2X4SZ2gj4hW1o5rRASfSq1-gvZINQoSBTHe3m-Uusnj-MY5SAQ"},{"algorithm":"Ed25519","key_id":"dm:key:v1:cQhpr9D-av61wTjSZYxTRAij46l3w17pyEOvsmumozc","role":"recovery-authorization","value":"D0HiK6m_BfcwtFv8k38kKWgYh4jHJ-qy7WQ_1cmF0X8J3kzejBrm_33onuVlU8rBmGJ1PmmjQKrsKftr-jWSCA"}]},"recovery_artifact_id":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","revoked_embodiment_ids":["embodiment:vector:legion"],"schema":"dm.we.recovery-rebirth/v1","signatures":[{"alg":"Ed25519","kid":"dm:key:v1:P3JLTCEkvQUO6q7m2Of-bwamgueenaIOk61mcHvogFA","value":"J8oXKkFFDRB1cWcLl69RGlb38br0LKLSjlQrD9w1u2MD_eUnRLLZuHuwMYS8Ef9OoMj2wKezrjWDbRopDywSBA"},{"alg":"Ed25519","kid":"dm:key:v1:UKmWc9MZ5tT_HckH3xy1_3ynfyEd9thzCfRU3mB13Ng","value":"i1ipHcIfNBQecxPCjADCBd6ytXh8nxeKV2FtX_mLEGCLixarRdJrR6mafHfR5ZFkTOwA95GtnQ2j5Td7yaFTAQ"}],"successor_control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","successor_manifest_hash":"7c5ea9a4f7f347b20bc4dcd3ded402abbaa2e9c2559855f0ece4f0b74c7f8d48","successor_revision":2} diff --git a/vectors/weave/v1/recovery-rebirth/successor-manifest.json b/vectors/weave/v1/recovery-rebirth/successor-manifest.json index 28d70b4..9d5b7f9 100644 --- a/vectors/weave/v1/recovery-rebirth/successor-manifest.json +++ b/vectors/weave/v1/recovery-rebirth/successor-manifest.json @@ -1 +1 @@ -{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:rXeE9dar383o7nlzeXS6E2DXQibjnxMppn-0QLHcnm8","embodiments":[{"body_ref":"cluster:vector:recovered","embodiment_credential_id":"dm:identity:v1:j9_TlnhGobxUQamhnbRjsQ09En4dSq2r6-ALij1fnEI","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:AlEgZ1rhc-2m0-x66SUqf1X79qIG4KQsH3P_YbMO_PE","incarnation_id":"incarnation:vector:recovered:0","status":"active"}],"history_binding_id":null,"revision":2,"schema":"being-manifest/v2"} +{"being_ref":"dm:being:v1:VlG-k1yMoEXlpk0GvxVCALmpTlFp7GizcHzt3hPcQQs","control_head":"dm:identity:v1:Xo41RRCziu7PoNMJwG_BADyk_zeE83iL6vVRTRrR_L0","embodiments":[{"body_ref":"cluster:vector:recovered","embodiment_credential_id":"dm:identity:v1:EG2rtk1iQSd24RI7lkGsSZ2z-nUD0j-Zk7ATI8Vi-DE","embodiment_id":"embodiment:vector:recovered","incarnation_authorization_id":"dm:identity:v1:WXXqiHGr9AWMbBCLjlRDnCKiTij0Sl_9xLaaitx54so","incarnation_id":"incarnation:vector:recovered:0","status":"active"}],"history_binding_id":null,"revision":2,"schema":"being-manifest/v2"} From fefdaf1d66d25a8023815517b91eb13bb7a13395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Sun, 16 Aug 2026 04:27:09 -0300 Subject: [PATCH 03/11] security: partition operator capability surface --- src/daimon_matrix/service.py | 56 ++++++++++++++++++++++++ tests/test_dm079_capability_profiles.py | 57 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/test_dm079_capability_profiles.py diff --git a/src/daimon_matrix/service.py b/src/daimon_matrix/service.py index f3762fa..5bac828 100644 --- a/src/daimon_matrix/service.py +++ b/src/daimon_matrix/service.py @@ -188,6 +188,60 @@ | RELATIONSHIP_METHODS ) +# Bootstrap/rebirth must never mint one bearer capability for the complete +# service surface. These profiles are deliberately disjoint: compromise of +# one client key cannot be used as a signer oracle for another mutation +# family. ``observe`` is the safe default client; every mutating family is an +# explicit separate custody slot and client configuration. +OBSERVE_METHODS: Final = frozenset( + { + "communication.page", + "curator.inspect", + "memory.context", + "memory.evaluate", + "relationship.cursor", + "relationship.disclose", + "relationship.snapshot", + "relationship.status", + "review.inspect", + "review.queue", + "route.inspect", + "runtime.status", + "scope.me", + "scope.resolve", + "scope.tribe", + "scope.we", + "scope.we.diff", + "scope.we.sync-plan", + "source.assess", + "source.diff", + "source.incoming", + "source.projection", + "source.status", + "species.incoming", + "we.diff", + "we.heads", + "we.observe", + "we.preview", + "we.projection.get", + "we.sync.serve", + "we.sync.validate-receipt", + } +) + +OPERATOR_CAPABILITY_PROFILES: Final = { + "observe": OBSERVE_METHODS, + "weave": (METHODS | PEER_METHODS) - OBSERVE_METHODS, + "communication": COMMUNICATION_METHODS - OBSERVE_METHODS, + "curator": CURATOR_METHODS - OBSERVE_METHODS, + "memory": MEMORY_METHODS - OBSERVE_METHODS, + "relationships": RELATIONSHIP_METHODS - OBSERVE_METHODS, + "review": REVIEW_METHODS - OBSERVE_METHODS, + "routes": ROUTE_METHODS - OBSERVE_METHODS, + "sources": SOURCE_METHODS - OBSERVE_METHODS, + "species": SPECIES_METHODS - OBSERVE_METHODS, +} + Clock = Callable[[], int] @@ -1793,6 +1847,8 @@ def _diff(self, params: Any) -> dict[str, Any]: "COMMUNICATION_METHODS", "MEMORY_METHODS", "METHODS", + "OBSERVE_METHODS", + "OPERATOR_CAPABILITY_PROFILES", "PEER_METHODS", "RELATIONSHIP_METHODS", "SCOPE_METHODS", diff --git a/tests/test_dm079_capability_profiles.py b/tests/test_dm079_capability_profiles.py new file mode 100644 index 0000000..fc52ed0 --- /dev/null +++ b/tests/test_dm079_capability_profiles.py @@ -0,0 +1,57 @@ +"""Least-authority capability profile invariants for operator clients.""" + +from __future__ import annotations + +import itertools + +from daimon_matrix.service import ( + OBSERVE_METHODS, + OPERATOR_CAPABILITY_PROFILES, + SERVICE_METHODS, +) + + +def test_profiles_partition_the_complete_service_surface() -> None: + profiles = OPERATOR_CAPABILITY_PROFILES + assert set(profiles) == { + "communication", + "curator", + "memory", + "observe", + "relationships", + "review", + "routes", + "sources", + "species", + "weave", + } + assert all(methods for methods in profiles.values()) + assert frozenset().union(*profiles.values()) == SERVICE_METHODS + for (left_name, left), (right_name, right) in itertools.combinations( + profiles.items(), 2 + ): + assert not left & right, f"profiles overlap: {left_name}, {right_name}" + + +def test_observe_profile_has_no_semantic_mutation() -> None: + forbidden = { + "communication.accept", + "curator.enqueue", + "memory.execute", + "relationship.accept", + "review.authorize", + "route.submit", + "source.claim", + "species.apply", + "we.decide", + } + assert OPERATOR_CAPABILITY_PROFILES["observe"] == OBSERVE_METHODS + assert not forbidden & OBSERVE_METHODS + assert OBSERVE_METHODS < SERVICE_METHODS + + +def test_no_profile_is_a_complete_signer_oracle() -> None: + assert all( + methods < SERVICE_METHODS + for methods in OPERATOR_CAPABILITY_PROFILES.values() + ) From 61d645704c1a0c212a88d3eecb0a7857943641a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Sun, 16 Aug 2026 04:31:10 -0300 Subject: [PATCH 04/11] test: exhaust cross-profile capability refusal --- tests/test_dm079_capability_profiles.py | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/test_dm079_capability_profiles.py b/tests/test_dm079_capability_profiles.py index fc52ed0..e206848 100644 --- a/tests/test_dm079_capability_profiles.py +++ b/tests/test_dm079_capability_profiles.py @@ -3,7 +3,11 @@ from __future__ import annotations import itertools +import uuid +import pytest + +from daimon_matrix.local_api import LocalApiError, create_capability, create_request from daimon_matrix.service import ( OBSERVE_METHODS, OPERATOR_CAPABILITY_PROFILES, @@ -55,3 +59,29 @@ def test_no_profile_is_a_complete_signer_oracle() -> None: methods < SERVICE_METHODS for methods in OPERATOR_CAPABILITY_PROFILES.values() ) + + +@pytest.mark.parametrize( + ("profile", "methods"), OPERATOR_CAPABILITY_PROFILES.items() +) +def test_stolen_profile_key_cannot_prepare_cross_profile_request( + profile: str, methods: frozenset[str] +) -> None: + now_ms = 1_800_000_000_000 + capability = create_capability( + bytes([len(profile)]) * 32, + client_id=f"client:operator:{profile}", + methods=sorted(methods), + not_before_ms=now_ms - 1, + not_after_ms=now_ms + 60_000, + ) + for method in sorted(SERVICE_METHODS - methods): + with pytest.raises(LocalApiError, match="authentication_failed"): + create_request( + capability, + request_id=str(uuid.uuid4()), + issued_at_ms=now_ms, + method=method, + params={}, + nonce=b"x" * 16, + ) From 64e41b82b6ed8a4216f1a8afe2e7913c627a55e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Sun, 16 Aug 2026 05:00:16 -0300 Subject: [PATCH 05/11] Split operator capabilities by role --- docs/dm078-fresh-host-rebirth.md | 25 ++- docs/dm083-two-host-dogfood.md | 10 ++ docs/runbooks/operator-bootstrap.md | 38 +++-- docs/verification/dm078-fresh-host-rebirth.md | 7 + provenance/hermes-agent-0.19.0.json | 2 +- src/daimon_matrix/operator_bootstrap.py | 114 ++++++------- src/daimon_matrix/operator_capabilities.py | 149 +++++++++++++++++ src/daimon_matrix/operator_rebirth.py | 157 +++++++++--------- src/daimon_matrix/runtime.py | 8 + tests/test_dm024_runtime.py | 31 ++++ tests/test_dm041_hermes_body.py | 6 +- tests/test_dm078_rebirth.py | 53 +++++- tests/test_dm079_capability_profiles.py | 110 ++++++++++-- tests/test_dm083_dogfood.py | 79 +++++++-- tests/test_package_scaffold.py | 2 + tools/check_distribution.py | 6 + tools/reproducible_build.py | 1 + vectors/hermes/v1/index.json | 2 +- vectors/hermes/v1/valid/launch-receipt.json | 2 +- vectors/hermes/v1/valid/profile-manifest.json | 2 +- 20 files changed, 612 insertions(+), 192 deletions(-) create mode 100644 src/daimon_matrix/operator_capabilities.py diff --git a/docs/dm078-fresh-host-rebirth.md b/docs/dm078-fresh-host-rebirth.md index 2059a1f..66b73a2 100644 --- a/docs/dm078-fresh-host-rebirth.md +++ b/docs/dm078-fresh-host-rebirth.md @@ -68,18 +68,29 @@ python -m daimon_matrix.operator_rebirth activate \ ``` Preparation is a one-shot, fsynced owner-only directory. It contains encrypted -body custody, separately encrypted transitional transport custody, public -operator/status capability descriptors, the target peer profile and the public -request. Root activation is an owner-only public artifact. The target-only -`activate` process reopens and revalidates its custody against the root-signed -activation, emits a loadable V7 runtime with empty writable stores, and retains -only hashes in its public receipt. Cluster still owns authenticated transfer, -journaled installation, target start and peer update. Cluster H7/H8 now +body custody, separately encrypted transitional transport custody, ten public +least-authority operator descriptors, the target peer profile and the public +request. Every operator role has an independent random key and custody slot; +the default client is the non-mutating `observe` role and no descriptor spans +the full service surface. Root activation is an owner-only public artifact. The +target-only `activate` process reopens and revalidates its custody against the +root-signed activation, emits a loadable V7 runtime with empty writable stores, +and retains only hashes in its public receipt. Cluster still owns authenticated +transfer, journaled installation, target start and peer update. Cluster H7/H8 now implement and remotely rehearse that boundary: installation is crash-resumable and activation-idempotent, then a foreground supervisor admits only the signed initial incarnation, supplies the password by inherited descriptor and authenticates status, `/me` and `/we` before reporting `running-ready`. +Operator capabilities have a fixed 30-day lifetime. Preparation and activation +receipts bind a reprovision instant seven days before hard expiry. Before that +instant, repeat the split-custody rebirth ceremony to create a fresh +root-authorized embodiment with a completely new role-key set, cut over, and +park or revoke the predecessor. Rebirth never regroups the roles and never +copies capability keys. Expired or revoked descriptors are rejected at runtime +load as well as at request authentication; in-place rotation of a live bundle, +custody and plaintext client directories is intentionally unsupported. + ## Only permitted manifest delta The enrollment validator accepts exactly one new active manifest row while diff --git a/docs/dm083-two-host-dogfood.md b/docs/dm083-two-host-dogfood.md index 3064432..1cb6b9a 100644 --- a/docs/dm083-two-host-dogfood.md +++ b/docs/dm083-two-host-dogfood.md @@ -253,3 +253,13 @@ cross-being consent or semantic-delivery claim. Matrix PR #112 and Cluster PR #77 remain unmerged pending independent review. Fresh-host private custody, external participant consent and the final human cutover/archive decisions remain separate gates. + +## Release-candidate capability successor + +The historical five-method status client above is superseded for future +provisioning. Current bootstrap and rebirth packages mint ten disjoint, +independently keyed operator roles. `observe` is the default and contains no +mutation; every mutation family requires its own owner-only client directory. +All role descriptors expire after 30 days and advertise a seven-day +reprovisioning window. This changes no historical dogfood evidence and has not +been deployed to those hosts. diff --git a/docs/runbooks/operator-bootstrap.md b/docs/runbooks/operator-bootstrap.md index 73e4ec4..360f99a 100644 --- a/docs/runbooks/operator-bootstrap.md +++ b/docs/runbooks/operator-bootstrap.md @@ -104,20 +104,34 @@ The output contains: capability secrets; - `runtimes/