commit e3909d775cdc489c786486913758ce1f6cdc21d4 parent a32e0e58ade2811dcd6af541f3cf87317fbb75e4 Author: triesap <tyson@radroots.org> Date: Fri, 31 Jul 2026 06:25:05 +0000 transport: quarantine superseded package surface - Remove the monolithic transport SPI and migrate active consumers to canonical split contracts. - Keep bounded unpublished shims with exact Step 170, Step 235, and Step 305 removal gates. - Move Reticulum policy validation to adapter and outbox boundaries while preserving generic targets. - Refresh reviewed API and SourceMaintenance artifacts after full workspace verification. Diffstat:
26 files changed, 478 insertions(+), 475 deletions(-)
diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.json b/crates/event_store/contracts/source_maintenance_v1.manifest.json @@ -712,8 +712,8 @@ { "role": "predecessor_model_public_surface", "path": "crates/event_store/src/model.rs", - "byte_length": 32050, - "sha256": "910053012eb65cb1a6c24455ffacee056ce013c950e9b545822f9c2e679a8892", + "byte_length": 32070, + "sha256": "2cdbf6d1a4e0fa6680f45f02758513509339a7cf3a2387e824fb73b0edf749a4", "hash_algorithm": "sha256_bytes_v1" }, { @@ -775,8 +775,8 @@ { "role": "public_store_and_transaction_authority", "path": "crates/event_store/src/store.rs", - "byte_length": 393195, - "sha256": "03fc56bf151f53c35792d09b247513d722ae26e3d112810f28f167a004c83273", + "byte_length": 393218, + "sha256": "64565c3bc043779e1c2f26d9761901b109b4acab0f48c2021102c0e4e1f1f726", "hash_algorithm": "sha256_bytes_v1" }, { @@ -867,7 +867,7 @@ "role": "source_maintenance_governance", "path": "tools/xtask/src/contract/source_maintenance.rs", "byte_length": 199270, - "sha256": "d3681d09e60763baf3c59edfbcb48e3c370c315a75fcd5a01756c8c961e784c9", + "sha256": "65b83a3ee1a700527d15c7403ca042cf2aaa4cd8935a2f8d2ab190c491a815bd", "hash_algorithm": "sha256_bytes_v1" }, { diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.sha256 b/crates/event_store/contracts/source_maintenance_v1.manifest.sha256 @@ -1 +1 @@ -51205a1c1c7c3b91d79354ea5deabefc0f42d83423d4a7da3a0e6658f34863b5 +85b4e199f34b12f6a0c74617cac2c2de91ae7d616a38ef0ba7483073b72d0386 diff --git a/crates/event_store/src/generated/source_maintenance_manifest.rs b/crates/event_store/src/generated/source_maintenance_manifest.rs @@ -1,8 +1,8 @@ // @generated by `cargo xtask contract source-maintenance-manifest --write`; do not edit. -pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"96ea98a5abf3348b61565954cd34f2c622411d8dd34205ef04134bbe0e3d3f2b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"ab2724188f8d08c897eebea2533a635e7c74282a25e84e4c0c37e78b08837a43\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"074f85b663444ac150239ecd8441ea4a96ad83a798a55e22d2e5e2f7ee943a8c\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 12414,\n \"sha256\": \"895bf9d57a1cb9a89c0f70232cb628106897b421561ae0b9e9ec57577149b273\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_currency_value_authority\",\n \"path\": \"crates/core/src/currency.rs\",\n \"byte_length\": 4142,\n \"sha256\": \"42e6da2d8d2fdd6955dc2d83e98dd00266e02bc910b69923b117662c05089d14\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_decimal_value_authority\",\n \"path\": \"crates/core/src/decimal.rs\",\n \"byte_length\": 7839,\n \"sha256\": \"4b1d681a92d7a9e074bee7e1f20eb1499bb05bfc6528a6c9ba69fa7d00416550\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_money_value_authority\",\n \"path\": \"crates/core/src/money.rs\",\n \"byte_length\": 8025,\n \"sha256\": \"3f30bc21e21951a62fdc5d4033736ed42a883df590e8403940775bd2edfeebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_percent_value_authority\",\n \"path\": \"crates/core/src/percent.rs\",\n \"byte_length\": 2652,\n \"sha256\": \"42ceab109881329f3539d06a0ee0381ab4359985fe185a18d58a9c9558c5eb7f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_value_authority\",\n \"path\": \"crates/core/src/quantity.rs\",\n \"byte_length\": 6785,\n \"sha256\": \"d14e619da3829cdaf3ca7385fb892d330c286c1e21455c135c37518cbb588b5f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_price_value_authority\",\n \"path\": \"crates/core/src/quantity_price.rs\",\n \"byte_length\": 7061,\n \"sha256\": \"d60f70377099470ed283f45868a5119d7bb46143cb250b5082a4c038ba7f6cdc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_unit_value_authority\",\n \"path\": \"crates/core/src/unit.rs\",\n \"byte_length\": 10023,\n \"sha256\": \"550e9582b6a5290aac3f637b83853b21fefcbab92c432d067c83448f4b2fe3e9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_public_surface\",\n \"path\": \"crates/blossom/src/lib.rs\",\n \"byte_length\": 510,\n \"sha256\": \"a4dfcbd193457c50c1b0fbfc91bb547981ecd3a6e23cfc6f1030edfb511dc58c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_authorization_authority\",\n \"path\": \"crates/blossom/src/authorization.rs\",\n \"byte_length\": 39360,\n \"sha256\": \"461947b30516315a342b3b5697599f701d27050888f29f67f5d7a7ad4afa4c28\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_descriptor_authority\",\n \"path\": \"crates/blossom/src/descriptor.rs\",\n \"byte_length\": 13702,\n \"sha256\": \"b2a4ffa760256e1316f70e012d200e2b5f4afd8ede771b85a5147f96b247f599\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_error_authority\",\n \"path\": \"crates/blossom/src/error.rs\",\n \"byte_length\": 17229,\n \"sha256\": \"02af55beacf437040be17d1fc3271d6e2a62915f656ec2dbe06247deef83f95a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_hash_authority\",\n \"path\": \"crates/blossom/src/hash.rs\",\n \"byte_length\": 11006,\n \"sha256\": \"45eca0d81ac0f46c305a32f81aafcdd90b14679d0614f72862289a6e5d4cb08f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_media_type_authority\",\n \"path\": \"crates/blossom/src/media_type.rs\",\n \"byte_length\": 2368,\n \"sha256\": \"69db5c0f9fcdd7dbe1f22daa499f65603ae4ce198083dab2c73a36813fa18a46\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_url_authority\",\n \"path\": \"crates/blossom/src/url.rs\",\n \"byte_length\": 14580,\n \"sha256\": \"342c995fcf620e5fb32461a7d8276f5668ba2662890bd32d12780cc9121c6451\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_public_surface\",\n \"path\": \"crates/event/src/lib.rs\",\n \"byte_length\": 1892,\n \"sha256\": \"5d609b963a9b8db18ef96a10f394617413734d2631774d3296e6d8fc029cb02c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_facade\",\n \"path\": \"crates/event/src/contract.rs\",\n \"byte_length\": 208,\n \"sha256\": \"27466bf36461071931f391c60646a13f7781d19584583166cc54c0eece76d010\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_registry_v7_authority\",\n \"path\": \"crates/event/src/contract/registry_v7.rs\",\n \"byte_length\": 145805,\n \"sha256\": \"0a62603f6fc05dc9f758561cf7da258c676ada810ceaeca7fb151364a5c83d62\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_envelope_authority\",\n \"path\": \"crates/event/src/envelope.rs\",\n \"byte_length\": 29310,\n \"sha256\": \"29f8d8c4b17c01cb5a8c7f59e4ce52e134180e0e6f37d265063d278b8f2ff26e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_verification_typestate_authority\",\n \"path\": \"crates/event/src/verification.rs\",\n \"byte_length\": 9400,\n \"sha256\": \"417acb2ce670d266b1fa4fcafd6b48a910218deab38f57fb8b942056db76f94d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_admission_typestate_authority\",\n \"path\": \"crates/event/src/admission.rs\",\n \"byte_length\": 7052,\n \"sha256\": \"62bd2ceb0f434946fdfc2b81f4efb13738676a29a6936c23a427e43aadf2d9cb\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_facade\",\n \"path\": \"crates/event/src/event_head.rs\",\n \"byte_length\": 97,\n \"sha256\": \"f761ff3e74c4f5e1e28381db00ce698c633ff048669b22984d14a587482e8e83\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_v1_authority\",\n \"path\": \"crates/event/src/event_head/v1.rs\",\n \"byte_length\": 6849,\n \"sha256\": \"9f4144d8d240023cf493ce0b538ade4c16dc44204d6b5f2797152caf6f9e7dba\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_ids_authority\",\n \"path\": \"crates/event/src/id.rs\",\n \"byte_length\": 48381,\n \"sha256\": \"a08fe3873815453a03318a706e2fa98a8a6728ec69d24368a87ec60025566fe0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_authority\",\n \"path\": \"crates/event/src/trade.rs\",\n \"byte_length\": 65549,\n \"sha256\": \"f818a7287ed82de3fb9ef1d973f0fb18d84b30eb4827ddc576ee502dd980145f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_kinds_authority\",\n \"path\": \"crates/event/src/kinds.rs\",\n \"byte_length\": 34961,\n \"sha256\": \"8b3ce6193cab1f7e1587d0c1b2880a81b0aa43c77d89671b602772db37edcebf\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_tags_authority\",\n \"path\": \"crates/event/src/tags.rs\",\n \"byte_length\": 4530,\n \"sha256\": \"cb8f12a639fc72d9238e32495d9e67e928e6627fb8c94ec0a58c5d95e7373cd6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_draft_authority\",\n \"path\": \"crates/event/src/draft.rs\",\n \"byte_length\": 51150,\n \"sha256\": \"ecc619aae28d688d13f35467398f17766b60f26c2bf3616d772ce65b7fa4fb53\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_calendar_authority\",\n \"path\": \"crates/event/src/calendar.rs\",\n \"byte_length\": 97992,\n \"sha256\": \"73e79d61ee175cb46e6123e1d28239252ddf0ad094dc1113bdb1286b63b88d57\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_classified_listing_authority\",\n \"path\": \"crates/event/src/classified_listing.rs\",\n \"byte_length\": 7956,\n \"sha256\": \"ac58484b87c7712c81f50f884d1c0dd60938571aa5f6f05d555191f529a4c2b0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_profile_authority\",\n \"path\": \"crates/event/src/profile.rs\",\n \"byte_length\": 17011,\n \"sha256\": \"572bfe3b6f1711d109db445077f0a8453cad3f021cc0638f79201b618e16b978\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_post_authority\",\n \"path\": \"crates/event/src/post.rs\",\n \"byte_length\": 19968,\n \"sha256\": \"a822cd51eff9cbaa39db998cc5c90d117f7a983f0fe2d42d42118cb00e95982c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_comment_authority\",\n \"path\": \"crates/event/src/comment.rs\",\n \"byte_length\": 34745,\n \"sha256\": \"24b7fda13229f21cee5cef2a9941e3aaafdbc486b19ed42731b0b6ee37c28703\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_food_availability_authority\",\n \"path\": \"crates/event/src/food_availability.rs\",\n \"byte_length\": 42829,\n \"sha256\": \"52472c07eb0855d884c60755dc3a3e1a963793451d707e51f769f2d78384f318\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_deletion_authority\",\n \"path\": \"crates/event/src/deletion.rs\",\n \"byte_length\": 30022,\n \"sha256\": \"8c3f2e32407520a8e8242c1293af32613c648fb76218b8003a18929ac0b3811f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_dto_authority\",\n \"path\": \"crates/event/src/dto.rs\",\n \"byte_length\": 5038,\n \"sha256\": \"9e228c95ff6ff33f99441d8682781fd0a1c4d3f1db24284a43985dcbac3ba136\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_farm_crdt_authority\",\n \"path\": \"crates/event/src/farm_crdt.rs\",\n \"byte_length\": 21118,\n \"sha256\": \"cec265ac9c42b59a14be4eb96924d26a83477c27291f667f23e6e47a64b6b2c7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_knowledge_authority\",\n \"path\": \"crates/event/src/knowledge.rs\",\n \"byte_length\": 54624,\n \"sha256\": \"3d55ff88a4e30d5d605f9ae4b2e96b9df6978d6c14e52f9aeb0b8039cfc200d7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_operational_listing_authority\",\n \"path\": \"crates/event/src/operational_listing.rs\",\n \"byte_length\": 10307,\n \"sha256\": \"52e710db816c89c8d4b87c86541987f7447f2f7091c394c68d9cb98a27f90159\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_order_authority\",\n \"path\": \"crates/event/src/order.rs\",\n \"byte_length\": 60218,\n \"sha256\": \"4203fcc469612cac5c705884564e140b9fb076cd57079e22ed85275a8b936e50\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_reply_authority\",\n \"path\": \"crates/event/src/reply.rs\",\n \"byte_length\": 15581,\n \"sha256\": \"b0b83a1e96125c0892d932e9314519242a715fa8f46334aff765ffd248c91d9a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_validation_authority\",\n \"path\": \"crates/event/src/trade_validation.rs\",\n \"byte_length\": 4669,\n \"sha256\": \"1486f79bd1d0eadea6cc1b7646ce1f7f57ccade18334a3ea37286426676b9b98\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_relay_hint_authority\",\n \"path\": \"crates/event/src/relay_hint.rs\",\n \"byte_length\": 13598,\n \"sha256\": \"fb8b026eeccf51d78f20771c44223e7b48825aa0e3c760d6b3cb90148021a674\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_media_authority\",\n \"path\": \"crates/event/src/media.rs\",\n \"byte_length\": 3455,\n \"sha256\": \"10678af3a202d5367dbcf3bcdc0bf9fdf8d8fb89b045d7d3fb96b3cc195ab6f9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_social_authority\",\n \"path\": \"crates/event/src/social.rs\",\n \"byte_length\": 6076,\n \"sha256\": \"a2dab19caad46eab14fac86e7c878a801a450d8ac374b0e728b71a5a224eef19\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_facade\",\n \"path\": \"crates/event/src/wire.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_v1_authority\",\n \"path\": \"crates/event/src/wire/v1.rs\",\n \"byte_length\": 22198,\n \"sha256\": \"c818bc1f67e215948ba253c8c62b09ab788db9aeec9b6df8096014b55565cb8c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_public_surface\",\n \"path\": \"crates/event_codec/src/lib.rs\",\n \"byte_length\": 2448,\n \"sha256\": \"7d4b0040ad3971f34395b17cb1aa40617e9c2c9e1b4222676c388c3adaa47e07\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_facade\",\n \"path\": \"crates/event_codec/src/verification.rs\",\n \"byte_length\": 253,\n \"sha256\": \"b49a32df605035c87f295c0a151140d43d2e588c5e11b05183e5fc92993dae0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_v1_authority\",\n \"path\": \"crates/event_codec/src/verification/v1.rs\",\n \"byte_length\": 6584,\n \"sha256\": \"72f64615bd6b6dc3b051579e3069b2213f3781dcc28e12f0b533c7d81b81b9b1\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_registry_v7_admission_authority\",\n \"path\": \"crates/event_codec/src/admission/registry_v7.rs\",\n \"byte_length\": 5228,\n \"sha256\": \"62da30cb6ef7d0ed2d43715a73bd529283e8ae8c12571bc5c68722cb400f17b8\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_admission_facade\",\n \"path\": \"crates/event_codec/src/admission.rs\",\n \"byte_length\": 21251,\n \"sha256\": \"25928e734e7b3688f92af806a12a13458c48fb47e94249f60f0226300d5da2b4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_inbound_facade\",\n \"path\": \"crates/event_codec/src/profile/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/profile/inbound/registry_v7.rs\",\n \"byte_length\": 10347,\n \"sha256\": \"32111c0e0592229c11a93a3a8054e7b124b039451aa696a9abcb56df2a6608d3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_inbound_facade\",\n \"path\": \"crates/event_codec/src/post/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/post/inbound/registry_v7.rs\",\n \"byte_length\": 15985,\n \"sha256\": \"e17083b0596e3a55994c399bc6272c0ebf04cabaa513260b3d45dd546f7f50ef\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_inbound_facade\",\n \"path\": \"crates/event_codec/src/reply/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/reply/inbound/registry_v7.rs\",\n \"byte_length\": 26662,\n \"sha256\": \"d1d11116ca27801b7e2f17da60900e6a9ca0b6f27ea9ee0bf19e97dbf24c0c32\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_inbound_facade\",\n \"path\": \"crates/event_codec/src/comment/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/comment/inbound/registry_v7.rs\",\n \"byte_length\": 53311,\n \"sha256\": \"c63fe853536b17bded6073f32b53434b60fc5123f911d01c642a6b3982f09c70\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_facade\",\n \"path\": \"crates/event_codec/src/deletion/mod.rs\",\n \"byte_length\": 115,\n \"sha256\": \"17d56f82ddb0a86bc97abbcf3e037fe460ea31fff2548f527be4bf004dac3a95\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_reconciliation_v1_authority\",\n \"path\": \"crates/event_codec/src/deletion/reconciliation_v1.rs\",\n \"byte_length\": 38028,\n \"sha256\": \"c6901f559e83700610595720c47a4d062078ae8411e1fc2fb952dd215b19dd90\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_error_authority\",\n \"path\": \"crates/event_codec/src/error.rs\",\n \"byte_length\": 3666,\n \"sha256\": \"174ce982bca37fac28d016c3ab3236441c9b31b2169ec94db6c9288b36c72849\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_admission_authority\",\n \"path\": \"crates/event_codec/src/food_availability/admission.rs\",\n \"byte_length\": 5681,\n \"sha256\": \"99809859090f5295572688d06bbb30dbe50524ba2c996de7ec5b26f6dd390643\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_authored_authority\",\n \"path\": \"crates/event_codec/src/food_availability/authored.rs\",\n \"byte_length\": 9724,\n \"sha256\": \"8673ec62ed3fc47b691efd9ca828643719803971e72b756c5b4603d409d0fba5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_inbound_facade\",\n \"path\": \"crates/event_codec/src/food_availability/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/food_availability/inbound/registry_v7.rs\",\n \"byte_length\": 26037,\n \"sha256\": \"92a7e75da3293967cec17a514479144c3c948a64542577dd58c7f07992968e4f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_traits_authority\",\n \"path\": \"crates/event_codec/src/job/traits.rs\",\n \"byte_length\": 4946,\n \"sha256\": \"1b3558a3196744005978dceddd33d3a72ad1ab8f50fb1cbfd997dcfe98bb9e39\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_encode_authority\",\n \"path\": \"crates/event_codec/src/job/encode.rs\",\n \"byte_length\": 1670,\n \"sha256\": \"5d6cae6309fcdd02deb7c751b5c451d257cf1900986391cb39cbb2bfd1ddb577\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_knowledge_verification_authority\",\n \"path\": \"crates/event_codec/src/knowledge/verification.rs\",\n \"byte_length\": 6986,\n \"sha256\": \"6f8a10ed262ce37f05acd9c89a1e37ca2fa99ce7da18407891e8abe228ee2349\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_operational_listing_tags_authority\",\n \"path\": \"crates/event_codec/src/operational_listing/tags.rs\",\n \"byte_length\": 50222,\n \"sha256\": \"55b6860bc6f4699dd47a49f8336764e9db78aafb7c32849102dedeb96804b373\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_order_decode_authority\",\n \"path\": \"crates/event_codec/src/order/decode.rs\",\n \"byte_length\": 42667,\n \"sha256\": \"033d9465bc041d205eab3f820572b719a35651340dcb50bdce7c60fabee5f6e1\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_facade\",\n \"path\": \"crates/event_codec/src/profile/mod.rs\",\n \"byte_length\": 1456,\n \"sha256\": \"06b890da54580e2ec68f7e729281b7c3b650ac0a95a4fcb0db3c6cdaa591a7ef\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_tag_builders_authority\",\n \"path\": \"crates/event_codec/src/tag_builders.rs\",\n \"byte_length\": 9232,\n \"sha256\": \"bd81ccf04fd72358331b48eddcde087a95e115c5bd90c05fd9332644979ad5e6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_trade_facade\",\n \"path\": \"crates/event_codec/src/trade/mod.rs\",\n \"byte_length\": 21472,\n \"sha256\": \"10cee4b6cd6429ac9eb00327a521bcd0982fae4de98bda77e4728fda002965d3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_dependency_authority\",\n \"path\": \"crates/event_store/Cargo.toml\",\n \"byte_length\": 1550,\n \"sha256\": \"9901a09402592ee873c255edf2e0c0d770105401255679d86ad47c35cb159d72\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19458,\n \"sha256\": \"3d87df984af6ae5decf7ca8c3a8d5422be6c647e6a05490225b6b2037bf3701b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 32050,\n \"sha256\": \"910053012eb65cb1a6c24455ffacee056ce013c950e9b545822f9c2e679a8892\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_model\",\n \"path\": \"crates/event_store/src/model/reconciliation_v1.rs\",\n \"byte_length\": 10446,\n \"sha256\": \"475b7b840bffdae7e3f7a31f5b940b9dfa579eaa1cac640729aaaeadabf83ba6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_ingest_model\",\n \"path\": \"crates/event_store/src/model/ingest_reconciliation_v1.rs\",\n \"byte_length\": 1588,\n \"sha256\": \"6117e797674c35bb1ccebffbc6a8c0108bfc38c7c9f82607066dfd802b8f0734\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_model\",\n \"path\": \"crates/event_store/src/model/addressable_transition_feed_v1.rs\",\n \"byte_length\": 22229,\n \"sha256\": \"e651952d73ed0a29d2b137076f56f699beada205ac84ed8a96834e86e343fa78\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_model\",\n \"path\": \"crates/event_store/src/model/current_visibility_v1.rs\",\n \"byte_length\": 5634,\n \"sha256\": \"d64429bc7985231e923c1c3e5295d94270a817dd5dc96693f35dfdf2dc398e4f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_model\",\n \"path\": \"crates/event_store/src/model/food_availability_projection_v1.rs\",\n \"byte_length\": 17108,\n \"sha256\": \"67861a966d674efbc40f0b0433db2ec006048ff923f6261df4161e2ddba717b5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 184833,\n \"sha256\": \"3ca802cb7a8444c8512b2be1390a019281704316a430fb9b75a8ad2cad358a07\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"nip09_successor_result_vector_executor\",\n \"path\": \"crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs\",\n \"byte_length\": 18205,\n \"sha256\": \"c632beee70dc777b8dd2a4f88cadb55296fee7aa5742e6e2c9f6d49f26ae2c78\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 393195,\n \"sha256\": \"03fc56bf151f53c35792d09b247513d722ae26e3d112810f28f167a004c83273\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_store\",\n \"path\": \"crates/event_store/src/store/addressable_transition_feed_v1.rs\",\n \"byte_length\": 40209,\n \"sha256\": \"2cebcca9602633652a79e7041d18d3a7d3feb1388a98f4f68137b9814efcedfa\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_store\",\n \"path\": \"crates/event_store/src/store/current_visibility_v1.rs\",\n \"byte_length\": 15737,\n \"sha256\": \"aa5d1bff7a5368cbac2b37906bb40f54bfc8aa6906bab34369569fdd105bf899\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_store\",\n \"path\": \"crates/event_store/src/store/food_availability_projection_v1.rs\",\n \"byte_length\": 48919,\n \"sha256\": \"3a30da308ef863b806ed63d1cb65ac80f6a946a71821af84a0c595330491757e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_extension\",\n \"path\": \"crates/event_store/src/store/post_core_extensions_v1.rs\",\n \"byte_length\": 6569,\n \"sha256\": \"f5bd8ddb45e1b2144895bd8da737fc92db051be1844ce7e8047a32b47376ce37\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_storage\",\n \"path\": \"crates/event_store/src/store/post_core_storage_v1.rs\",\n \"byte_length\": 16538,\n \"sha256\": \"fcd6546c23a6cba12b70d92728d19b6ee5d43174b83495f820348b77efa33aab\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 29950,\n \"sha256\": \"c72aaff06e4f35d0b4523c6625e83345879f9e15b9fe9563dccf589e5d01e277\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_protocol_storage\",\n \"path\": \"crates/event_store/src/store/protocol_storage_v1.rs\",\n \"byte_length\": 10835,\n \"sha256\": \"c82179b9f57968191f3e71dd008cc701787c62715ada74090eeecaa3b213076d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51849,\n \"sha256\": \"db43df5849c811d767a87bfa7884ce499400d0b169734e18242542a4415b3a51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_food_result_vector_executor\",\n \"path\": \"crates/event_store/tests/food_availability_projection_v1_result_vector.rs\",\n \"byte_length\": 34046,\n \"sha256\": \"776903c6431ff07f26c6ad6b713db5628efce1a72f34d86de6e47d4404fa3a6d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 39149,\n \"sha256\": \"3d1bba3980b80698d2a21e26caf7af6fe4d9dc4a7fbabc6e406b5d33928b457f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 198991,\n \"sha256\": \"36fd3d8b5dda3b0855a06a47dae14cae7f4cc84aaca9902c31477c218859fccc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 842402,\n \"sha256\": \"4abb141771cfa257440dba7ebeae21f5ad1f9856cc0d5f6437ba68614d997810\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 199270,\n \"sha256\": \"d3681d09e60763baf3c59edfbcb48e3c370c315a75fcd5a01756c8c961e784c9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 508630,\n \"sha256\": \"203cc8578100894be51122d69cfb431650c3b5331e4071564f4b1095b0b46b94\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"dto_root_generation_authority\",\n \"path\": \"tools/xtask/src/dto_roots.rs\",\n \"byte_length\": 35940,\n \"sha256\": \"a899a5695d7533c151d03477c4bd697a6e55c47a4980549724ac42046af694f5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 16426,\n \"sha256\": \"abc24896c6c740c10811a3e003ec4be8ebdb376a7b6b01445bfa7a5889f094a7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n"; +pub(crate) const SOURCE_MAINTENANCE_MANIFEST_JSON: &str = "{\n \"schema_version\": 1,\n \"contract_id\": \"radroots_event_store.source_maintenance_v1\",\n \"hook_id\": \"source_maintenance_v1\",\n \"manifest_schema\": {\n \"path\": \"crates/event_store/contracts/source_maintenance_v1.manifest.schema.json\",\n \"byte_length\": 12315,\n \"sha256\": \"96ea98a5abf3348b61565954cd34f2c622411d8dd34205ef04134bbe0e3d3f2b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"predecessor\": {\n \"hook_id\": \"food_availability_projection_v1\",\n \"manifest\": {\n \"path\": \"crates/event_store/contracts/food_availability_projection_v1.manifest.json\",\n \"byte_length\": 17455,\n \"sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n },\n \"migration\": {\n \"version\": 4,\n \"name\": \"source_maintenance\",\n \"up\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.up.sql\",\n \"byte_length\": 19841,\n \"sha256\": \"ab2724188f8d08c897eebea2533a635e7c74282a25e84e4c0c37e78b08837a43\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"down\": {\n \"path\": \"crates/event_store/migrations/0004_source_maintenance.down.sql\",\n \"byte_length\": 5172,\n \"sha256\": \"fe44fd53c51545c08ea479b385e6781079dab70fc63da2a3c205d727a00ce860\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n \"schema_sha256\": \"074f85b663444ac150239ecd8441ea4a96ad83a798a55e22d2e5e2f7ee943a8c\",\n \"catalog\": {\n \"objects\": [\n \"radroots_event_store_source_capacity_delete_guard\",\n \"radroots_event_store_source_capacity_insert_guard\",\n \"radroots_event_store_source_capacity_marker_close_guard\",\n \"radroots_event_store_source_capacity_update_guard\",\n \"radroots_event_store_source_capacity_v1\",\n \"radroots_event_store_source_generation_capacity_advance\",\n \"radroots_event_store_source_generation_capacity_guard\"\n ],\n \"replaced_objects\": [\n \"radroots_event_store_food_availability_image_delete_guard\",\n \"radroots_event_store_food_availability_projection_delete_guard\",\n \"radroots_event_store_source_rebuild_marker_insert_guard\"\n ],\n \"tables\": [\n \"radroots_event_store_source_capacity_v1\"\n ],\n \"fts5_tables\": []\n }\n },\n \"source_maintenance\": {\n \"version\": 1,\n \"event_contract_registry_version\": 7,\n \"capacity_authority_id\": \"radroots_event_store_source_capacity_v1\",\n \"accounting\": {\n \"algorithm\": \"sqlite_cast_blob_octet_sum_v1\",\n \"raw_event_columns\": [\n \"event_id\",\n \"pubkey\",\n \"tags_json\",\n \"content\",\n \"sig\",\n \"raw_json\"\n ],\n \"raw_tag_columns\": [\n \"event_id\",\n \"tag_name\",\n \"tag_value\",\n \"tag_json\"\n ],\n \"nullable_raw_tag_columns\": [\n \"tag_value\"\n ]\n },\n \"limits\": {\n \"raw_events\": 25000,\n \"raw_tags\": 250000,\n \"raw_event_text_bytes\": 67108864,\n \"raw_tag_text_bytes\": 33554432,\n \"retained_source_generations\": 8\n },\n \"reopen_validation\": {\n \"mode\": \"bounded_full_raw_recount_v1\",\n \"raw_event_rejection_scan_bound\": 25001,\n \"raw_tag_rejection_scan_bound\": 250001,\n \"generation_history_validation\": \"bounded_count_plus_active_ordinal_v1\",\n \"retained_generation_rejection_scan_bound\": 9\n },\n \"rebuild_seal\": {\n \"nip09_hook_id\": \"nip09_reconciliation_v1\",\n \"nip09_manifest_sha256\": \"74af832420ffbaa9805e89df3c0b34f126a443e1598f757e3372f407f9003b77\",\n \"food_hook_id\": \"food_availability_projection_v1\",\n \"food_manifest_sha256\": \"02dfe1b450fbdac16e718888215b4dd5c85d8975440fa21e8f439fb24c2b2990\",\n \"food_scope_fingerprint_sha256\": \"8b63c5ddc48a2cc7db69295238b96d5f814dba50427c80b4d0079f061e6d3de0\",\n \"active_generation_authority\": \"radroots_event_store_source_state\",\n \"marker_close_authority\": \"radroots_event_store_source_capacity_marker_close_guard\"\n }\n },\n \"entry_points\": [\n {\n \"role\": \"migration_registry\",\n \"rust_path\": \"radroots_event_store::migrations::EVENT_STORE_MIGRATIONS[3]\"\n },\n {\n \"role\": \"migration_apply_hook\",\n \"rust_path\": \"radroots_event_store::schema::apply_migration_hook\"\n },\n {\n \"role\": \"migration_validation_hook\",\n \"rust_path\": \"radroots_event_store::schema::validate_migration_hook_state\"\n },\n {\n \"role\": \"capacity_query\",\n \"rust_path\": \"radroots_event_store::RadrootsEventStore::source_capacity_v1\"\n },\n {\n \"role\": \"raw_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_unique_raw_source_append_v1\"\n },\n {\n \"role\": \"raw_append_advance\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::advance_source_capacity_after_insert_v1\"\n },\n {\n \"role\": \"generation_append_preflight\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::preflight_source_generation_append_v1\"\n },\n {\n \"role\": \"generation_rebuild_bind\",\n \"rust_path\": \"radroots_event_store::source_maintenance_v1::bind_source_capacity_to_generation_v1\"\n },\n {\n \"role\": \"sqlite_encoding_preflight\",\n \"rust_path\": \"radroots_event_store::store::validate_main_database_encoding\"\n },\n {\n \"role\": \"source_generation_history_rollback_guard\",\n \"rust_path\": \"radroots_event_store::schema::validate_rollback_preserves_source_generation_history\"\n },\n {\n \"role\": \"result_vector_executor\",\n \"rust_path\": \"source_maintenance_v1_result_vector\"\n }\n ],\n \"source_files\": [\n {\n \"role\": \"workspace_dependency_authority\",\n \"path\": \"Cargo.toml\",\n \"byte_length\": 12414,\n \"sha256\": \"895bf9d57a1cb9a89c0f70232cb628106897b421561ae0b9e9ec57577149b273\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_currency_value_authority\",\n \"path\": \"crates/core/src/currency.rs\",\n \"byte_length\": 4142,\n \"sha256\": \"42e6da2d8d2fdd6955dc2d83e98dd00266e02bc910b69923b117662c05089d14\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_decimal_value_authority\",\n \"path\": \"crates/core/src/decimal.rs\",\n \"byte_length\": 7839,\n \"sha256\": \"4b1d681a92d7a9e074bee7e1f20eb1499bb05bfc6528a6c9ba69fa7d00416550\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_money_value_authority\",\n \"path\": \"crates/core/src/money.rs\",\n \"byte_length\": 8025,\n \"sha256\": \"3f30bc21e21951a62fdc5d4033736ed42a883df590e8403940775bd2edfeebc5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_percent_value_authority\",\n \"path\": \"crates/core/src/percent.rs\",\n \"byte_length\": 2652,\n \"sha256\": \"42ceab109881329f3539d06a0ee0381ab4359985fe185a18d58a9c9558c5eb7f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_value_authority\",\n \"path\": \"crates/core/src/quantity.rs\",\n \"byte_length\": 6785,\n \"sha256\": \"d14e619da3829cdaf3ca7385fb892d330c286c1e21455c135c37518cbb588b5f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_quantity_price_value_authority\",\n \"path\": \"crates/core/src/quantity_price.rs\",\n \"byte_length\": 7061,\n \"sha256\": \"d60f70377099470ed283f45868a5119d7bb46143cb250b5082a4c038ba7f6cdc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"core_unit_value_authority\",\n \"path\": \"crates/core/src/unit.rs\",\n \"byte_length\": 10023,\n \"sha256\": \"550e9582b6a5290aac3f637b83853b21fefcbab92c432d067c83448f4b2fe3e9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_public_surface\",\n \"path\": \"crates/blossom/src/lib.rs\",\n \"byte_length\": 510,\n \"sha256\": \"a4dfcbd193457c50c1b0fbfc91bb547981ecd3a6e23cfc6f1030edfb511dc58c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_authorization_authority\",\n \"path\": \"crates/blossom/src/authorization.rs\",\n \"byte_length\": 39360,\n \"sha256\": \"461947b30516315a342b3b5697599f701d27050888f29f67f5d7a7ad4afa4c28\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_descriptor_authority\",\n \"path\": \"crates/blossom/src/descriptor.rs\",\n \"byte_length\": 13702,\n \"sha256\": \"b2a4ffa760256e1316f70e012d200e2b5f4afd8ede771b85a5147f96b247f599\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_error_authority\",\n \"path\": \"crates/blossom/src/error.rs\",\n \"byte_length\": 17229,\n \"sha256\": \"02af55beacf437040be17d1fc3271d6e2a62915f656ec2dbe06247deef83f95a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_hash_authority\",\n \"path\": \"crates/blossom/src/hash.rs\",\n \"byte_length\": 11006,\n \"sha256\": \"45eca0d81ac0f46c305a32f81aafcdd90b14679d0614f72862289a6e5d4cb08f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_media_type_authority\",\n \"path\": \"crates/blossom/src/media_type.rs\",\n \"byte_length\": 2368,\n \"sha256\": \"69db5c0f9fcdd7dbe1f22daa499f65603ae4ce198083dab2c73a36813fa18a46\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"blossom_url_authority\",\n \"path\": \"crates/blossom/src/url.rs\",\n \"byte_length\": 14580,\n \"sha256\": \"342c995fcf620e5fb32461a7d8276f5668ba2662890bd32d12780cc9121c6451\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_public_surface\",\n \"path\": \"crates/event/src/lib.rs\",\n \"byte_length\": 1892,\n \"sha256\": \"5d609b963a9b8db18ef96a10f394617413734d2631774d3296e6d8fc029cb02c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_facade\",\n \"path\": \"crates/event/src/contract.rs\",\n \"byte_length\": 208,\n \"sha256\": \"27466bf36461071931f391c60646a13f7781d19584583166cc54c0eece76d010\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_contract_registry_v7_authority\",\n \"path\": \"crates/event/src/contract/registry_v7.rs\",\n \"byte_length\": 145805,\n \"sha256\": \"0a62603f6fc05dc9f758561cf7da258c676ada810ceaeca7fb151364a5c83d62\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_envelope_authority\",\n \"path\": \"crates/event/src/envelope.rs\",\n \"byte_length\": 29310,\n \"sha256\": \"29f8d8c4b17c01cb5a8c7f59e4ce52e134180e0e6f37d265063d278b8f2ff26e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_verification_typestate_authority\",\n \"path\": \"crates/event/src/verification.rs\",\n \"byte_length\": 9400,\n \"sha256\": \"417acb2ce670d266b1fa4fcafd6b48a910218deab38f57fb8b942056db76f94d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_admission_typestate_authority\",\n \"path\": \"crates/event/src/admission.rs\",\n \"byte_length\": 7052,\n \"sha256\": \"62bd2ceb0f434946fdfc2b81f4efb13738676a29a6936c23a427e43aadf2d9cb\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_facade\",\n \"path\": \"crates/event/src/event_head.rs\",\n \"byte_length\": 97,\n \"sha256\": \"f761ff3e74c4f5e1e28381db00ce698c633ff048669b22984d14a587482e8e83\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_head_v1_authority\",\n \"path\": \"crates/event/src/event_head/v1.rs\",\n \"byte_length\": 6849,\n \"sha256\": \"9f4144d8d240023cf493ce0b538ade4c16dc44204d6b5f2797152caf6f9e7dba\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_ids_authority\",\n \"path\": \"crates/event/src/id.rs\",\n \"byte_length\": 48381,\n \"sha256\": \"a08fe3873815453a03318a706e2fa98a8a6728ec69d24368a87ec60025566fe0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_authority\",\n \"path\": \"crates/event/src/trade.rs\",\n \"byte_length\": 65549,\n \"sha256\": \"f818a7287ed82de3fb9ef1d973f0fb18d84b30eb4827ddc576ee502dd980145f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_kinds_authority\",\n \"path\": \"crates/event/src/kinds.rs\",\n \"byte_length\": 34961,\n \"sha256\": \"8b3ce6193cab1f7e1587d0c1b2880a81b0aa43c77d89671b602772db37edcebf\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_tags_authority\",\n \"path\": \"crates/event/src/tags.rs\",\n \"byte_length\": 4530,\n \"sha256\": \"cb8f12a639fc72d9238e32495d9e67e928e6627fb8c94ec0a58c5d95e7373cd6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_draft_authority\",\n \"path\": \"crates/event/src/draft.rs\",\n \"byte_length\": 51150,\n \"sha256\": \"ecc619aae28d688d13f35467398f17766b60f26c2bf3616d772ce65b7fa4fb53\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_calendar_authority\",\n \"path\": \"crates/event/src/calendar.rs\",\n \"byte_length\": 97992,\n \"sha256\": \"73e79d61ee175cb46e6123e1d28239252ddf0ad094dc1113bdb1286b63b88d57\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_classified_listing_authority\",\n \"path\": \"crates/event/src/classified_listing.rs\",\n \"byte_length\": 7956,\n \"sha256\": \"ac58484b87c7712c81f50f884d1c0dd60938571aa5f6f05d555191f529a4c2b0\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_profile_authority\",\n \"path\": \"crates/event/src/profile.rs\",\n \"byte_length\": 17011,\n \"sha256\": \"572bfe3b6f1711d109db445077f0a8453cad3f021cc0638f79201b618e16b978\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_post_authority\",\n \"path\": \"crates/event/src/post.rs\",\n \"byte_length\": 19968,\n \"sha256\": \"a822cd51eff9cbaa39db998cc5c90d117f7a983f0fe2d42d42118cb00e95982c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_comment_authority\",\n \"path\": \"crates/event/src/comment.rs\",\n \"byte_length\": 34745,\n \"sha256\": \"24b7fda13229f21cee5cef2a9941e3aaafdbc486b19ed42731b0b6ee37c28703\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_food_availability_authority\",\n \"path\": \"crates/event/src/food_availability.rs\",\n \"byte_length\": 42829,\n \"sha256\": \"52472c07eb0855d884c60755dc3a3e1a963793451d707e51f769f2d78384f318\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_deletion_authority\",\n \"path\": \"crates/event/src/deletion.rs\",\n \"byte_length\": 30022,\n \"sha256\": \"8c3f2e32407520a8e8242c1293af32613c648fb76218b8003a18929ac0b3811f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_dto_authority\",\n \"path\": \"crates/event/src/dto.rs\",\n \"byte_length\": 5038,\n \"sha256\": \"9e228c95ff6ff33f99441d8682781fd0a1c4d3f1db24284a43985dcbac3ba136\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_farm_crdt_authority\",\n \"path\": \"crates/event/src/farm_crdt.rs\",\n \"byte_length\": 21118,\n \"sha256\": \"cec265ac9c42b59a14be4eb96924d26a83477c27291f667f23e6e47a64b6b2c7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_knowledge_authority\",\n \"path\": \"crates/event/src/knowledge.rs\",\n \"byte_length\": 54624,\n \"sha256\": \"3d55ff88a4e30d5d605f9ae4b2e96b9df6978d6c14e52f9aeb0b8039cfc200d7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_operational_listing_authority\",\n \"path\": \"crates/event/src/operational_listing.rs\",\n \"byte_length\": 10307,\n \"sha256\": \"52e710db816c89c8d4b87c86541987f7447f2f7091c394c68d9cb98a27f90159\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_order_authority\",\n \"path\": \"crates/event/src/order.rs\",\n \"byte_length\": 60218,\n \"sha256\": \"4203fcc469612cac5c705884564e140b9fb076cd57079e22ed85275a8b936e50\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_reply_authority\",\n \"path\": \"crates/event/src/reply.rs\",\n \"byte_length\": 15581,\n \"sha256\": \"b0b83a1e96125c0892d932e9314519242a715fa8f46334aff765ffd248c91d9a\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_trade_validation_authority\",\n \"path\": \"crates/event/src/trade_validation.rs\",\n \"byte_length\": 4669,\n \"sha256\": \"1486f79bd1d0eadea6cc1b7646ce1f7f57ccade18334a3ea37286426676b9b98\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_relay_hint_authority\",\n \"path\": \"crates/event/src/relay_hint.rs\",\n \"byte_length\": 13598,\n \"sha256\": \"fb8b026eeccf51d78f20771c44223e7b48825aa0e3c760d6b3cb90148021a674\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_media_authority\",\n \"path\": \"crates/event/src/media.rs\",\n \"byte_length\": 3455,\n \"sha256\": \"10678af3a202d5367dbcf3bcdc0bf9fdf8d8fb89b045d7d3fb96b3cc195ab6f9\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_social_authority\",\n \"path\": \"crates/event/src/social.rs\",\n \"byte_length\": 6076,\n \"sha256\": \"a2dab19caad46eab14fac86e7c878a801a450d8ac374b0e728b71a5a224eef19\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_facade\",\n \"path\": \"crates/event/src/wire.rs\",\n \"byte_length\": 68,\n \"sha256\": \"cb52f6006f7ecd862707d6b048f9fc407e0cd5ebcd3091b3c54e195ffa5cba64\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_wire_v1_authority\",\n \"path\": \"crates/event/src/wire/v1.rs\",\n \"byte_length\": 22198,\n \"sha256\": \"c818bc1f67e215948ba253c8c62b09ab788db9aeec9b6df8096014b55565cb8c\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_public_surface\",\n \"path\": \"crates/event_codec/src/lib.rs\",\n \"byte_length\": 2448,\n \"sha256\": \"7d4b0040ad3971f34395b17cb1aa40617e9c2c9e1b4222676c388c3adaa47e07\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_facade\",\n \"path\": \"crates/event_codec/src/verification.rs\",\n \"byte_length\": 253,\n \"sha256\": \"b49a32df605035c87f295c0a151140d43d2e588c5e11b05183e5fc92993dae0e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_verification_v1_authority\",\n \"path\": \"crates/event_codec/src/verification/v1.rs\",\n \"byte_length\": 6584,\n \"sha256\": \"72f64615bd6b6dc3b051579e3069b2213f3781dcc28e12f0b533c7d81b81b9b1\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_registry_v7_admission_authority\",\n \"path\": \"crates/event_codec/src/admission/registry_v7.rs\",\n \"byte_length\": 5228,\n \"sha256\": \"62da30cb6ef7d0ed2d43715a73bd529283e8ae8c12571bc5c68722cb400f17b8\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_admission_facade\",\n \"path\": \"crates/event_codec/src/admission.rs\",\n \"byte_length\": 21251,\n \"sha256\": \"25928e734e7b3688f92af806a12a13458c48fb47e94249f60f0226300d5da2b4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_inbound_facade\",\n \"path\": \"crates/event_codec/src/profile/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/profile/inbound/registry_v7.rs\",\n \"byte_length\": 10347,\n \"sha256\": \"32111c0e0592229c11a93a3a8054e7b124b039451aa696a9abcb56df2a6608d3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_inbound_facade\",\n \"path\": \"crates/event_codec/src/post/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_post_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/post/inbound/registry_v7.rs\",\n \"byte_length\": 15985,\n \"sha256\": \"e17083b0596e3a55994c399bc6272c0ebf04cabaa513260b3d45dd546f7f50ef\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_inbound_facade\",\n \"path\": \"crates/event_codec/src/reply/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_reply_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/reply/inbound/registry_v7.rs\",\n \"byte_length\": 26662,\n \"sha256\": \"d1d11116ca27801b7e2f17da60900e6a9ca0b6f27ea9ee0bf19e97dbf24c0c32\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_inbound_facade\",\n \"path\": \"crates/event_codec/src/comment/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_comment_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/comment/inbound/registry_v7.rs\",\n \"byte_length\": 53311,\n \"sha256\": \"c63fe853536b17bded6073f32b53434b60fc5123f911d01c642a6b3982f09c70\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_facade\",\n \"path\": \"crates/event_codec/src/deletion/mod.rs\",\n \"byte_length\": 115,\n \"sha256\": \"17d56f82ddb0a86bc97abbcf3e037fe460ea31fff2548f527be4bf004dac3a95\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_deletion_reconciliation_v1_authority\",\n \"path\": \"crates/event_codec/src/deletion/reconciliation_v1.rs\",\n \"byte_length\": 38028,\n \"sha256\": \"c6901f559e83700610595720c47a4d062078ae8411e1fc2fb952dd215b19dd90\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_error_authority\",\n \"path\": \"crates/event_codec/src/error.rs\",\n \"byte_length\": 3666,\n \"sha256\": \"174ce982bca37fac28d016c3ab3236441c9b31b2169ec94db6c9288b36c72849\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_admission_authority\",\n \"path\": \"crates/event_codec/src/food_availability/admission.rs\",\n \"byte_length\": 5681,\n \"sha256\": \"99809859090f5295572688d06bbb30dbe50524ba2c996de7ec5b26f6dd390643\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_authored_authority\",\n \"path\": \"crates/event_codec/src/food_availability/authored.rs\",\n \"byte_length\": 9724,\n \"sha256\": \"8673ec62ed3fc47b691efd9ca828643719803971e72b756c5b4603d409d0fba5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_inbound_facade\",\n \"path\": \"crates/event_codec/src/food_availability/inbound.rs\",\n \"byte_length\": 86,\n \"sha256\": \"9994cbca9dcc7450ad133b6b927e7c06bee08ef813652452cc1e56a284c1be10\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_food_registry_v7_authority\",\n \"path\": \"crates/event_codec/src/food_availability/inbound/registry_v7.rs\",\n \"byte_length\": 26037,\n \"sha256\": \"92a7e75da3293967cec17a514479144c3c948a64542577dd58c7f07992968e4f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_traits_authority\",\n \"path\": \"crates/event_codec/src/job/traits.rs\",\n \"byte_length\": 4946,\n \"sha256\": \"1b3558a3196744005978dceddd33d3a72ad1ab8f50fb1cbfd997dcfe98bb9e39\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_job_encode_authority\",\n \"path\": \"crates/event_codec/src/job/encode.rs\",\n \"byte_length\": 1670,\n \"sha256\": \"5d6cae6309fcdd02deb7c751b5c451d257cf1900986391cb39cbb2bfd1ddb577\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_knowledge_verification_authority\",\n \"path\": \"crates/event_codec/src/knowledge/verification.rs\",\n \"byte_length\": 6986,\n \"sha256\": \"6f8a10ed262ce37f05acd9c89a1e37ca2fa99ce7da18407891e8abe228ee2349\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_operational_listing_tags_authority\",\n \"path\": \"crates/event_codec/src/operational_listing/tags.rs\",\n \"byte_length\": 50222,\n \"sha256\": \"55b6860bc6f4699dd47a49f8336764e9db78aafb7c32849102dedeb96804b373\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_order_decode_authority\",\n \"path\": \"crates/event_codec/src/order/decode.rs\",\n \"byte_length\": 42667,\n \"sha256\": \"033d9465bc041d205eab3f820572b719a35651340dcb50bdce7c60fabee5f6e1\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_profile_facade\",\n \"path\": \"crates/event_codec/src/profile/mod.rs\",\n \"byte_length\": 1456,\n \"sha256\": \"06b890da54580e2ec68f7e729281b7c3b650ac0a95a4fcb0db3c6cdaa591a7ef\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_tag_builders_authority\",\n \"path\": \"crates/event_codec/src/tag_builders.rs\",\n \"byte_length\": 9232,\n \"sha256\": \"bd81ccf04fd72358331b48eddcde087a95e115c5bd90c05fd9332644979ad5e6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_codec_trade_facade\",\n \"path\": \"crates/event_codec/src/trade/mod.rs\",\n \"byte_length\": 21472,\n \"sha256\": \"10cee4b6cd6429ac9eb00327a521bcd0982fae4de98bda77e4728fda002965d3\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_dependency_authority\",\n \"path\": \"crates/event_store/Cargo.toml\",\n \"byte_length\": 1550,\n \"sha256\": \"9901a09402592ee873c255edf2e0c0d770105401255679d86ad47c35cb159d72\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_error_and_limits\",\n \"path\": \"crates/event_store/src/error.rs\",\n \"byte_length\": 19458,\n \"sha256\": \"3d87df984af6ae5decf7ca8c3a8d5422be6c647e6a05490225b6b2037bf3701b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"generated_descriptor_registration\",\n \"path\": \"crates/event_store/src/generated.rs\",\n \"byte_length\": 144,\n \"sha256\": \"6b0a8d6f249bd4fc3f878d37cb5e418680f0f1be2d9eec2518dedf03efc47121\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_surface\",\n \"path\": \"crates/event_store/src/lib.rs\",\n \"byte_length\": 3844,\n \"sha256\": \"3cd9653bcb752fb3c4442d4904b98a0a6208011a9a238125b7b7073d7f4e312b\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"migration_registry\",\n \"path\": \"crates/event_store/src/migrations.rs\",\n \"byte_length\": 73585,\n \"sha256\": \"a47477d04759ec6e71d14d3d05459864eae78fce0e3c2481a1b03147f04637f7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_model_public_surface\",\n \"path\": \"crates/event_store/src/model.rs\",\n \"byte_length\": 32070,\n \"sha256\": \"2cdbf6d1a4e0fa6680f45f02758513509339a7cf3a2387e824fb73b0edf749a4\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_model\",\n \"path\": \"crates/event_store/src/model/reconciliation_v1.rs\",\n \"byte_length\": 10446,\n \"sha256\": \"475b7b840bffdae7e3f7a31f5b940b9dfa579eaa1cac640729aaaeadabf83ba6\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"event_store_reconciliation_v1_ingest_model\",\n \"path\": \"crates/event_store/src/model/ingest_reconciliation_v1.rs\",\n \"byte_length\": 1588,\n \"sha256\": \"6117e797674c35bb1ccebffbc6a8c0108bfc38c7c9f82607066dfd802b8f0734\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_model\",\n \"path\": \"crates/event_store/src/model/addressable_transition_feed_v1.rs\",\n \"byte_length\": 22229,\n \"sha256\": \"e651952d73ed0a29d2b137076f56f699beada205ac84ed8a96834e86e343fa78\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_model\",\n \"path\": \"crates/event_store/src/model/current_visibility_v1.rs\",\n \"byte_length\": 5634,\n \"sha256\": \"d64429bc7985231e923c1c3e5295d94270a817dd5dc96693f35dfdf2dc398e4f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_model\",\n \"path\": \"crates/event_store/src/model/food_availability_projection_v1.rs\",\n \"byte_length\": 17108,\n \"sha256\": \"67861a966d674efbc40f0b0433db2ec006048ff923f6261df4161e2ddba717b5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_generation_rebuild_authority\",\n \"path\": \"crates/event_store/src/nip09/reconciliation_v1.rs\",\n \"byte_length\": 184833,\n \"sha256\": \"3ca802cb7a8444c8512b2be1390a019281704316a430fb9b75a8ad2cad358a07\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"nip09_successor_result_vector_executor\",\n \"path\": \"crates/event_store/tests/support/nip09_reconciliation_v1_result_vector_v2.rs\",\n \"byte_length\": 18205,\n \"sha256\": \"c632beee70dc777b8dd2a4f88cadb55296fee7aa5742e6e2c9f6d49f26ae2c78\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"schema_migration_and_reopen_authority\",\n \"path\": \"crates/event_store/src/schema.rs\",\n \"byte_length\": 146146,\n \"sha256\": \"93b060e80d3edd73f86208e4bf698fa9d53eaf1eeb04526c9261fb8b5726fb0d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"public_store_and_transaction_authority\",\n \"path\": \"crates/event_store/src/store.rs\",\n \"byte_length\": 393218,\n \"sha256\": \"64565c3bc043779e1c2f26d9761901b109b4acab0f48c2021102c0e4e1f1f726\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"addressable_transition_feed_store\",\n \"path\": \"crates/event_store/src/store/addressable_transition_feed_v1.rs\",\n \"byte_length\": 40209,\n \"sha256\": \"2cebcca9602633652a79e7041d18d3a7d3feb1388a98f4f68137b9814efcedfa\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"current_visibility_store\",\n \"path\": \"crates/event_store/src/store/current_visibility_v1.rs\",\n \"byte_length\": 15737,\n \"sha256\": \"aa5d1bff7a5368cbac2b37906bb40f54bfc8aa6906bab34369569fdd105bf899\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"food_projection_store\",\n \"path\": \"crates/event_store/src/store/food_availability_projection_v1.rs\",\n \"byte_length\": 48919,\n \"sha256\": \"3a30da308ef863b806ed63d1cb65ac80f6a946a71821af84a0c595330491757e\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_extension\",\n \"path\": \"crates/event_store/src/store/post_core_extensions_v1.rs\",\n \"byte_length\": 6569,\n \"sha256\": \"f5bd8ddb45e1b2144895bd8da737fc92db051be1844ce7e8047a32b47376ce37\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_post_core_v1_storage\",\n \"path\": \"crates/event_store/src/store/post_core_storage_v1.rs\",\n \"byte_length\": 16538,\n \"sha256\": \"fcd6546c23a6cba12b70d92728d19b6ee5d43174b83495f820348b77efa33aab\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"raw_ingest_capacity_authority\",\n \"path\": \"crates/event_store/src/store/protocol_reconciliation_v1.rs\",\n \"byte_length\": 29950,\n \"sha256\": \"c72aaff06e4f35d0b4523c6625e83345879f9e15b9fe9563dccf589e5d01e277\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_protocol_storage\",\n \"path\": \"crates/event_store/src/store/protocol_storage_v1.rs\",\n \"byte_length\": 10835,\n \"sha256\": \"c82179b9f57968191f3e71dd008cc701787c62715ada74090eeecaa3b213076d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_runtime\",\n \"path\": \"crates/event_store/src/source_maintenance_v1.rs\",\n \"byte_length\": 51849,\n \"sha256\": \"db43df5849c811d767a87bfa7884ce499400d0b169734e18242542a4415b3a51\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_food_result_vector_executor\",\n \"path\": \"crates/event_store/tests/food_availability_projection_v1_result_vector.rs\",\n \"byte_length\": 34046,\n \"sha256\": \"776903c6431ff07f26c6ad6b713db5628efce1a72f34d86de6e47d4404fa3a6d\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"artifact_transaction_authority\",\n \"path\": \"tools/xtask/src/contract/artifact_bundle.rs\",\n \"byte_length\": 39149,\n \"sha256\": \"3d1bba3980b80698d2a21e26caf7af6fe4d9dc4a7fbabc6e406b5d33928b457f\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"predecessor_successor_governance\",\n \"path\": \"tools/xtask/src/contract/food_availability_projection.rs\",\n \"byte_length\": 198991,\n \"sha256\": \"36fd3d8b5dda3b0855a06a47dae14cae7f4cc84aaca9902c31477c218859fccc\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"transitive_predecessor_membership_governance\",\n \"path\": \"tools/xtask/src/contract/nip09_reconciliation.rs\",\n \"byte_length\": 842402,\n \"sha256\": \"4abb141771cfa257440dba7ebeae21f5ad1f9856cc0d5f6437ba68614d997810\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"source_maintenance_governance\",\n \"path\": \"tools/xtask/src/contract/source_maintenance.rs\",\n \"byte_length\": 199270,\n \"sha256\": \"65b83a3ee1a700527d15c7403ca042cf2aaa4cd8935a2f8d2ab190c491a815bd\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 508630,\n \"sha256\": \"203cc8578100894be51122d69cfb431650c3b5331e4071564f4b1095b0b46b94\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"dto_root_generation_authority\",\n \"path\": \"tools/xtask/src/dto_roots.rs\",\n \"byte_length\": 35940,\n \"sha256\": \"a899a5695d7533c151d03477c4bd697a6e55c47a4980549724ac42046af694f5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"xtask_dispatch_and_release_preflight\",\n \"path\": \"tools/xtask/src/main.rs\",\n \"byte_length\": 16426,\n \"sha256\": \"abc24896c6c740c10811a3e003ec4be8ebdb376a7b6b01445bfa7a5889f094a7\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n }\n ],\n \"public_api\": {\n \"inherited_predecessor_symbols\": [\n \"RADROOTS_ADDRESSABLE_TRANSITION_CURSOR_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_D_TAG_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_FEED_VERSION_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_LIMIT_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_RAW_JSON_MAX_BYTES_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_PAGE_SCAN_MAX_V1\",\n \"RADROOTS_ADDRESSABLE_TRANSITION_SCOPE_KIND_MAX_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_APPLY_PAGE_LIMIT_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_PROJECTION_VERSION_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_BYTES_V1\",\n \"RADROOTS_FOOD_AVAILABILITY_SEARCH_QUERY_MAX_TERMS_V1\",\n \"RadrootsAddressableTransitionCauseV1\",\n \"RadrootsAddressableTransitionCoordinateV1\",\n \"RadrootsAddressableTransitionCursorV1\",\n \"RadrootsAddressableTransitionEventReferenceV1\",\n \"RadrootsAddressableTransitionOriginV1\",\n \"RadrootsAddressableTransitionPageV1\",\n \"RadrootsAddressableTransitionRawHeadDecisionV1\",\n \"RadrootsAddressableTransitionScopeFingerprintV1\",\n \"RadrootsAddressableTransitionScopeV1\",\n \"RadrootsAddressableTransitionV1\",\n \"RadrootsAddressableTransitionVisibilityV1\",\n \"RadrootsCurrentEventVisibilityV1\",\n \"RadrootsCurrentVisibilityDecisionV1\",\n \"RadrootsFoodAvailabilitySearchQueryV1\",\n \"RadrootsFoodAvailabilityStatusFilterV1\",\n \"RadrootsNip09SuppressionEvidenceV1\",\n \"RadrootsNip09SuppressionOutcome\",\n \"RadrootsNip09SuppressionReason\",\n \"RadrootsStoreProducedCanonicalEventV1\",\n \"RadrootsStoredFoodAvailabilityImageV1\",\n \"RadrootsStoredFoodAvailabilityV1\"\n ],\n \"added_symbols\": [\n \"RADROOTS_EVENT_STORE_RAW_EVENT_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_EVENT_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_COUNT_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RAW_TAG_TEXT_BYTES_LIMIT_V1\",\n \"RADROOTS_EVENT_STORE_RETAINED_SOURCE_GENERATION_LIMIT_V1\",\n \"RadrootsEventStoreSourceCapacityResourceV1\",\n \"RadrootsEventStoreSourceCapacityV1\"\n ],\n \"methods\": [\n \"RadrootsEventStore::source_capacity_v1\",\n \"RadrootsEventStoreSourceCapacityResourceV1::as_str\",\n \"RadrootsEventStoreSourceCapacityV1::source_generation\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_count\",\n \"RadrootsEventStoreSourceCapacityV1::raw_event_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_tag_text_bytes\",\n \"RadrootsEventStoreSourceCapacityV1::raw_high_water_seq\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_count\",\n \"RadrootsEventStoreSourceCapacityV1::retained_generation_limit\"\n ],\n \"error_variants\": [\n \"SourceCapacityExceeded\",\n \"SourceGenerationHistoryLimitReached\",\n \"PersistedEphemeralRawEvent\",\n \"SourceCapacityStateDrift\",\n \"SqliteMainDatabaseEncodingNotUtf8\",\n \"RollbackWouldDiscardSourceGenerationHistory\"\n ],\n \"removed_symbols\": [\n \"RadrootsEventStoreReconciliationResource\",\n \"RadrootsEventStoreError::ReconciliationCapacityExceeded\"\n ],\n \"breaking_replacements\": [\n {\n \"removed\": \"RadrootsEventStoreReconciliationResource\",\n \"replacement\": \"RadrootsEventStoreSourceCapacityResourceV1\"\n },\n {\n \"removed\": \"RadrootsEventStoreError::ReconciliationCapacityExceeded\",\n \"replacement\": \"RadrootsEventStoreError::SourceCapacityExceeded\"\n }\n ]\n },\n \"result_vector\": {\n \"canonical_path\": \"contracts/conformance/vectors/event_store/source_maintenance.v1.json\",\n \"mirror_path\": \"crates/event_store/tests/fixtures/source_maintenance.v1.json\",\n \"byte_length\": 16253,\n \"sha256\": \"997aba2604a2b9d199fb87dc9d07942ca50d91863aeadcf3eeacf16d191dd71f\",\n \"hash_algorithm\": \"sha256_bytes_v1\",\n \"executor_id\": \"radroots_event_store.source_maintenance_v1.result_vector_executor.v1\",\n \"executor_path\": \"crates/event_store/tests/source_maintenance_v1_result_vector.rs\",\n \"executor_test\": \"source_maintenance_v1_result_vector\",\n \"executor_byte_length\": 23510,\n \"executor_sha256\": \"a7487afdfe19fc5fc794811d0f0e6035203e1aabcf0a33a1d398f6b3555d38f3\",\n \"executor_hash_algorithm\": \"sha256_bytes_v1\"\n }\n}\n"; pub(crate) const SOURCE_MAINTENANCE_MANIFEST_BYTE_LENGTH: usize = 38397; pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SHA256: &str = - "51205a1c1c7c3b91d79354ea5deabefc0f42d83423d4a7da3a0e6658f34863b5"; + "85b4e199f34b12f6a0c74617cac2c2de91ae7d616a38ef0ba7483073b72d0386"; pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SCHEMA_VERSION: u32 = 1; pub(crate) const SOURCE_MAINTENANCE_CONTRACT_ID: &str = "radroots_event_store.source_maintenance_v1"; diff --git a/crates/event_store/src/model.rs b/crates/event_store/src/model.rs @@ -38,8 +38,9 @@ use radroots_event::envelope::EventKind; use radroots_event::id::{CandidateId, DTag, EventId, InventoryBinId, MutationId, TradeId}; use radroots_event::trade::TradeMutationKindV1; use radroots_identity::PublicKey; +use radroots_transport::target::EndpointUri; use radroots_transport::target::TargetFingerprint; -use radroots_transport::{RadrootsTransportTargetUri, Target, TransportId}; +use radroots_transport::{Target, TransportId}; pub use reconciliation_v1::{ RadrootsEventAdmissionStatus, RadrootsEventIngest, RadrootsEventIngestReceipt, RadrootsEventPersistence, RadrootsEventStoreSourceGeneration, RadrootsRawHeadDecision, @@ -167,7 +168,7 @@ impl RadrootsTransportObservationType { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTransportObservation { transport_kind: TransportId, - endpoint_uri: RadrootsTransportTargetUri, + endpoint_uri: EndpointUri, endpoint_fingerprint: TargetFingerprint, observation_type: RadrootsTransportObservationType, observed_at_ms: i64, @@ -208,7 +209,7 @@ impl RadrootsTransportObservation { &self.transport_kind } - pub fn endpoint_uri(&self) -> &RadrootsTransportTargetUri { + pub fn endpoint_uri(&self) -> &EndpointUri { &self.endpoint_uri } @@ -258,7 +259,7 @@ impl RadrootsTransportObservation { #[cfg(test)] pub(crate) fn from_unchecked_parts_for_test( transport_kind: TransportId, - endpoint_uri: RadrootsTransportTargetUri, + endpoint_uri: EndpointUri, endpoint_fingerprint: TargetFingerprint, observation_type: RadrootsTransportObservationType, observed_at_ms: i64, @@ -739,7 +740,8 @@ mod tests { 1, ) .expect("Reticulum observation"); - let expected_reticulum = Target::reticulum().expect("canonical Reticulum target"); + let expected_reticulum = Target::new(TransportId::RETICULUM, "reticulum:local") + .expect("canonical Reticulum target"); assert_eq!(reticulum.endpoint_uri(), expected_reticulum.uri()); assert_eq!( reticulum.endpoint_fingerprint(), diff --git a/crates/event_store/src/store.rs b/crates/event_store/src/store.rs @@ -70,8 +70,9 @@ use radroots_event::envelope::event_head::v1::{ }; use radroots_event::id::{DTag, EventId, MutationId, TradeId}; use radroots_event::trade::TradeMutationKindV1; +use radroots_transport::target::EndpointUri; use radroots_transport::target::TargetFingerprint; -use radroots_transport::{RadrootsTransportTargetUri, Target, TransportId}; +use radroots_transport::{Target, TransportId}; #[cfg(test)] use sha2::{Digest, Sha256}; use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions}; @@ -1149,7 +1150,7 @@ pub async fn inspect_event_store_status( pub struct RadrootsTransportObservationRow { pub event_id: String, pub transport_kind: TransportId, - pub endpoint_uri: RadrootsTransportTargetUri, + pub endpoint_uri: EndpointUri, pub endpoint_fingerprint: TargetFingerprint, pub observation_type: RadrootsTransportObservationType, pub first_observed_at_ms: i64, @@ -9279,7 +9280,8 @@ CREATE TABLE aux.event_transport_observation (event_id TEXT);", .await .expect("Reticulum endpoint observations"); assert_eq!(reticulum_observations.len(), 1); - let expected_reticulum = Target::reticulum().expect("canonical Reticulum target"); + let expected_reticulum = Target::new(TransportId::RETICULUM, "reticulum:local") + .expect("canonical Reticulum target"); assert_eq!( &reticulum_observations[0].endpoint_fingerprint, expected_reticulum.fingerprint() @@ -9290,8 +9292,7 @@ CREATE TABLE aux.event_transport_observation (event_id TEXT);", async fn transport_observation_ingest_rejects_forged_endpoint_identity_atomically() { let store = RadrootsEventStore::open_memory().await.expect("open"); let event = signed_event(KIND_POST, 16, Vec::new(), "forged observation"); - let endpoint_uri = - RadrootsTransportTargetUri::parse("wss://relay-a.local").expect("endpoint A"); + let endpoint_uri = EndpointUri::parse("wss://relay-a.local").expect("endpoint A"); let endpoint_b = Target::new(TransportId::NOSTR, "wss://relay-b.local").expect("endpoint B"); let observation = RadrootsTransportObservation::from_unchecked_parts_for_test( diff --git a/crates/outbox/src/model.rs b/crates/outbox/src/model.rs @@ -3,10 +3,11 @@ use crate::RadrootsOutboxError; use radroots_event::draft::{EventDraft, SignedEvent}; use radroots_event::id::{MutationId, TradeId}; +use radroots_transport::target::EndpointUri; use radroots_transport::target::{TargetFingerprint, TargetLabel, TargetScope}; use radroots_transport::{ RadrootsTransportOutcomeKind, RadrootsTransportSatisfactionClass, - RadrootsTransportSatisfactionPolicy, RadrootsTransportTargetUri, Target, TransportId, + RadrootsTransportSatisfactionPolicy, Target, TransportId, }; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -518,7 +519,7 @@ pub struct RadrootsOutboxDeliveryTargetRecord { pub delivery_target_id: i64, pub delivery_plan_id: i64, pub transport_kind: TransportId, - pub endpoint_uri: RadrootsTransportTargetUri, + pub endpoint_uri: EndpointUri, pub target_scope: Option<TargetScope>, pub target_label: Option<TargetLabel>, pub endpoint_fingerprint: TargetFingerprint, diff --git a/crates/outbox/src/store.rs b/crates/outbox/src/store.rs @@ -2004,12 +2004,17 @@ fn satisfaction_target_count( } fn validate_delivery_target(target: &Target) -> Result<(), RadrootsOutboxError> { - if target.kind() == &TransportId::RETICULUM - && target.uri().as_str() != RADROOTS_RETICULUM_ENDPOINT_URI - { - return Err(RadrootsOutboxError::Transport( - radroots_transport::RadrootsTransportError::InvalidTargetUri, - )); + if target.kind() == &TransportId::RETICULUM { + if target.uri().as_str() != RADROOTS_RETICULUM_ENDPOINT_URI { + return Err(RadrootsOutboxError::Transport( + RadrootsTransportError::InvalidTargetUri, + )); + } + if target.scope().is_none() { + return Err(RadrootsOutboxError::Transport( + RadrootsTransportError::EmptyTargetScope, + )); + } } Ok(()) } @@ -2899,6 +2904,7 @@ fn delivery_target_from_row( ), } .map_err(|_| invalid_identity("canonical_fields"))?; + validate_delivery_target(&target).map_err(|_| invalid_identity("canonical_fields"))?; if target.uri().as_str() != endpoint_uri_raw || target.scope() != target_scope.as_ref() || target.label() != target_label.as_ref() @@ -3615,7 +3621,13 @@ mod tests { fn reticulum_target(uri: &str) -> Target { assert_eq!(uri, RADROOTS_RETICULUM_ENDPOINT_URI); - Target::reticulum().expect("reticulum target") + Target::new_with_metadata( + TransportId::RETICULUM, + "reticulum:local", + Some(TargetScope::parse("local").expect("Reticulum scope")), + None, + ) + .expect("reticulum target") } #[test] @@ -6032,7 +6044,7 @@ mod tests { .enqueue_operation(RadrootsOutboxOperationInput::new( "publish_post", draft, - delivery_plan(vec![Target::reticulum().expect("Reticulum target")]), + delivery_plan(vec![reticulum_target(RADROOTS_RETICULUM_ENDPOINT_URI)]), 1_000, )) .await @@ -6110,11 +6122,43 @@ mod tests { #[tokio::test] async fn enqueue_rejects_invalid_reticulum_targets_before_persistence() { let outbox = RadrootsOutbox::open_memory().await.expect("open"); - assert_eq!( - Target::new(TransportId::RETICULUM, "reticulum:alternate",) - .expect_err("invalid Reticulum target"), - RadrootsTransportError::InvalidTargetUri - ); + let draft = generic_draft(FIXTURE_ALICE_PUBLIC_KEY_HEX, "invalid Reticulum target"); + let invalid_uri = Target::new_with_metadata( + TransportId::RETICULUM, + "reticulum:alternate", + Some(TargetScope::parse("local").expect("Reticulum scope")), + None, + ) + .expect("transport-neutral target"); + assert!(matches!( + outbox + .enqueue_operation(RadrootsOutboxOperationInput::new( + "publish_post", + draft.clone(), + delivery_plan(vec![invalid_uri]), + 1_000, + )) + .await, + Err(RadrootsOutboxError::Transport( + RadrootsTransportError::InvalidTargetUri + )) + )); + + let missing_scope = Target::new(TransportId::RETICULUM, "reticulum:local") + .expect("transport-neutral target"); + assert!(matches!( + outbox + .enqueue_operation(RadrootsOutboxOperationInput::new( + "publish_post", + draft, + delivery_plan(vec![missing_scope]), + 1_000, + )) + .await, + Err(RadrootsOutboxError::Transport( + RadrootsTransportError::EmptyTargetScope + )) + )); assert_eq!(table_count(&outbox, "outbox_operations").await, 0); assert_eq!(table_count(&outbox, "outbox_event").await, 0); assert_eq!(table_count(&outbox, "outbox_delivery_plan").await, 0); diff --git a/crates/runtime/src/transport.rs b/crates/runtime/src/transport.rs @@ -7,15 +7,42 @@ use radroots_event::{draft::SignedEvent, wire::Nip01EventWire}; #[cfg(feature = "transport-workers")] use radroots_transport::RadrootsTransportTargetReceipt; use radroots_transport::{ - EventSink, EventSource, RadrootsTransport, RadrootsTransportDeliveryRequest, - RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, RadrootsTransportPayload, - RadrootsTransportSatisfactionPolicy, Target, TargetSet, TransportId, + EventSink, EventSource, RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, + RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, RadrootsTransportFetchReceipt, + RadrootsTransportFetchRequest, RadrootsTransportPayload, RadrootsTransportSatisfactionPolicy, + RadrootsTransportStatus, Target, TargetSet, TransportId, }; use thiserror::Error; pub type RadrootsRuntimeTransportFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, RadrootsRuntimeTransportError>> + Send + 'a>>; +/// Unpublished predecessor future alias; removed at Step 170. +#[doc(hidden)] +pub type RadrootsRuntimeTransportShimFuture<'a, T> = + Pin<Box<dyn Future<Output = Result<T, RadrootsTransportError>> + Send + 'a>>; + +/// Unpublished bridge for the predecessor mixed runtime delivery workers. +/// +/// This runtime-owned shim is retired at Step 170 in RCLD 40 when those +/// workers move to the independent source and sink registries. +#[doc(hidden)] +pub trait RadrootsRuntimeTransportShim: Send + Sync { + fn transport_id(&self) -> TransportId; + + fn status(&self) -> RadrootsRuntimeTransportShimFuture<'_, RadrootsTransportStatus>; + + fn deliver( + &self, + request: RadrootsTransportDeliveryRequest, + ) -> RadrootsRuntimeTransportShimFuture<'_, RadrootsTransportDeliveryReceipt>; + + fn fetch( + &self, + request: RadrootsTransportFetchRequest, + ) -> RadrootsRuntimeTransportShimFuture<'_, RadrootsTransportFetchReceipt>; +} + #[derive(Debug, Error)] pub enum RadrootsRuntimeTransportError { #[error("transport `{0}` is not registered")] @@ -143,7 +170,7 @@ pub struct RadrootsRuntimeTransportRegistry { sources: BTreeMap<TransportId, Arc<dyn EventSource>>, sinks: BTreeMap<TransportId, Arc<dyn EventSink>>, #[doc(hidden)] - transports: BTreeMap<TransportId, Arc<dyn RadrootsTransport>>, + transports: BTreeMap<TransportId, Arc<dyn RadrootsRuntimeTransportShim>>, } impl RadrootsRuntimeTransportRegistry { @@ -214,9 +241,9 @@ impl RadrootsRuntimeTransportRegistry { #[doc(hidden)] pub fn register<T>(&mut self, transport: T) -> Result<(), RadrootsRuntimeTransportError> where - T: RadrootsTransport + 'static, + T: RadrootsRuntimeTransportShim + 'static, { - let kind = transport.transport_kind(); + let kind = transport.transport_id(); if self.transports.contains_key(&kind) { return Err(RadrootsRuntimeTransportError::TransportAlreadyRegistered( kind.canonical_label(), @@ -230,7 +257,7 @@ impl RadrootsRuntimeTransportRegistry { pub fn transport( &self, kind: &TransportId, - ) -> Result<Arc<dyn RadrootsTransport>, RadrootsRuntimeTransportError> { + ) -> Result<Arc<dyn RadrootsRuntimeTransportShim>, RadrootsRuntimeTransportError> { self.transports.get(kind).cloned().ok_or_else(|| { RadrootsRuntimeTransportError::TransportNotRegistered(kind.canonical_label()) }) @@ -729,6 +756,7 @@ mod tests { RadrootsRuntimeBoundedQueue, RadrootsRuntimeTransportDispatchRequest, RadrootsRuntimeTransportError, RadrootsRuntimeTransportFuture, RadrootsRuntimeTransportPayload, RadrootsRuntimeTransportRegistry, + RadrootsRuntimeTransportShim, RadrootsRuntimeTransportShimFuture, }; #[cfg(feature = "transport-workers")] use super::{ @@ -741,10 +769,10 @@ mod tests { #[cfg(feature = "transport-workers")] use radroots_event::{draft::SignedEvent, wire::Nip01EventWire}; use radroots_transport::{ - RadrootsTransport, RadrootsTransportCapabilities, RadrootsTransportDeliveryReceipt, + RadrootsTransportCapabilities, RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, RadrootsTransportFetchReceipt, RadrootsTransportFetchRequest, - RadrootsTransportFuture, RadrootsTransportImplementationState, RadrootsTransportOutcome, + RadrootsTransportImplementationState, RadrootsTransportOutcome, RadrootsTransportOutcomeKind, RadrootsTransportPayload, RadrootsTransportSatisfactionClass, RadrootsTransportSatisfactionPolicy, RadrootsTransportStatus, RadrootsTransportTargetReceipt, Target, TargetSet, TransportId, @@ -786,12 +814,12 @@ mod tests { } } - impl RadrootsTransport for StaticTransport { - fn transport_kind(&self) -> TransportId { + impl RadrootsRuntimeTransportShim for StaticTransport { + fn transport_id(&self) -> TransportId { self.kind } - fn status<'a>(&'a self) -> RadrootsTransportFuture<'a, RadrootsTransportStatus> { + fn status(&self) -> RadrootsRuntimeTransportShimFuture<'_, RadrootsTransportStatus> { Box::pin(async move { Ok(RadrootsTransportStatus::new( self.kind, @@ -807,7 +835,7 @@ mod tests { fn deliver<'a>( &'a self, request: RadrootsTransportDeliveryRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportDeliveryReceipt> { + ) -> RadrootsRuntimeTransportShimFuture<'a, RadrootsTransportDeliveryReceipt> { Box::pin(async move { #[cfg(feature = "transport-workers")] if let Some(captured_now_ms) = &self.captured_now_ms { @@ -837,7 +865,7 @@ mod tests { fn fetch<'a>( &'a self, request: RadrootsTransportFetchRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportFetchReceipt> { + ) -> RadrootsRuntimeTransportShimFuture<'a, RadrootsTransportFetchReceipt> { Box::pin(async move { Ok(RadrootsTransportFetchReceipt::new( request.request_id, @@ -872,12 +900,12 @@ mod tests { } #[cfg(feature = "transport-workers")] - impl RadrootsTransport for ForgedReceiptTransport { - fn transport_kind(&self) -> TransportId { + impl RadrootsRuntimeTransportShim for ForgedReceiptTransport { + fn transport_id(&self) -> TransportId { TransportId::NOSTR } - fn status<'a>(&'a self) -> RadrootsTransportFuture<'a, RadrootsTransportStatus> { + fn status(&self) -> RadrootsRuntimeTransportShimFuture<'_, RadrootsTransportStatus> { Box::pin(async { Ok(RadrootsTransportStatus::new( TransportId::NOSTR, @@ -892,7 +920,7 @@ mod tests { fn deliver<'a>( &'a self, request: RadrootsTransportDeliveryRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportDeliveryReceipt> { + ) -> RadrootsRuntimeTransportShimFuture<'a, RadrootsTransportDeliveryReceipt> { Box::pin(async move { match self.forged { ForgedDeliveryReceipt::RequestId => RadrootsTransportDeliveryReceipt::new( @@ -933,7 +961,7 @@ mod tests { fn fetch<'a>( &'a self, _request: RadrootsTransportFetchRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportFetchReceipt> { + ) -> RadrootsRuntimeTransportShimFuture<'a, RadrootsTransportFetchReceipt> { Box::pin(async { Err(RadrootsTransportError::UnsupportedOperation) }) } } diff --git a/crates/transport/src/delivery.rs b/crates/transport/src/delivery.rs @@ -1,7 +1,7 @@ use crate::{ RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, RadrootsTransportOutcome, - RadrootsTransportOutcomeKind, RadrootsTransportPayload, RadrootsTransportTarget, - RadrootsTransportTargetFingerprint, RadrootsTransportTargetSet, + RadrootsTransportOutcomeKind, RadrootsTransportPayload, Target, TargetSet, + target::TargetFingerprint, }; use alloc::collections::{BTreeMap, BTreeSet}; use alloc::string::String; @@ -36,7 +36,7 @@ pub enum RadrootsTransportSatisfactionPolicy { }, RequiredTargets { class: RadrootsTransportSatisfactionClass, - targets: Vec<RadrootsTransportTargetFingerprint>, + targets: Vec<TargetFingerprint>, }, } @@ -161,7 +161,7 @@ impl RadrootsTransportSatisfactionPolicy { pub fn required_targets( class: RadrootsTransportSatisfactionClass, - mut targets: Vec<RadrootsTransportTargetFingerprint>, + mut targets: Vec<TargetFingerprint>, ) -> Result<Self, RadrootsTransportError> { validate_required_targets(&targets)?; targets.sort(); @@ -178,7 +178,7 @@ impl RadrootsTransportSatisfactionPolicy { } } - pub fn required_target_fingerprints(&self) -> Option<&[RadrootsTransportTargetFingerprint]> { + pub fn required_target_fingerprints(&self) -> Option<&[TargetFingerprint]> { match self { Self::RequiredTargets { targets, .. } => Some(targets), Self::NoWait | Self::Any { .. } | Self::All { .. } | Self::Quorum { .. } => None, @@ -226,7 +226,7 @@ impl RadrootsTransportSatisfactionPolicy { pub fn validate_for_target_set( &self, - target_set: &RadrootsTransportTargetSet, + target_set: &TargetSet, ) -> Result<(), RadrootsTransportError> { self.required_target_count(target_set.len())?; if let Self::RequiredTargets { targets, .. } = self { @@ -260,7 +260,7 @@ enum RadrootsTransportSatisfactionPolicyWire { }, RequiredTargets { class: RadrootsTransportSatisfactionClass, - targets: Vec<RadrootsTransportTargetFingerprint>, + targets: Vec<TargetFingerprint>, }, } @@ -289,9 +289,7 @@ impl<'de> serde::Deserialize<'de> for RadrootsTransportSatisfactionPolicy { } } -fn validate_required_targets( - targets: &[RadrootsTransportTargetFingerprint], -) -> Result<(), RadrootsTransportError> { +fn validate_required_targets(targets: &[TargetFingerprint]) -> Result<(), RadrootsTransportError> { if targets.is_empty() { return Err(RadrootsTransportError::EmptyRequiredTargetSet); } @@ -309,7 +307,7 @@ fn validate_required_targets( pub struct RadrootsTransportDeliveryRequest { request_id: String, payload: RadrootsTransportPayload, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, satisfaction_policy: RadrootsTransportSatisfactionPolicy, now_ms: i64, } @@ -318,7 +316,7 @@ impl RadrootsTransportDeliveryRequest { pub fn new( request_id: impl Into<String>, payload: RadrootsTransportPayload, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, satisfaction_policy: RadrootsTransportSatisfactionPolicy, ) -> Result<Self, RadrootsTransportError> { let request_id = request_id.into(); @@ -348,7 +346,7 @@ impl RadrootsTransportDeliveryRequest { &self.payload } - pub fn target_set(&self) -> &RadrootsTransportTargetSet { + pub fn target_set(&self) -> &TargetSet { &self.target_set } @@ -387,7 +385,7 @@ fn validate_delivery_timestamp(now_ms: i64) -> Result<(), RadrootsTransportError struct RadrootsTransportDeliveryRequestWire { request_id: String, payload: RadrootsTransportPayload, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, satisfaction_policy: RadrootsTransportSatisfactionPolicy, now_ms: i64, } @@ -413,18 +411,18 @@ impl<'de> serde::Deserialize<'de> for RadrootsTransportDeliveryRequest { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTransportTargetReceipt { - pub target: RadrootsTransportTarget, + pub target: Target, pub attempted: bool, pub status: RadrootsTransportDeliveryTargetStatus, pub outcome: RadrootsTransportOutcome, } impl RadrootsTransportTargetReceipt { - pub fn new(target: RadrootsTransportTarget, outcome: RadrootsTransportOutcome) -> Self { + pub fn new(target: Target, outcome: RadrootsTransportOutcome) -> Self { Self::attempted(target, outcome) } - pub fn attempted(target: RadrootsTransportTarget, outcome: RadrootsTransportOutcome) -> Self { + pub fn attempted(target: Target, outcome: RadrootsTransportOutcome) -> Self { Self { target, attempted: true, @@ -433,7 +431,7 @@ impl RadrootsTransportTargetReceipt { } } - pub fn skipped(target: RadrootsTransportTarget, outcome: RadrootsTransportOutcome) -> Self { + pub fn skipped(target: Target, outcome: RadrootsTransportOutcome) -> Self { Self { target, attempted: false, @@ -461,7 +459,7 @@ impl RadrootsTransportTargetReceipt { #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct RadrootsTransportTargetReceiptWire { - target: RadrootsTransportTarget, + target: Target, attempted: bool, status: RadrootsTransportDeliveryTargetStatus, outcome: RadrootsTransportOutcome, @@ -489,7 +487,7 @@ impl<'de> serde::Deserialize<'de> for RadrootsTransportTargetReceipt { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTransportDeliveryReceipt { request_id: String, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, target_receipts: Vec<RadrootsTransportTargetReceipt>, } @@ -507,7 +505,7 @@ impl RadrootsTransportDeliveryReceipt { pub fn new( request_id: impl Into<String>, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, target_receipts: Vec<RadrootsTransportTargetReceipt>, ) -> Result<Self, RadrootsTransportError> { let request_id = request_id.into(); @@ -559,7 +557,7 @@ impl RadrootsTransportDeliveryReceipt { self.request_id.as_str() } - pub fn target_set(&self) -> &RadrootsTransportTargetSet { + pub fn target_set(&self) -> &TargetSet { &self.target_set } @@ -620,7 +618,7 @@ impl RadrootsTransportDeliveryReceipt { #[serde(deny_unknown_fields)] struct RadrootsTransportDeliveryReceiptWire { request_id: String, - target_set: RadrootsTransportTargetSet, + target_set: TargetSet, target_receipts: Vec<RadrootsTransportTargetReceipt>, } diff --git a/crates/transport/src/id.rs b/crates/transport/src/id.rs @@ -25,11 +25,16 @@ impl TransportId { /// Daemon-mediated transport. pub const RADROOTSD: Self = Self(ProtocolTransportKind::RADROOTSD); - // Compatibility spellings retained until the planned consumer cutover. + /// Publish-frozen spelling retained only for external first-party consumers. + #[doc(hidden)] #[allow(non_upper_case_globals)] pub const Local: Self = Self::LOCAL; + /// Publish-frozen spelling retained only for external first-party consumers. + #[doc(hidden)] #[allow(non_upper_case_globals)] pub const Nostr: Self = Self::NOSTR; + /// Publish-frozen spelling retained only for external first-party consumers. + #[doc(hidden)] #[allow(non_upper_case_globals)] pub const Reticulum: Self = Self::RETICULUM; @@ -50,12 +55,16 @@ impl TransportId { self.0.as_str() } - /// Returns an owned canonical identity for compatibility with predecessor APIs. + /// Returns an owned canonical identity label. pub fn canonical_label(&self) -> String { self.as_str().to_string() } } +/// Publish-frozen external-consumer alias; removed at Step 305. +#[doc(hidden)] +pub type RadrootsTransportKind = TransportId; + fn map_protocol_error(error: ProtocolError) -> RadrootsTransportError { match error { ProtocolError::EmptyTransportKind => RadrootsTransportError::EmptyTransportKind, @@ -131,7 +140,3 @@ impl<'de> serde::Deserialize<'de> for TransportId { <ProtocolTransportKind as serde::Deserialize>::deserialize(deserializer).map(Self) } } - -/// Compatibility name retained until the planned workspace consumer cutover. -#[doc(hidden)] -pub type RadrootsTransportKind = TransportId; diff --git a/crates/transport/src/lib.rs b/crates/transport/src/lib.rs @@ -43,10 +43,7 @@ pub use target::{ RadrootsTransportTargetLabel, RadrootsTransportTargetSet, RadrootsTransportTargetUri, TARGET_SET_MAX_ITEMS, Target, TargetSet, }; -pub use transport::{ - RadrootsTransport, RadrootsTransportFetchReceipt, RadrootsTransportFetchRequest, - RadrootsTransportFuture, -}; +pub use transport::{RadrootsTransportFetchReceipt, RadrootsTransportFetchRequest}; #[cfg(test)] extern crate self as radroots_transport; diff --git a/crates/transport/src/status.rs b/crates/transport/src/status.rs @@ -1,6 +1,6 @@ use crate::{ RadrootsTransportCapabilityAvailability, RadrootsTransportCapabilityMaturity, - RadrootsTransportImplementationState, RadrootsTransportKind, TransportId, + RadrootsTransportImplementationState, TransportId, capability::{Availability, Maturity, SinkCapabilities, SourceCapabilities}, delivery::RadrootsTransportSatisfactionClass, }; @@ -484,7 +484,7 @@ impl RadrootsTransportCapabilities { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTransportStatus { #[cfg_attr(feature = "serde", serde(rename = "transport"))] - pub kind: RadrootsTransportKind, + pub kind: TransportId, pub profile_id: Option<String>, pub endpoint_uri: Option<String>, pub configured: bool, @@ -498,7 +498,7 @@ pub struct RadrootsTransportStatus { impl RadrootsTransportStatus { pub fn new( - kind: RadrootsTransportKind, + kind: TransportId, configured: bool, implementation: RadrootsTransportImplementationState, usable_for_delivery: bool, diff --git a/crates/transport/src/target.rs b/crates/transport/src/target.rs @@ -1,12 +1,9 @@ //! Extensible transport identities and canonical operation targets. use crate::{ - RadrootsTransportError, RadrootsTransportKind, + RadrootsTransportError, TransportId, endpoint::{ENDPOINT_URI_MAX_BYTES, TARGET_LABEL_MAX_BYTES, TARGET_SCOPE_MAX_BYTES}, }; - -const LEGACY_RETICULUM_ENDPOINT_URI: &str = "reticulum:local"; -const LEGACY_RETICULUM_SCOPE_ID: &str = "local"; use alloc::collections::BTreeSet; use alloc::format; use alloc::string::{String, ToString}; @@ -15,6 +12,9 @@ use core::net::Ipv6Addr; use core::str::FromStr; use sha2::{Digest, Sha256}; +const EXTERNAL_RETICULUM_ENDPOINT_URI: &str = "reticulum:local"; +const EXTERNAL_RETICULUM_SCOPE_ID: &str = "local"; + /// Maximum number of targets in one operation. pub const TARGET_SET_MAX_ITEMS: usize = 64; @@ -106,8 +106,10 @@ impl TargetScope { Ok(Self(value.to_string())) } + /// Publish-frozen external-consumer helper; removed at Step 305. + #[doc(hidden)] pub fn local_reticulum() -> Self { - Self::parse(LEGACY_RETICULUM_SCOPE_ID).expect("default Reticulum scope id") + Self::parse(EXTERNAL_RETICULUM_SCOPE_ID).expect("external Reticulum scope id") } pub fn as_str(&self) -> &str { @@ -228,11 +230,7 @@ impl<'de> serde::Deserialize<'de> for TargetLabel { pub struct TargetFingerprint(String); impl TargetFingerprint { - pub fn from_target( - kind: &RadrootsTransportKind, - uri: &EndpointUri, - scope: Option<&TargetScope>, - ) -> Self { + pub fn from_target(kind: &TransportId, uri: &EndpointUri, scope: Option<&TargetScope>) -> Self { let mut hasher = Sha256::new(); hasher.update(kind.canonical_label().as_bytes()); hasher.update([0]); @@ -317,7 +315,7 @@ impl<'de> serde::Deserialize<'de> for TargetFingerprint { #[derive(Clone, Debug, PartialEq, Eq)] /// Validated transport-neutral target. pub struct Target { - kind: RadrootsTransportKind, + kind: TransportId, uri: EndpointUri, scope: Option<TargetScope>, label: Option<TargetLabel>, @@ -325,10 +323,7 @@ pub struct Target { } impl Target { - pub fn new( - kind: RadrootsTransportKind, - uri: impl AsRef<str>, - ) -> Result<Self, RadrootsTransportError> { + pub fn new(kind: TransportId, uri: impl AsRef<str>) -> Result<Self, RadrootsTransportError> { Self::new_with_metadata(kind, uri, None, None) } @@ -341,19 +336,31 @@ impl Target { scope: Option<TargetScope>, label: Option<TargetLabel>, ) -> Result<Self, RadrootsTransportError> { - Self::new_with_metadata(RadrootsTransportKind::Nostr, uri, scope, label) + Self::new_with_metadata(TransportId::NOSTR, uri, scope, label) } + /// Publish-frozen external-consumer helper; removed at Step 305. + #[doc(hidden)] pub fn reticulum() -> Result<Self, RadrootsTransportError> { - Self::reticulum_with_metadata(LEGACY_RETICULUM_ENDPOINT_URI, None, None) + Self::reticulum_with_metadata(EXTERNAL_RETICULUM_ENDPOINT_URI, None, None) } + /// Publish-frozen external-consumer helper; removed at Step 305. + #[doc(hidden)] pub fn reticulum_with_metadata( uri: impl AsRef<str>, scope: Option<TargetScope>, label: Option<TargetLabel>, ) -> Result<Self, RadrootsTransportError> { - Self::new_with_metadata(RadrootsTransportKind::Reticulum, uri, scope, label) + if uri.as_ref() != EXTERNAL_RETICULUM_ENDPOINT_URI { + return Err(RadrootsTransportError::InvalidTargetUri); + } + Self::new_with_metadata( + TransportId::RETICULUM, + uri, + Some(scope.unwrap_or_else(TargetScope::local_reticulum)), + label, + ) } pub fn local(uri: impl AsRef<str>) -> Result<Self, RadrootsTransportError> { @@ -365,24 +372,20 @@ impl Target { scope: Option<TargetScope>, label: Option<TargetLabel>, ) -> Result<Self, RadrootsTransportError> { - Self::new_with_metadata(RadrootsTransportKind::Local, uri, scope, label) + Self::new_with_metadata(TransportId::LOCAL, uri, scope, label) } pub fn new_with_metadata( - kind: RadrootsTransportKind, + kind: TransportId, uri: impl AsRef<str>, scope: Option<TargetScope>, label: Option<TargetLabel>, ) -> Result<Self, RadrootsTransportError> { let raw_uri = uri.as_ref(); let uri = match kind { - RadrootsTransportKind::Nostr => EndpointUri::parse_nostr_relay(raw_uri)?, + TransportId::NOSTR => EndpointUri::parse_nostr_relay(raw_uri)?, _ => EndpointUri::parse(raw_uri)?, }; - if kind == RadrootsTransportKind::Reticulum && raw_uri != LEGACY_RETICULUM_ENDPOINT_URI { - return Err(RadrootsTransportError::InvalidTargetUri); - } - let scope = scope.or_else(|| default_scope_for_kind(&kind)); let fingerprint = TargetFingerprint::from_target(&kind, &uri, scope.as_ref()); Ok(Self { kind, @@ -393,7 +396,7 @@ impl Target { }) } - pub fn kind(&self) -> &RadrootsTransportKind { + pub fn kind(&self) -> &TransportId { &self.kind } @@ -418,7 +421,7 @@ impl Target { #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct TargetWire { - kind: RadrootsTransportKind, + kind: TransportId, uri: String, scope: Option<String>, label: Option<String>, @@ -471,10 +474,6 @@ impl<'de> serde::Deserialize<'de> for Target { } } -fn default_scope_for_kind(kind: &RadrootsTransportKind) -> Option<TargetScope> { - (*kind == RadrootsTransportKind::Reticulum).then(TargetScope::local_reticulum) -} - #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Clone, Debug, PartialEq, Eq)] /// Bounded non-empty set of targets with unique fingerprints. @@ -850,26 +849,26 @@ fn is_local_ws_relay_host(host: &str) -> bool { matches!(host, "localhost" | "127.0.0.1" | "[::1]") } -/// Compatibility endpoint URI name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportTargetUri = EndpointUri; -/// Compatibility target scope name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportMeshScopeId = TargetScope; -/// Compatibility target label name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportTargetLabel = TargetLabel; -/// Compatibility target fingerprint name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportTargetFingerprint = TargetFingerprint; -/// Compatibility target name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportTarget = Target; -/// Compatibility target-set name retained until the workspace cutover. +/// Publish-frozen external-consumer alias; removed at Step 305. #[doc(hidden)] pub type RadrootsTransportTargetSet = TargetSet; diff --git a/crates/transport/src/transport.rs b/crates/transport/src/transport.rs @@ -1,44 +1,16 @@ -use crate::{ - RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, RadrootsTransportError, - RadrootsTransportKind, RadrootsTransportStatus, RadrootsTransportTargetReceipt, - RadrootsTransportTargetSet, -}; -use alloc::boxed::Box; +use crate::{RadrootsTransportTargetReceipt, TargetSet}; use alloc::string::String; use alloc::vec::Vec; -use core::future::Future; -use core::pin::Pin; - -pub type RadrootsTransportFuture<'a, T> = - Pin<Box<dyn Future<Output = Result<T, RadrootsTransportError>> + Send + 'a>>; - -/// Compatibility contract retained until the planned workspace consumer cutover. -#[doc(hidden)] -pub trait RadrootsTransport: Send + Sync { - fn transport_kind(&self) -> RadrootsTransportKind; - - fn status<'a>(&'a self) -> RadrootsTransportFuture<'a, RadrootsTransportStatus>; - - fn deliver<'a>( - &'a self, - request: RadrootsTransportDeliveryRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportDeliveryReceipt>; - - fn fetch<'a>( - &'a self, - request: RadrootsTransportFetchRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportFetchReceipt>; -} #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTransportFetchRequest { pub request_id: String, - pub target_set: RadrootsTransportTargetSet, + pub target_set: TargetSet, } impl RadrootsTransportFetchRequest { - pub fn new(request_id: impl Into<String>, target_set: RadrootsTransportTargetSet) -> Self { + pub fn new(request_id: impl Into<String>, target_set: TargetSet) -> Self { Self { request_id: request_id.into(), target_set, diff --git a/crates/transport/tests/package_boundary.rs b/crates/transport/tests/package_boundary.rs @@ -16,7 +16,9 @@ const ROOT: &str = include_str!("../src/lib.rs"); const SOURCE: &str = include_str!("../src/source.rs"); const SINK: &str = include_str!("../src/sink.rs"); const ID: &str = include_str!("../src/id.rs"); -const LEGACY_TRANSPORT: &str = include_str!("../src/transport.rs"); +const PREDECESSOR_MODELS: &str = include_str!("../src/transport.rs"); +const TARGET: &str = include_str!("../src/target.rs"); +const DEVIATIONS: &str = include_str!("../../../docs/implementation/deviations.toml"); #[test] fn manifest_has_final_identity_features_and_required_radroots_dependencies() { @@ -107,12 +109,27 @@ fn package_documentation_and_reviewed_api_baseline_are_complete() { "pub mod radroots_transport::target", "pub trait radroots_transport::EventSource", "pub trait radroots_transport::EventSink", + "pub fn radroots_transport::target::Target::new(radroots_transport::TransportId", + "pub fn radroots_transport::target::Target::kind(&self) -> &radroots_transport::TransportId", ] { assert!( PUBLIC_API.contains(required), "public API baseline is missing {required}" ); } + for forbidden in [ + "pub trait radroots_transport::RadrootsTransport", + "RadrootsTransportFuture", + "RadrootsTransportKind", + "RadrootsTransportTargetSet", + "Target::reticulum", + "TargetScope::local_reticulum", + ] { + assert!( + !PUBLIC_API.contains(forbidden), + "reviewed public API baseline still exposes `{forbidden}`" + ); + } } #[test] @@ -164,7 +181,30 @@ fn source_and_sink_are_independent_dyn_compatible_host_spis() { } assert!(!SINK.contains("fn fetch(")); - assert!(LEGACY_TRANSPORT.contains("#[doc(hidden)]\npub trait RadrootsTransport: Send + Sync")); + assert!(!PREDECESSOR_MODELS.contains("pub trait RadrootsTransport")); + for required in [ + "Publish-frozen external-consumer helper; removed at Step 305.", + "#[doc(hidden)]\n pub fn reticulum(", + "#[doc(hidden)]\n pub fn reticulum_with_metadata(", + "#[doc(hidden)]\n pub fn local_reticulum(", + ] { + assert!( + TARGET.contains(required), + "external-consumer quarantine is missing `{required}`" + ); + } + for required in [ + "id = \"RCRV1-DEV-007\"", + "affected_steps = [\"122\", \"170\", \"235\", \"305\"]", + "runtime-owned unpublished shim until Step 170", + "SDK-local unpublished target/satisfaction mapping until Step 235", + "external-consumer aliases/helpers until the fail-closed package-realistic Step 305 gate", + ] { + assert!( + DEVIATIONS.contains(required), + "temporary downstream ownership record is missing `{required}`" + ); + } } #[test] diff --git a/crates/transport/tests/source_boundary.rs b/crates/transport/tests/source_boundary.rs @@ -533,8 +533,12 @@ fn workspace_consumers_use_split_transport_spis_with_one_runtime_shim() { ); } assert!( - runtime_source.contains("Arc<dyn RadrootsTransport>"), - "mixed runtime delivery workers retain the sole temporary monolithic shim until RCLD 40" + runtime_source.contains("Arc<dyn RadrootsRuntimeTransportShim>"), + "mixed runtime delivery workers retain the sole runtime-owned unpublished shim until RCLD 40" + ); + assert!( + runtime_source.contains("pub trait RadrootsRuntimeTransportShim: Send + Sync"), + "the temporary runtime shim must have an explicit, searchable final-removal owner" ); let removed_reticulum_runtime_transport = ["RadrootsRuntimeReticulum", "Pre", "viewTransport"].concat(); @@ -558,7 +562,8 @@ fn workspace_consumers_use_split_transport_spis_with_one_runtime_shim() { ); } assert!( - !reticulum_source.contains("impl RadrootsTransport for RadrootsReticulumTransport"), + !reticulum_source + .contains("impl RadrootsRuntimeTransportShim for RadrootsReticulumTransport"), "Reticulum preview must not implement the predecessor monolithic SPI" ); @@ -568,7 +573,8 @@ fn workspace_consumers_use_split_transport_spis_with_one_runtime_shim() { "Nostr adapter must implement the final sink-only SPI" ); assert!( - !nostr_source.contains("impl<A> RadrootsTransport for RadrootsNostrTransport<A>"), + !nostr_source + .contains("impl<A> RadrootsRuntimeTransportShim for RadrootsNostrTransport<A>"), "Nostr adapter must not implement the predecessor monolithic SPI" ); } diff --git a/crates/transport/tests/spi.rs b/crates/transport/tests/spi.rs @@ -2,7 +2,7 @@ use futures::executor::block_on; use radroots_event::{SignedEvent, wire::v1::Nip01EventWire}; use radroots_transport::{ BoxFuture, DeliveryReceipt, DeliveryRequest, EventSink, EventSource, FetchPage, FetchRequest, - RadrootsTransportTarget, RadrootsTransportTargetSet, SinkStatus, SourceStatus, TransportId, + SinkStatus, SourceStatus, Target, TargetSet, TransportId, capability::{Availability, Maturity, SinkCapabilities, SourceCapabilities}, outcome::DeliveryOutcome, policy::{SatisfactionClass, SatisfactionPolicy, TargetPolicy}, @@ -106,11 +106,8 @@ fn sink_status() -> SinkStatus { ) } -fn target_set() -> RadrootsTransportTargetSet { - RadrootsTransportTargetSet::new(vec![ - RadrootsTransportTarget::local("local:spi").expect("local target"), - ]) - .expect("target set") +fn target_set() -> TargetSet { + TargetSet::new(vec![Target::local("local:spi").expect("local target")]).expect("target set") } fn delivery_payload() -> DeliveryPayload { diff --git a/crates/transport/tests/transport.rs b/crates/transport/tests/transport.rs @@ -1,20 +1,16 @@ +use radroots_transport::target::{EndpointUri, TargetFingerprint, TargetLabel, TargetScope}; use radroots_transport::{ - RADROOTS_TRANSPORT_DELIVERY_REQUEST_ID_MAX_BYTES, RadrootsTransport, - RadrootsTransportCapabilities, RadrootsTransportCapabilityAvailability, - RadrootsTransportCapabilityMaturity, RadrootsTransportDeliveryReceipt, - RadrootsTransportDeliveryRequest, RadrootsTransportDeliveryTargetStatus, - RadrootsTransportError, RadrootsTransportFetchReceipt, RadrootsTransportFetchRequest, - RadrootsTransportFuture, RadrootsTransportImplementationState, RadrootsTransportKind, - RadrootsTransportMeshScopeId, RadrootsTransportOutcome, RadrootsTransportOutcomeKind, + RADROOTS_TRANSPORT_DELIVERY_REQUEST_ID_MAX_BYTES, RadrootsTransportCapabilities, + RadrootsTransportCapabilityAvailability, RadrootsTransportCapabilityMaturity, + RadrootsTransportDeliveryReceipt, RadrootsTransportDeliveryRequest, + RadrootsTransportDeliveryTargetStatus, RadrootsTransportError, + RadrootsTransportImplementationState, RadrootsTransportOutcome, RadrootsTransportOutcomeKind, RadrootsTransportPayload, RadrootsTransportSatisfactionClass, - RadrootsTransportSatisfactionPolicy, RadrootsTransportStatus, RadrootsTransportTarget, - RadrootsTransportTargetFingerprint, RadrootsTransportTargetLabel, - RadrootsTransportTargetReceipt, RadrootsTransportTargetSet, RadrootsTransportTargetUri, - TRANSPORT_ID_MAX_BYTES, TransportId, + RadrootsTransportSatisfactionPolicy, RadrootsTransportStatus, RadrootsTransportTargetReceipt, + TRANSPORT_ID_MAX_BYTES, Target, TargetSet, TransportId, }; use serde_json::Value; use std::borrow::ToOwned; -use std::boxed::Box; use std::string::ToString; use std::vec; use std::vec::Vec; @@ -24,13 +20,21 @@ fn opaque_payload() -> RadrootsTransportPayload { .expect("payload") } +fn reticulum_target() -> Target { + Target::new_with_metadata( + TransportId::RETICULUM, + "reticulum:local", + Some(TargetScope::parse("local").expect("Reticulum scope")), + None, + ) + .expect("Reticulum target") +} + #[test] fn target_fingerprints_are_stable_and_transport_scoped() { - let nostr_upper = - RadrootsTransportTarget::nostr_relay("WSS://Relay.Example/Events").expect("nostr target"); - let nostr_lower = - RadrootsTransportTarget::nostr_relay("wss://relay.example/Events").expect("nostr target"); - let reticulum = RadrootsTransportTarget::reticulum().expect("reticulum target"); + let nostr_upper = Target::nostr_relay("WSS://Relay.Example/Events").expect("nostr target"); + let nostr_lower = Target::nostr_relay("wss://relay.example/Events").expect("nostr target"); + let reticulum = reticulum_target(); assert_eq!(nostr_upper.uri().as_str(), "wss://relay.example/Events"); assert_eq!(nostr_upper.scope(), None); @@ -59,40 +63,35 @@ fn transport_id_round_trips_built_ins_and_custom_values() { let custom = TransportId::parse("fieldbus-v2").expect("custom transport id"); assert_eq!(custom.as_str(), "fieldbus-v2"); - let custom_target = - RadrootsTransportTarget::new(custom, "fieldbus:node-7").expect("custom target"); + let custom_target = Target::new(custom, "fieldbus:node-7").expect("custom target"); assert_eq!(custom_target.kind(), &custom); - assert_eq!( - RadrootsTransportKind::Local.canonical_label(), - "local".to_owned() - ); + assert_eq!(TransportId::LOCAL.canonical_label(), "local".to_owned()); } #[test] fn transport_id_parser_enforces_canonical_syntax_and_bound() { assert_eq!( - RadrootsTransportKind::parse_canonical("nostr").expect("nostr kind"), - RadrootsTransportKind::Nostr + TransportId::parse_canonical("nostr").expect("nostr kind"), + TransportId::NOSTR ); assert_eq!( - RadrootsTransportKind::parse_canonical("NOSTR").expect_err("uppercase kind"), + TransportId::parse_canonical("NOSTR").expect_err("uppercase kind"), RadrootsTransportError::InvalidTransportKind ); assert_eq!( - RadrootsTransportKind::parse_canonical(" nostr ").expect_err("trimmed kind"), + TransportId::parse_canonical(" nostr ").expect_err("trimmed kind"), RadrootsTransportError::InvalidTransportKind ); assert_eq!( - RadrootsTransportKind::parse_canonical("radrootsd_proxy") - .expect_err("underscore separator"), + TransportId::parse_canonical("radrootsd_proxy").expect_err("underscore separator"), RadrootsTransportError::InvalidTransportKind ); assert_eq!( - RadrootsTransportKind::parse_canonical("fieldbus").expect("custom kind"), + TransportId::parse_canonical("fieldbus").expect("custom kind"), TransportId::parse("fieldbus").expect("same custom kind") ); assert_eq!( - RadrootsTransportKind::parse_canonical("").expect_err("empty kind"), + TransportId::parse_canonical("").expect_err("empty kind"), RadrootsTransportError::EmptyTransportKind ); assert_eq!( @@ -103,28 +102,22 @@ fn transport_id_parser_enforces_canonical_syntax_and_bound() { #[test] fn target_set_rejects_duplicate_fingerprints() { - let first = - RadrootsTransportTarget::nostr_relay("wss://relay.example/a").expect("first target"); - let duplicate = - RadrootsTransportTarget::nostr_relay("WSS://RELAY.EXAMPLE/a").expect("duplicate target"); - let err = RadrootsTransportTargetSet::new(vec![first, duplicate]) - .expect_err("duplicate fingerprints must fail"); + let first = Target::nostr_relay("wss://relay.example/a").expect("first target"); + let duplicate = Target::nostr_relay("WSS://RELAY.EXAMPLE/a").expect("duplicate target"); + let err = TargetSet::new(vec![first, duplicate]).expect_err("duplicate fingerprints must fail"); assert_eq!(err, RadrootsTransportError::DuplicateTargetFingerprint); } #[test] fn nostr_relay_targets_use_canonical_endpoint_identity() { - let root = RadrootsTransportTarget::nostr_relay("wss://relay.example").expect("root target"); - let root_slash = - RadrootsTransportTarget::nostr_relay("WSS://RELAY.EXAMPLE/").expect("root slash target"); - let root_default_port = RadrootsTransportTarget::nostr_relay("wss://relay.example:443") - .expect("default-port root target"); - let path = - RadrootsTransportTarget::nostr_relay("wss://relay.example/path").expect("path target"); + let root = Target::nostr_relay("wss://relay.example").expect("root target"); + let root_slash = Target::nostr_relay("WSS://RELAY.EXAMPLE/").expect("root slash target"); + let root_default_port = + Target::nostr_relay("wss://relay.example:443").expect("default-port root target"); + let path = Target::nostr_relay("wss://relay.example/path").expect("path target"); let generic = - RadrootsTransportTarget::new(RadrootsTransportKind::Nostr, "wss://relay.example/") - .expect("generic nostr target"); + Target::new(TransportId::NOSTR, "wss://relay.example/").expect("generic nostr target"); assert_eq!(root.uri().as_str(), "wss://relay.example"); assert_eq!(root_slash.uri().as_str(), "wss://relay.example"); @@ -135,8 +128,7 @@ fn nostr_relay_targets_use_canonical_endpoint_identity() { assert_ne!(root.fingerprint(), path.fingerprint()); assert_eq!(path.uri().as_str(), "wss://relay.example/path"); assert_eq!( - RadrootsTransportTargetSet::new(vec![root, root_slash]) - .expect_err("canonical-equivalent roots collide"), + TargetSet::new(vec![root, root_slash]).expect_err("canonical-equivalent roots collide"), RadrootsTransportError::DuplicateTargetFingerprint ); } @@ -161,16 +153,14 @@ fn nostr_relay_targets_reject_noncanonical_or_unsupported_endpoint_forms() { "ws://relay.example", ] { assert_eq!( - RadrootsTransportTarget::nostr_relay(invalid).expect_err("invalid Nostr relay target"), + Target::nostr_relay(invalid).expect_err("invalid Nostr relay target"), RadrootsTransportError::InvalidTargetUri ); } - let local_ws = - RadrootsTransportTarget::nostr_relay("ws://LOCALHOST:7777/").expect("local ws relay"); + let local_ws = Target::nostr_relay("ws://LOCALHOST:7777/").expect("local ws relay"); assert_eq!(local_ws.uri().as_str(), "ws://localhost:7777"); - let local_ipv6 = - RadrootsTransportTarget::nostr_relay("ws://[::1]:7777").expect("local ipv6 relay"); + let local_ipv6 = Target::nostr_relay("ws://[::1]:7777").expect("local ipv6 relay"); assert_eq!(local_ipv6.uri().as_str(), "ws://[::1]:7777"); } @@ -271,7 +261,7 @@ fn satisfaction_policy_counts_target_statuses() { #[cfg(feature = "serde")] fn transport_status_models_canonical_configuration_and_delivery_usability() { let status = RadrootsTransportStatus::new( - RadrootsTransportKind::Nostr, + TransportId::NOSTR, true, RadrootsTransportImplementationState::Real, true, @@ -280,7 +270,7 @@ fn transport_status_models_canonical_configuration_and_delivery_usability() { .with_profile_id("transport.nostr.default") .with_endpoint_uri("wss://relay.example"); - assert_eq!(status.kind, RadrootsTransportKind::Nostr); + assert_eq!(status.kind, TransportId::NOSTR); assert_eq!( status.profile_id.as_deref(), Some("transport.nostr.default") @@ -326,10 +316,10 @@ fn transport_status_models_canonical_configuration_and_delivery_usability() { #[test] fn deferred_transport_outcomes_are_terminal_but_not_satisfied() { - let target = RadrootsTransportTarget::reticulum().expect("target"); + let target = reticulum_target(); let receipt = RadrootsTransportDeliveryReceipt::new( "reticulum", - RadrootsTransportTargetSet::new(vec![target.clone()]).expect("target set"), + TargetSet::new(vec![target.clone()]).expect("target set"), vec![RadrootsTransportTargetReceipt::new( target, RadrootsTransportOutcome::new(RadrootsTransportOutcomeKind::DeferredUntilImplemented), @@ -358,8 +348,8 @@ fn deferred_transport_outcomes_are_terminal_but_not_satisfied() { #[test] #[cfg(feature = "serde")] fn request_models_round_trip_with_serde() { - let target = RadrootsTransportTarget::nostr_relay("wss://relay.example").expect("target"); - let target_set = RadrootsTransportTargetSet::new(vec![target]).expect("target set"); + let target = Target::nostr_relay("wss://relay.example").expect("target"); + let target_set = TargetSet::new(vec![target]).expect("target set"); let request = RadrootsTransportDeliveryRequest::new( "req-1", opaque_payload(), @@ -467,11 +457,11 @@ fn payload_contract_rejects_invalid_unchecked_signed_event_ids_bytes_labels_and_ #[test] fn fingerprint_parser_rejects_non_sha256_hex() { assert_eq!( - RadrootsTransportTargetFingerprint::parse("abc").expect_err("short fingerprint"), + TargetFingerprint::parse("abc").expect_err("short fingerprint"), RadrootsTransportError::InvalidTargetFingerprint ); assert_eq!( - RadrootsTransportTargetUri::parse("wss://relay example").expect_err("space in target uri"), + EndpointUri::parse("wss://relay example").expect_err("space in target uri"), RadrootsTransportError::InvalidTargetUri ); } @@ -562,23 +552,21 @@ fn transport_kind_and_target_parsers_cover_negative_edges() { "fieldbus--v2", ] { assert_eq!( - RadrootsTransportKind::parse(invalid).expect_err("invalid kind"), + TransportId::parse(invalid).expect_err("invalid kind"), RadrootsTransportError::InvalidTransportKind ); } - let no_scheme = - RadrootsTransportTargetUri::parse("transport-target").expect("schemeless target uri"); + let no_scheme = EndpointUri::parse("transport-target").expect("schemeless target uri"); assert_eq!(no_scheme.as_str(), "transport-target"); assert_eq!(no_scheme.to_string(), "transport-target"); - let opaque = RadrootsTransportTargetUri::parse("RNS:PeerA").expect("opaque uri"); + let opaque = EndpointUri::parse("RNS:PeerA").expect("opaque uri"); assert_eq!(opaque.as_str(), "rns:PeerA"); - let authority = RadrootsTransportTargetUri::parse("MESH://Node.Example/path?q=1#frag") - .expect("authority uri"); + let authority = EndpointUri::parse("MESH://Node.Example/path?q=1#frag").expect("authority uri"); assert_eq!(authority.as_str(), "mesh://node.example/path?q=1#frag"); assert_eq!( - RadrootsTransportTargetUri::parse(" ").expect_err("empty uri"), + EndpointUri::parse(" ").expect_err("empty uri"), RadrootsTransportError::EmptyTargetUri ); for invalid in [ @@ -590,7 +578,7 @@ fn transport_kind_and_target_parsers_cover_negative_edges() { "bad\target", ] { assert_eq!( - RadrootsTransportTargetUri::parse(invalid).expect_err("invalid uri"), + EndpointUri::parse(invalid).expect_err("invalid uri"), RadrootsTransportError::InvalidTargetUri ); } @@ -616,7 +604,7 @@ fn checked_in_transport_target_uri_vectors_match_parser_behavior() { let expected = entry.get("expected").expect("expected"); match kind { "transport.target_uri.valid" => { - let target = RadrootsTransportTargetUri::parse(raw_uri).expect("target URI"); + let target = EndpointUri::parse(raw_uri).expect("target URI"); assert_eq!( target.as_str(), expected @@ -626,10 +614,10 @@ fn checked_in_transport_target_uri_vectors_match_parser_behavior() { ); } "transport.target_uri.invalid" => { - assert!(RadrootsTransportTargetUri::parse(raw_uri).is_err()); + assert!(EndpointUri::parse(raw_uri).is_err()); } "transport.nostr_relay_target.valid" => { - let target = RadrootsTransportTarget::nostr_relay(raw_uri).expect("relay target"); + let target = Target::nostr_relay(raw_uri).expect("relay target"); assert_eq!( target.uri().as_str(), expected @@ -639,7 +627,7 @@ fn checked_in_transport_target_uri_vectors_match_parser_behavior() { ); } "transport.nostr_relay_target.invalid" => { - assert!(RadrootsTransportTarget::nostr_relay(raw_uri).is_err()); + assert!(Target::nostr_relay(raw_uri).is_err()); } other => panic!("unknown transport target vector kind {other}"), } @@ -647,21 +635,19 @@ fn checked_in_transport_target_uri_vectors_match_parser_behavior() { } #[test] -fn reticulum_transport_targets_use_default_destination_and_scope() { - let target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); +fn generic_targets_do_not_own_reticulum_destination_policy() { + let target = reticulum_target(); assert_eq!(target.uri().as_str(), "reticulum:local"); assert_eq!(target.scope().map(|scope| scope.as_str()), Some("local")); - let invalid_reticulum_destination = ["reticulum:", "remote"].concat(); - for invalid in [ - " reticulum:local".to_owned(), - "reticulum:local ".to_owned(), - "RETICULUM:local".to_owned(), - invalid_reticulum_destination, - ] { + let remote = Target::new(TransportId::RETICULUM, "reticulum:remote") + .expect("adapter-owned Reticulum destination is accepted generically"); + assert_eq!(remote.uri().as_str(), "reticulum:remote"); + assert_eq!(remote.scope(), None); + + for invalid in [" reticulum:local", "reticulum:local "] { assert_eq!( - RadrootsTransportTarget::new(RadrootsTransportKind::Reticulum, invalid.as_str()) - .expect_err("invalid Reticulum endpoint"), + Target::new(TransportId::RETICULUM, invalid).expect_err("invalid generic endpoint"), RadrootsTransportError::InvalidTargetUri ); } @@ -669,23 +655,21 @@ fn reticulum_transport_targets_use_default_destination_and_scope() { #[test] fn target_fingerprints_and_sets_cover_accessors_and_validation() { - let target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); - let parsed = RadrootsTransportTargetFingerprint::parse( - target.fingerprint().as_str().to_ascii_uppercase(), - ) - .expect("uppercase fingerprint parses"); + let target = reticulum_target(); + let parsed = TargetFingerprint::parse(target.fingerprint().as_str().to_ascii_uppercase()) + .expect("uppercase fingerprint parses"); assert_eq!(parsed.as_str(), target.fingerprint().as_str()); assert_eq!(parsed.to_string(), target.fingerprint().as_str()); assert_eq!( - RadrootsTransportTargetFingerprint::parse("g".repeat(64)).expect_err("non-hex fingerprint"), + TargetFingerprint::parse("g".repeat(64)).expect_err("non-hex fingerprint"), RadrootsTransportError::InvalidTargetFingerprint ); assert_eq!( - RadrootsTransportTargetSet::new(Vec::new()).expect_err("empty target set"), + TargetSet::new(Vec::new()).expect_err("empty target set"), RadrootsTransportError::EmptyTargetSet ); - let target_set = RadrootsTransportTargetSet::new(vec![target]).expect("target set"); + let target_set = TargetSet::new(vec![target]).expect("target set"); assert_eq!(target_set.len(), 1); assert!(!target_set.is_empty()); assert_eq!(target_set.targets().len(), 1); @@ -693,24 +677,24 @@ fn target_fingerprints_and_sets_cover_accessors_and_validation() { #[test] fn target_scope_participates_in_identity_and_label_does_not() { - let local_scope = RadrootsTransportMeshScopeId::parse("local").expect("local scope"); - let remote_scope = RadrootsTransportMeshScopeId::parse("remote").expect("remote scope"); - let local = RadrootsTransportTarget::new_with_metadata( - RadrootsTransportKind::Reticulum, + let local_scope = TargetScope::parse("local").expect("local scope"); + let remote_scope = TargetScope::parse("remote").expect("remote scope"); + let local = Target::new_with_metadata( + TransportId::RETICULUM, "reticulum:local", Some(local_scope.clone()), - Some(RadrootsTransportTargetLabel::parse("Local Reticulum node").expect("label")), + Some(TargetLabel::parse("Local Reticulum node").expect("label")), ) .expect("local Reticulum target"); - let relabeled = RadrootsTransportTarget::new_with_metadata( - RadrootsTransportKind::Reticulum, + let relabeled = Target::new_with_metadata( + TransportId::RETICULUM, "reticulum:local", Some(local_scope), - Some(RadrootsTransportTargetLabel::parse("Renamed node").expect("label")), + Some(TargetLabel::parse("Renamed node").expect("label")), ) .expect("relabeled mesh target"); - let remote = RadrootsTransportTarget::new_with_metadata( - RadrootsTransportKind::Reticulum, + let remote = Target::new_with_metadata( + TransportId::RETICULUM, "reticulum:local", Some(remote_scope), None, @@ -725,19 +709,19 @@ fn target_scope_participates_in_identity_and_label_does_not() { Some("Local Reticulum node") ); assert_eq!( - RadrootsTransportMeshScopeId::parse("").expect_err("empty scope"), + TargetScope::parse("").expect_err("empty scope"), RadrootsTransportError::EmptyTargetScope ); assert_eq!( - RadrootsTransportMeshScopeId::parse("bad scope").expect_err("invalid scope"), + TargetScope::parse("bad scope").expect_err("invalid scope"), RadrootsTransportError::InvalidTargetScope ); assert_eq!( - RadrootsTransportTargetLabel::parse(" ").expect_err("empty label"), + TargetLabel::parse(" ").expect_err("empty label"), RadrootsTransportError::EmptyTargetLabel ); assert_eq!( - RadrootsTransportTargetLabel::parse("bad\nlabel").expect_err("invalid label"), + TargetLabel::parse("bad\nlabel").expect_err("invalid label"), RadrootsTransportError::InvalidTargetLabel ); } @@ -995,10 +979,8 @@ fn typed_outcome_kinds_drive_status_and_satisfaction_semantics() { #[test] fn required_target_satisfaction_uses_fingerprints_not_target_counts() { - let required = - RadrootsTransportTarget::nostr_relay("wss://one.example").expect("required target"); - let optional = - RadrootsTransportTarget::nostr_relay("wss://two.example").expect("optional target"); + let required = Target::nostr_relay("wss://one.example").expect("required target"); + let optional = Target::nostr_relay("wss://two.example").expect("optional target"); let policy = RadrootsTransportSatisfactionPolicy::required_targets( RadrootsTransportSatisfactionClass::Accepted, vec![required.fingerprint().clone()], @@ -1049,7 +1031,7 @@ fn required_target_satisfaction_uses_fingerprints_not_target_counts() { let optional_only = RadrootsTransportDeliveryReceipt::new( "required-target", - RadrootsTransportTargetSet::new(vec![optional.clone()]).expect("optional target set"), + TargetSet::new(vec![optional.clone()]).expect("optional target set"), vec![RadrootsTransportTargetReceipt::new( optional.clone(), RadrootsTransportOutcome::new(RadrootsTransportOutcomeKind::Accepted), @@ -1065,8 +1047,7 @@ fn required_target_satisfaction_uses_fingerprints_not_target_counts() { let required_delivered = RadrootsTransportDeliveryReceipt::new( "required-target", - RadrootsTransportTargetSet::new(vec![optional.clone(), required.clone()]) - .expect("required target set"), + TargetSet::new(vec![optional.clone(), required.clone()]).expect("required target set"), vec![ RadrootsTransportTargetReceipt::new( optional, @@ -1106,107 +1087,13 @@ fn required_target_satisfaction_uses_fingerprints_not_target_counts() { } #[test] -fn neutral_transport_trait_covers_status_delivery_and_fetch() { - struct MemoryTransport { - target: RadrootsTransportTarget, - } - - impl RadrootsTransport for MemoryTransport { - fn transport_kind(&self) -> RadrootsTransportKind { - RadrootsTransportKind::Local - } - - fn status<'a>(&'a self) -> RadrootsTransportFuture<'a, RadrootsTransportStatus> { - Box::pin(async move { - Ok(RadrootsTransportStatus::new( - RadrootsTransportKind::Local, - true, - RadrootsTransportImplementationState::Real, - true, - "ready", - ) - .with_capabilities(RadrootsTransportCapabilities::deliver_and_fetch())) - }) - } - - fn deliver<'a>( - &'a self, - request: RadrootsTransportDeliveryRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportDeliveryReceipt> { - Box::pin(async move { - RadrootsTransportDeliveryReceipt::new( - request.request_id(), - request.target_set().clone(), - vec![RadrootsTransportTargetReceipt::new( - self.target.clone(), - RadrootsTransportOutcome::new(RadrootsTransportOutcomeKind::Delivered), - )], - ) - }) - } - - fn fetch<'a>( - &'a self, - request: RadrootsTransportFetchRequest, - ) -> RadrootsTransportFuture<'a, RadrootsTransportFetchReceipt> { - Box::pin(async move { - Ok(RadrootsTransportFetchReceipt::new( - request.request_id, - vec![RadrootsTransportTargetReceipt::new( - self.target.clone(), - RadrootsTransportOutcome::new(RadrootsTransportOutcomeKind::Seen), - )], - 1, - )) - }) - } - } - - let target = RadrootsTransportTarget::local("local:memory").expect("local target"); - let target_set = RadrootsTransportTargetSet::new(vec![target.clone()]).expect("target set"); - let transport = MemoryTransport { target }; - assert_eq!(transport.transport_kind(), RadrootsTransportKind::Local); - let status = futures::executor::block_on(transport.status()).expect("status"); - assert_eq!(status.kind, RadrootsTransportKind::Local); - assert_eq!( - status.capabilities, - RadrootsTransportCapabilities::deliver_and_fetch() - ); - let delivery = futures::executor::block_on( - transport.deliver( - RadrootsTransportDeliveryRequest::new( - "deliver-1", - opaque_payload(), - target_set.clone(), - RadrootsTransportSatisfactionPolicy::all_delivered(), - ) - .expect("delivery request"), - ), - ) - .expect("deliver"); - assert_eq!( - delivery.target_receipts()[0].outcome.kind, - RadrootsTransportOutcomeKind::Delivered - ); - let fetch = futures::executor::block_on( - transport.fetch(RadrootsTransportFetchRequest::new("fetch-1", target_set)), - ) - .expect("fetch"); - assert_eq!(fetch.fetched_count, 1); - assert_eq!( - fetch.target_receipts[0].outcome.kind, - RadrootsTransportOutcomeKind::Seen - ); -} - -#[test] fn delivery_contract_covers_every_policy_and_receipt_path() { - let one = RadrootsTransportTarget::nostr_relay("wss://one.example").expect("one"); - let two = RadrootsTransportTarget::nostr_relay("wss://two.example").expect("two"); + let one = Target::nostr_relay("wss://one.example").expect("one"); + let two = Target::nostr_relay("wss://two.example").expect("two"); let request = RadrootsTransportDeliveryRequest::new( "delivery", opaque_payload(), - RadrootsTransportTargetSet::new(vec![one.clone(), two.clone()]).expect("target set"), + TargetSet::new(vec![one.clone(), two.clone()]).expect("target set"), RadrootsTransportSatisfactionPolicy::all_accepted(), ) .expect("delivery request") @@ -1257,7 +1144,7 @@ fn delivery_contract_covers_every_policy_and_receipt_path() { ); let receipt = RadrootsTransportDeliveryReceipt::new( "delivery", - RadrootsTransportTargetSet::new(vec![one.clone(), two]).expect("receipt target set"), + TargetSet::new(vec![one.clone(), two]).expect("receipt target set"), vec![accepted, rejected], ) .expect("delivery receipt"); @@ -1314,12 +1201,10 @@ fn delivery_contract_covers_every_policy_and_receipt_path() { #[test] fn delivery_requests_and_receipts_reject_forged_identity_and_cardinality() { - let first = RadrootsTransportTarget::nostr_relay("wss://one.example").expect("first"); - let second = RadrootsTransportTarget::nostr_relay("wss://two.example").expect("second"); - let unexpected = - RadrootsTransportTarget::nostr_relay("wss://unexpected.example").expect("unexpected"); - let targets = - RadrootsTransportTargetSet::new(vec![first.clone(), second.clone()]).expect("target set"); + let first = Target::nostr_relay("wss://one.example").expect("first"); + let second = Target::nostr_relay("wss://two.example").expect("second"); + let unexpected = Target::nostr_relay("wss://unexpected.example").expect("unexpected"); + let targets = TargetSet::new(vec![first.clone(), second.clone()]).expect("target set"); let payload = opaque_payload(); assert_eq!( @@ -1509,7 +1394,7 @@ fn delivery_requests_and_receipts_reject_forged_identity_and_cardinality() { ); let wrong_targets = RadrootsTransportDeliveryReceipt::new( "request", - RadrootsTransportTargetSet::new(vec![unexpected.clone()]).expect("unexpected target set"), + TargetSet::new(vec![unexpected.clone()]).expect("unexpected target set"), vec![RadrootsTransportTargetReceipt::new( unexpected, RadrootsTransportOutcome::new(RadrootsTransportOutcomeKind::Accepted), @@ -1527,11 +1412,11 @@ fn delivery_requests_and_receipts_reject_forged_identity_and_cardinality() { #[test] #[cfg(feature = "serde")] fn delivery_request_and_receipt_deserialization_revalidates_invariants() { - let target = RadrootsTransportTarget::nostr_relay("wss://relay.example").expect("target"); + let target = Target::nostr_relay("wss://relay.example").expect("target"); let request = RadrootsTransportDeliveryRequest::new( "request", opaque_payload(), - RadrootsTransportTargetSet::new(vec![target.clone()]).expect("target set"), + TargetSet::new(vec![target.clone()]).expect("target set"), RadrootsTransportSatisfactionPolicy::all_accepted(), ) .expect("request"); @@ -1569,8 +1454,8 @@ fn delivery_request_and_receipt_deserialization_revalidates_invariants() { payload_wire["OpaqueBytes"]["digest"] = Value::String("0".repeat(64)); assert!(serde_json::from_value::<RadrootsTransportPayload>(payload_wire).is_err()); - let first = RadrootsTransportTarget::nostr_relay("wss://one.example").expect("first target"); - let second = RadrootsTransportTarget::nostr_relay("wss://two.example").expect("second target"); + let first = Target::nostr_relay("wss://one.example").expect("first target"); + let second = Target::nostr_relay("wss://two.example").expect("second target"); let mut reversed = vec![first.fingerprint().clone(), second.fingerprint().clone()]; reversed.sort(); reversed.reverse(); @@ -1726,7 +1611,7 @@ fn status_contract_covers_builders_and_availability_defaults() { assert!(capabilities.receipt_observation); let unavailable = RadrootsTransportStatus::new( - RadrootsTransportKind::Reticulum, + TransportId::RETICULUM, true, RadrootsTransportImplementationState::Mock, false, @@ -1758,8 +1643,8 @@ fn status_contract_covers_builders_and_availability_defaults() { #[test] #[cfg(feature = "serde")] fn transport_kind_deserializer_rejects_non_string_values() { - assert!(serde_json::from_str::<RadrootsTransportKind>("1").is_err()); - assert!(serde_json::from_str::<RadrootsTransportKind>("\"NOSTR\"").is_err()); + assert!(serde_json::from_str::<TransportId>("1").is_err()); + assert!(serde_json::from_str::<TransportId>("\"NOSTR\"").is_err()); } #[test] @@ -1778,11 +1663,10 @@ fn transport_id_serde_uses_the_protocol_wire_contract() { #[test] #[cfg(feature = "serde")] fn transport_target_deserialization_rejects_forged_and_noncanonical_identity() { - let target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); + let target = reticulum_target(); let canonical = serde_json::to_value(&target).expect("serialize target"); assert_eq!( - serde_json::from_value::<RadrootsTransportTarget>(canonical.clone()) - .expect("deserialize canonical target"), + serde_json::from_value::<Target>(canonical.clone()).expect("deserialize canonical target"), target ); @@ -1800,15 +1684,15 @@ fn transport_target_deserialization_rejects_forged_and_noncanonical_identity() { .expect("target object") .insert(field.to_owned(), forged); assert!( - serde_json::from_value::<RadrootsTransportTarget>(wire).is_err(), + serde_json::from_value::<Target>(wire).is_err(), "forged {field} must fail" ); } - let labeled = RadrootsTransportTarget::nostr_relay_with_metadata( + let labeled = Target::nostr_relay_with_metadata( "wss://relay.example", None, - Some(RadrootsTransportTargetLabel::parse("Relay").expect("label")), + Some(TargetLabel::parse("Relay").expect("label")), ) .expect("labeled target"); let mut noncanonical_label = serde_json::to_value(&labeled).expect("serialize labeled target"); @@ -1816,56 +1700,50 @@ fn transport_target_deserialization_rejects_forged_and_noncanonical_identity() { .as_object_mut() .expect("target object") .insert("label".to_owned(), Value::String(" Relay ".to_owned())); - assert!(serde_json::from_value::<RadrootsTransportTarget>(noncanonical_label).is_err()); + assert!(serde_json::from_value::<Target>(noncanonical_label).is_err()); let mut unknown_field = canonical; unknown_field .as_object_mut() .expect("target object") .insert("unexpected".to_owned(), Value::Bool(true)); - assert!(serde_json::from_value::<RadrootsTransportTarget>(unknown_field).is_err()); + assert!(serde_json::from_value::<Target>(unknown_field).is_err()); } #[test] #[cfg(feature = "serde")] fn transport_target_set_deserialization_revalidates_nonempty_unique_targets() { - let target = RadrootsTransportTarget::reticulum().expect("Reticulum target"); - let set = RadrootsTransportTargetSet::new(vec![target.clone()]).expect("target set"); + let target = reticulum_target(); + let set = TargetSet::new(vec![target.clone()]).expect("target set"); let canonical = serde_json::to_value(&set).expect("serialize target set"); assert_eq!( - serde_json::from_value::<RadrootsTransportTargetSet>(canonical) - .expect("deserialize canonical target set"), + serde_json::from_value::<TargetSet>(canonical).expect("deserialize canonical target set"), set ); + assert!(serde_json::from_value::<TargetSet>(serde_json::json!({ "targets": [] })).is_err()); assert!( - serde_json::from_value::<RadrootsTransportTargetSet>(serde_json::json!({ "targets": [] })) + serde_json::from_value::<TargetSet>(serde_json::json!({ "targets": [target, target] })) .is_err() ); - assert!( - serde_json::from_value::<RadrootsTransportTargetSet>( - serde_json::json!({ "targets": [target, target] }) - ) - .is_err() - ); } #[test] fn target_contract_covers_parser_and_authority_boundaries() { - let scope = RadrootsTransportMeshScopeId::parse("farm_1.alpha-beta").expect("scope"); + let scope = TargetScope::parse("farm_1.alpha-beta").expect("scope"); assert_eq!(scope.as_str(), "farm_1.alpha-beta"); assert_eq!(scope.to_string(), "farm_1.alpha-beta"); for invalid_scope in [" scope", "scope ", "scope/path", "scope\n"] { assert_eq!( - RadrootsTransportMeshScopeId::parse(invalid_scope).expect_err("invalid scope"), + TargetScope::parse(invalid_scope).expect_err("invalid scope"), RadrootsTransportError::InvalidTargetScope ); } - let label = RadrootsTransportTargetLabel::parse(" Relay One ").expect("label"); + let label = TargetLabel::parse(" Relay One ").expect("label"); assert_eq!(label.as_str(), "Relay One"); assert_eq!(label.to_string(), "Relay One"); assert_eq!( - RadrootsTransportTargetLabel::parse("\u{7f}").expect_err("control label"), + TargetLabel::parse("\u{7f}").expect_err("control label"), RadrootsTransportError::InvalidTargetLabel ); @@ -1917,7 +1795,7 @@ fn target_contract_covers_parser_and_authority_boundaries() { "ws://[2001:db8::1]", ] { assert_eq!( - RadrootsTransportTarget::nostr_relay(invalid).expect_err("invalid relay URI"), + Target::nostr_relay(invalid).expect_err("invalid relay URI"), if invalid.is_empty() { RadrootsTransportError::EmptyTargetUri } else { @@ -1937,10 +1815,7 @@ fn target_contract_covers_parser_and_authority_boundaries() { ("ws://127.0.0.1", "ws://127.0.0.1"), ] { assert_eq!( - RadrootsTransportTarget::nostr_relay(raw) - .expect("relay URI") - .uri() - .as_str(), + Target::nostr_relay(raw).expect("relay URI").uri().as_str(), canonical ); } diff --git a/crates/transport_nostr/tests/transport.rs b/crates/transport_nostr/tests/transport.rs @@ -499,6 +499,16 @@ fn scoped_nostr_target(relay_url: &str, scope: &str, label: &str) -> Target { .expect("scoped nostr target") } +fn reticulum_target() -> Target { + Target::new_with_metadata( + TransportId::RETICULUM, + "reticulum:local", + Some(TargetScope::parse("local").expect("Reticulum scope")), + None, + ) + .expect("Reticulum target") +} + fn sink_delivery_request( request_id: &str, signed_event: &SignedEvent, @@ -1180,7 +1190,7 @@ async fn nostr_transport_sink_rejects_non_nostr_targets() { let request = sink_delivery_request( "facade-request-target", &signed, - vec![Target::new(TransportId::RETICULUM, "reticulum:local").expect("target")], + vec![reticulum_target()], SinkTargetPolicy::all(), ); let error = transport @@ -4160,7 +4170,7 @@ async fn outbox_publish_required_targets_fan_out_same_endpoint_scoped_receipts() required.clone(), optional.clone(), terminal.clone(), - Target::reticulum().expect("reticulum target"), + reticulum_target(), ], ), 1_000, @@ -4494,10 +4504,7 @@ async fn outbox_publish_skips_non_nostr_targets() { "transport.mixed.local", 1, RadrootsTransportSatisfactionPolicy::all_accepted(), - vec![ - nostr_target(RELAY_PRIMARY_WSS), - Target::reticulum().expect("reticulum target"), - ], + vec![nostr_target(RELAY_PRIMARY_WSS), reticulum_target()], ), 1_000, )) diff --git a/crates/transport_reticulum/src/contract.rs b/crates/transport_reticulum/src/contract.rs @@ -1,6 +1,7 @@ use crate::RADROOTS_RETICULUM_ENDPOINT_URI; +use radroots_transport::target::EndpointUri; use radroots_transport::target::{TargetFingerprint, TargetLabel, TargetScope}; -use radroots_transport::{RadrootsTransportError, RadrootsTransportTargetUri, Target, TransportId}; +use radroots_transport::{RadrootsTransportError, Target, TransportId}; pub const RETICULUM_V1_MAX_PAYLOAD_BYTES: usize = 64 * 1024; @@ -94,7 +95,7 @@ impl ReticulumRoutingMetadataV1 { #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct ReticulumDestinationV1 { - uri: RadrootsTransportTargetUri, + uri: EndpointUri, routing: ReticulumRoutingMetadataV1, label: Option<TargetLabel>, fingerprint: TargetFingerprint, @@ -161,7 +162,7 @@ impl ReticulumDestinationV1 { ) } - pub fn uri(&self) -> &RadrootsTransportTargetUri { + pub fn uri(&self) -> &EndpointUri { &self.uri } @@ -182,7 +183,7 @@ impl ReticulumDestinationV1 { #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct ReticulumDestinationV1Wire { - uri: RadrootsTransportTargetUri, + uri: EndpointUri, routing: ReticulumRoutingMetadataV1, label: Option<TargetLabel>, fingerprint: TargetFingerprint, diff --git a/crates/transport_reticulum/src/lib.rs b/crates/transport_reticulum/src/lib.rs @@ -528,7 +528,9 @@ mod tests { }; fn reticulum_target() -> Target { - Target::reticulum().expect("Reticulum target") + ReticulumDestinationV1::local() + .transport_target() + .expect("Reticulum target") } fn delivery_request(targets: Vec<Target>) -> RadrootsTransportDeliveryRequest { @@ -710,10 +712,17 @@ mod tests { ensure_reticulum_targets(&[wrong_kind]), Err(RadrootsReticulumError::NonReticulumTarget) ); + let wrong_uri = Target::new(TransportId::RETICULUM, "reticulum:other") + .expect("transport-neutral target"); assert_eq!( - Target::new(TransportId::RETICULUM, "reticulum:other") - .expect_err("wrong Reticulum URI"), - RadrootsTransportError::InvalidTargetUri + ensure_reticulum_targets(&[wrong_uri]), + Err(RadrootsReticulumError::InvalidEndpoint) + ); + let missing_scope = Target::new(TransportId::RETICULUM, RADROOTS_RETICULUM_ENDPOINT_URI) + .expect("transport-neutral target"); + assert_eq!( + ensure_reticulum_targets(&[missing_scope]), + Err(RadrootsReticulumError::InvalidEndpoint) ); assert!(reticulum_target().scope().is_some()); diff --git a/crates/transport_reticulum/tests/reticulum.rs b/crates/transport_reticulum/tests/reticulum.rs @@ -22,7 +22,9 @@ use serde_json::Value; fn reticulum_target(uri: &str) -> Target { assert_eq!(uri, RADROOTS_RETICULUM_ENDPOINT_URI); - Target::reticulum().expect("reticulum target") + ReticulumDestinationV1::local() + .transport_target() + .expect("reticulum target") } fn scoped_reticulum_target(scope: &str) -> Target { @@ -243,7 +245,7 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { RadrootsReticulumProfile::new( "transport reticulum", endpoint, - TargetScope::local_reticulum(), + TargetScope::parse(RADROOTS_RETICULUM_SCOPE_ID).expect("Reticulum scope"), None, RadrootsReticulumBehavior::RejectDeliveryAttempts, ) @@ -254,7 +256,7 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { RadrootsReticulumProfile::new( "", RadrootsReticulumEndpoint::default(), - TargetScope::local_reticulum(), + TargetScope::parse(RADROOTS_RETICULUM_SCOPE_ID).expect("Reticulum scope"), None, RadrootsReticulumBehavior::RejectDeliveryAttempts, ) @@ -264,7 +266,7 @@ fn endpoint_and_profile_validation_are_strict_and_canonical() { let profile = RadrootsReticulumProfile::new( "transport.reticulum.custom", RadrootsReticulumEndpoint::default(), - TargetScope::local_reticulum(), + TargetScope::parse(RADROOTS_RETICULUM_SCOPE_ID).expect("Reticulum scope"), Some(agent_endpoint), RadrootsReticulumBehavior::DeferDeliveryPlans, ) @@ -411,9 +413,10 @@ fn noncanonical_reticulum_targets_are_rejected() { "reticulum:unavailable-alt", "reticulum:custom", ] { + let result = Target::new(TransportId::RETICULUM, invalid) + .and_then(|target| ReticulumDestinationV1::from_target(&target).map(|_| target)); assert_eq!( - Target::new(TransportId::RETICULUM, invalid) - .expect_err("noncanonical Reticulum target"), + result.expect_err("noncanonical Reticulum target"), radroots_transport::RadrootsTransportError::InvalidTargetUri ); } diff --git a/docs/api/radroots_transport.txt b/docs/api/radroots_transport.txt @@ -72,7 +72,6 @@ pub fn radroots_transport::target::TargetLabel::deserialize<D>(D) -> core::resul pub struct radroots_transport::endpoint::TargetScope(_) impl radroots_transport::target::TargetScope pub fn radroots_transport::target::TargetScope::as_str(&self) -> &str -pub fn radroots_transport::target::TargetScope::local_reticulum() -> Self pub fn radroots_transport::target::TargetScope::parse(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> impl core::convert::AsRef<str> for radroots_transport::target::TargetScope pub fn radroots_transport::target::TargetScope::as_ref(&self) -> &str @@ -370,16 +369,14 @@ pub fn radroots_transport::target::EndpointUri::deserialize<D>(D) -> core::resul pub struct radroots_transport::target::Target impl radroots_transport::target::Target pub fn radroots_transport::target::Target::fingerprint(&self) -> &radroots_transport::target::TargetFingerprint -pub fn radroots_transport::target::Target::kind(&self) -> &RadrootsTransportKind +pub fn radroots_transport::target::Target::kind(&self) -> &radroots_transport::TransportId pub fn radroots_transport::target::Target::label(&self) -> core::option::Option<&radroots_transport::target::TargetLabel> pub fn radroots_transport::target::Target::local(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::local_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::new(RadrootsTransportKind, impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::new_with_metadata(RadrootsTransportKind, impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::target::Target::new(radroots_transport::TransportId, impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::target::Target::new_with_metadata(radroots_transport::TransportId, impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::nostr_relay(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::nostr_relay_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::reticulum() -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::reticulum_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::scope(&self) -> core::option::Option<&radroots_transport::target::TargetScope> pub fn radroots_transport::target::Target::uri(&self) -> &radroots_transport::target::EndpointUri impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::target::Target @@ -387,7 +384,7 @@ pub fn radroots_transport::target::Target::deserialize<D>(D) -> core::result::Re pub struct radroots_transport::target::TargetFingerprint(_) impl radroots_transport::target::TargetFingerprint pub fn radroots_transport::target::TargetFingerprint::as_str(&self) -> &str -pub fn radroots_transport::target::TargetFingerprint::from_target(&RadrootsTransportKind, &radroots_transport::target::EndpointUri, core::option::Option<&radroots_transport::target::TargetScope>) -> Self +pub fn radroots_transport::target::TargetFingerprint::from_target(&radroots_transport::TransportId, &radroots_transport::target::EndpointUri, core::option::Option<&radroots_transport::target::TargetScope>) -> Self pub fn radroots_transport::target::TargetFingerprint::parse(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> impl core::convert::AsRef<str> for radroots_transport::target::TargetFingerprint pub fn radroots_transport::target::TargetFingerprint::as_ref(&self) -> &str @@ -420,7 +417,6 @@ pub fn radroots_transport::target::TargetLabel::deserialize<D>(D) -> core::resul pub struct radroots_transport::target::TargetScope(_) impl radroots_transport::target::TargetScope pub fn radroots_transport::target::TargetScope::as_str(&self) -> &str -pub fn radroots_transport::target::TargetScope::local_reticulum() -> Self pub fn radroots_transport::target::TargetScope::parse(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> impl core::convert::AsRef<str> for radroots_transport::target::TargetScope pub fn radroots_transport::target::TargetScope::as_ref(&self) -> &str @@ -580,7 +576,7 @@ pub radroots_transport::RadrootsTransportSatisfactionPolicy::Quorum::class: radr pub radroots_transport::RadrootsTransportSatisfactionPolicy::Quorum::threshold: u16 pub radroots_transport::RadrootsTransportSatisfactionPolicy::RequiredTargets pub radroots_transport::RadrootsTransportSatisfactionPolicy::RequiredTargets::class: radroots_transport::RadrootsTransportSatisfactionClass -pub radroots_transport::RadrootsTransportSatisfactionPolicy::RequiredTargets::targets: alloc::vec::Vec<crate::RadrootsTransportTargetFingerprint> +pub radroots_transport::RadrootsTransportSatisfactionPolicy::RequiredTargets::targets: alloc::vec::Vec<radroots_transport::target::TargetFingerprint> impl radroots_transport::RadrootsTransportSatisfactionPolicy pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::all_accepted() -> Self pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::all_delivered() -> Self @@ -603,10 +599,10 @@ pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::quorum_forwarded pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::quorum_seen(u16) -> Self pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::quorum_stored(u16) -> Self pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::required_target_count(&self, usize) -> core::result::Result<usize, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::required_target_fingerprints(&self) -> core::option::Option<&[RadrootsTransportTargetFingerprint]> -pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::required_targets(radroots_transport::RadrootsTransportSatisfactionClass, alloc::vec::Vec<RadrootsTransportTargetFingerprint>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::required_target_fingerprints(&self) -> core::option::Option<&[radroots_transport::target::TargetFingerprint]> +pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::required_targets(radroots_transport::RadrootsTransportSatisfactionClass, alloc::vec::Vec<radroots_transport::target::TargetFingerprint>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::target_satisfaction_class(&self) -> core::option::Option<radroots_transport::RadrootsTransportSatisfactionClass> -pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::validate_for_target_set(&self, &RadrootsTransportTargetSet) -> core::result::Result<(), radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::validate_for_target_set(&self, &radroots_transport::target::TargetSet) -> core::result::Result<(), radroots_transport::error::RadrootsTransportError> impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::RadrootsTransportSatisfactionPolicy pub fn radroots_transport::RadrootsTransportSatisfactionPolicy::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> pub struct radroots_transport::DeliveryReceipt @@ -668,22 +664,22 @@ pub struct radroots_transport::RadrootsTransportDeliveryReceipt impl radroots_transport::RadrootsTransportDeliveryReceipt pub fn radroots_transport::RadrootsTransportDeliveryReceipt::for_request(&radroots_transport::RadrootsTransportDeliveryRequest, alloc::vec::Vec<radroots_transport::RadrootsTransportTargetReceipt>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::RadrootsTransportDeliveryReceipt::is_satisfied_by(&self, &radroots_transport::RadrootsTransportSatisfactionPolicy) -> core::result::Result<bool, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::RadrootsTransportDeliveryReceipt::new(impl core::convert::Into<alloc::string::String>, RadrootsTransportTargetSet, alloc::vec::Vec<radroots_transport::RadrootsTransportTargetReceipt>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::RadrootsTransportDeliveryReceipt::new(impl core::convert::Into<alloc::string::String>, radroots_transport::target::TargetSet, alloc::vec::Vec<radroots_transport::RadrootsTransportTargetReceipt>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::RadrootsTransportDeliveryReceipt::request_id(&self) -> &str pub fn radroots_transport::RadrootsTransportDeliveryReceipt::satisfied_target_count(&self, radroots_transport::RadrootsTransportSatisfactionClass) -> usize pub fn radroots_transport::RadrootsTransportDeliveryReceipt::target_receipts(&self) -> &[radroots_transport::RadrootsTransportTargetReceipt] -pub fn radroots_transport::RadrootsTransportDeliveryReceipt::target_set(&self) -> &RadrootsTransportTargetSet +pub fn radroots_transport::RadrootsTransportDeliveryReceipt::target_set(&self) -> &radroots_transport::target::TargetSet pub fn radroots_transport::RadrootsTransportDeliveryReceipt::validate_for_request(&self, &radroots_transport::RadrootsTransportDeliveryRequest) -> core::result::Result<(), radroots_transport::error::RadrootsTransportError> impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::RadrootsTransportDeliveryReceipt pub fn radroots_transport::RadrootsTransportDeliveryReceipt::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> pub struct radroots_transport::RadrootsTransportDeliveryRequest impl radroots_transport::RadrootsTransportDeliveryRequest -pub fn radroots_transport::RadrootsTransportDeliveryRequest::new(impl core::convert::Into<alloc::string::String>, radroots_transport::RadrootsTransportPayload, RadrootsTransportTargetSet, radroots_transport::RadrootsTransportSatisfactionPolicy) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::RadrootsTransportDeliveryRequest::new(impl core::convert::Into<alloc::string::String>, radroots_transport::RadrootsTransportPayload, radroots_transport::target::TargetSet, radroots_transport::RadrootsTransportSatisfactionPolicy) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::RadrootsTransportDeliveryRequest::now_ms(&self) -> i64 pub fn radroots_transport::RadrootsTransportDeliveryRequest::payload(&self) -> &radroots_transport::RadrootsTransportPayload pub fn radroots_transport::RadrootsTransportDeliveryRequest::request_id(&self) -> &str pub fn radroots_transport::RadrootsTransportDeliveryRequest::satisfaction_policy(&self) -> &radroots_transport::RadrootsTransportSatisfactionPolicy -pub fn radroots_transport::RadrootsTransportDeliveryRequest::target_set(&self) -> &RadrootsTransportTargetSet +pub fn radroots_transport::RadrootsTransportDeliveryRequest::target_set(&self) -> &radroots_transport::target::TargetSet pub fn radroots_transport::RadrootsTransportDeliveryRequest::try_with_now_ms(self, i64) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::RadrootsTransportDeliveryRequest pub fn radroots_transport::RadrootsTransportDeliveryRequest::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> @@ -695,9 +691,9 @@ impl radroots_transport::RadrootsTransportFetchReceipt pub fn radroots_transport::RadrootsTransportFetchReceipt::new(impl core::convert::Into<alloc::string::String>, alloc::vec::Vec<radroots_transport::RadrootsTransportTargetReceipt>, usize) -> Self pub struct radroots_transport::RadrootsTransportFetchRequest pub radroots_transport::RadrootsTransportFetchRequest::request_id: alloc::string::String -pub radroots_transport::RadrootsTransportFetchRequest::target_set: crate::RadrootsTransportTargetSet +pub radroots_transport::RadrootsTransportFetchRequest::target_set: radroots_transport::target::TargetSet impl radroots_transport::RadrootsTransportFetchRequest -pub fn radroots_transport::RadrootsTransportFetchRequest::new(impl core::convert::Into<alloc::string::String>, RadrootsTransportTargetSet) -> Self +pub fn radroots_transport::RadrootsTransportFetchRequest::new(impl core::convert::Into<alloc::string::String>, radroots_transport::target::TargetSet) -> Self pub struct radroots_transport::RadrootsTransportOutcome pub radroots_transport::RadrootsTransportOutcome::code: core::option::Option<alloc::string::String> pub radroots_transport::RadrootsTransportOutcome::kind: radroots_transport::RadrootsTransportOutcomeKind @@ -716,13 +712,13 @@ pub radroots_transport::RadrootsTransportStatus::capabilities: radroots_transpor pub radroots_transport::RadrootsTransportStatus::configured: bool pub radroots_transport::RadrootsTransportStatus::endpoint_uri: core::option::Option<alloc::string::String> pub radroots_transport::RadrootsTransportStatus::implementation: radroots_transport::RadrootsTransportImplementationState -pub radroots_transport::RadrootsTransportStatus::kind: crate::RadrootsTransportKind +pub radroots_transport::RadrootsTransportStatus::kind: radroots_transport::TransportId pub radroots_transport::RadrootsTransportStatus::maturity: crate::RadrootsTransportCapabilityMaturity pub radroots_transport::RadrootsTransportStatus::message: alloc::string::String pub radroots_transport::RadrootsTransportStatus::profile_id: core::option::Option<alloc::string::String> pub radroots_transport::RadrootsTransportStatus::usable_for_delivery: bool impl radroots_transport::RadrootsTransportStatus -pub fn radroots_transport::RadrootsTransportStatus::new(RadrootsTransportKind, bool, radroots_transport::RadrootsTransportImplementationState, bool, impl core::convert::Into<alloc::string::String>) -> Self +pub fn radroots_transport::RadrootsTransportStatus::new(radroots_transport::TransportId, bool, radroots_transport::RadrootsTransportImplementationState, bool, impl core::convert::Into<alloc::string::String>) -> Self pub fn radroots_transport::RadrootsTransportStatus::with_availability(self, RadrootsTransportCapabilityAvailability) -> Self pub fn radroots_transport::RadrootsTransportStatus::with_capabilities(self, radroots_transport::RadrootsTransportCapabilities) -> Self pub fn radroots_transport::RadrootsTransportStatus::with_endpoint_uri(self, impl core::convert::Into<alloc::string::String>) -> Self @@ -732,11 +728,11 @@ pub struct radroots_transport::RadrootsTransportTargetReceipt pub radroots_transport::RadrootsTransportTargetReceipt::attempted: bool pub radroots_transport::RadrootsTransportTargetReceipt::outcome: radroots_transport::RadrootsTransportOutcome pub radroots_transport::RadrootsTransportTargetReceipt::status: radroots_transport::RadrootsTransportDeliveryTargetStatus -pub radroots_transport::RadrootsTransportTargetReceipt::target: crate::RadrootsTransportTarget +pub radroots_transport::RadrootsTransportTargetReceipt::target: radroots_transport::target::Target impl radroots_transport::RadrootsTransportTargetReceipt -pub fn radroots_transport::RadrootsTransportTargetReceipt::attempted(RadrootsTransportTarget, radroots_transport::RadrootsTransportOutcome) -> Self -pub fn radroots_transport::RadrootsTransportTargetReceipt::new(RadrootsTransportTarget, radroots_transport::RadrootsTransportOutcome) -> Self -pub fn radroots_transport::RadrootsTransportTargetReceipt::skipped(RadrootsTransportTarget, radroots_transport::RadrootsTransportOutcome) -> Self +pub fn radroots_transport::RadrootsTransportTargetReceipt::attempted(radroots_transport::target::Target, radroots_transport::RadrootsTransportOutcome) -> Self +pub fn radroots_transport::RadrootsTransportTargetReceipt::new(radroots_transport::target::Target, radroots_transport::RadrootsTransportOutcome) -> Self +pub fn radroots_transport::RadrootsTransportTargetReceipt::skipped(radroots_transport::target::Target, radroots_transport::RadrootsTransportOutcome) -> Self impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::RadrootsTransportTargetReceipt pub fn radroots_transport::RadrootsTransportTargetReceipt::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> pub struct radroots_transport::SinkStatus @@ -762,16 +758,14 @@ pub const fn radroots_transport::SourceStatus::transport_id(&self) -> radroots_t pub struct radroots_transport::Target impl radroots_transport::target::Target pub fn radroots_transport::target::Target::fingerprint(&self) -> &radroots_transport::target::TargetFingerprint -pub fn radroots_transport::target::Target::kind(&self) -> &RadrootsTransportKind +pub fn radroots_transport::target::Target::kind(&self) -> &radroots_transport::TransportId pub fn radroots_transport::target::Target::label(&self) -> core::option::Option<&radroots_transport::target::TargetLabel> pub fn radroots_transport::target::Target::local(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::local_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::new(RadrootsTransportKind, impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::new_with_metadata(RadrootsTransportKind, impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::target::Target::new(radroots_transport::TransportId, impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> +pub fn radroots_transport::target::Target::new_with_metadata(radroots_transport::TransportId, impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::nostr_relay(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::nostr_relay_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::reticulum() -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> -pub fn radroots_transport::target::Target::reticulum_with_metadata(impl core::convert::AsRef<str>, core::option::Option<radroots_transport::target::TargetScope>, core::option::Option<radroots_transport::target::TargetLabel>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> pub fn radroots_transport::target::Target::scope(&self) -> core::option::Option<&radroots_transport::target::TargetScope> pub fn radroots_transport::target::Target::uri(&self) -> &radroots_transport::target::EndpointUri impl<'de> serde_core::de::Deserialize<'de> for radroots_transport::target::Target @@ -787,12 +781,9 @@ pub fn radroots_transport::target::TargetSet::deserialize<D>(D) -> core::result: pub struct radroots_transport::TransportId(_) impl radroots_transport::TransportId pub const radroots_transport::TransportId::LOCAL: Self -pub const radroots_transport::TransportId::Local: Self pub const radroots_transport::TransportId::NOSTR: Self -pub const radroots_transport::TransportId::Nostr: Self pub const radroots_transport::TransportId::RADROOTSD: Self pub const radroots_transport::TransportId::RETICULUM: Self -pub const radroots_transport::TransportId::Reticulum: Self pub fn radroots_transport::TransportId::as_str(&self) -> &str pub fn radroots_transport::TransportId::canonical_label(&self) -> alloc::string::String pub fn radroots_transport::TransportId::parse(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_transport::error::RadrootsTransportError> @@ -829,4 +820,3 @@ pub fn radroots_transport::EventSource::fetch(&self, radroots_transport::source: pub fn radroots_transport::EventSource::status(&self) -> radroots_transport::source::BoxFuture<'_, core::result::Result<radroots_transport::SourceStatus, radroots_transport::error::Error>> pub type radroots_transport::BoxFuture<'a, T> = core::pin::Pin<alloc::boxed::Box<(dyn core::future::future::Future<Output = T> + core::marker::Send + 'a)>> pub type radroots_transport::Error = radroots_transport::error::RadrootsTransportError -pub type radroots_transport::RadrootsTransportFuture<'a, T> = core::pin::Pin<alloc::boxed::Box<(dyn core::future::future::Future<Output = core::result::Result<T, radroots_transport::error::RadrootsTransportError>> + core::marker::Send + 'a)>> diff --git a/docs/implementation/DEVIATIONS.md b/docs/implementation/DEVIATIONS.md @@ -13,6 +13,7 @@ silently change `radroots.crates.release.v1`. | `RCRV1-DEV-002` | 249 | Pull only the facade scaffold forward to immediately after Step 014 in `sdk`; do not repeat it later. | | `RCRV1-DEV-004` | 098, 155, 225, 260, 268, 294, 298-299, 301-304, 314 | Enforce a temporary 90% four-dimension coverage baseline during heavy development; restore 100% only through a future explicit contract update. | | `RCRV1-DEV-005` | 013, 019-026, 027-315 | Pin every Rust crate and internal Radroots dependency in `radrootslabs/lib` to exactly `0.1.0-alpha` until further explicit authority. | +| `RCRV1-DEV-007` | 122, 170, 235, 305 | Remove the predecessor monolithic transport SPI now; quarantine publish-frozen runtime, SDK, CLI, and daemon consumer shims until their explicit removal gates. | ## Record template diff --git a/docs/implementation/deviations.toml b/docs/implementation/deviations.toml @@ -37,6 +37,33 @@ normative_architecture_change = false adr_required = false [[deviation]] +id = "RCRV1-DEV-007" +date = "2026-07-30" +status = "active" +approval = "All code-review recommendations and the full multi-RCLD implementation sequence were explicitly approved by the user." +affected_steps = ["122", "170", "235", "305"] +spec_anchors = [ + "docs/specs/radroots_crates_release_v1.md#9-radroots_transport", + "docs/specs/radroots_crates_release_v1.md#111-separate-source-and-sink-contracts", +] +source_evidence = [ + "Step 120 migrated canonical adapters and registries to independent EventSource and EventSink contracts.", + "Mixed runtime delivery workers still consume predecessor request and receipt models until their ordered RCLD 40 migration.", + "The standalone publish-frozen SDK still maps user-facing target and satisfaction models into predecessor outbox orchestration until Step 235.", + "oss/cli/src/runtime/{config,sync,transport}.rs and oss/radrootsd/src/core/transport_publish.rs still import the predecessor identity aliases and Reticulum target helpers; those standalone repositories are outside the approved crate-surface mutation scope.", +] +replacement_action = "Remove the monolithic trait from radroots_transport in Step 122; retain one explicitly named runtime-owned unpublished shim until Step 170, the SDK-local unpublished target/satisfaction mapping until Step 235, and documentation-hidden external-consumer aliases/helpers until the fail-closed package-realistic Step 305 gate." +verification = [ + "Transport source-boundary tests reject every removed public predecessor name and require the singular runtime-owned shim.", + "Release policy keeps runtime and SDK publication disabled while either downstream shim exists.", + "Steps 170 and 235 are the exact fail-closed final-removal gates for the remaining downstream mappings.", + "Step 305 rejects publication until oss/cli and oss/radrootsd no longer require the documentation-hidden external-consumer aliases and Reticulum helpers.", +] +unresolved_risk = "Runtime, SDK, CLI, and daemon consumers retain bounded publish-frozen predecessor mappings; package-realistic publication remains blocked until every mapping is removed at its explicit gate." +normative_architecture_change = false +adr_required = false + +[[deviation]] id = "RCRV1-DEV-005" date = "2026-07-28" status = "active" diff --git a/tools/xtask/src/contract/source_maintenance.rs b/tools/xtask/src/contract/source_maintenance.rs @@ -3728,7 +3728,7 @@ struct DelegatedAuthoritySpec { const EXECUTABLE_AUTHORITY_AST_SHA256: &str = "b1a7658f47b4561ad816ef65dab47cf68c75de3c459383371319e96e6051d435"; const BOUND_AUTHORITY_SOURCE_AST_SHA256: &str = - "b0a59506a3a49b86875699a545cd9eec388c4b030b46264f2cb6540aab0b6a8d"; + "f3e62e27e48c661b81aaabc37de75264caf433896b1db881b6cb984ddd23554b"; #[derive(Clone, Debug, Serialize)] struct ExecutableAuthorityIdentity {