diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 30ebde1..48c78db 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,6 +38,9 @@ jobs: - run: python -m ruff format --check src tools/check_distribution.py tools/check_tribe_provenance.py tools/generate_dm021_vectors.py tools/generate_dm022_vectors.py tools/generate_dm030_vectors.py tools/generate_dm031_vectors.py tools/generate_dm032_vectors.py tools/generate_dm033_vectors.py tools/generate_dm034_vectors.py tools/generate_dm035_vectors.py tools/generate_dm040_vectors.py tools/generate_dm041_vectors.py tools/generate_dm061_vectors.py tools/generate_dm079_vectors.py tools/generate_dm081_vectors.py tools/reproducible_build.py tools/scan_secrets.py tests/test_package_scaffold.py tests/test_dm021_identity.py tests/test_dm021_keystore.py tests/test_dm021_process.py tests/test_dm021_vectors.py tests/test_dm022_ledger.py tests/test_dm023_sync.py tests/test_dm024_service.py tests/test_dm024_runtime.py tests/test_dm025_client.py tests/test_dm025_cli_mcp.py tests/test_dm026_conformance.py tests/test_dm030_memory_policy.py tests/test_dm031_curator.py tests/test_dm032_curator_worker.py tests/test_dm033_human_review.py tests/test_dm034_memory_projection.py tests/test_dm035_publication.py tests/test_dm037_cluster_effects.py tests/test_dm040_codex_body.py tests/test_dm041_hermes_body.py tests/test_dm050_tribe_provenance.py tests/test_dm051_sealed.py tests/test_dm052_communication.py tests/test_dm053_routes.py tests/test_dm054_scopes.py tests/test_dm055_peer_transport.py tests/test_dm060_synthetic_birth.py tests/test_dm061_species.py tests/test_dm079_authority_epochs.py tests/test_dm081_sources.py tests/test_dm083_dogfood.py - run: python -m ruff check src tools/check_distribution.py tools/check_tribe_provenance.py tools/generate_dm021_vectors.py tools/generate_dm022_vectors.py tools/generate_dm030_vectors.py tools/generate_dm031_vectors.py tools/generate_dm032_vectors.py tools/generate_dm033_vectors.py tools/generate_dm034_vectors.py tools/generate_dm035_vectors.py tools/generate_dm040_vectors.py tools/generate_dm041_vectors.py tools/generate_dm061_vectors.py tools/generate_dm079_vectors.py tools/generate_dm081_vectors.py tools/reproducible_build.py tools/scan_secrets.py tests/test_package_scaffold.py tests/test_dm021_identity.py tests/test_dm021_keystore.py tests/test_dm021_process.py tests/test_dm021_vectors.py tests/test_dm022_ledger.py tests/test_dm023_sync.py tests/test_dm024_service.py tests/test_dm024_runtime.py tests/test_dm025_client.py tests/test_dm025_cli_mcp.py tests/test_dm026_conformance.py tests/test_dm030_memory_policy.py tests/test_dm031_curator.py tests/test_dm032_curator_worker.py tests/test_dm033_human_review.py tests/test_dm034_memory_projection.py tests/test_dm035_publication.py tests/test_dm037_cluster_effects.py tests/test_dm040_codex_body.py tests/test_dm041_hermes_body.py tests/test_dm050_tribe_provenance.py tests/test_dm051_sealed.py tests/test_dm052_communication.py tests/test_dm053_routes.py tests/test_dm054_scopes.py tests/test_dm055_peer_transport.py tests/test_dm060_synthetic_birth.py tests/test_dm061_species.py tests/test_dm079_authority_epochs.py tests/test_dm081_sources.py tests/test_dm083_dogfood.py - run: MYPYPATH=src python -m mypy src tools/check_distribution.py tools/check_tribe_provenance.py tools/generate_dm021_vectors.py tools/generate_dm022_vectors.py tools/generate_dm030_vectors.py tools/generate_dm031_vectors.py tools/generate_dm032_vectors.py tools/generate_dm033_vectors.py tools/generate_dm034_vectors.py tools/generate_dm035_vectors.py tools/generate_dm040_vectors.py tools/generate_dm041_vectors.py tools/generate_dm061_vectors.py tools/generate_dm079_vectors.py tools/generate_dm081_vectors.py tools/reproducible_build.py tools/scan_secrets.py tests/test_package_scaffold.py tests/test_dm021_identity.py tests/test_dm021_keystore.py tests/test_dm021_process.py tests/test_dm021_vectors.py tests/test_dm022_ledger.py tests/test_dm023_sync.py tests/test_dm024_service.py tests/test_dm024_runtime.py tests/test_dm025_client.py tests/test_dm025_cli_mcp.py tests/test_dm026_conformance.py tests/test_dm030_memory_policy.py tests/test_dm031_curator.py tests/test_dm032_curator_worker.py tests/test_dm033_human_review.py tests/test_dm034_memory_projection.py tests/test_dm035_publication.py tests/test_dm037_cluster_effects.py tests/test_dm040_codex_body.py tests/test_dm041_hermes_body.py tests/test_dm050_tribe_provenance.py tests/test_dm051_sealed.py tests/test_dm052_communication.py tests/test_dm053_routes.py tests/test_dm054_scopes.py tests/test_dm055_peer_transport.py tests/test_dm060_synthetic_birth.py tests/test_dm061_species.py tests/test_dm079_authority_epochs.py tests/test_dm081_sources.py tests/test_dm083_dogfood.py + - run: python -m ruff format --check tests/test_operator_first_embodiment.py + - run: python -m ruff check tests/test_operator_first_embodiment.py + - run: MYPYPATH=src python -m mypy tests/test_operator_first_embodiment.py - run: python -m ruff format --check tools/build_cross_being_canary_preflight.py tests/test_cross_being_canary_preflight.py - run: python -m ruff check tools/build_cross_being_canary_preflight.py tests/test_cross_being_canary_preflight.py - run: MYPYPATH=src python -m mypy tools/build_cross_being_canary_preflight.py tests/test_cross_being_canary_preflight.py @@ -167,6 +170,7 @@ jobs: "${RUNNER_TEMP}/dm020-wheel/bin/python" -W error::ResourceWarning -m unittest tests.test_dm036_collective_memory.DM036ContractTests -q "${RUNNER_TEMP}/dm020-wheel/bin/daimon-matrixd" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon-genesis" --help + "${RUNNER_TEMP}/dm020-wheel/bin/daimon-first-embodiment" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon-rebirth" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon-conformance" --help @@ -177,6 +181,14 @@ jobs: "${RUNNER_TEMP}/dm020-wheel/bin/daimon-reviewer" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon-synthetic-species" --help "${RUNNER_TEMP}/dm020-wheel/bin/daimon-synthetic-sources" --help + dm087_fifo="${RUNNER_TEMP}/dm087-genesis.fifo" + mkfifo "${dm087_fifo}" + set +e + timeout 2 "${RUNNER_TEMP}/dm020-wheel/bin/daimon-first-embodiment" prepare --genesis "${dm087_fifo}" --profile README.md --password-fd 0 --output "${RUNNER_TEMP}/dm087-forbidden-output" =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":"5c8870c1625761956a56fd2b225720dbe9083e45","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":"09789981423142fec1a26239d5209f96c41453078ff73e2fc4a11e1d45728660","license":"MIT","matrix_payload":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","package_tree_sha256":"02893cf3e50708c41395219eb9cf91ec9ae4c6b9cb45efc175a4996dd858de4c","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 0c2845f..e36f3ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ daimon-hermes-body = "daimon_matrix.hermes_body:main" daimon-mcp = "daimon_matrix.mcp_server:main" daimon-matrixd = "daimon_matrix.daemon:main" daimon-genesis = "daimon_matrix.operator_genesis:main" +daimon-first-embodiment = "daimon_matrix.operator_first_embodiment:main" daimon-rebirth = "daimon_matrix.operator_rebirth:main" daimon-reviewer = "daimon_matrix.reviewer_cli:main" daimon-synthetic-bootstrap = "daimon_matrix.operator_bootstrap:main" diff --git a/src/daimon_matrix/operator_first_embodiment.py b/src/daimon_matrix/operator_first_embodiment.py new file mode 100644 index 0000000..c3c4957 --- /dev/null +++ b/src/daimon_matrix/operator_first_embodiment.py @@ -0,0 +1,977 @@ +"""Split-custody activation of the first runtime for a distributed genesis.""" + +from __future__ import annotations + +import argparse +import contextlib +import copy +import hashlib +import json +import os +import shutil +import stat +import sys +import tempfile +import time +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Final + +from .canonical import b64url, canonical_bytes, digest, domain_bytes +from .client import CLIENT_CONFIG_SCHEMA_V3 +from .identity import ( + DOMAINS, + ControlState, + signing_descriptor, + verify_embodiment_credential, + verify_genesis, + verify_incarnation_authorization, +) +from .keystore import EncryptedKeystore, KeystoreError, PasswordReader +from .operator_capabilities import ( + HOST_PROFILE_NAMES, + OBSERVE_PROFILE, + OPERATOR_PROFILE_NAMES, + OperatorCapabilityError, + create_operator_capability_binding, + host_capability_profile, + operator_capability_profile, +) +from .operator_genesis import HOLDER_SCHEMA, PENDING_CONTROL_HEAD +from .operator_rebirth import ( + RebirthError, + _holder_attestation, + _identity_signature, + _origin, + _validated_preparation, + _verify_holder_attestation, + create_target_preparation, + validate_enrollment_request, +) +from .weave import BeingManifest, RootAuthority + +SHARE_SCHEMA: Final = "dm.operator.first-embodiment-root-share/v1" +SHARE_DOMAIN: Final = "dm.operator.first-embodiment-root-share/v1" +ACTIVATION_SCHEMA: Final = "dm.operator.first-embodiment-activation/v1" +ACTIVATION_DOMAIN: Final = "dm.operator.first-embodiment-activation/v1" +ACTIVATION_ID_PREFIX: Final = "dm:first-embodiment-activation:v1:" +INITIAL_BASE_SCHEMA: Final = "dm.operator.initial-manifest-base/v1" +RECEIPT_SCHEMA: Final = "dm.operator.first-embodiment-runtime-receipt/v1" +REQUEST_LIFETIME_MS: Final = 7 * 24 * 60 * 60 * 1000 +MAX_DOCUMENT_BYTES: Final = 4 * 1024 * 1024 + + +class FirstEmbodimentError(RuntimeError): + """The first embodiment ceremony failed closed.""" + + +@dataclass(frozen=True) +class _InitialBase: + manifest: BeingManifest + state: ControlState + + +def _closed(value: Any, fields: set[str], code: str) -> Mapping[str, Any]: + if not isinstance(value, Mapping) or set(value) != fields: + raise FirstEmbodimentError(code) + return value + + +def _initial_base(genesis: Any) -> _InitialBase: + if not isinstance(genesis, Mapping): + raise FirstEmbodimentError("invalid_first_embodiment_genesis") + try: + state = verify_genesis(genesis) + except (TypeError, ValueError) as exception: + raise FirstEmbodimentError("invalid_first_embodiment_genesis") from exception + value = { + "schema": INITIAL_BASE_SCHEMA, + "being_ref": state.being_ref, + "control_head": state.head, + "revision": 0, + "embodiments": [], + } + return _InitialBase( + manifest=BeingManifest( + value=value, + digest=hashlib.sha256(canonical_bytes(value)).hexdigest(), + trust_mode="root-bound", + ), + state=state, + ) + + +def _owner_directory(path: Path, code: str) -> Path: + target = Path(os.path.abspath(path)) + _reject_symlink_ancestors(target, code) + try: + info = target.lstat() + except OSError as exception: + raise FirstEmbodimentError(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 FirstEmbodimentError(code) + return target + + +def _reject_symlink_ancestors(path: Path, code: str) -> None: + ancestor = path.parent + while ancestor != ancestor.parent: + try: + info = ancestor.lstat() + except OSError as exception: + raise FirstEmbodimentError(code) from exception + if stat.S_ISLNK(info.st_mode) or not stat.S_ISDIR(info.st_mode): + raise FirstEmbodimentError(code) + ancestor = ancestor.parent + + +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] | bytes) -> None: + raw = value if isinstance(value, bytes) else 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: + target = Path(os.path.abspath(path)) + _reject_symlink_ancestors(target, code) + descriptor = -1 + try: + before = target.lstat() + if ( + stat.S_ISLNK(before.st_mode) + or not stat.S_ISREG(before.st_mode) + or before.st_uid != os.geteuid() + or stat.S_IMODE(before.st_mode) & 0o077 + or not 1 <= before.st_size <= MAX_DOCUMENT_BYTES + ): + raise FirstEmbodimentError(code) + descriptor = os.open( + target, + os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0), + ) + after = os.fstat(descriptor) + if ( + (before.st_dev, before.st_ino) != (after.st_dev, after.st_ino) + or not stat.S_ISREG(after.st_mode) + or after.st_uid != os.geteuid() + or stat.S_IMODE(after.st_mode) & 0o077 + or not 1 <= after.st_size <= MAX_DOCUMENT_BYTES + ): + raise FirstEmbodimentError(code) + raw = b"" + while chunk := os.read(descriptor, 1024 * 1024): + raw += chunk + if len(raw) > MAX_DOCUMENT_BYTES: + raise FirstEmbodimentError(code) + value = json.loads(raw) + if canonical_bytes(value) != raw.rstrip(b"\n"): + raise FirstEmbodimentError(code) + return value + except FirstEmbodimentError: + raise + except (OSError, ValueError) as exception: + raise FirstEmbodimentError(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, "first_embodiment_output_parent_rejected") + if target.exists() or target.is_symlink(): + if target.is_file() and canonical_bytes( + _document(target, "first_embodiment_output_exists") + ) == canonical_bytes(value): + return + raise FirstEmbodimentError("first_embodiment_output_exists") + _private_write(target, value) + _fsync_directory(target.parent) + + +def _password(descriptor: int) -> bytearray: + if descriptor < 0: + raise FirstEmbodimentError("invalid_first_embodiment_password_descriptor") + try: + raw = os.read(descriptor, 1025) + except OSError as exception: + raise FirstEmbodimentError( + "first_embodiment_password_unavailable" + ) from exception + finally: + with contextlib.suppress(OSError): + os.close(descriptor) + if not 12 <= len(raw) <= 1024: + raise FirstEmbodimentError("invalid_first_embodiment_password_length") + return bytearray(raw) + + +def _reader(password: bytearray) -> PasswordReader: + return lambda: bytearray(password) + + +def prepare_target( + output: Path, + genesis: Any, + profile: Any, + password_reader: PasswordReader, + *, + created_at_ms: int, +) -> dict[str, Any]: + """Create target-only custody and a body-accepted public request.""" + + base = _initial_base(genesis) + try: + return create_target_preparation( + output, + base, + profile, + password_reader, + created_at_ms=created_at_ms, + expires_at_ms=created_at_ms + REQUEST_LIFETIME_MS, + expected_targets=set(), + ) + except RebirthError as exception: + raise FirstEmbodimentError( + "first_embodiment_preparation_rejected" + ) from exception + + +def _root_holder_seed( + holder: Path, + base: _InitialBase, + password_reader: PasswordReader, +) -> bytes: + package = _owner_directory(holder, "first_embodiment_root_holder_rejected") + descriptor = _closed( + _document(package / "descriptor.json", "first_embodiment_root_holder_rejected"), + {"schema", "role", "key"}, + "first_embodiment_root_holder_rejected", + ) + if ( + descriptor["schema"] != HOLDER_SCHEMA + or descriptor["role"] != "root" + or not isinstance(descriptor["key"], Mapping) + ): + raise FirstEmbodimentError("first_embodiment_root_holder_rejected") + try: + contents = EncryptedKeystore(package / "holder.json").open( + password_reader, + minimum_counter=1, + required_control_head=PENDING_CONTROL_HEAD, + ) + except KeystoreError as exception: + raise FirstEmbodimentError( + "first_embodiment_root_holder_rejected" + ) from exception + if set(contents.secrets) != {"genesis.root.v1:holder"}: + raise FirstEmbodimentError("first_embodiment_root_holder_rejected") + seed = contents.secrets["genesis.root.v1:holder"] + key = signing_descriptor(seed) + if key != descriptor["key"] or key["key_id"] not in { + row["key_id"] for row in base.state.root_policy["keys"] + }: + raise FirstEmbodimentError("first_embodiment_root_holder_rejected") + return seed + + +def create_root_share( + genesis: Any, + request: Any, + holder: Path, + password_reader: PasswordReader, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Open one isolated root package and attest the exact target request.""" + + base = _initial_base(genesis) + try: + verified = validate_enrollment_request( + request, + base, + observed_at_ms=observed_at_ms, + ) + except (KeyError, RebirthError, TypeError) as exception: + raise FirstEmbodimentError("first_embodiment_request_rejected") from exception + seed = _root_holder_seed(holder, base, password_reader) + credential = verified["body"]["credential"] + signature = _identity_signature( + seed, + "root-authorization", + domain_bytes(DOMAINS["embodiment-credential"], credential["body"]), + ) + share_body = { + "being_ref": base.state.being_ref, + "control_head": base.state.head, + "request_id": verified["request_id"], + "request_sha256": hashlib.sha256(canonical_bytes(verified)).hexdigest(), + "credential_signature": signature, + } + return { + "schema": SHARE_SCHEMA, + **share_body, + "attestation": _holder_attestation(seed, SHARE_DOMAIN, share_body), + } + + +def _expected_manifest( + base: _InitialBase, + credential: Mapping[str, Any], + incarnation: Mapping[str, Any], + origin: Mapping[str, Any], +) -> BeingManifest: + return BeingManifest.from_value( + { + "schema": "being-manifest/v2", + "being_ref": base.state.being_ref, + "control_head": base.state.head, + "history_binding_id": None, + "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", + } + ], + } + ) + + +def aggregate_activation( + genesis: Any, + request: Any, + shares: Sequence[Any], + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Keyless aggregation of root shares into one first-runtime activation.""" + + base = _initial_base(genesis) + try: + issued_at_ms = observed_at_ms + verified = validate_enrollment_request( + request, base, observed_at_ms=issued_at_ms + ) + except (KeyError, RebirthError, TypeError) as exception: + raise FirstEmbodimentError("first_embodiment_request_rejected") from exception + credential = copy.deepcopy(verified["body"]["credential"]) + request_sha256 = hashlib.sha256(canonical_bytes(verified)).hexdigest() + signatures: list[Mapping[str, Any]] = [] + approvals: list[dict[str, Any]] = [] + approved_ids: set[str] = set() + for value in shares: + row = _closed( + value, + { + "schema", + "being_ref", + "control_head", + "request_id", + "request_sha256", + "credential_signature", + "attestation", + }, + "first_embodiment_share_rejected", + ) + credential_signature = row["credential_signature"] + if ( + row["schema"] != SHARE_SCHEMA + or row["being_ref"] != base.state.being_ref + or row["control_head"] != base.state.head + or row["request_id"] != verified["request_id"] + or row["request_sha256"] != request_sha256 + or not isinstance(credential_signature, Mapping) + or credential_signature.get("role") != "root-authorization" + ): + raise FirstEmbodimentError("first_embodiment_share_rejected") + share_body = { + "being_ref": row["being_ref"], + "control_head": row["control_head"], + "request_id": row["request_id"], + "request_sha256": row["request_sha256"], + "credential_signature": credential_signature, + } + try: + attested_kid = _verify_holder_attestation( + row["attestation"], + SHARE_DOMAIN, + share_body, + base.state.root_policy["keys"], + code="first_embodiment_share_rejected", + ) + except RebirthError as exception: + raise FirstEmbodimentError("first_embodiment_share_rejected") from exception + if ( + credential_signature.get("key_id") != attested_kid + or attested_kid in approved_ids + ): + raise FirstEmbodimentError("first_embodiment_share_rejected") + approved_ids.add(attested_kid) + signatures.append(copy.deepcopy(dict(credential_signature))) + approvals.append( + { + **copy.deepcopy(share_body), + "attestation": copy.deepcopy(dict(row["attestation"])), + } + ) + credential["signatures"].extend(signatures) + credential["signatures"].sort(key=lambda row: (row["key_id"], row["role"])) + approvals.sort(key=lambda row: row["attestation"]["kid"]) + incarnation = copy.deepcopy(verified["body"]["incarnation"]) + try: + credential_body = verify_embodiment_credential( + credential, base.state, at_ms=issued_at_ms + ) + incarnation_body = verify_incarnation_authorization( + incarnation, credential, base.state, at_ms=issued_at_ms + ) + except ValueError as exception: + raise FirstEmbodimentError("first_embodiment_threshold_rejected") from exception + origin = _origin(verified["body"]["origin"]) + if ( + credential_body["embodiment_id"] != origin["embodiment_id"] + or credential_body["body_ref"] != origin["body_ref"] + or incarnation_body["incarnation_id"] != origin["incarnation_id"] + or incarnation_body["incarnation_sequence"] != 0 + ): + raise FirstEmbodimentError("first_embodiment_binding_rejected") + manifest = _expected_manifest(base, credential, incarnation, origin) + RootAuthority( + manifest, + base.state, + {credential["artifact_id"]: credential}, + {incarnation["artifact_id"]: incarnation}, + ) + body = { + "request_id": verified["request_id"], + "being_ref": base.state.being_ref, + "control_head": base.state.head, + "initial_base_hash": base.manifest.digest, + "manifest": manifest.value, + "credential": credential, + "incarnation": incarnation, + "origin": origin, + "root_approvals": approvals, + "issued_at_ms": issued_at_ms, + } + return { + "schema": ACTIVATION_SCHEMA, + "activation_id": ACTIVATION_ID_PREFIX + b64url(digest(ACTIVATION_DOMAIN, body)), + "body": body, + } + + +def validate_activation( + genesis: Any, + request: Any, + activation: Any, +) -> tuple[dict[str, Any], RootAuthority]: + """Validate the complete first manifest and its root-authorized credential.""" + + base = _initial_base(genesis) + row = _closed( + activation, + {"schema", "activation_id", "body"}, + "invalid_first_embodiment_activation", + ) + if row["schema"] != ACTIVATION_SCHEMA: + raise FirstEmbodimentError("unsupported_first_embodiment_activation") + body = _closed( + row["body"], + { + "request_id", + "being_ref", + "control_head", + "initial_base_hash", + "manifest", + "credential", + "incarnation", + "origin", + "root_approvals", + "issued_at_ms", + }, + "invalid_first_embodiment_activation", + ) + try: + verified_request = validate_enrollment_request( + request, base, observed_at_ms=body["issued_at_ms"] + ) + except RebirthError as exception: + raise FirstEmbodimentError("first_embodiment_request_rejected") from exception + if ( + body["request_id"] != verified_request["request_id"] + or body["being_ref"] != base.state.being_ref + or body["control_head"] != base.state.head + or body["initial_base_hash"] != base.manifest.digest + ): + raise FirstEmbodimentError("first_embodiment_activation_base_mismatch") + credential = copy.deepcopy(body["credential"]) + incarnation = copy.deepcopy(body["incarnation"]) + origin = _origin(body["origin"]) + approvals = body["root_approvals"] + if not isinstance(approvals, list): + raise FirstEmbodimentError("invalid_first_embodiment_activation") + request_sha256 = hashlib.sha256(canonical_bytes(verified_request)).hexdigest() + approved_ids: set[str] = set() + approved_signatures: list[Mapping[str, Any]] = [] + for value in approvals: + approval = _closed( + value, + { + "being_ref", + "control_head", + "request_id", + "request_sha256", + "credential_signature", + "attestation", + }, + "invalid_first_embodiment_activation", + ) + credential_signature = approval["credential_signature"] + if ( + approval["being_ref"] != base.state.being_ref + or approval["control_head"] != base.state.head + or approval["request_id"] != verified_request["request_id"] + or approval["request_sha256"] != request_sha256 + or not isinstance(credential_signature, Mapping) + or credential_signature.get("role") != "root-authorization" + ): + raise FirstEmbodimentError("invalid_first_embodiment_activation") + approval_body = { + key: approval[key] + for key in ( + "being_ref", + "control_head", + "request_id", + "request_sha256", + "credential_signature", + ) + } + try: + attested_kid = _verify_holder_attestation( + approval["attestation"], + SHARE_DOMAIN, + approval_body, + base.state.root_policy["keys"], + code="invalid_first_embodiment_activation", + ) + except RebirthError as exception: + raise FirstEmbodimentError( + "invalid_first_embodiment_activation" + ) from exception + if ( + credential_signature.get("key_id") != attested_kid + or attested_kid in approved_ids + ): + raise FirstEmbodimentError("invalid_first_embodiment_activation") + approved_ids.add(attested_kid) + approved_signatures.append(credential_signature) + root_signatures = [ + signature + for signature in credential.get("signatures", []) + if isinstance(signature, Mapping) + and signature.get("role") == "root-authorization" + ] + if canonical_bytes( + sorted(approved_signatures, key=lambda row: row["key_id"]) + ) != canonical_bytes(sorted(root_signatures, key=lambda row: row["key_id"])): + raise FirstEmbodimentError("invalid_first_embodiment_activation") + try: + verify_embodiment_credential(credential, base.state, at_ms=body["issued_at_ms"]) + verify_incarnation_authorization( + incarnation, credential, base.state, at_ms=body["issued_at_ms"] + ) + manifest = BeingManifest.from_value(body["manifest"]) + expected = _expected_manifest(base, credential, incarnation, origin) + if manifest.digest != expected.digest: + raise FirstEmbodimentError("first_embodiment_manifest_mismatch") + authority = RootAuthority( + manifest, + base.state, + {credential["artifact_id"]: credential}, + {incarnation["artifact_id"]: incarnation}, + ) + except ValueError as exception: + raise FirstEmbodimentError("invalid_first_embodiment_activation") from exception + expected_id = ACTIVATION_ID_PREFIX + b64url(digest(ACTIVATION_DOMAIN, body)) + if row["activation_id"] != expected_id: + raise FirstEmbodimentError("first_embodiment_activation_id_mismatch") + normalized = copy.deepcopy(dict(row)) + if canonical_bytes(normalized) != canonical_bytes(activation): + raise FirstEmbodimentError("noncanonical_first_embodiment_activation") + return normalized, authority + + +def _client( + runtime_id: str, + label: str, + origin: Mapping[str, Any], + capability: Mapping[str, Any], +) -> dict[str, Any]: + return { + "schema": CLIENT_CONFIG_SCHEMA_V3, + "capability": capability, + "expected_server": copy.deepcopy(dict(origin)), + "runtime_id": runtime_id, + "runtime_label": label, + } + + +def activate_runtime( + output: Path, + genesis: Any, + preparation_directory: Path, + preparation: Any, + request: Any, + activation: Any, + password_reader: PasswordReader, +) -> dict[str, Any]: + """Build one fresh V7 package without centralizing any root seed.""" + + base = _initial_base(genesis) + verified_activation, authority = validate_activation(genesis, request, activation) + issued_at_ms = verified_activation["body"]["issued_at_ms"] + try: + verified_preparation, body_secrets, transport_secrets = _validated_preparation( + preparation_directory, + preparation, + request, + base, + password_reader, + observed_at_ms=issued_at_ms, + expected_targets=set(), + ) + except RebirthError as exception: + raise FirstEmbodimentError("first_embodiment_target_rejected") from exception + target = Path(os.path.abspath(output)) + parent = _owner_directory(target.parent, "first_embodiment_output_parent_rejected") + if target.exists() or target.is_symlink(): + raise FirstEmbodimentError("first_embodiment_output_exists") + staging: Path | None = None + supplied = password_reader() + if not isinstance(supplied, (bytes, bytearray)) or not 12 <= len(supplied) <= 1024: + raise FirstEmbodimentError("invalid_first_embodiment_password_length") + password = bytearray(supplied) + if isinstance(supplied, bytearray): + supplied[:] = b"\x00" * len(supplied) + try: + staging = Path(tempfile.mkdtemp(prefix=f".{target.name}.tmp-", dir=parent)) + staging.chmod(0o700) + runtime = staging / "runtime" + runtime.mkdir(mode=0o700) + operator_clients = runtime / "operator-clients" + operator_clients.mkdir(mode=0o700) + host_clients = runtime / "host-clients" + host_clients.mkdir(mode=0o700) + profile = verified_preparation["profile"] + label = profile["label"] + origin = verified_preparation["origin"] + runtime_id = verified_preparation["runtime_id"] + slots = verified_preparation["slots"] + capabilities = verified_preparation["capabilities"] + host_capabilities = verified_preparation["host_capabilities"] + capability_rows = [ + { + "descriptor": capabilities[name], + "profile": operator_capability_profile(name), + "runtime_id": runtime_id, + "secret_slot": slots["operator_capabilities"][name], + } + for name in OPERATOR_PROFILE_NAMES + ] + capability_rows.extend( + { + "descriptor": host_capabilities[name], + "profile": host_capability_profile(name), + "runtime_id": runtime_id, + "secret_slot": slots["host_capabilities"][name], + } + for name in HOST_PROFILE_NAMES + ) + try: + capability_binding = create_operator_capability_binding( + runtime_id=runtime_id, + runtime_label=label, + being_ref=authority.manifest.being_ref, + origin=origin, + signing_seed=body_secrets[slots["signing"]], + capability_rows=capability_rows, + ) + except (KeyError, OperatorCapabilityError) as exception: + raise FirstEmbodimentError( + "first_embodiment_target_capability_rejected" + ) from exception + bundle = { + "schema": "dm.runtime.bundle/v7", + "runtime_id": runtime_id, + "runtime_label": label, + "control_artifacts": [copy.deepcopy(dict(genesis))], + "control_head": authority.state.head, + "manifest": authority.manifest.value, + "authority_history": [], + "credentials": list(authority.credentials.values()), + "incarnations": list(authority.incarnations.values()), + "binding": None, + "binding_activation": None, + "provisional_history": None, + "local_origin": origin, + "ledger": "ledger.sqlite", + "socket": "matrix.sock", + "keystore": { + "filename": "custody.json", + "counter": 1, + "signing_slot": slots["signing"], + }, + "capabilities": capability_rows, + "operator_capability_binding": capability_binding, + "routing": None, + "scopes": {"body_capabilities": [], "relationships_filename": None}, + "peer_transport": { + "enabled": True, + "encryption_slot": slots["encryption"], + "exchange_filename": "peer-exchange.sqlite", + "listen_host": profile["listen_host"], + "listen_port": profile["listen_port"], + "outbox_filename": "peer-outbox.sqlite", + "targets": [], + }, + "species": None, + "sources": {"cas_filename": "sources.sqlite3", "known_beings": []}, + "relationships": { + "known_being_refs": [], + "store_filename": "relationships.sqlite3", + }, + } + EncryptedKeystore.create( + runtime / "custody.json", + _reader(password), + control_head=authority.state.head, + secrets=body_secrets, + ) + EncryptedKeystore.create( + runtime / "transport-custody.json", + _reader(password), + control_head=authority.state.head, + secrets=transport_secrets, + ) + _private_write(runtime / "runtime.json", bundle) + _private_write( + runtime / "client.json", + _client(runtime_id, label, origin, capabilities[OBSERVE_PROFILE]), + ) + _private_write( + runtime / "client.key", + body_secrets[slots["operator_capabilities"][OBSERVE_PROFILE]], + ) + for name in OPERATOR_PROFILE_NAMES: + if name == OBSERVE_PROFILE: + continue + role = operator_clients / name + role.mkdir(mode=0o700) + _private_write( + role / "client.json", + _client(runtime_id, label, origin, capabilities[name]), + ) + _private_write( + role / "capability.key", + body_secrets[slots["operator_capabilities"][name]], + ) + _fsync_directory(role) + for name in HOST_PROFILE_NAMES: + role = host_clients / name + role.mkdir(mode=0o700) + _private_write( + role / "client.json", + _client(runtime_id, label, origin, host_capabilities[name]), + ) + _private_write( + role / "capability.key", + body_secrets[slots["host_capabilities"][name]], + ) + _fsync_directory(role) + authority_document = { + "schema": "dm.operator.authority/v1", + "control_artifacts": [copy.deepcopy(dict(genesis))], + "control_head": authority.state.head, + "manifest": authority.manifest.value, + "credentials": list(authority.credentials.values()), + "incarnations": list(authority.incarnations.values()), + } + _private_write(staging / "authority.json", authority_document) + _private_write(staging / "request.json", request) + _private_write(staging / "activation.json", verified_activation) + receipt = { + "schema": RECEIPT_SCHEMA, + "activation_id": verified_activation["activation_id"], + "request_id": verified_activation["body"]["request_id"], + "being_ref": authority.manifest.being_ref, + "control_head": authority.state.head, + "manifest_hash": authority.manifest.digest, + "origin": origin, + "runtime_schema": bundle["schema"], + "runtime_sha256": hashlib.sha256(canonical_bytes(bundle)).hexdigest(), + "empty_writable_state": True, + "root_seeds_in_target": False, + "capability_lifecycle": verified_preparation["capability_lifecycle"], + } + _private_write(staging / "receipt.json", receipt) + _fsync_directory(operator_clients) + _fsync_directory(host_clients) + _fsync_directory(runtime) + _fsync_directory(staging) + os.replace(staging, target) + _fsync_directory(parent) + staging = None + return receipt + finally: + password[:] = b"\x00" * len(password) + if staging is not None and staging.exists(): + shutil.rmtree(staging) + + +def parser() -> argparse.ArgumentParser: + result = argparse.ArgumentParser( + prog="daimon-first-embodiment", description=__doc__ + ) + commands = result.add_subparsers(dest="command", required=True) + prepare = commands.add_parser("prepare") + prepare.add_argument("--genesis", type=Path, required=True) + prepare.add_argument("--profile", type=Path, required=True) + prepare.add_argument("--password-fd", type=int, required=True) + prepare.add_argument("--output", type=Path, required=True) + share = commands.add_parser("root-share") + share.add_argument("--genesis", type=Path, required=True) + share.add_argument("--request", type=Path, required=True) + share.add_argument("--holder", type=Path, required=True) + share.add_argument("--password-fd", type=int, required=True) + share.add_argument("--output", type=Path, required=True) + aggregate = commands.add_parser("aggregate") + aggregate.add_argument("--genesis", type=Path, required=True) + aggregate.add_argument("--request", type=Path, required=True) + aggregate.add_argument("--share", type=Path, action="append", required=True) + aggregate.add_argument("--output", type=Path, required=True) + activate = commands.add_parser("activate") + activate.add_argument("--genesis", type=Path, required=True) + activate.add_argument("--preparation-dir", type=Path, required=True) + activate.add_argument("--request", type=Path, required=True) + activate.add_argument("--activation", type=Path, required=True) + activate.add_argument("--password-fd", type=int, required=True) + activate.add_argument("--output", type=Path, required=True) + return result + + +def main(argv: Sequence[str] | None = None) -> int: + arguments = parser().parse_args(argv) + passwords: list[bytearray] = [] + try: + genesis = _document(arguments.genesis, "first_embodiment_genesis_unavailable") + if arguments.command == "prepare": + password = _password(arguments.password_fd) + passwords.append(password) + profile = _document( + arguments.profile, "first_embodiment_profile_unavailable" + ) + receipt = prepare_target( + arguments.output, + genesis, + profile, + _reader(password), + created_at_ms=time.time_ns() // 1_000_000, + ) + elif arguments.command == "root-share": + password = _password(arguments.password_fd) + passwords.append(password) + request = _document( + arguments.request, "first_embodiment_request_unavailable" + ) + receipt = create_root_share( + genesis, + request, + arguments.holder, + _reader(password), + observed_at_ms=time.time_ns() // 1_000_000, + ) + _write_new(arguments.output, receipt) + elif arguments.command == "aggregate": + request = _document( + arguments.request, "first_embodiment_request_unavailable" + ) + receipt = aggregate_activation( + genesis, + request, + [ + _document(path, "first_embodiment_share_unavailable") + for path in arguments.share + ], + observed_at_ms=time.time_ns() // 1_000_000, + ) + _write_new(arguments.output, receipt) + else: + password = _password(arguments.password_fd) + passwords.append(password) + request = _document( + arguments.request, "first_embodiment_request_unavailable" + ) + activation = _document( + arguments.activation, "first_embodiment_activation_unavailable" + ) + preparation = _document( + arguments.preparation_dir / "preparation.json", + "first_embodiment_preparation_unavailable", + ) + receipt = activate_runtime( + arguments.output, + genesis, + arguments.preparation_dir, + preparation, + request, + activation, + _reader(password), + ) + print(canonical_bytes(receipt).decode("utf-8")) + return 0 + except ( + FirstEmbodimentError, + RebirthError, + OSError, + TypeError, + ValueError, + ) as exception: + print(f"daimon-first-embodiment: {exception}", file=sys.stderr) + return 2 + finally: + for password in passwords: + password[:] = b"\x00" * len(password) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/daimon_matrix/operator_rebirth.py b/src/daimon_matrix/operator_rebirth.py index 997078c..8128c57 100644 --- a/src/daimon_matrix/operator_rebirth.py +++ b/src/daimon_matrix/operator_rebirth.py @@ -96,6 +96,19 @@ ACTIVATION_SCHEMA: Final = "dm.operator.embodiment-activation/v1" ACTIVATION_DOMAIN: Final = "dm.operator.embodiment-activation/v1" ACTIVATION_ID_PREFIX: Final = "dm:embodiment-activation:v1:" +DISTRIBUTED_ENROLLMENT_INTENT_SCHEMA: Final = ( + "dm.operator.distributed-enrollment-intent/v1" +) +DISTRIBUTED_ENROLLMENT_INTENT_DOMAIN: Final = ( + "dm.operator.distributed-enrollment-intent/v1" +) +DISTRIBUTED_ENROLLMENT_INTENT_ID_PREFIX: Final = "dm:distributed-enrollment-intent:v1:" +DISTRIBUTED_ENROLLMENT_SHARE_SCHEMA: Final = ( + "dm.operator.distributed-enrollment-share/v1" +) +DISTRIBUTED_ENROLLMENT_SHARE_DOMAIN: Final = ( + "dm.operator.distributed-enrollment-share/v1" +) 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:" @@ -713,6 +726,320 @@ def authorize_enrollment_request( return activation +def _distributed_enrollment_material( + request: Any, + base: RootAuthority, + *, + issued_at_ms: int, +) -> dict[str, Any]: + """Build the exact unsigned credential successor and epoch transition.""" + + issued = _uint(issued_at_ms, "invalid_rebirth_time") + verified = validate_enrollment_request(request, base, observed_at_ms=issued) + credential = copy.deepcopy(verified["body"]["credential"]) + incarnation = copy.deepcopy(verified["body"]["incarnation"]) + origin = _origin(verified["body"]["origin"]) + rows = copy.deepcopy(base.manifest.value["embodiments"]) + rows.append( + { + "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", + } + ) + rows.sort(key=lambda row: (row["embodiment_id"], row["incarnation_id"])) + manifest = BeingManifest.from_value( + { + **base.manifest.value, + "revision": base.manifest.value["revision"] + 1, + "embodiments": rows, + } + ) + transition = create_embodiment_enrollment( + base.manifest, + manifest, + request_id=verified["request_id"], + 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 enrollment transition contains signatures") + return { + "request_id": verified["request_id"], + "being_ref": base.manifest.being_ref, + "control_head": base.state.head, + "previous_manifest_hash": base.manifest.digest, + "successor_manifest": manifest.value, + "credential": credential, + "incarnation": incarnation, + "origin": origin, + "transition": transition, + "issued_at_ms": issued, + } + + +def create_distributed_enrollment_intent( + request: Any, + base: RootAuthority, + *, + issued_at_ms: int, + expires_at_ms: int, + nonce: bytes, +) -> dict[str, Any]: + """Freeze one normal enrollment before isolated root holders sign it.""" + + material = _distributed_enrollment_material( + request, base, issued_at_ms=issued_at_ms + ) + expires = _uint(expires_at_ms, "invalid_rebirth_enrollment_intent_time") + if ( + expires <= material["issued_at_ms"] + or expires - material["issued_at_ms"] > 24 * 60 * 60 * 1000 + ): + raise RebirthError("invalid_rebirth_enrollment_intent_time") + if len(nonce) != 32: + raise RebirthError("invalid_rebirth_enrollment_intent_nonce") + body = { + "activation_body": material, + "expires_at_ms": expires, + "nonce": b64url(nonce), + } + result = { + "schema": DISTRIBUTED_ENROLLMENT_INTENT_SCHEMA, + "intent_id": DISTRIBUTED_ENROLLMENT_INTENT_ID_PREFIX + + b64url(digest(DISTRIBUTED_ENROLLMENT_INTENT_DOMAIN, body)), + "body": body, + } + _canonical(result, "invalid_rebirth_enrollment_intent") + return result + + +def validate_distributed_enrollment_intent( + value: Any, + request: Any, + base: RootAuthority, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Rebuild every public byte before accepting a holder signature.""" + + row = _closed( + value, + {"schema", "intent_id", "body"}, + "invalid_rebirth_enrollment_intent", + ) + if row["schema"] != DISTRIBUTED_ENROLLMENT_INTENT_SCHEMA: + raise RebirthError("unsupported_rebirth_enrollment_intent") + body = _closed( + row["body"], + {"activation_body", "expires_at_ms", "nonce"}, + "invalid_rebirth_enrollment_intent", + ) + observed = _uint(observed_at_ms, "invalid_rebirth_enrollment_intent_time") + expires = _uint(body["expires_at_ms"], "invalid_rebirth_enrollment_intent_time") + activation_body = body["activation_body"] + if not isinstance(activation_body, Mapping): + raise RebirthError("invalid_rebirth_enrollment_intent") + issued = _uint( + activation_body.get("issued_at_ms"), + "invalid_rebirth_enrollment_intent_time", + ) + try: + nonce = b64url(unb64url(str(body["nonce"]), length=32)) + except (TypeError, ValueError) as exception: + raise RebirthError("invalid_rebirth_enrollment_intent_nonce") from exception + if ( + nonce != body["nonce"] + or expires <= issued + or expires - issued > 24 * 60 * 60 * 1000 + or not issued <= observed < expires + ): + raise RebirthError("rebirth_enrollment_intent_not_timely") + expected = _distributed_enrollment_material(request, base, issued_at_ms=issued) + if canonical_bytes(expected) != canonical_bytes(activation_body): + raise RebirthError("rebirth_enrollment_intent_mismatch") + expected_id = DISTRIBUTED_ENROLLMENT_INTENT_ID_PREFIX + b64url( + digest(DISTRIBUTED_ENROLLMENT_INTENT_DOMAIN, body) + ) + normalized = copy.deepcopy(dict(row)) + if row["intent_id"] != expected_id or _canonical( + normalized, "invalid_rebirth_enrollment_intent" + ) != _canonical(value, "invalid_rebirth_enrollment_intent"): + raise RebirthError("invalid_rebirth_enrollment_intent") + return normalized + + +def create_distributed_enrollment_share( + intent: Any, + request: Any, + base: RootAuthority, + root_seed: bytes, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Sign credential and manifest transition with exactly one root key.""" + + verified = validate_distributed_enrollment_intent( + intent, request, base, observed_at_ms=observed_at_ms + ) + body = verified["body"]["activation_body"] + kid = key_id("Ed25519", ed25519_public(root_seed)) + if kid not in {row["key_id"] for row in base.state.root_policy["keys"]}: + raise RebirthError("rebirth_root_holder_not_authorized") + credential = body["credential"] + credential_signature = _identity_signature( + root_seed, + "root-authorization", + domain_bytes(DOMAINS["embodiment-credential"], credential["body"]), + ) + signed_transition = create_embodiment_enrollment( + base.manifest, + BeingManifest.from_value(body["successor_manifest"]), + request_id=body["request_id"], + body_ref=body["origin"]["body_ref"], + embodiment_id=body["origin"]["embodiment_id"], + incarnation_id=body["origin"]["incarnation_id"], + embodiment_credential_id=credential["artifact_id"], + incarnation_authorization_id=body["incarnation"]["artifact_id"], + principal_id=body["origin"]["principal_id"], + root_seeds=[root_seed], + issued_at_ms=body["issued_at_ms"], + ) + transition = body["transition"] + 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_enrollment_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_ENROLLMENT_SHARE_SCHEMA, + **share_body, + "attestation": _holder_attestation( + root_seed, DISTRIBUTED_ENROLLMENT_SHARE_DOMAIN, share_body + ), + } + + +def aggregate_distributed_enrollment( + intent: Any, + request: Any, + base: RootAuthority, + shares: Sequence[Any], + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Build a normal activation without opening any root holder store.""" + + verified = validate_distributed_enrollment_intent( + intent, request, base, observed_at_ms=observed_at_ms + ) + body = copy.deepcopy(verified["body"]["activation_body"]) + credential = body["credential"] + transition = body["transition"] + 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_enrollment_share", + ) + credential_signature = share["credential_signature"] + transition_signature = share["transition_signature"] + if ( + share["schema"] != DISTRIBUTED_ENROLLMENT_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_enrollment_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_ENROLLMENT_SHARE_DOMAIN, + share_body, + base.state.root_policy["keys"], + code="invalid_rebirth_enrollment_share", + ) + if credential_signature.get("key_id") != attested_kid: + raise RebirthError("invalid_rebirth_enrollment_share") + paired_ids.add(str(attested_kid)) + 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, base.state, at_ms=body["issued_at_ms"]) + verify_incarnation_authorization( + body["incarnation"], + credential, + base.state, + at_ms=body["issued_at_ms"], + ) + successor = RootAuthority( + BeingManifest.from_value(body["successor_manifest"]), + base.state, + {**base.credentials, credential["artifact_id"]: credential}, + { + **base.incarnations, + body["incarnation"]["artifact_id"]: body["incarnation"], + }, + ) + verify_embodiment_enrollment(transition, base, successor) + RootHistoryAuthority(successor, [base], [transition]) + except (TypeError, ValueError) as exception: + raise RebirthError("rebirth_enrollment_share_threshold_rejected") from exception + activation = { + "schema": ACTIVATION_SCHEMA, + "activation_id": ACTIVATION_ID_PREFIX + b64url(digest(ACTIVATION_DOMAIN, body)), + "body": body, + } + validate_activation(activation, base, request=request) + return activation + + def validate_activation( value: Any, base: RootAuthority, @@ -769,6 +1096,7 @@ def validate_activation( origin = _origin(body["origin"]) if ( transition["request_id"] != body["request_id"] + or transition["issued_at_ms"] != body["issued_at_ms"] or transition["body_ref"] != origin["body_ref"] or transition["embodiment_id"] != origin["embodiment_id"] or transition["incarnation_id"] != origin["incarnation_id"] @@ -1313,7 +1641,8 @@ def validate_recovery_activation( ) origin = _origin(body["origin"]) if ( - transition["embodiment_id"] != origin["embodiment_id"] + transition["issued_at_ms"] != body["issued_at_ms"] + or transition["embodiment_id"] != origin["embodiment_id"] or transition["incarnation_id"] != origin["incarnation_id"] or transition["body_ref"] != origin["body_ref"] or transition["principal_id"] != origin["principal_id"] @@ -1333,6 +1662,103 @@ def validate_recovery_activation( return normalized, successor, history +def _validate_runtime_peer_transport( + bundle: Mapping[str, Any], + authority: RootAuthority, + *, + exact_targets: bool, +) -> None: + """Validate the public peer portion of a V7 candidate without custody.""" + + peer = _closed( + bundle.get("peer_transport"), + { + "enabled", + "encryption_slot", + "exchange_filename", + "listen_host", + "listen_port", + "outbox_filename", + "targets", + }, + "rebirth_runtime_peer_transport_invalid", + ) + encryption_slot = peer["encryption_slot"] + listen_host = peer["listen_host"] + listen_port = peer["listen_port"] + filenames = (peer["exchange_filename"], peer["outbox_filename"]) + if ( + peer["enabled"] is not True + or not isinstance(encryption_slot, str) + or not encryption_slot.startswith("peer.encryption.v1:") + or not isinstance(listen_host, str) + or not 1 <= len(listen_host.encode("utf-8")) <= 255 + or any(character.isspace() for character in listen_host) + or not isinstance(listen_port, int) + or isinstance(listen_port, bool) + or not 1 <= listen_port <= 65_535 + or any( + not isinstance(filename, str) + or re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]{0,127}", filename) is None + for filename in filenames + ) + or len(set(filenames)) != 2 + ): + raise RebirthError("rebirth_runtime_peer_transport_invalid") + origin = _origin(bundle.get("local_origin")) + manifested_origins = [ + row + for row in authority.manifest.value["embodiments"] + if row["status"] == "active" + and row["body_ref"] == origin["body_ref"] + and row["embodiment_id"] == origin["embodiment_id"] + and row["incarnation_id"] == origin["incarnation_id"] + ] + if len(manifested_origins) != 1: + raise RebirthError("rebirth_runtime_peer_transport_invalid") + targets = peer["targets"] + if not isinstance(targets, list) or len(targets) > 255: + raise RebirthError("rebirth_runtime_peer_transport_invalid") + normalized: list[tuple[str, str, int]] = [] + for value in targets: + target = _closed( + value, + {"embodiment_id", "endpoint", "timeout_ms"}, + "rebirth_runtime_peer_transport_invalid", + ) + embodiment_id = target["embodiment_id"] + endpoint = target["endpoint"] + timeout_ms = target["timeout_ms"] + if ( + not isinstance(embodiment_id, str) + or not embodiment_id + or not isinstance(endpoint, str) + or not isinstance(timeout_ms, int) + or isinstance(timeout_ms, bool) + or not 1 <= timeout_ms <= 30_000 + ): + raise RebirthError("rebirth_runtime_peer_transport_invalid") + try: + http_peer_round_trip(endpoint, timeout_seconds=timeout_ms / 1000) + except (PeerTransportError, TypeError, ValueError) as exception: + raise RebirthError("rebirth_runtime_peer_transport_invalid") from exception + normalized.append((embodiment_id, endpoint, timeout_ms)) + if normalized != sorted(normalized) or len({row[0] for row in normalized}) != len( + normalized + ): + raise RebirthError("rebirth_runtime_peer_transport_invalid") + expected = { + str(row["embodiment_id"]) + for row in authority.manifest.value["embodiments"] + if row["status"] == "active" and row["embodiment_id"] != origin["embodiment_id"] + } + actual = {row[0] for row in normalized} + if (exact_targets and actual != expected) or ( + not exact_targets and not actual.issubset(expected) + ): + raise RebirthError("rebirth_runtime_peer_transport_invalid") + + def apply_activation_to_runtime_bundle( bundle: Mapping[str, Any], activation: Any, @@ -1343,8 +1769,10 @@ def apply_activation_to_runtime_bundle( """Return a forward-only public bundle update for an existing peer.""" verified, successor, _history = validate_activation(activation, base) - if bundle.get("manifest") != base.manifest.value: + source_authority = authority_from_runtime_bundle(bundle) + if source_authority != base: raise RebirthError("rebirth_runtime_base_mismatch") + _validate_runtime_peer_transport(bundle, source_authority, exact_targets=True) result = copy.deepcopy(dict(bundle)) authority_history = result.get("authority_history") if not isinstance(authority_history, list): @@ -1375,6 +1803,14 @@ def apply_activation_to_runtime_bundle( } ) peer["targets"].sort(key=lambda row: row["embodiment_id"]) + candidate_authority = authority_from_runtime_bundle(result) + if candidate_authority != successor: + raise RebirthError("rebirth_runtime_candidate_mismatch") + _validate_runtime_peer_transport( + result, + candidate_authority, + exact_targets=target_endpoint is not None, + ) return result @@ -1386,11 +1822,10 @@ def apply_recovery_activation_to_runtime_bundle( """Return a target-only V7 bundle while preserving old public history.""" verified, successor, _history = validate_recovery_activation(activation, previous) - if ( - bundle.get("manifest") != previous.manifest.value - or bundle.get("control_head") != previous.state.head - ): + source_authority = authority_from_runtime_bundle(bundle) + if source_authority != previous: raise RebirthError("rebirth_recovery_runtime_base_mismatch") + _validate_runtime_peer_transport(bundle, source_authority, exact_targets=True) result = copy.deepcopy(dict(bundle)) authority_history = result.get("authority_history") control_artifacts = result.get("control_artifacts") @@ -1429,6 +1864,8 @@ def apply_recovery_activation_to_runtime_bundle( if not isinstance(peer, dict) or not isinstance(peer.get("targets"), list): raise RebirthError("rebirth_runtime_peer_transport_missing") peer["targets"] = [] + if authority_from_runtime_bundle(result) != successor: + raise RebirthError("rebirth_runtime_candidate_mismatch") return result @@ -1653,6 +2090,7 @@ def _safe_document(path: Path, code: str) -> Any: def _owner_directory(path: Path, code: str) -> Path: absolute = Path(os.path.abspath(path)) + _reject_symlink_ancestors(absolute, code) try: info = absolute.lstat() except FileNotFoundError as exception: @@ -1667,6 +2105,18 @@ def _owner_directory(path: Path, code: str) -> Path: return absolute +def _reject_symlink_ancestors(path: Path, code: str) -> None: + ancestor = path.parent + while ancestor != ancestor.parent: + try: + info = ancestor.lstat() + except OSError as exception: + raise RebirthError(code) from exception + if stat.S_ISLNK(info.st_mode) or not stat.S_ISDIR(info.st_mode): + raise RebirthError(code) + ancestor = ancestor.parent + + def _owner_file_descriptor( path: Path, code: str, @@ -1677,6 +2127,7 @@ def _owner_file_descriptor( """Open one stable owner-only regular file without following replacements.""" absolute = Path(os.path.abspath(path)) + _reject_symlink_ancestors(absolute, code) try: before = absolute.lstat() except FileNotFoundError as exception: @@ -1693,7 +2144,7 @@ def _owner_file_descriptor( try: descriptor = os.open( absolute, - os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0), + os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0), ) after = os.fstat(descriptor) if ( @@ -2422,6 +2873,9 @@ def _activate_target_runtime( "known_being_refs": known_refs, "store_filename": "relationships.sqlite3", } + if authority_from_runtime_bundle(bundle) != successor: + raise RebirthError("rebirth_runtime_candidate_mismatch") + _validate_runtime_peer_transport(bundle, successor, exact_targets=True) EncryptedKeystore.create( runtime_root / "custody.json", _password_reader(password), @@ -3186,6 +3640,33 @@ def _validated_existing_replacement_holder( raise RebirthError("rebirth_holder_conflict") from exception +def create_distributed_enrollment_share_from_holder( + intent: Any, + request: Any, + base: RootAuthority, + holder_path: Path, + password_reader: PasswordReader, + *, + observed_at_ms: int, +) -> dict[str, Any]: + """Open one isolated root holder and emit one paired enrollment share.""" + + holder_seed = _single_holder_seed( + holder_path, + base, + password_reader, + allowed_prefixes=("root.signing.v1:",), + code="rebirth_root_holder_store_rejected", + ) + return create_distributed_enrollment_share( + intent, + request, + base, + holder_seed, + observed_at_ms=observed_at_ms, + ) + + def create_distributed_recovery_share_from_holder( intent: Any, previous: RootAuthority, @@ -3445,6 +3926,45 @@ def parser() -> argparse.ArgumentParser: prepare.add_argument("--output", type=Path, required=True) prepare.add_argument("--password-fd", type=int, required=True) prepare.add_argument("--ttl-seconds", type=int, default=3600) + enrollment_intent = commands.add_parser( + "create-enrollment-intent", + help="freeze one normal enrollment for isolated root holders", + ) + enrollment_intent.add_argument("--authority", type=Path, required=True) + enrollment_intent.add_argument("--request", type=Path, required=True) + enrollment_intent.add_argument("--ttl-seconds", type=int, default=3600) + enrollment_intent.add_argument("--output", type=Path, required=True) + enrollment_share = commands.add_parser( + "enrollment-share", + help="emit one paired enrollment share from one isolated root holder", + ) + enrollment_share.add_argument("--authority", type=Path, required=True) + enrollment_share.add_argument("--request", type=Path, required=True) + enrollment_share.add_argument("--intent", type=Path, required=True) + enrollment_share.add_argument("--holder", type=Path, required=True) + enrollment_share.add_argument("--password-fd", type=int, required=True) + enrollment_share.add_argument("--output", type=Path, required=True) + aggregate_enrollment = commands.add_parser( + "aggregate-enrollment", + help="aggregate enrollment shares without opening holder stores", + ) + aggregate_enrollment.add_argument("--authority", type=Path, required=True) + aggregate_enrollment.add_argument("--request", type=Path, required=True) + aggregate_enrollment.add_argument("--intent", type=Path, required=True) + aggregate_enrollment.add_argument( + "--share", type=Path, action="append", required=True + ) + aggregate_enrollment.add_argument("--output", type=Path, required=True) + advance_bundle = commands.add_parser( + "advance-bundle", + help="write a verified forward-only bundle candidate for an existing peer", + ) + advance_bundle.add_argument("--authority", type=Path, required=True) + advance_bundle.add_argument("--base-runtime", type=Path, required=True) + advance_bundle.add_argument("--request", type=Path, required=True) + advance_bundle.add_argument("--activation", type=Path, required=True) + advance_bundle.add_argument("--target-endpoint", required=True) + advance_bundle.add_argument("--output", type=Path, required=True) authorize = commands.add_parser("authorize", help="offline-root authorization") authorize.add_argument("--authority", type=Path, required=True) authorize.add_argument("--request", type=Path, required=True) @@ -3594,6 +4114,77 @@ def main(argv: Sequence[str] | None = None) -> int: ) finally: password[:] = b"\x00" * len(password) + elif arguments.command == "create-enrollment-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_enrollment_intent( + _safe_document(arguments.request, "rebirth_request_unavailable"), + authority, + issued_at_ms=now, + expires_at_ms=now + ttl * 1000, + nonce=secrets.token_bytes(32), + ) + _write_new_document(arguments.output, receipt) + elif arguments.command == "enrollment-share": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + password = _password(arguments.password_fd) + try: + receipt = create_distributed_enrollment_share_from_holder( + _safe_document( + arguments.intent, "rebirth_enrollment_intent_unavailable" + ), + _safe_document(arguments.request, "rebirth_request_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-enrollment": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + receipt = aggregate_distributed_enrollment( + _safe_document( + arguments.intent, "rebirth_enrollment_intent_unavailable" + ), + _safe_document(arguments.request, "rebirth_request_unavailable"), + authority, + [ + _safe_document(path, "rebirth_enrollment_share_unavailable") + for path in arguments.share + ], + observed_at_ms=now, + ) + _write_new_document(arguments.output, receipt) + elif arguments.command == "advance-bundle": + authority = authority_from_document( + _safe_document(arguments.authority, "rebirth_authority_unavailable") + ) + request = _safe_document(arguments.request, "rebirth_request_unavailable") + activation = _safe_document( + arguments.activation, "rebirth_activation_unavailable" + ) + validate_activation(activation, authority, request=request) + receipt = apply_activation_to_runtime_bundle( + _safe_document(arguments.base_runtime, "rebirth_runtime_unavailable"), + activation, + authority, + target_endpoint=arguments.target_endpoint, + ) + _write_new_document(arguments.output, receipt) elif arguments.command == "create-recovery-intent": authority = authority_from_document( _safe_document(arguments.authority, "rebirth_authority_unavailable") @@ -3923,6 +4514,7 @@ def main(argv: Sequence[str] | None = None) -> int: "RecoveryRequestBase", "activate_recovery_target_runtime", "activate_target_runtime", + "aggregate_distributed_enrollment", "apply_activation_to_runtime_bundle", "apply_recovery_activation_to_runtime_bundle", "authority_from_document", @@ -3930,6 +4522,9 @@ def main(argv: Sequence[str] | None = None) -> int: "authorize_enrollment_request", "authorize_from_root_custody", "authorize_synthetic_single_store_recovery", + "create_distributed_enrollment_intent", + "create_distributed_enrollment_share", + "create_distributed_enrollment_share_from_holder", "create_enrollment_request", "create_recovery_target_preparation", "create_synthetic_single_store_recovery_custody", @@ -3939,6 +4534,7 @@ def main(argv: Sequence[str] | None = None) -> int: "recovery_request_base", "restore_recovery_ledger", "validate_activation", + "validate_distributed_enrollment_intent", "validate_enrollment_request", "validate_recovery_activation", ] diff --git a/tests/test_dm041_hermes_body.py b/tests/test_dm041_hermes_body.py index c761a13..3c84e7c 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"]), 51) + self.assertEqual(len(manifest["matrix_package"]["modules"]), 52) self.assertIn( "collective_memory.py", {item["name"] for item in manifest["matrix_package"]["modules"]}, @@ -519,6 +519,10 @@ def test_profile_is_deterministic_exclusive_and_native_memory_free(self) -> None "operator_genesis.py", {item["name"] for item in manifest["matrix_package"]["modules"]}, ) + self.assertIn( + "operator_first_embodiment.py", + {item["name"] for item in manifest["matrix_package"]["modules"]}, + ) self.assertEqual( len(manifest["matrix_package"]["tree_sha256"]), 64, diff --git a/tests/test_dm078_rebirth.py b/tests/test_dm078_rebirth.py index ef8c703..720a3cf 100644 --- a/tests/test_dm078_rebirth.py +++ b/tests/test_dm078_rebirth.py @@ -20,7 +20,7 @@ create_embodiment_enrollment, verify_embodiment_enrollment, ) -from daimon_matrix.canonical import canonical_bytes +from daimon_matrix.canonical import b64url, canonical_bytes, digest from daimon_matrix.identity import signing_descriptor, verify_genesis from daimon_matrix.keystore import EncryptedKeystore from daimon_matrix.ledger import Ledger @@ -33,6 +33,8 @@ operator_runtime_id, ) from daimon_matrix.operator_rebirth import ( + ACTIVATION_DOMAIN, + ACTIVATION_ID_PREFIX, RebirthError, activate_target_runtime, apply_activation_to_runtime_bundle, @@ -311,18 +313,7 @@ def test_threshold_stale_tamper_replay_and_unrelated_delta_fail(self) -> None: def test_activation_updates_public_peers_forward_only(self) -> None: request, activation = self.activation() - existing_target = { - "embodiment_id": self.origins["daimonmatrix"]["embodiment_id"], - "endpoint": "http://127.0.0.1:18686/dm-peer/v1", - "timeout_ms": 5_000, - } - bundle = { - "manifest": copy.deepcopy(self.manifest.value), - "authority_history": [], - "credentials": list(self.credentials.values()), - "incarnations": list(self.incarnations.values()), - "peer_transport": {"targets": [existing_target]}, - } + bundle = self.base_runtime_bundle() updated = apply_activation_to_runtime_bundle( bundle, activation, @@ -344,6 +335,36 @@ def test_activation_updates_public_peers_forward_only(self) -> None: with self.assertRaisesRegex(RebirthError, "origin_mismatch"): validate_activation(forged, self.authority, request=request) + changed_time = copy.deepcopy(activation) + changed_time["body"]["issued_at_ms"] += 1 + changed_time["activation_id"] = ACTIVATION_ID_PREFIX + b64url( + digest(ACTIVATION_DOMAIN, changed_time["body"]) + ) + with self.assertRaisesRegex(RebirthError, "origin_mismatch"): + validate_activation(changed_time, self.authority, request=request) + + incomplete = { + "manifest": copy.deepcopy(self.manifest.value), + "authority_history": [], + "peer_transport": {"targets": []}, + } + with self.assertRaisesRegex(RebirthError, "invalid_rebirth_runtime_bundle"): + apply_activation_to_runtime_bundle( + incomplete, + activation, + self.authority, + target_endpoint="http://127.0.0.1:28686/dm-peer/v1", + ) + with self.assertRaisesRegex( + RebirthError, "rebirth_runtime_peer_transport_invalid" + ): + apply_activation_to_runtime_bundle( + self.base_runtime_bundle(), + activation, + self.authority, + target_endpoint="file:///etc/passwd", + ) + def test_target_preparation_and_offline_root_custody_are_separate(self) -> None: parent = self.root_path / "rebirth" parent.mkdir(mode=0o700) diff --git a/tests/test_dm078_recovery_rebirth.py b/tests/test_dm078_recovery_rebirth.py index 5ed264e..fcd0305 100644 --- a/tests/test_dm078_recovery_rebirth.py +++ b/tests/test_dm078_recovery_rebirth.py @@ -24,7 +24,7 @@ create_recovery_rebirth, verify_recovery_rebirth, ) -from daimon_matrix.canonical import canonical_bytes +from daimon_matrix.canonical import b64url, canonical_bytes, digest from daimon_matrix.client import CLIENT_CONFIG_SCHEMA_V3 from daimon_matrix.identity import ( create_embodiment_credential, @@ -54,6 +54,8 @@ ) from daimon_matrix.operator_genesis import PENDING_CONTROL_HEAD from daimon_matrix.operator_rebirth import ( + RECOVERY_ACTIVATION_DOMAIN, + RECOVERY_ACTIVATION_ID_PREFIX, RebirthError, activate_recovery_target_runtime, aggregate_distributed_recovery, @@ -106,12 +108,14 @@ def test_documented_rebirth_commands_are_current_public_surface(self) -> None: expected = { "activate", "activate-recovery", + "aggregate-enrollment", "aggregate-recovery", "aggregate-recovery-authorization", - "authorize", + "create-enrollment-intent", "create-recovery-authorization-intent", "create-recovery-intent", "create-replacement-root-holder", + "enrollment-share", "prepare", "prepare-recovery", "recovery-authorization-share", @@ -828,6 +832,13 @@ def test_target_request_is_signed_only_after_verified_recovery(self) -> None: changed["body"]["recovered_control_head"] = self.state.head with self.assertRaisesRegex(RebirthError, "base_mismatch"): validate_recovery_activation(changed, self.authority, request=request) + changed_time = copy.deepcopy(activation) + changed_time["body"]["issued_at_ms"] += 1 + changed_time["activation_id"] = RECOVERY_ACTIVATION_ID_PREFIX + b64url( + digest(RECOVERY_ACTIVATION_DOMAIN, changed_time["body"]) + ) + with self.assertRaisesRegex(RebirthError, "origin_mismatch"): + validate_recovery_activation(changed_time, self.authority, request=request) def test_recovery_preparation_builds_loadable_target_only_runtime(self) -> None: old_event = self.append(self.ledger_a, "legion", "before-runtime-recovery") diff --git a/tests/test_operator_first_embodiment.py b/tests/test_operator_first_embodiment.py new file mode 100644 index 0000000..b10d397 --- /dev/null +++ b/tests/test_operator_first_embodiment.py @@ -0,0 +1,707 @@ +from __future__ import annotations + +import copy +import hashlib +import json +import os +import socket +import subprocess +import sys +import threading +import uuid +from collections.abc import Sequence +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest import TestCase + +from daimon_matrix.canonical import b64url, canonical_bytes, digest +from daimon_matrix.daemon import create_peer_http_server +from daimon_matrix.keystore import EncryptedKeystore, PasswordReader +from daimon_matrix.local_api import LocalCapability, create_request +from daimon_matrix.operator_first_embodiment import ( + ACTIVATION_DOMAIN as FIRST_ACTIVATION_DOMAIN, +) +from daimon_matrix.operator_first_embodiment import ( + ACTIVATION_ID_PREFIX as FIRST_ACTIVATION_ID_PREFIX, +) +from daimon_matrix.operator_first_embodiment import ( + FirstEmbodimentError, + activate_runtime, + aggregate_activation, + create_root_share, + prepare_target, + validate_activation, +) +from daimon_matrix.operator_genesis import ( + PENDING_CONTROL_HEAD, + aggregate_intent, + create_holder_package, + create_holder_share, + create_intent, +) +from daimon_matrix.operator_rebirth import ( + REQUEST_DOMAIN, + REQUEST_ID_PREFIX, + TRANSPORT_REQUEST_DOMAIN, + RebirthError, + _request_signature, + activate_target_runtime, + aggregate_distributed_enrollment, + apply_activation_to_runtime_bundle, + create_distributed_enrollment_intent, + create_distributed_enrollment_share_from_holder, + create_target_preparation, +) +from daimon_matrix.operator_rebirth import ( + validate_activation as validate_rebirth_activation, +) +from daimon_matrix.runtime import load_runtime + +NOW = 1_800_000_000_000 +ROOT = Path(__file__).resolve().parents[1] + + +def _reader(value: bytes) -> PasswordReader: + return lambda: bytearray(value) + + +def _available_port() -> int: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as candidate: + candidate.bind(("127.0.0.1", 0)) + return int(candidate.getsockname()[1]) + + +class FirstEmbodimentTests(TestCase): + def setUp(self) -> None: + self.temporary = TemporaryDirectory(prefix="dm-first-embodiment-") + self.root = Path(self.temporary.name) + self.root.chmod(0o700) + self.passwords: dict[str, bytes] = {} + self.holders: dict[str, Path] = {} + descriptors: list[dict[str, object]] = [] + for role in ("root", "recovery"): + for index in range(3): + name = f"{role}-{index}" + password = f"first-{name}-password".encode() + holder = self.root / name + descriptor = create_holder_package( + holder, + role, + _reader(password), + ) + self.passwords[name] = password + self.holders[name] = holder + descriptors.append(descriptor) + intent = create_intent( + descriptors, + root_threshold=2, + recovery_threshold=2, + created_at_ms=NOW, + nonce=b"g" * 32, + ) + shares = [ + create_holder_share( + intent, self.holders[name], _reader(self.passwords[name]) + ) + for name in ("root-0", "root-1", "recovery-0", "recovery-1") + ] + self.genesis = aggregate_intent(intent, shares) + self.target_password = b"first-target-password" + self.first_port = _available_port() + self.preparation_directory = self.root / "preparation" + self.preparation = prepare_target( + self.preparation_directory, + self.genesis, + { + "schema": "dm.operator.rebirth-target-profile/v1", + "label": "first", + "body_ref": "cluster:test:first", + "principal_id": "first@test", + "listen_host": "127.0.0.1", + "listen_port": self.first_port, + "advertised_endpoint": ( + f"http://127.0.0.1:{self.first_port}/dm-peer/v1" + ), + "targets": [], + }, + _reader(self.target_password), + created_at_ms=NOW + 10, + ) + self.request = json.loads( + (self.preparation_directory / "request.json").read_bytes() + ) + + def tearDown(self) -> None: + self.temporary.cleanup() + + def invoke_rebirth( + 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_rebirth", *values], + cwd=ROOT, + env={**os.environ, "PYTHONPATH": str(ROOT / "src")}, + pass_fds=descriptors, + capture_output=True, + check=False, + ) + finally: + if reader >= 0: + os.close(reader) + + def root_share(self, name: str) -> dict[str, object]: + return create_root_share( + self.genesis, + self.request, + self.holders[name], + _reader(self.passwords[name]), + observed_at_ms=NOW + 20, + ) + + def activation(self) -> dict[str, object]: + return aggregate_activation( + self.genesis, + self.request, + [self.root_share("root-0"), self.root_share("root-1")], + observed_at_ms=NOW + 20, + ) + + def resigned_request(self) -> dict[str, object]: + body = copy.deepcopy(self.request["body"]) + body["expires_at_ms"] += 30_000 + body["nonce"] = b64url(b"r" * 32) + body_custody = EncryptedKeystore( + self.preparation_directory / "custody.json" + ).open( + _reader(self.target_password), + required_control_head=self.request["body"]["control_head"], + ) + transport_custody = EncryptedKeystore( + self.preparation_directory / "transport-custody.json" + ).open( + _reader(self.target_password), + required_control_head=self.request["body"]["control_head"], + ) + signing_seed = body_custody.secrets[self.preparation["slots"]["signing"]] + transport_seed = transport_custody.secrets[ + self.preparation["slots"]["transport"] + ] + return { + "schema": self.request["schema"], + "request_id": REQUEST_ID_PREFIX + b64url(digest(REQUEST_DOMAIN, body)), + "body": body, + "signature": _request_signature(signing_seed, body), + "transport_signature": _request_signature( + transport_seed, + body, + domain=TRANSPORT_REQUEST_DOMAIN, + ), + } + + def test_distributed_genesis_becomes_a_loadable_first_runtime(self) -> None: + activation = self.activation() + verified, authority = validate_activation( + self.genesis, self.request, activation + ) + self.assertEqual(verified, activation) + self.assertEqual(authority.manifest.value["revision"], 1) + self.assertEqual(len(authority.manifest.value["embodiments"]), 1) + + output = self.root / "activated" + receipt = activate_runtime( + output, + self.genesis, + self.preparation_directory, + self.preparation, + self.request, + activation, + _reader(self.target_password), + ) + self.assertEqual(receipt["runtime_schema"], "dm.runtime.bundle/v7") + self.assertFalse(receipt["root_seeds_in_target"]) + bundle = json.loads((output / "runtime/runtime.json").read_bytes()) + self.assertEqual(bundle["manifest"]["revision"], 1) + self.assertEqual(bundle["peer_transport"]["targets"], []) + runtime = load_runtime( + output / "runtime", + "runtime.json", + _reader(self.target_password), + clock=lambda: NOW + 20, + ) + self.assertEqual( + runtime.service.ledger.authority.manifest.digest, + receipt["manifest_hash"], + ) + + public = json.dumps( + { + "genesis": self.genesis, + "request": self.request, + "activation": activation, + "receipt": receipt, + } + ).encode() + for name, holder in self.holders.items(): + self.assertNotIn(self.passwords[name], public) + self.assertNotIn((holder / "holder.json").read_bytes(), public) + body_custody = EncryptedKeystore( + self.preparation_directory / "custody.json" + ).open( + _reader(self.target_password), + required_control_head=authority.state.head, + ) + self.assertFalse(any(slot.startswith("root.") for slot in body_custody.secrets)) + + def test_threshold_duplicates_roles_tamper_and_password_fail_closed(self) -> None: + first = self.root_share("root-0") + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_threshold_rejected" + ): + aggregate_activation( + self.genesis, + self.request, + [first], + observed_at_ms=NOW + 20, + ) + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_share_rejected" + ): + aggregate_activation( + self.genesis, + self.request, + [first, first], + observed_at_ms=NOW + 20, + ) + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_root_holder_rejected" + ): + self.root_share("recovery-0") + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_request_rejected" + ): + create_root_share( + self.genesis, + self.request, + self.holders["root-0"], + _reader(self.passwords["root-0"]), + observed_at_ms=self.request["body"]["expires_at_ms"], + ) + + activation = self.activation() + tampered = copy.deepcopy(activation) + tampered["body"]["origin"]["principal_id"] = "forged@test" # type: ignore[index] + with self.assertRaises(FirstEmbodimentError): + validate_activation(self.genesis, self.request, tampered) + + output = self.root / "wrong-password-output" + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_target_rejected" + ): + activate_runtime( + output, + self.genesis, + self.preparation_directory, + self.preparation, + self.request, + activation, + _reader(b"wrong-target-password"), + ) + self.assertFalse(output.exists()) + + root_store = EncryptedKeystore(self.holders["root-0"] / "holder.json").open( + _reader(self.passwords["root-0"]), + required_control_head=PENDING_CONTROL_HEAD, + ) + self.assertEqual(set(root_store.secrets), {"genesis.root.v1:holder"}) + + def test_root_approvals_cannot_be_reassociated_to_a_new_request(self) -> None: + shares = [self.root_share("root-0"), self.root_share("root-1")] + replacement = self.resigned_request() + rebound = copy.deepcopy(shares) + for share in rebound: + share["request_id"] = replacement["request_id"] + share["request_sha256"] = hashlib.sha256( + canonical_bytes(replacement) + ).hexdigest() + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_share_rejected" + ): + aggregate_activation( + self.genesis, + replacement, + rebound, + observed_at_ms=NOW + 20, + ) + + activation = aggregate_activation( + self.genesis, + self.request, + shares, + observed_at_ms=NOW + 20, + ) + reassociated = copy.deepcopy(activation) + reassociated["body"]["request_id"] = replacement["request_id"] + replacement_hash = hashlib.sha256(canonical_bytes(replacement)).hexdigest() + for approval in reassociated["body"]["root_approvals"]: + approval["request_id"] = replacement["request_id"] + approval["request_sha256"] = replacement_hash + reassociated["activation_id"] = FIRST_ACTIVATION_ID_PREFIX + b64url( + digest(FIRST_ACTIVATION_DOMAIN, reassociated["body"]) + ) + with self.assertRaisesRegex( + FirstEmbodimentError, "invalid_first_embodiment_activation" + ): + validate_activation(self.genesis, replacement, reassociated) + + def test_intermediate_symlink_and_fifo_inputs_fail_without_writes(self) -> None: + real = self.root / "real-output-parent" + real.mkdir(mode=0o700) + parent = real / "parent" + parent.mkdir(mode=0o700) + alias = self.root / "output-alias" + alias.symlink_to(real, target_is_directory=True) + with self.assertRaisesRegex( + FirstEmbodimentError, "first_embodiment_preparation_rejected" + ): + prepare_target( + alias / "parent/output", + self.genesis, + self.preparation["profile"], + _reader(b"another-target-password"), + created_at_ms=NOW + 20, + ) + self.assertFalse((parent / "output").exists()) + + fifo = self.root / "genesis.fifo" + os.mkfifo(fifo, 0o600) + result = subprocess.run( + [ + sys.executable, + "-m", + "daimon_matrix.operator_first_embodiment", + "prepare", + "--genesis", + str(fifo), + "--profile", + str(self.preparation_directory / "request.json"), + "--password-fd", + "0", + "--output", + str(self.root / "fifo-output"), + ], + cwd=ROOT, + env={**os.environ, "PYTHONPATH": str(ROOT / "src")}, + stdin=subprocess.DEVNULL, + capture_output=True, + check=False, + timeout=2, + ) + self.assertEqual(result.returncode, 2, result.stderr.decode()) + self.assertIn(b"first_embodiment_genesis_unavailable", result.stderr) + self.assertFalse((self.root / "fifo-output").exists()) + + def test_second_embodiment_syncs_an_event_from_before_its_birth(self) -> None: + first_activation = self.activation() + _verified, first_authority = validate_activation( + self.genesis, self.request, first_activation + ) + first_output = self.root / "first" + activate_runtime( + first_output, + self.genesis, + self.preparation_directory, + self.preparation, + self.request, + first_activation, + _reader(self.target_password), + ) + first_runtime = load_runtime( + first_output / "runtime", + "runtime.json", + _reader(self.target_password), + clock=lambda: NOW + 20, + ) + old_event = first_runtime.service.ledger.append_local( + kind="experience.observed", + subject="before-second-embodiment", + payload={"summary": "memory predates the second embodiment"}, + signer=first_runtime.service.signer, + sensitivity="shareable", + ) + self.assertEqual(old_event["manifest_hash"], first_authority.manifest.digest) + + second_port = _available_port() + second_password = b"second-target-password" + second_preparation_directory = self.root / "second-preparation" + second_preparation = create_target_preparation( + second_preparation_directory, + first_authority, + { + "schema": "dm.operator.rebirth-target-profile/v1", + "label": "second", + "body_ref": "cluster:test:second", + "principal_id": "second@test", + "listen_host": "127.0.0.1", + "listen_port": second_port, + "advertised_endpoint": (f"http://127.0.0.1:{second_port}/dm-peer/v1"), + "targets": [ + { + "embodiment_id": first_runtime.service.origin["embodiment_id"], + "endpoint": (f"http://127.0.0.1:{self.first_port}/dm-peer/v1"), + "timeout_ms": 5_000, + } + ], + }, + _reader(second_password), + created_at_ms=NOW + 30, + expires_at_ms=NOW + 60_030, + ) + second_request = json.loads( + (second_preparation_directory / "request.json").read_bytes() + ) + intent = create_distributed_enrollment_intent( + second_request, + first_authority, + issued_at_ms=NOW + 40, + expires_at_ms=NOW + 60_040, + nonce=b"e" * 32, + ) + enrollment_shares = [ + create_distributed_enrollment_share_from_holder( + intent, + second_request, + first_authority, + self.holders[name], + _reader(self.passwords[name]), + observed_at_ms=NOW + 40, + ) + for name in ("root-0", "root-1") + ] + with self.assertRaisesRegex( + RebirthError, "rebirth_enrollment_share_threshold_rejected" + ): + aggregate_distributed_enrollment( + intent, + second_request, + first_authority, + enrollment_shares[:1], + observed_at_ms=NOW + 40, + ) + second_activation = aggregate_distributed_enrollment( + intent, + second_request, + first_authority, + enrollment_shares, + observed_at_ms=NOW + 40, + ) + first_bundle_path = first_output / "runtime/runtime.json" + first_bundle = json.loads(first_bundle_path.read_bytes()) + second_output = self.root / "second" + activate_target_runtime( + second_output, + second_preparation_directory, + second_preparation, + second_request, + second_activation, + first_bundle, + _reader(second_password), + ) + updated_first = apply_activation_to_runtime_bundle( + first_bundle, + second_activation, + first_authority, + target_endpoint=f"http://127.0.0.1:{second_port}/dm-peer/v1", + ) + first_bundle_path.write_bytes(canonical_bytes(updated_first)) + first = load_runtime( + first_output / "runtime", + "runtime.json", + _reader(self.target_password), + clock=lambda: NOW + 50, + ) + second = load_runtime( + second_output / "runtime", + "runtime.json", + _reader(second_password), + clock=lambda: NOW + 50, + ) + self.assertEqual(first.service.ledger.event(old_event["event_id"]), old_event) + self.assertIsNone(second.service.ledger.event(old_event["event_id"])) + + server = create_peer_http_server(first) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + client_directory = second_output / "runtime/operator-clients/weave" + capability = LocalCapability.from_value( + json.loads((client_directory / "client.json").read_bytes())[ + "capability" + ], + (client_directory / "capability.key").read_bytes(), + ) + response = second.service.handle( + create_request( + capability, + request_id=str(uuid.uuid4()), + issued_at_ms=NOW + 50, + method="we.sync.peer-pull", + params={ + "sync_request_id": str(uuid.uuid4()), + "target_embodiment_id": first.service.origin["embodiment_id"], + "limit": 16, + }, + ) + ) + self.assertTrue(response["ok"], response) + self.assertEqual(response["result"]["events"], 1) + self.assertEqual( + second.service.ledger.event(old_event["event_id"]), old_event + ) + finally: + server.shutdown() + server.server_close() + thread.join(timeout=2) + + def test_distributed_enrollment_cli_keeps_holders_in_separate_processes( + self, + ) -> None: + first_activation = self.activation() + _verified, authority = validate_activation( + self.genesis, self.request, first_activation + ) + authority_path = self.root / "authority.json" + authority_path.write_bytes( + canonical_bytes( + { + "schema": "dm.operator.authority/v1", + "control_artifacts": [self.genesis], + "control_head": authority.state.head, + "manifest": authority.manifest.value, + "credentials": list(authority.credentials.values()), + "incarnations": list(authority.incarnations.values()), + } + ) + ) + authority_path.chmod(0o600) + port = _available_port() + profile_path = self.root / "cli-profile.json" + profile_path.write_bytes( + canonical_bytes( + { + "schema": "dm.operator.rebirth-target-profile/v1", + "label": "cli-second", + "body_ref": "cluster:test:cli-second", + "principal_id": "cli-second@test", + "listen_host": "127.0.0.1", + "listen_port": port, + "advertised_endpoint": (f"http://127.0.0.1:{port}/dm-peer/v1"), + "targets": [ + { + "embodiment_id": self.request["body"]["origin"][ + "embodiment_id" + ], + "endpoint": ( + f"http://127.0.0.1:{self.first_port}/dm-peer/v1" + ), + "timeout_ms": 5_000, + } + ], + } + ) + ) + profile_path.chmod(0o600) + password = b"cli-second-target-password" + preparation = self.root / "cli-preparation" + prepared = self.invoke_rebirth( + [ + "prepare", + "--authority", + str(authority_path), + "--profile", + str(profile_path), + "--output", + str(preparation), + "--password-fd", + "{password}", + ], + password, + ) + self.assertEqual(prepared.returncode, 0, prepared.stderr.decode()) + request_path = preparation / "request.json" + intent_path = self.root / "cli-intent.json" + created = self.invoke_rebirth( + [ + "create-enrollment-intent", + "--authority", + str(authority_path), + "--request", + str(request_path), + "--output", + str(intent_path), + ] + ) + self.assertEqual(created.returncode, 0, created.stderr.decode()) + share_paths: list[Path] = [] + for name in ("root-0", "root-1"): + share_path = self.root / f"cli-{name}-share.json" + shared = self.invoke_rebirth( + [ + "enrollment-share", + "--authority", + str(authority_path), + "--request", + str(request_path), + "--intent", + str(intent_path), + "--holder", + str(self.holders[name]), + "--password-fd", + "{password}", + "--output", + str(share_path), + ], + self.passwords[name], + ) + self.assertEqual(shared.returncode, 0, shared.stderr.decode()) + share_paths.append(share_path) + activation_path = self.root / "cli-activation.json" + aggregated = self.invoke_rebirth( + [ + "aggregate-enrollment", + "--authority", + str(authority_path), + "--request", + str(request_path), + "--intent", + str(intent_path), + *(item for path in share_paths for item in ("--share", str(path))), + "--output", + str(activation_path), + ] + ) + self.assertEqual(aggregated.returncode, 0, aggregated.stderr.decode()) + activation = json.loads(activation_path.read_bytes()) + validate_rebirth_activation( + activation, + authority, + request=json.loads(request_path.read_bytes()), + ) + public = b"".join( + path.read_bytes() + for path in [request_path, intent_path, *share_paths, activation_path] + ) + for name in ("root-0", "root-1"): + self.assertNotIn((self.holders[name] / "holder.json").read_bytes(), public) + self.assertNotIn(self.passwords[name], public) diff --git a/tests/test_package_scaffold.py b/tests/test_package_scaffold.py index d8501f7..f0d4cb7 100644 --- a/tests/test_package_scaffold.py +++ b/tests/test_package_scaffold.py @@ -18,6 +18,7 @@ SDIST_FILES, WHEEL_FILES, PackageCheckError, + _assert_source_parity, validate_member, ) from tools.reproducible_build import BUILD_INPUTS @@ -46,6 +47,9 @@ def test_public_metadata_and_runtime_dependency_boundary(self) -> None: project["scripts"], { "daimon": "daimon_matrix.cli:main", + "daimon-first-embodiment": ( + "daimon_matrix.operator_first_embodiment:main" + ), "daimon-genesis": "daimon_matrix.operator_genesis:main", "daimon-codex-body": "daimon_matrix.codex_body:main", "daimon-conformance": "daimon_matrix.conformance:main", @@ -178,6 +182,7 @@ def test_allowlists_are_frozen(self) -> None: "src/daimon_matrix/operator_bootstrap.py", "src/daimon_matrix/operator_capabilities.py", "src/daimon_matrix/operator_genesis.py", + "src/daimon_matrix/operator_first_embodiment.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -235,6 +240,7 @@ def test_allowlists_are_frozen(self) -> None: "daimon_matrix/operator_bootstrap.py", "daimon_matrix/operator_capabilities.py", "daimon_matrix/operator_genesis.py", + "daimon_matrix/operator_first_embodiment.py", "daimon_matrix/operator_rebirth.py", "daimon_matrix/memory_policy.py", "daimon_matrix/memory_projection.py", @@ -304,6 +310,26 @@ def test_zip_symlink_is_rejected_without_extraction(self) -> None: with self.assertRaises(SecretScanError): scan_archive(archive_path) + def test_first_embodiment_distribution_bytes_are_source_bound(self) -> None: + member = "daimon_matrix/operator_first_embodiment.py" + relative = "src/daimon_matrix/operator_first_embodiment.py" + _assert_source_parity( + {member: (ROOT / relative).read_bytes()}, + ROOT, + {member: relative}, + "wheel", + ) + with self.assertRaisesRegex( + PackageCheckError, + "wheel source drift: daimon_matrix/operator_first_embodiment.py", + ): + _assert_source_parity( + {member: b"mutated installed payload"}, + ROOT, + {member: relative}, + "wheel", + ) + def test_checkout_secret_scan_is_clean(self) -> None: excluded_roots: tuple[Path, ...] = () configured = os.environ.get("COLLECTIVE_MEMORY_CONTRACT_ROOT") diff --git a/tools/check_distribution.py b/tools/check_distribution.py index a3c7bb1..cf95b33 100644 --- a/tools/check_distribution.py +++ b/tools/check_distribution.py @@ -13,6 +13,7 @@ import stat import tarfile import zipfile +from collections.abc import Mapping from datetime import UTC, datetime from email import policy from email.parser import BytesParser @@ -61,6 +62,7 @@ "src/daimon_matrix/operator_bootstrap.py", "src/daimon_matrix/operator_capabilities.py", "src/daimon_matrix/operator_genesis.py", + "src/daimon_matrix/operator_first_embodiment.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -117,6 +119,7 @@ "daimon_matrix/operator_bootstrap.py", "daimon_matrix/operator_capabilities.py", "daimon_matrix/operator_genesis.py", + "daimon_matrix/operator_first_embodiment.py", "daimon_matrix/operator_rebirth.py", "daimon_matrix/memory_policy.py", "daimon_matrix/memory_projection.py", @@ -261,6 +264,17 @@ def _check_metadata(data: bytes, source: str) -> None: ) +def _assert_source_parity( + files: Mapping[str, bytes], + source_root: Path, + source_map: Mapping[str, str], + kind: str, +) -> None: + for member, relative in source_map.items(): + if files.get(member) != (source_root / relative).read_bytes(): + raise PackageCheckError(f"{kind} source drift: {member}") + + def inspect_sdist(path: Path, source_root: Path) -> dict[str, object]: """Inspect an sdist without extracting it.""" @@ -329,6 +343,7 @@ def inspect_sdist(path: Path, source_root: Path) -> dict[str, object]: "src/daimon_matrix/operator_bootstrap.py", "src/daimon_matrix/operator_capabilities.py", "src/daimon_matrix/operator_genesis.py", + "src/daimon_matrix/operator_first_embodiment.py", "src/daimon_matrix/operator_rebirth.py", "src/daimon_matrix/memory_policy.py", "src/daimon_matrix/memory_projection.py", @@ -354,8 +369,7 @@ def inspect_sdist(path: Path, source_root: Path) -> dict[str, object]: "src/daimon_matrix/synthetic_sources.py", "src/daimon_matrix/weave.py", ): - if files[relative] != (source_root / relative).read_bytes(): - raise PackageCheckError(f"sdist source drift: {relative}") + _assert_source_parity(files, source_root, {relative: relative}, "sdist") return { "filename": path.name, "sha256": sha256_bytes(raw), @@ -405,6 +419,7 @@ def inspect_wheel(path: Path, source_root: Path) -> dict[str, object]: 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-first-embodiment = daimon_matrix.operator_first_embodiment: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" @@ -477,6 +492,9 @@ def inspect_wheel(path: Path, source_root: Path) -> dict[str, object]: "src/daimon_matrix/operator_capabilities.py" ), "daimon_matrix/operator_genesis.py": "src/daimon_matrix/operator_genesis.py", + "daimon_matrix/operator_first_embodiment.py": ( + "src/daimon_matrix/operator_first_embodiment.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": ( @@ -514,9 +532,7 @@ def inspect_wheel(path: Path, source_root: Path) -> dict[str, object]: ), "daimon_matrix/weave.py": "src/daimon_matrix/weave.py", } - for member, relative in source_map.items(): - if files[member] != (source_root / relative).read_bytes(): - raise PackageCheckError(f"wheel source drift: {member}") + _assert_source_parity(files, source_root, source_map, "wheel") return { "filename": path.name, "sha256": sha256_bytes(raw), diff --git a/tools/reproducible_build.py b/tools/reproducible_build.py index d97d5da..06a52e0 100644 --- a/tools/reproducible_build.py +++ b/tools/reproducible_build.py @@ -55,6 +55,7 @@ Path("src/daimon_matrix/operator_bootstrap.py"), Path("src/daimon_matrix/operator_capabilities.py"), Path("src/daimon_matrix/operator_genesis.py"), + Path("src/daimon_matrix/operator_first_embodiment.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 b15f381..f8b97e1 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":"fb81d85d3da0e5a1d8c6fd1d604bba6878b4a3f69638fa2296d3f1ff4b6e02ec","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"cec5d9ae1518bfbcb6b64840614023e6d05912ec129db1e6ca248d69ba359c2f","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"a5445a21f87f17207c6aaea35ce1b5c0d827b60a5fd9f9521cfed090d78bb9d5","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":"74a030e054892e6486dd4fd6e6f1b41b4e466de6b201badcb132668bb4216efe","valid":true},{"name":"valid/park-receipt.json","sha256":"c393f4ed6ed0fbd9d8e0e7f4e874fc72e03beee6949856d72f7248bbdac250db","valid":true},{"name":"valid/park-request.json","sha256":"b9041eed33692c02744b204ff1c99fdee6fc31b41f0223d1ca52fba652243363","valid":true},{"name":"valid/plan.json","sha256":"41c06bb6997ecbb4d436f64748e42f2461d1f18009815dc64e125d752b25949c","valid":true},{"name":"valid/profile-manifest.json","sha256":"f2f454fe1d60861df896cf420a683254a69aac076ee7cb7b429ff1e97365c4ba","valid":true},{"name":"valid/provider-config.json","sha256":"b9adae34f7314d6a1e45a382610c2514c1795bcf0aaeb6b9b28f4351a19524e2","valid":true},{"name":"valid/provider-ready.json","sha256":"97828e364e72ceac39af31a68b3f48dca13de57810cb991ee8e02b54a365c49e","valid":true},{"name":"valid/runtime-active.json","sha256":"77ad98f91b71e7104b1a3ff40947bc6e3058bea202eb31e7414d9afbee74af72","valid":true},{"name":"valid/runtime-parking.json","sha256":"e2fe19909afb7f64fb14eb2cef16764159aed66129f781b085ad956003387738","valid":true},{"name":"valid/runtime-starting.json","sha256":"bc810ac6a23f39d7cfe1b66bd4f6dbd6a70243bbb3b0af8bc22ec5102de8b2b3","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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":"fb81d85d3da0e5a1d8c6fd1d604bba6878b4a3f69638fa2296d3f1ff4b6e02ec","valid":false},{"name":"negative/plan-unknown-field.json","sha256":"cec5d9ae1518bfbcb6b64840614023e6d05912ec129db1e6ca248d69ba359c2f","valid":false},{"name":"valid/bootstrap.json","sha256":"288f985c7a31a1ac9dd54d16ed7f49f81752918d60caaf0f7a4dabfb868aef4b","valid":true},{"name":"valid/compatibility.json","sha256":"a5445a21f87f17207c6aaea35ce1b5c0d827b60a5fd9f9521cfed090d78bb9d5","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":"38bf9732267095490666e6818a36de7793bae07661218023aad5c256a613e38f","valid":true},{"name":"valid/park-receipt.json","sha256":"c393f4ed6ed0fbd9d8e0e7f4e874fc72e03beee6949856d72f7248bbdac250db","valid":true},{"name":"valid/park-request.json","sha256":"b9041eed33692c02744b204ff1c99fdee6fc31b41f0223d1ca52fba652243363","valid":true},{"name":"valid/plan.json","sha256":"41c06bb6997ecbb4d436f64748e42f2461d1f18009815dc64e125d752b25949c","valid":true},{"name":"valid/profile-manifest.json","sha256":"fe19fbe90cb222e1feda9c57696fe26b3d1b8c2d165005436ca41a0e78f29e68","valid":true},{"name":"valid/provider-config.json","sha256":"b9adae34f7314d6a1e45a382610c2514c1795bcf0aaeb6b9b28f4351a19524e2","valid":true},{"name":"valid/provider-ready.json","sha256":"97828e364e72ceac39af31a68b3f48dca13de57810cb991ee8e02b54a365c49e","valid":true},{"name":"valid/runtime-active.json","sha256":"77ad98f91b71e7104b1a3ff40947bc6e3058bea202eb31e7414d9afbee74af72","valid":true},{"name":"valid/runtime-parking.json","sha256":"e2fe19909afb7f64fb14eb2cef16764159aed66129f781b085ad956003387738","valid":true},{"name":"valid/runtime-starting.json","sha256":"bc810ac6a23f39d7cfe1b66bd4f6dbd6a70243bbb3b0af8bc22ec5102de8b2b3","valid":true},{"name":"valid/scope-result.json","sha256":"0dd3cc8b1c72062666e4d9af67b2eb2b149fe1a89da730113952fccb12b15db4","valid":true},{"name":"valid/tool-error.json","sha256":"01bd1caee46e7972e3e246094f77979231c8bd173515aa2fe12151dbe22edf75","valid":true}],"hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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 62d67b0..fccead0 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:wvhlZAZBrpM5Al9LISi-BSf0ijuei-LBveBxEzuIT5Q","deployment":"synthetic-isolated","hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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:kWY3LHHKP2ogdNc80IHIz9gWcY9VrrTsOMmOz8WVSts","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"2f707179c489195b44a2c484cd5837df8bcce9851bdbdc3d9d1ee82e63aba4d4"},{"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":"b08822a3876dda8b4ca8817b3a8ee258dbeedd2f9483dbde6c58417392bb2b06"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"84be83ed386078fd4c865af5d7f7a250d10b73ee01b0cb6bc455812fdfbb80e7"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"f55ce93d69b62f21669553be60dfaf1b7b4fbe5d78d81aaa7df622424c9908b5"},{"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":"c538f10debba8a4a55ad4dac6de64053707dc23ae30d53811efd3f83c318149a"},{"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":"262036beedbde639eee0ca0013957f8cfd183302e2783a6b5ba8ef6ea6ea0a1a"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"ff41f41693448c7a3e508af4b125a4d377738c2414135aec3b73a73ab2aa993a"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"eaeaaaac847f3467ae630fc7364d5e9d7d46b7109ff10879c454d11b91b6c1de"},{"name":"operator_bootstrap.py","sha256":"b92014654ab2a3481628a211e29222dcdb3944891dcb905b14feef9069e20850"},{"name":"operator_capabilities.py","sha256":"15e4ef63741763fdbf41cbadfcdef7688d0a3d29fd68d147085261f21a621848"},{"name":"operator_genesis.py","sha256":"852f40162ba4aface9151b5a8ab3eab9049a37c92a6884560801a589d55b5cd6"},{"name":"operator_rebirth.py","sha256":"2b885d196b380527d2c1ea8058bf4ac31f46ca279e4c8cef730e50aa115afef8"},{"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":"b01b6fd17c4debf88619a8c642b65545b80c349459ee69238dcc7da48427dd91"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"a7075c94e6b5be24c91e181426eff95aaea70717c5246452d22617dc288e27c4"},{"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":"8806962a51aa03b9de9e3dce1ce979fd5880d09180d27fd0f26bb317ba805071"},{"name":"synthetic_multihost.py","sha256":"879253dc7188dc55bf657eec0f1eb4aebc91b24d8ad5ac3f9f910b5d2b32503a"},{"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":"3825e225eb3a7d5d195f8660c79e231af44d66014730ca1fb8b96a4d1ec56129"},"plan_id":"dm:hermes-plan:v1:vcQj4Nf7hRPul1MJiFvzpokbU9Mom6iBevSvBY1jtQQ","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:2W3EXFuW29ajDKYC6So-xp_4Ul0F8w3j1Tah_zwjxXo","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:R5HGmRRLYKrl9xQimSBq1RubMbRTuQ2sv7CMlpk5HUI"} +{"active_handle_id":"dm:hermes-handle:v1:wvhlZAZBrpM5Al9LISi-BSf0ijuei-LBveBxEzuIT5Q","deployment":"synthetic-isolated","hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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:nePMWwH7oOSHWGdhMjvYJ3pVZZHfNLC3_ufiNgHIqwE","matrix_high_water":"654a43432c2bc0c249a5be9c749c50257b047ee6b9fc7c8a5c92dac0f145df82","matrix_package":{"contract_schema_sha256":"e23b47040d45c64676b2fc793e375f9be9d7d95078f0b5c384bb5a33383f499f","current_memory_schema_sha256":"3422f822f7b3e05c4b2f422a19a96283993938f11c798cb1982cb5d3cdc38169","modules":[{"name":"__init__.py","sha256":"2f707179c489195b44a2c484cd5837df8bcce9851bdbdc3d9d1ee82e63aba4d4"},{"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":"b08822a3876dda8b4ca8817b3a8ee258dbeedd2f9483dbde6c58417392bb2b06"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"84be83ed386078fd4c865af5d7f7a250d10b73ee01b0cb6bc455812fdfbb80e7"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"f55ce93d69b62f21669553be60dfaf1b7b4fbe5d78d81aaa7df622424c9908b5"},{"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":"c538f10debba8a4a55ad4dac6de64053707dc23ae30d53811efd3f83c318149a"},{"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":"262036beedbde639eee0ca0013957f8cfd183302e2783a6b5ba8ef6ea6ea0a1a"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"ff41f41693448c7a3e508af4b125a4d377738c2414135aec3b73a73ab2aa993a"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"eaeaaaac847f3467ae630fc7364d5e9d7d46b7109ff10879c454d11b91b6c1de"},{"name":"operator_bootstrap.py","sha256":"b92014654ab2a3481628a211e29222dcdb3944891dcb905b14feef9069e20850"},{"name":"operator_capabilities.py","sha256":"15e4ef63741763fdbf41cbadfcdef7688d0a3d29fd68d147085261f21a621848"},{"name":"operator_first_embodiment.py","sha256":"06c58d5b8e24a52117e3e54c8ae8dc1ffec03cd3efba864f97171a173bc4ff54"},{"name":"operator_genesis.py","sha256":"852f40162ba4aface9151b5a8ab3eab9049a37c92a6884560801a589d55b5cd6"},{"name":"operator_rebirth.py","sha256":"f5c3780d5090436b0704448181785155bfdb5fe5c80c7be6c00755d2879caa02"},{"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":"b01b6fd17c4debf88619a8c642b65545b80c349459ee69238dcc7da48427dd91"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"a7075c94e6b5be24c91e181426eff95aaea70717c5246452d22617dc288e27c4"},{"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":"8806962a51aa03b9de9e3dce1ce979fd5880d09180d27fd0f26bb317ba805071"},{"name":"synthetic_multihost.py","sha256":"879253dc7188dc55bf657eec0f1eb4aebc91b24d8ad5ac3f9f910b5d2b32503a"},{"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":"02893cf3e50708c41395219eb9cf91ec9ae4c6b9cb45efc175a4996dd858de4c"},"plan_id":"dm:hermes-plan:v1:vcQj4Nf7hRPul1MJiFvzpokbU9Mom6iBevSvBY1jtQQ","profile_id":"dm:hermes-profile:v1:-_Nflr6K0kKZE-trs6NGUeRn3l9mZTdOgx6oZ0D3nPY","provider_ready_id":"dm:hermes-ready:v1:2W3EXFuW29ajDKYC6So-xp_4Ul0F8w3j1Tah_zwjxXo","schema":"dm.hermes-body.launch-receipt/v1","starting_handle_id":"dm:hermes-handle:v1:R5HGmRRLYKrl9xQimSBq1RubMbRTuQ2sv7CMlpk5HUI"} diff --git a/vectors/hermes/v1/valid/profile-manifest.json b/vectors/hermes/v1/valid/profile-manifest.json index f5f055f..e587141 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":"b9adae34f7314d6a1e45a382610c2514c1795bcf0aaeb6b9b28f4351a19524e2"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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":"2f707179c489195b44a2c484cd5837df8bcce9851bdbdc3d9d1ee82e63aba4d4"},{"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":"b08822a3876dda8b4ca8817b3a8ee258dbeedd2f9483dbde6c58417392bb2b06"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"84be83ed386078fd4c865af5d7f7a250d10b73ee01b0cb6bc455812fdfbb80e7"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"f55ce93d69b62f21669553be60dfaf1b7b4fbe5d78d81aaa7df622424c9908b5"},{"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":"c538f10debba8a4a55ad4dac6de64053707dc23ae30d53811efd3f83c318149a"},{"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":"262036beedbde639eee0ca0013957f8cfd183302e2783a6b5ba8ef6ea6ea0a1a"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"ff41f41693448c7a3e508af4b125a4d377738c2414135aec3b73a73ab2aa993a"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"eaeaaaac847f3467ae630fc7364d5e9d7d46b7109ff10879c454d11b91b6c1de"},{"name":"operator_bootstrap.py","sha256":"b92014654ab2a3481628a211e29222dcdb3944891dcb905b14feef9069e20850"},{"name":"operator_capabilities.py","sha256":"15e4ef63741763fdbf41cbadfcdef7688d0a3d29fd68d147085261f21a621848"},{"name":"operator_genesis.py","sha256":"852f40162ba4aface9151b5a8ab3eab9049a37c92a6884560801a589d55b5cd6"},{"name":"operator_rebirth.py","sha256":"2b885d196b380527d2c1ea8058bf4ac31f46ca279e4c8cef730e50aa115afef8"},{"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":"b01b6fd17c4debf88619a8c642b65545b80c349459ee69238dcc7da48427dd91"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"a7075c94e6b5be24c91e181426eff95aaea70717c5246452d22617dc288e27c4"},{"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":"8806962a51aa03b9de9e3dce1ce979fd5880d09180d27fd0f26bb317ba805071"},{"name":"synthetic_multihost.py","sha256":"879253dc7188dc55bf657eec0f1eb4aebc91b24d8ad5ac3f9f910b5d2b32503a"},{"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":"3825e225eb3a7d5d195f8660c79e231af44d66014730ca1fb8b96a4d1ec56129"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"bdc423e0d7fb8513ee975309885bf3a6891b53d3289ba8817af4af058d63b504","profile_id":"dm:hermes-profile:v1:hnp0D6aKRqz1DjAVk3nP1B_8RabV1han4ARgyS_vF_Y","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":"b9adae34f7314d6a1e45a382610c2514c1795bcf0aaeb6b9b28f4351a19524e2"},{"name":"plugins/daimon-matrix/plugin.yaml","sha256":"6e2bcb16b394f4653727334653e5a4007149e5a3bc72c9958f9dc7e814dd8b92"},{"name":"skills/daimon-matrix/SKILL.md","sha256":"6e0e021c6b0a2423dc95930c6901387738c15600deee2bb8f90a20110c66a275"}],"hermes_commit":"5c8870c1625761956a56fd2b225720dbe9083e45","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":"2f707179c489195b44a2c484cd5837df8bcce9851bdbdc3d9d1ee82e63aba4d4"},{"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":"b08822a3876dda8b4ca8817b3a8ee258dbeedd2f9483dbde6c58417392bb2b06"},{"name":"cluster.py","sha256":"f183c54a2876b3531a1bbb3fe6d45d9c9c60069608b5c3ec3d2f07dbb50cae91"},{"name":"codex_body.py","sha256":"84be83ed386078fd4c865af5d7f7a250d10b73ee01b0cb6bc455812fdfbb80e7"},{"name":"collective_memory.py","sha256":"90dbc64cc0a9e8c9d37e7ec509d963958eb90915d88d563b6a4d6e6ed4f4bb5f"},{"name":"communication.py","sha256":"ea9342f8326074a0c69ea8534f21310e784b0bdc93c62e1221142078ae22aa3b"},{"name":"conformance.py","sha256":"f55ce93d69b62f21669553be60dfaf1b7b4fbe5d78d81aaa7df622424c9908b5"},{"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":"c538f10debba8a4a55ad4dac6de64053707dc23ae30d53811efd3f83c318149a"},{"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":"262036beedbde639eee0ca0013957f8cfd183302e2783a6b5ba8ef6ea6ea0a1a"},{"name":"local_we.py","sha256":"2d9fddcf6c4c1a93a704d99c65d7fe839377d6801e66fea8f552b474083ee224"},{"name":"mcp_server.py","sha256":"ff41f41693448c7a3e508af4b125a4d377738c2414135aec3b73a73ab2aa993a"},{"name":"memory_policy.py","sha256":"59390545f633ba8d71b40b51ef5b6a897f994e1adb4bcfa246ab4301fb3f6310"},{"name":"memory_projection.py","sha256":"f260473f31731d8d63f69f1cb3568d10c3b65f2b5948801deeab2d75243a0652"},{"name":"multihost.py","sha256":"eaeaaaac847f3467ae630fc7364d5e9d7d46b7109ff10879c454d11b91b6c1de"},{"name":"operator_bootstrap.py","sha256":"b92014654ab2a3481628a211e29222dcdb3944891dcb905b14feef9069e20850"},{"name":"operator_capabilities.py","sha256":"15e4ef63741763fdbf41cbadfcdef7688d0a3d29fd68d147085261f21a621848"},{"name":"operator_first_embodiment.py","sha256":"06c58d5b8e24a52117e3e54c8ae8dc1ffec03cd3efba864f97171a173bc4ff54"},{"name":"operator_genesis.py","sha256":"852f40162ba4aface9151b5a8ab3eab9049a37c92a6884560801a589d55b5cd6"},{"name":"operator_rebirth.py","sha256":"f5c3780d5090436b0704448181785155bfdb5fe5c80c7be6c00755d2879caa02"},{"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":"b01b6fd17c4debf88619a8c642b65545b80c349459ee69238dcc7da48427dd91"},{"name":"scopes.py","sha256":"7a7e07f38ddf3a7866dbba7a66cc4e7ecbcad563c6d8041c2f0ecd859d0978c4"},{"name":"sealed.py","sha256":"60d4f0d85ed89afeeb56ddc2bde9beb331dfe7abf56f82a04d41a4c2f03d6482"},{"name":"service.py","sha256":"a7075c94e6b5be24c91e181426eff95aaea70717c5246452d22617dc288e27c4"},{"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":"8806962a51aa03b9de9e3dce1ce979fd5880d09180d27fd0f26bb317ba805071"},{"name":"synthetic_multihost.py","sha256":"879253dc7188dc55bf657eec0f1eb4aebc91b24d8ad5ac3f9f910b5d2b32503a"},{"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":"02893cf3e50708c41395219eb9cf91ec9ae4c6b9cb45efc175a4996dd858de4c"},"matrix_session_id":"dm:session:v1:RlH_IRj2gD0v96VNOccuSaxzQix7p-HZcXTvPhOSu3w","plan_hash":"bdc423e0d7fb8513ee975309885bf3a6891b53d3289ba8817af4af058d63b504","profile_id":"dm:hermes-profile:v1:xSoxTw-uDoOsa0CBNwAwPizvS60QWXL4EOCI59bNAZk","schema":"dm.hermes-body.profile-manifest/v1","workspace_ref":"dm:workspace:v1:1g9p07RgFRlBvtWJcvb92rOP7KNe9mB8v3TeueVtERM"}