commit 6f0255c7adb493852bbe57d8dc918f8b7ba0fbe0
parent 377c866126b9bf5121ec4e1858771977490e14e6
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Jul 2026 10:20:13 +0000
workspace: align public package layout and names
- establish all seventeen governed public package identities at version 0.1.0
- preserve underscore Rust crate paths behind hyphenated Cargo package names
- align contract, coverage, Nix, and architecture checks with the final inventory
- refresh lock and generated governance artifacts without dependency upgrades
Diffstat:
68 files changed, 1609 insertions(+), 1268 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -4495,17 +4495,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
-name = "radroots_authority"
-version = "1.0.0-alpha.1"
-dependencies = [
- "radroots_event",
- "radroots_nostr",
- "serde_json",
-]
-
-[[package]]
-name = "radroots_blossom"
-version = "1.0.0-alpha.1"
+name = "radroots-blossom"
+version = "0.1.0"
dependencies = [
"hex",
"mediatype",
@@ -4517,8 +4508,8 @@ dependencies = [
]
[[package]]
-name = "radroots_core"
-version = "1.0.0-alpha.1"
+name = "radroots-core"
+version = "0.1.0"
dependencies = [
"dto_bindgen",
"rust_decimal",
@@ -4528,14 +4519,14 @@ dependencies = [
]
[[package]]
-name = "radroots_event"
-version = "1.0.0-alpha.1"
+name = "radroots-event"
+version = "0.1.0"
dependencies = [
"dto_bindgen",
"hex",
"jiff-tzdb",
- "radroots_blossom",
- "radroots_core",
+ "radroots-blossom",
+ "radroots-core",
"secp256k1",
"serde",
"serde_json",
@@ -4545,18 +4536,157 @@ dependencies = [
]
[[package]]
-name = "radroots_event_codec"
-version = "1.0.0-alpha.1"
+name = "radroots-event-codec"
+version = "0.1.0"
+dependencies = [
+ "hex",
+ "nostr",
+ "radroots-blossom",
+ "radroots-core",
+ "radroots-event",
+ "radroots_test_fixtures",
+ "serde",
+ "serde_json",
+ "sha2",
+]
+
+[[package]]
+name = "radroots-geonames"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-identity"
+version = "0.1.0"
+dependencies = [
+ "nostr",
+ "radroots_protected_store",
+ "radroots_runtime",
+ "radroots_runtime_paths",
+ "radroots_secret_vault",
+ "secrecy",
+ "serde",
+ "serde_json",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tracing",
+ "zeroize",
+]
+
+[[package]]
+name = "radroots-nostr"
+version = "0.1.0"
+dependencies = [
+ "base64 0.22.1",
+ "nostr",
+ "nostr-sdk",
+ "radroots-blossom",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-identity",
+ "radroots_test_fixtures",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "tokio",
+]
+
+[[package]]
+name = "radroots-nostr-connect"
+version = "0.1.0"
dependencies = [
+ "nostr",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "radroots-protocol"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-secrets"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-signing"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-storage"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-storage-sqlite"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-sync"
+version = "0.1.0"
+
+[[package]]
+name = "radroots-trade"
+version = "0.1.0"
+dependencies = [
+ "base64 0.22.1",
+ "dto_bindgen",
+ "dto_bindgen_core",
"hex",
"nostr",
- "radroots_blossom",
- "radroots_core",
- "radroots_event",
+ "radroots-core",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-nostr",
+ "radroots-transport",
+ "radroots_authority",
+ "radroots_event_store",
"radroots_test_fixtures",
"serde",
"serde_json",
"sha2",
+ "sqlx",
+ "thiserror 1.0.69",
+ "tokio",
+]
+
+[[package]]
+name = "radroots-transport"
+version = "0.1.0"
+dependencies = [
+ "futures",
+ "serde",
+ "serde_json",
+ "sha2",
+]
+
+[[package]]
+name = "radroots-transport-nostr"
+version = "0.1.0"
+dependencies = [
+ "futures",
+ "nostr",
+ "radroots-event",
+ "radroots-nostr",
+ "radroots-transport",
+ "radroots_event_store",
+ "radroots_outbox",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "radroots_authority"
+version = "1.0.0-alpha.1"
+dependencies = [
+ "radroots-event",
+ "radroots-nostr",
+ "serde_json",
]
[[package]]
@@ -4576,10 +4706,10 @@ dependencies = [
"getrandom 0.2.17",
"hex",
"nostr",
- "radroots_blossom",
- "radroots_event",
- "radroots_event_codec",
- "radroots_transport",
+ "radroots-blossom",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-transport",
"serde",
"serde_json",
"sha2",
@@ -4607,24 +4737,6 @@ dependencies = [
]
[[package]]
-name = "radroots_identity"
-version = "1.0.0-alpha.1"
-dependencies = [
- "nostr",
- "radroots_protected_store",
- "radroots_runtime",
- "radroots_runtime_paths",
- "radroots_secret_vault",
- "secrecy",
- "serde",
- "serde_json",
- "tempfile",
- "thiserror 1.0.69",
- "tracing",
- "zeroize",
-]
-
-[[package]]
name = "radroots_log"
version = "1.0.0-alpha.1"
dependencies = [
@@ -4640,7 +4752,7 @@ dependencies = [
name = "radroots_mesh"
version = "1.0.0-alpha.1"
dependencies = [
- "radroots_transport",
+ "radroots-transport",
"serde",
"serde_json",
]
@@ -4649,9 +4761,9 @@ dependencies = [
name = "radroots_mesh_agent_client"
version = "1.0.0-alpha.1"
dependencies = [
+ "radroots-transport",
"radroots_mesh",
"radroots_mesh_agent_proto",
- "radroots_transport",
"serde",
"serde_json",
]
@@ -4670,11 +4782,11 @@ dependencies = [
"directories",
"futures",
"hex",
- "radroots_event",
- "radroots_event_codec",
- "radroots_identity",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-identity",
+ "radroots-nostr",
"radroots_log",
- "radroots_nostr",
"radroots_nostr_accounts",
"radroots_nostr_signer",
"radroots_runtime_paths",
@@ -4688,29 +4800,10 @@ dependencies = [
]
[[package]]
-name = "radroots_nostr"
-version = "1.0.0-alpha.1"
-dependencies = [
- "base64 0.22.1",
- "nostr",
- "nostr-sdk",
- "radroots_blossom",
- "radroots_event",
- "radroots_event_codec",
- "radroots_identity",
- "radroots_test_fixtures",
- "reqwest",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tokio",
-]
-
-[[package]]
name = "radroots_nostr_accounts"
version = "1.0.0-alpha.1"
dependencies = [
- "radroots_identity",
+ "radroots-identity",
"radroots_nostr_signer",
"radroots_nostrdb",
"radroots_protected_store",
@@ -4724,24 +4817,12 @@ dependencies = [
]
[[package]]
-name = "radroots_nostr_connect"
-version = "1.0.0-alpha.1"
-dependencies = [
- "nostr",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tokio",
- "url",
-]
-
-[[package]]
name = "radroots_nostr_runtime"
version = "1.0.0-alpha.1"
dependencies = [
"futures",
"nostr",
- "radroots_nostr",
+ "radroots-nostr",
"thiserror 1.0.69",
"tokio",
]
@@ -4752,9 +4833,9 @@ version = "1.0.0-alpha.1"
dependencies = [
"hex",
"nostr",
- "radroots_identity",
- "radroots_nostr",
- "radroots_nostr_connect",
+ "radroots-identity",
+ "radroots-nostr",
+ "radroots-nostr-connect",
"radroots_runtime",
"radroots_sql_core",
"serde",
@@ -4774,7 +4855,7 @@ dependencies = [
"hex",
"nostr",
"nostrdb",
- "radroots_nostr",
+ "radroots-nostr",
"radroots_nostr_runtime",
"serde_json",
"tempfile",
@@ -4787,10 +4868,10 @@ name = "radroots_outbox"
version = "1.0.0-alpha.1"
dependencies = [
"hex",
- "radroots_event",
+ "radroots-event",
+ "radroots-nostr",
+ "radroots-transport",
"radroots_event_store",
- "radroots_nostr",
- "radroots_transport",
"serde",
"serde_json",
"sha2",
@@ -4849,10 +4930,10 @@ dependencies = [
"base64 0.22.1",
"hex",
"nostr",
- "radroots_core",
- "radroots_event",
- "radroots_event_codec",
- "radroots_nostr",
+ "radroots-core",
+ "radroots-event",
+ "radroots-event-codec",
+ "radroots-nostr",
"radroots_replica_schema",
"radroots_replica_store",
"radroots_sql_core",
@@ -4872,12 +4953,12 @@ dependencies = [
"clap",
"config",
"getrandom 0.2.17",
- "radroots_event",
+ "radroots-event",
+ "radroots-transport",
"radroots_log",
"radroots_protected_store",
"radroots_runtime_paths",
"radroots_secret_vault",
- "radroots_transport",
"radroots_transport_reticulum",
"serde",
"serde_json",
@@ -5066,32 +5147,7 @@ dependencies = [
name = "radroots_test_fixtures"
version = "1.0.0-alpha.1"
dependencies = [
- "radroots_event",
-]
-
-[[package]]
-name = "radroots_trade"
-version = "1.0.0-alpha.1"
-dependencies = [
- "base64 0.22.1",
- "dto_bindgen",
- "dto_bindgen_core",
- "hex",
- "nostr",
- "radroots_authority",
- "radroots_core",
- "radroots_event",
- "radroots_event_codec",
- "radroots_event_store",
- "radroots_nostr",
- "radroots_test_fixtures",
- "radroots_transport",
- "serde",
- "serde_json",
- "sha2",
- "sqlx",
- "thiserror 1.0.69",
- "tokio",
+ "radroots-event",
]
[[package]]
@@ -5112,8 +5168,8 @@ dependencies = [
"base64 0.22.1",
"bincode",
"futures",
- "radroots_event",
- "radroots_trade",
+ "radroots-event",
+ "radroots-trade",
"radroots_trade_sp1_guest",
"serde",
"serde_json",
@@ -5125,38 +5181,10 @@ dependencies = [
]
[[package]]
-name = "radroots_transport"
-version = "1.0.0-alpha.1"
-dependencies = [
- "futures",
- "serde",
- "serde_json",
- "sha2",
-]
-
-[[package]]
-name = "radroots_transport_nostr"
-version = "1.0.0-alpha.1"
-dependencies = [
- "futures",
- "nostr",
- "radroots_event",
- "radroots_event_store",
- "radroots_nostr",
- "radroots_outbox",
- "radroots_transport",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tokio",
- "url",
-]
-
-[[package]]
name = "radroots_transport_publish_protocol"
version = "1.0.0-alpha.1"
dependencies = [
- "radroots_transport",
+ "radroots-transport",
"serde",
"serde_json",
]
@@ -5166,7 +5194,7 @@ name = "radroots_transport_reticulum"
version = "1.0.0-alpha.1"
dependencies = [
"futures",
- "radroots_transport",
+ "radroots-transport",
"serde",
"serde_json",
]
@@ -9004,7 +9032,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
- "radroots_event_codec",
+ "radroots-event-codec",
"radroots_protocol_contract_v1",
"semver",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
@@ -15,6 +15,13 @@ members = [
"crates/nostr",
"crates/nostr_accounts",
"crates/nostr_connect",
+ "crates/protocol",
+ "crates/signing",
+ "crates/secrets",
+ "crates/storage",
+ "crates/storage_sqlite",
+ "crates/sync",
+ "crates/geonames",
"crates/nostr_signer",
"crates/nostrdb",
"crates/nostr_runtime",
@@ -71,6 +78,7 @@ readme = "README.md"
[workspace.lints.rust]
unsafe_code = "forbid"
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
[workspace.lints.rustdoc]
broken_intra_doc_links = "deny"
@@ -83,19 +91,19 @@ unimplemented = "deny"
[workspace.dependencies]
dto_bindgen = { version = "0.1.0" }
dto_bindgen_core = { version = "0.1.0" }
-radroots_core = { path = "crates/core", version = "=1.0.0-alpha.1", default-features = false }
-radroots_event = { path = "crates/event", version = "=1.0.0-alpha.1", default-features = false }
+radroots_core = { package = "radroots-core", path = "crates/core", version = "=0.1.0", default-features = false }
+radroots_event = { package = "radroots-event", path = "crates/event", version = "=0.1.0", default-features = false }
radroots_event_store = { path = "crates/event_store", version = "=1.0.0-alpha.1", default-features = false }
-radroots_event_codec = { path = "crates/event_codec", version = "=1.0.0-alpha.1", default-features = false }
+radroots_event_codec = { package = "radroots-event-codec", path = "crates/event_codec", version = "=0.1.0", default-features = false }
radroots_event_index = { path = "crates/event_index", version = "=1.0.0-alpha.1", default-features = false }
radroots_authority = { path = "crates/authority", version = "=1.0.0-alpha.1", default-features = false }
-radroots_blossom = { path = "crates/blossom", version = "=1.0.0-alpha.1", default-features = false }
+radroots_blossom = { package = "radroots-blossom", path = "crates/blossom", version = "=0.1.0", default-features = false }
radroots_geocoder = { path = "crates/geocoder", version = "=1.0.0-alpha.1" }
-radroots_identity = { path = "crates/identity", version = "=1.0.0-alpha.1", default-features = false }
+radroots_identity = { package = "radroots-identity", path = "crates/identity", version = "=0.1.0", default-features = false }
radroots_runtime_store = { path = "crates/runtime_store", version = "=1.0.0-alpha.1", default-features = false }
-radroots_nostr = { path = "crates/nostr", version = "=1.0.0-alpha.1", default-features = false }
+radroots_nostr = { package = "radroots-nostr", path = "crates/nostr", version = "=0.1.0", default-features = false }
radroots_nostr_accounts = { path = "crates/nostr_accounts", version = "=1.0.0-alpha.1", default-features = false }
-radroots_nostr_connect = { path = "crates/nostr_connect", version = "=1.0.0-alpha.1", default-features = false }
+radroots_nostr_connect = { package = "radroots-nostr-connect", path = "crates/nostr_connect", version = "=0.1.0", default-features = false }
radroots_nostr_signer = { path = "crates/nostr_signer", version = "=1.0.0-alpha.1", default-features = false }
radroots_nostrdb = { path = "crates/nostrdb", version = "=1.0.0-alpha.1", default-features = false }
radroots_runtime = { path = "crates/runtime", version = "=1.0.0-alpha.1", default-features = false }
@@ -108,7 +116,7 @@ radroots_nostr_runtime = { path = "crates/nostr_runtime", version = "=1.0.0-alph
radroots_outbox = { path = "crates/outbox", version = "=1.0.0-alpha.1", default-features = false }
radroots_protocol_contract_v1 = { path = "crates/protocol_contract_v1", version = "=1.0.0-alpha.1", default-features = false }
radroots_transport_publish_protocol = { path = "crates/transport_publish_protocol", version = "=1.0.0-alpha.1", default-features = false }
-radroots_transport_nostr = { path = "crates/transport_nostr", version = "=1.0.0-alpha.1", default-features = false }
+radroots_transport_nostr = { package = "radroots-transport-nostr", path = "crates/transport_nostr", version = "=0.1.0", default-features = false }
radroots_transport_reticulum = { path = "crates/transport_reticulum", version = "=1.0.0-alpha.1", default-features = false }
radroots_simplex_agent_proto = { path = "crates/simplex_agent_proto", version = "=1.0.0-alpha.1", default-features = false }
radroots_simplex_agent_runtime = { path = "crates/simplex_agent_runtime", version = "=1.0.0-alpha.1", default-features = false }
@@ -123,8 +131,8 @@ radroots_test_fixtures = { path = "crates/test_fixtures", version = "=1.0.0-alph
radroots_replica_schema = { path = "crates/replica_schema", version = "=1.0.0-alpha.1", default-features = false }
radroots_replica_sync = { path = "crates/replica_sync", version = "=1.0.0-alpha.1", default-features = false }
radroots_replica_store = { path = "crates/replica_store", version = "=1.0.0-alpha.1", default-features = false }
-radroots_trade = { path = "crates/trade", version = "=1.0.0-alpha.1", default-features = false }
-radroots_transport = { path = "crates/transport", version = "=1.0.0-alpha.1", default-features = false }
+radroots_trade = { package = "radroots-trade", path = "crates/trade", version = "=0.1.0", default-features = false }
+radroots_transport = { package = "radroots-transport", path = "crates/transport", version = "=0.1.0", default-features = false }
radroots_mesh = { path = "crates/mesh", version = "=1.0.0-alpha.1", default-features = false }
radroots_mesh_agent_client = { path = "crates/mesh_agent_client", version = "=1.0.0-alpha.1", default-features = false }
radroots_mesh_agent_proto = { path = "crates/mesh_agent_proto", version = "=1.0.0-alpha.1", default-features = false }
diff --git a/build/nix/common.nix b/build/nix/common.nix
@@ -94,16 +94,16 @@ let
releaseRuntimeInputs = coverageRuntimeInputs;
coreContractCrates = [
"xtask"
- "radroots_blossom"
- "radroots_core"
- "radroots_event"
- "radroots_trade"
- "radroots_identity"
+ "radroots-blossom"
+ "radroots-core"
+ "radroots-event"
+ "radroots-trade"
+ "radroots-identity"
"radroots_replica_schema"
- "radroots_event_codec"
+ "radroots-event-codec"
"radroots_event_store"
- "radroots_nostr"
- "radroots_nostr_connect"
+ "radroots-nostr"
+ "radroots-nostr-connect"
"radroots_nostr_signer"
];
coreContractCargoArgs =
diff --git a/contracts/coverage-profiles.toml b/contracts/coverage-profiles.toml
@@ -8,7 +8,7 @@ no_default_features = true
features = []
test_threads = 1
-[profiles.crates."radroots_event_codec"]
+[profiles.crates."radroots-event-codec"]
no_default_features = false
features = ["serde_json", "nostr"]
test_threads = 1
@@ -18,7 +18,7 @@ no_default_features = true
features = ["std"]
test_threads = 1
-[profiles.crates."radroots_nostr"]
+[profiles.crates."radroots-nostr"]
no_default_features = false
features = ["blossom"]
test_threads = 1
diff --git a/contracts/coverage.toml b/contracts/coverage.toml
@@ -32,23 +32,24 @@ reason = "branch coverage is not applicable while the crate has no measured bran
[required]
crates = [
"radroots_authority",
- "radroots_blossom",
- "radroots_core",
+ "radroots-blossom",
+ "radroots-core",
"radroots_event_store",
- "radroots_event",
- "radroots_event_codec",
+ "radroots-event",
+ "radroots-event-codec",
"radroots_event_index",
"radroots_geocoder",
- "radroots_identity",
+ "radroots-geonames",
+ "radroots-identity",
"radroots_runtime_store",
"radroots_log",
"radroots_mesh",
"radroots_mesh_agent_client",
"radroots_mesh_agent_proto",
"radroots_net",
- "radroots_nostr",
+ "radroots-nostr",
"radroots_nostr_accounts",
- "radroots_nostr_connect",
+ "radroots-nostr-connect",
"radroots_nostrdb",
"radroots_nostr_runtime",
"radroots_nostr_signer",
@@ -56,7 +57,13 @@ crates = [
"radroots_protocol_contract_v1",
"radroots_protected_store",
"radroots_transport_publish_protocol",
- "radroots_transport_nostr",
+ "radroots-protocol",
+ "radroots-secrets",
+ "radroots-signing",
+ "radroots-storage",
+ "radroots-storage-sqlite",
+ "radroots-sync",
+ "radroots-transport-nostr",
"radroots_transport_reticulum",
"radroots_replica_store",
"radroots_replica_schema",
@@ -70,7 +77,7 @@ crates = [
"radroots_trade_sp1_host",
"radroots_sql_core",
"radroots_test_fixtures",
- "radroots_trade",
- "radroots_transport",
+ "radroots-trade",
+ "radroots-transport",
"xtask",
]
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v2.json b/contracts/knowledge/knowledge_event_contract_manifest.v2.json
@@ -1,8 +1,8 @@
{
"schema_version": 2,
"registry_version": 7,
- "radroots_event_version": "1.0.0-alpha.1",
- "radroots_event_codec_version": "1.0.0-alpha.1",
+ "radroots_event_version": "0.1.0",
+ "radroots_event_codec_version": "0.1.0",
"contract_count": 11,
"contracts": [
{
diff --git a/contracts/knowledge/knowledge_event_contract_manifest.v2.sha256 b/contracts/knowledge/knowledge_event_contract_manifest.v2.sha256
@@ -1 +1 @@
-d17a44375f52be93b0205be0f0541664b0310ee75106a160e7690b9b245a5bab
+4c7143b04c4c2113bfc7c992daf727ca7eed860fda107a1afd394a0063207d59
diff --git a/contracts/releases/publish_policy.toml b/contracts/releases/publish_policy.toml
@@ -51,19 +51,12 @@ external_packages = ["radroots-sdk", "radroots"]
[workspace_classification]
private = [
"radroots_authority",
- "radroots_blossom",
- "radroots_core",
- "radroots_event",
- "radroots_event_codec",
"radroots_event_index",
"radroots_event_store",
"radroots_geocoder",
- "radroots_identity",
"radroots_log",
"radroots_net",
- "radroots_nostr",
"radroots_nostr_accounts",
- "radroots_nostr_connect",
"radroots_nostr_runtime",
"radroots_nostr_signer",
"radroots_outbox",
@@ -76,9 +69,6 @@ private = [
"radroots_runtime_store",
"radroots_secret_vault",
"radroots_sql_core",
- "radroots_trade",
- "radroots_transport",
- "radroots_transport_nostr",
"radroots_transport_publish_protocol",
]
build_codegen = ["xtask"]
diff --git a/crates/blossom/Cargo.toml b/crates/blossom/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_blossom"
+name = "radroots-blossom"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Portable Blossom protocol primitives for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_blossom"
-readme = "README"
+documentation = "https://docs.rs/radroots-blossom"
+readme = "README.md"
+
+[lib]
+name = "radroots_blossom"
[features]
default = ["serde"]
@@ -28,5 +31,5 @@ url_nostd = { workspace = true }
hex = { workspace = true }
serde_json = { workspace = true, features = ["std"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/blossom/README b/crates/blossom/README
@@ -1,23 +0,0 @@
-# radroots_blossom
-
-`radroots_blossom` provides portable, runtime-independent primitives for
-Blossom blob hashes, root hash paths, blob URLs, BUD-02 descriptors,
-Radroots-approved byte verification, and pure BUD-11 authorization claims.
-
-The crate is `no_std + alloc`, performs no HTTP requests, and does not depend on
-Nostr event types. Structural Blossom validity is kept separate from the
-stricter Radroots reference policy: public HTTP descriptors remain parseable,
-but only HTTPS and loopback HTTP references can advance to an approved state.
-The byte-verified descriptor state proves local descriptor-to-byte agreement;
-it is not an upload receipt, and HTTP-capable runtimes must gate successful
-BUD-02 upload completion separately. BUD-11 support similarly stops at typed
-claim construction and endpoint validation; signing and canonical
-`Authorization: Nostr` encoding live behind the `radroots_nostr` `blossom`
-feature, and kind `24242` is never a relay-publication event.
-
-Protocol behavior is pinned to Blossom commit
-`b5bd2801d1763aa635fc8fea7a76597e0eb18990`:
-
-- BUD-01: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/01.md>
-- BUD-02: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/02.md>
-- BUD-11: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/11.md>
diff --git a/crates/blossom/README.md b/crates/blossom/README.md
@@ -0,0 +1,23 @@
+# radroots-blossom
+
+`radroots_blossom` provides portable, runtime-independent primitives for
+Blossom blob hashes, root hash paths, blob URLs, BUD-02 descriptors,
+Radroots-approved byte verification, and pure BUD-11 authorization claims.
+
+The crate is `no_std + alloc`, performs no HTTP requests, and does not depend on
+Nostr event types. Structural Blossom validity is kept separate from the
+stricter Radroots reference policy: public HTTP descriptors remain parseable,
+but only HTTPS and loopback HTTP references can advance to an approved state.
+The byte-verified descriptor state proves local descriptor-to-byte agreement;
+it is not an upload receipt, and HTTP-capable runtimes must gate successful
+BUD-02 upload completion separately. BUD-11 support similarly stops at typed
+claim construction and endpoint validation; signing and canonical
+`Authorization: Nostr` encoding live behind the `radroots_nostr` `blossom`
+feature, and kind `24242` is never a relay-publication event.
+
+Protocol behavior is pinned to Blossom commit
+`b5bd2801d1763aa635fc8fea7a76597e0eb18990`:
+
+- BUD-01: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/01.md>
+- BUD-02: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/02.md>
+- BUD-11: <https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/11.md>
diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_core"
+name = "radroots-core"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Core value model for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_core"
-readme = "README"
+documentation = "https://docs.rs/radroots-core"
+readme = "README.md"
+
+[lib]
+name = "radroots_core"
[features]
default = ["std", "serde"]
@@ -31,5 +34,5 @@ serde = { workspace = true, default-features = false, features = [
serde_json = { workspace = true }
rust_decimal = { workspace = true }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/core/README b/crates/core/README
@@ -1,21 +0,0 @@
-# radroots_core
-
-This is the README for `radroots_core`, which provides core value types and
-unit semantics for the `radroots` core libraries.
-
-## Overview
-
- * stable domain value types for currency, money, quantity, quantity pricing,
- percent, discounts, and decimals;
- * unit kinds, dimensions, parsing, and deterministic conversion helpers;
- * portable core semantics for both `std` and `no_std` builds;
- * optional integration with `serde` for serialization.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_core` distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_core` distribution.
diff --git a/crates/core/README.md b/crates/core/README.md
@@ -0,0 +1,21 @@
+# radroots-core
+
+This is the README for `radroots_core`, which provides core value types and
+unit semantics for the `radroots` core libraries.
+
+## Overview
+
+ * stable domain value types for currency, money, quantity, quantity pricing,
+ percent, discounts, and decimals;
+ * unit kinds, dimensions, parsing, and deterministic conversion helpers;
+ * portable core semantics for both `std` and `no_std` builds;
+ * optional integration with `serde` for serialization.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_core` distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_core` distribution.
diff --git a/crates/event/Cargo.toml b/crates/event/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_event"
+name = "radroots-event"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Domain event model for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_event"
-readme = "README"
+documentation = "https://docs.rs/radroots-event"
+readme = "README.md"
+
+[lib]
+name = "radroots_event"
[features]
default = ["std", "serde"]
@@ -55,5 +58,5 @@ serde = { workspace = true, default-features = false, features = [
"derive",
] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/event/README b/crates/event/README
@@ -1,236 +0,0 @@
-# radroots_event
-
-This is the README for `radroots_event`, which provides typed `radroots` event
-models, kinds, and tag conventions for the `radroots` core libraries.
-
-## Overview
-
- * typed content modules for accounts, app data, comments, coops, documents,
- farms, farm workspaces, farm CRDT changes, farm files, groups, auth events,
- jobs, lists, messages, posts, profiles, reactions, trades, and related
- domains;
- * shared event references, pointers, and kind and tag definitions used across
- event-processing code;
- * portable event model semantics for both `std` and `no_std` builds;
- * optional integration with `serde` for serialization.
-
-The Profile module exposes the exclusive strict authored model. It requires a
-non-whitespace, control-free name; its media fields accept only image-typed,
-byte-verified Blossom descriptors; and its NIP-05 identifier type validates
-syntax without making a network identity claim. The legacy read projection is
-not serializable or exported as a DTO. Tolerant reads use
-`RadrootsInboundProfileMetadata` from `radroots_event_codec`.
-
-The post module keeps the legacy mutable `RadrootsPost` model as a compatibility
-read projection only. New root kind-1 publication uses private-field
-`RadrootsAuthoredUpdate`, `RadrootsAuthoredPhotoUpdate`, and
-`RadrootsAuthoredAsk` types. Photo and optional Ask media require nonzero
-dimensions, bounded alt text, approved same-digest fallbacks, and an
-image-typed byte-verified Blossom descriptor. That descriptor state is not an
-upload receipt; BUD-02 completion remains a runtime prerequisite before
-signing.
-
-The shared relay-hint module exposes `RadrootsNostrRelayHint` for NIP-10 Reply
-and NIP-22 Comment references. It is a byte-stable subset of WebSocket URLs:
-exact lowercase `ws://` or `wss://`, visible ASCII, canonical lowercase DNS or
-four-octet IPv4 or bracketed pure-hex RFC 5952 IPv6, canonical optional port
-`1..65535`, and RFC 3986 path-abempty/query syntax with uppercase `%HH`
-escapes. It rejects IDNA and percent-encoded hosts, legacy IPv4, userinfo,
-fragments, controls, backslashes, and normalization-dependent spellings.
-
-The Reply module exposes opaque `RadrootsNip10ReplyReference` and
-`RadrootsAuthoredNip10Reply` types for strict direct and nested kind-1
-authoring. References carry a validated event id, referenced author, and
-optional shared relay hint; construction emits either one marked root or
-distinct marked root and parent references. Relay-hint syntax is not a
-wire-size claim: Reply construction separately enforces the 4,096-byte
-tag-element ceiling. These values prove syntax and authored shape, not target
-existence, target kind, signature, author, or relay availability.
-
-The Comment module implements the strict Radroots
-[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
-kind-`1111` profile. `RadrootsAuthoredNip22Comment` and its opaque event-root,
-address-root, parent, position, and root-kind values admit only kind-`30402`,
-kind-`31922`, or kind-`31923` event or address roots. External `I`/`i`
-references and kind-`1` roots are unsupported. The authored model has no Serde
-construction path.
-
-Canonical authoring emits `E,K,P,e,k,p` for a top-level event root,
-`A,K,P,a,e,k,p` for a top-level address root, or `E,K,P,e,k,p` and
-`A,K,P,e,k,p` for nested event and address roots. Event references always
-contain four elements, including an empty relay position when no hint exists
-and a final author hint. Address and participant references contain two
-elements plus an optional relay; an address root's current-revision `e` tag has
-no author hint. A direct `k` repeats the root kind and a nested `k` is `1111`.
-
-The event-contract registry v7 classifies `radroots.social.comment.v1` as
-`TypedOnly` and `AdmissionOnly`; serialized registry versions `1` through `6`
-are stale. Generic kind-`1111` draft and signing paths cannot claim the typed
-contract. The Comment resource profile limits content to 131072 UTF-8 bytes,
-tags to 1024, total tag elements including names to 4096, each element to 4096
-bytes, aggregate tag bytes to 131072, and compact signed wire JSON to 262144
-bytes. `RadrootsInboundNip22CommentProjection` and
-`RadrootsAdmittedNip22CommentEvent` provide verified inbound projection and
-admission. The three governed Comment operations are
-`social.comment.build_authored_draft`,
-`social.comment.project_verified_event`, and
-`social.comment.verify_and_admit_event`; they and the canonical self-contained
-114-case corpus are owned by `radroots_event_codec` and
-`contracts/conformance`.
-
-The Deletion module implements the effect-free request layer of
-[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md).
-`RadrootsAuthoredNip09DeletionRequest` requires at least one validated event-id
-or replaceable/addressable coordinate target. Event targets carry a
-caller-asserted kind advisory in `0..=65535`; this is metadata rather than
-proof of the target event. Address coordinates accept NIP-01 replaceable kinds
-`0`, `3`, and `10000..=19999` only with an empty identifier, and addressable
-kinds `30000..=39999` with an opaque identifier.
-
-Construction canonicalizes event targets by event id, address targets by
-coordinate, and unique derived kind advisories in ascending order. Duplicate
-normalized targets are rejected. The request enforces the shared content, tag,
-element, aggregate-tag-byte, and compact signed-event budgets before it can
-reach signing. It represents only a kind-`5` protocol request: it does not
-retrieve a target, prove same-author authority, compute an address cutoff,
-suppress content, mutate a store, or make a deletion request itself deletable.
-
-The immutable registry-v7 inventory and addressable-feed-v1 head functions are
-historical protocol inputs to event-store reconciliation v1. The explicit
-`event_contract_registry_v7`, `validate_event_contract_registry_v7`,
-`event_head_candidate_for_nip01_event_v1`, and `select_event_head_v1`
-entrypoints must retain their v7/v1 behavior when a later current registry or
-head algorithm is introduced.
-
-Kind `30402` has a raw, allocation-free marker partition before profile-specific
-tag-shape validation. Presence of `radroots:price_unit` or `radroots:quantity` selects
-the focused FoodAvailability marker family; presence of
-`radroots:primary_bin`, `radroots:bin`, or `radroots:price` selects the richer
-Operational Listing marker family. Focused-only, operational-only, marker-free
-generic NIP-99, and mixed-marker events produce
-`RadrootsClassifiedListingPartition::{FocusedFoodAvailability,
-OperationalListing, GenericNip99, Ambiguous}` respectively. A malformed
-one-element tag still contributes its raw first name, and marker matching is
-case-sensitive. `classify_classified_listing_tags` and the borrowed-slice
-variant inspect neither kind, tag values, nor tag arity.
-
-The FoodAvailability module provides `RadrootsFoodAvailabilityDetails` and
-checked identifier, text, publication timestamp, price, currency, unit,
-quantity, status, image-dimension, and image values. Content contains at least
-one scalar outside Unicode whitespace and U+001C through U+001F, and is bounded
-to 131072 UTF-8 bytes. Identifiers reject whitespace plus Unicode control and
-format characters; title, summary, and location use trimmed, nonempty,
-control-free text bounded to 4096 UTF-8 bytes. Food units are closed to `g`,
-`kg`, `lb`, `oz`, `each`, `dozen`, `bunch`, `punnet`, `bag`, and `basket`.
-Price permits zero; quantity is strictly positive and uses the price unit.
-Image dimensions use two nonzero canonical `u32` decimal values in
-`WIDTHxHEIGHT` form. Details accept at most 64 images, require unique image URLs
-and Blossom digests, and accept only `RadrootsAuthoredImage` values that already
-prove local descriptor-to-byte agreement. Details retain nonzero `published_at`
-and can validate that it is not later than a supplied `created_at`.
-
-These checked details are the exclusive typed input to the focused
-`radroots.food.availability.v1` authoring contract. The event-contract registry
-classifies that kind-`30402` profile as `TypedOnly` and `AdmissionOnly`, so a
-generic unsigned classified listing cannot claim the focused contract. The
-details themselves remain domain values rather than signed events;
-`radroots_event_codec` owns deterministic wire construction, verified inbound
-projection and admission, and strict revision comparison.
-
-An authored image proves local descriptor-to-byte agreement only. Successful
-BUD-02 upload completion and any required raster, retrieval, or availability
-checks remain runtime responsibilities before signing. Neither this domain
-module nor the registry signs, publishes, replicates, or retrieves an event.
-
-The calendar module keeps three different states explicit for NIP-52 kinds
-`31922`, `31923`, `31924`, and `31925`: the complete structural event envelope,
-a tolerant baseline NIP-52 projection, and a strict Radroots-admitted
-projection. The authored types are `RadrootsAuthoredCalendarDateEvent`,
-`RadrootsAuthoredCalendarTimeEvent`, `RadrootsAuthoredCalendar`, and
-`RadrootsAuthoredCalendarEventRsvp`; their inbound counterparts use matching
-`RadrootsParsedNip52*` and `RadrootsAdmitted*` types. Envelope construction
-validates structure, not a matching event id or Schnorr signature. Callers must
-perform those cryptographic checks independently and keep any parsed or
-admitted value bound to the verified envelope and expected kind.
-
-Baseline projections retain the pinned NIP-52 common fields: repeated
-locations, participants, categories, absolute-URI references, kind-`31924`
-calendar-inclusion requests, and deprecated `name` compatibility data in
-addition to `d`, `title`, description, summary, image, and geohash. Date
-events use semantic Gregorian dates and retain observed uppercase-`D` tags as
-uninterpreted extensions. Time events validate unsigned timestamps, exact IANA
-time-zone identifiers, and at least one in-range `D` day while tolerating the
-NIP's non-mandatory start-day and complete-coverage forms. An absent `end_tzid`
-falls back to `start_tzid` when one is present.
-
-Strict authoring and admission add canonical metadata and bounded resource
-rules. Authored common fields include repeated locations, participants,
-categories, references, and calendar-inclusion requests; deprecated `name` is
-not authored. Strict date events reject uppercase `D`, while strict time
-events derive or admit only the complete, ascending sequence of UTC-day
-indices and cover at most 366 days.
-
-Kind `31924` has one calendar-specific contract. It is not decoded or authored
-through either generic NIP-51 list codec. Its NIP-52 detailed description is
-plain-text event content and remains distinct from the optional NIP-51
-`description` tag. It requires one `d` and one `title`, permits optional NIP-51
-`description` and `image` tags, and contains zero or more `a` references to
-kind `31922` or `31923` events. Each reference may have its own relay hint, and
-an empty calendar collection is valid.
-
-Kind `31925` has exactly one required `d`, one `a` event coordinate, and one
-`status`; `e`, `fb`, and `p` are optional singletons. The `a`, `e`, and `p`
-references preserve independent optional relay hints. The `p` tag is an event
-author hint without participant-role semantics, and strict admission requires
-it to match the author in the `a` coordinate. An inbound declined RSVP may
-retain an observed `fb` for diagnostics but exposes no effective free/busy
-state. Authored declined RSVPs cannot carry `fb`.
-
-Strict collection and RSVP identifiers use exactly 22 unpadded base64url
-characters representing 128 bits. The type proves only syntax; the runtime is
-responsible for generating a fresh value for each new identity. Parsing or
-admission does not prove reference existence, revision correspondence, RSVP
-authority, upload completion, or network availability.
-
-Inbound images begin as unverified absolute URIs. Strict admission, including
-kind-`31924` collection images, requires a structural Blossom hash-path URL but
-makes no byte or network claim. Authored calendar images require the shared
-`image/*`, byte-verified Blossom descriptor. That state is not an upload
-receipt: a runtime must require successful BUD-02 upload completion and a
-bounded retrievability check before signing or publishing a media-bearing
-event.
-
-## Field Event Boundary
-
-`radroots_event` includes the public event-layer models needed by Field-style
-farming operations:
-
- * workspace manifests for discovering the farm group, relay set, media servers,
- and supported event kinds;
- * CRDT change envelopes for operation documents such as tasks, work sessions,
- harvest records, and approvals;
- * farm file metadata events for media attached to farm documents;
- * NIP-42 relay auth and NIP-98 HTTP auth payload models;
- * NIP-29 group metadata, member lists, roles, invites, joins, leaves, and user
- operations for the supported `9000`, `9001`, `9002`, `9005`, `9007`, `9008`,
- `9009`, `9021`, `9022`, `39000`, `39001`, `39002`, and `39003` subset.
-
-The NIP-29 group surface uses bare metadata marker tags such as `private`,
-`restricted`, `hidden`, and `closed`, `supported_kinds` declarations, and
-`code` tags for invite and join flows. User management and moderation events
-preserve optional reason content. LiveKit room metadata and live participant
-state are not part of this crate's current group event subset.
-
-Task records, work sessions, harvest records, approvals, and similar Field
-business objects are CRDT document semantics carried by
-`RadrootsFarmCrdtChange`. They are not separate `rr-rs` event families and this
-crate does not enforce private Field workflow authorization.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_event` distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_event` distribution.
diff --git a/crates/event/README.md b/crates/event/README.md
@@ -0,0 +1,236 @@
+# radroots-event
+
+This is the README for `radroots_event`, which provides typed `radroots` event
+models, kinds, and tag conventions for the `radroots` core libraries.
+
+## Overview
+
+ * typed content modules for accounts, app data, comments, coops, documents,
+ farms, farm workspaces, farm CRDT changes, farm files, groups, auth events,
+ jobs, lists, messages, posts, profiles, reactions, trades, and related
+ domains;
+ * shared event references, pointers, and kind and tag definitions used across
+ event-processing code;
+ * portable event model semantics for both `std` and `no_std` builds;
+ * optional integration with `serde` for serialization.
+
+The Profile module exposes the exclusive strict authored model. It requires a
+non-whitespace, control-free name; its media fields accept only image-typed,
+byte-verified Blossom descriptors; and its NIP-05 identifier type validates
+syntax without making a network identity claim. The legacy read projection is
+not serializable or exported as a DTO. Tolerant reads use
+`RadrootsInboundProfileMetadata` from `radroots_event_codec`.
+
+The post module keeps the legacy mutable `RadrootsPost` model as a compatibility
+read projection only. New root kind-1 publication uses private-field
+`RadrootsAuthoredUpdate`, `RadrootsAuthoredPhotoUpdate`, and
+`RadrootsAuthoredAsk` types. Photo and optional Ask media require nonzero
+dimensions, bounded alt text, approved same-digest fallbacks, and an
+image-typed byte-verified Blossom descriptor. That descriptor state is not an
+upload receipt; BUD-02 completion remains a runtime prerequisite before
+signing.
+
+The shared relay-hint module exposes `RadrootsNostrRelayHint` for NIP-10 Reply
+and NIP-22 Comment references. It is a byte-stable subset of WebSocket URLs:
+exact lowercase `ws://` or `wss://`, visible ASCII, canonical lowercase DNS or
+four-octet IPv4 or bracketed pure-hex RFC 5952 IPv6, canonical optional port
+`1..65535`, and RFC 3986 path-abempty/query syntax with uppercase `%HH`
+escapes. It rejects IDNA and percent-encoded hosts, legacy IPv4, userinfo,
+fragments, controls, backslashes, and normalization-dependent spellings.
+
+The Reply module exposes opaque `RadrootsNip10ReplyReference` and
+`RadrootsAuthoredNip10Reply` types for strict direct and nested kind-1
+authoring. References carry a validated event id, referenced author, and
+optional shared relay hint; construction emits either one marked root or
+distinct marked root and parent references. Relay-hint syntax is not a
+wire-size claim: Reply construction separately enforces the 4,096-byte
+tag-element ceiling. These values prove syntax and authored shape, not target
+existence, target kind, signature, author, or relay availability.
+
+The Comment module implements the strict Radroots
+[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
+kind-`1111` profile. `RadrootsAuthoredNip22Comment` and its opaque event-root,
+address-root, parent, position, and root-kind values admit only kind-`30402`,
+kind-`31922`, or kind-`31923` event or address roots. External `I`/`i`
+references and kind-`1` roots are unsupported. The authored model has no Serde
+construction path.
+
+Canonical authoring emits `E,K,P,e,k,p` for a top-level event root,
+`A,K,P,a,e,k,p` for a top-level address root, or `E,K,P,e,k,p` and
+`A,K,P,e,k,p` for nested event and address roots. Event references always
+contain four elements, including an empty relay position when no hint exists
+and a final author hint. Address and participant references contain two
+elements plus an optional relay; an address root's current-revision `e` tag has
+no author hint. A direct `k` repeats the root kind and a nested `k` is `1111`.
+
+The event-contract registry v7 classifies `radroots.social.comment.v1` as
+`TypedOnly` and `AdmissionOnly`; serialized registry versions `1` through `6`
+are stale. Generic kind-`1111` draft and signing paths cannot claim the typed
+contract. The Comment resource profile limits content to 131072 UTF-8 bytes,
+tags to 1024, total tag elements including names to 4096, each element to 4096
+bytes, aggregate tag bytes to 131072, and compact signed wire JSON to 262144
+bytes. `RadrootsInboundNip22CommentProjection` and
+`RadrootsAdmittedNip22CommentEvent` provide verified inbound projection and
+admission. The three governed Comment operations are
+`social.comment.build_authored_draft`,
+`social.comment.project_verified_event`, and
+`social.comment.verify_and_admit_event`; they and the canonical self-contained
+114-case corpus are owned by `radroots_event_codec` and
+`contracts/conformance`.
+
+The Deletion module implements the effect-free request layer of
+[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md).
+`RadrootsAuthoredNip09DeletionRequest` requires at least one validated event-id
+or replaceable/addressable coordinate target. Event targets carry a
+caller-asserted kind advisory in `0..=65535`; this is metadata rather than
+proof of the target event. Address coordinates accept NIP-01 replaceable kinds
+`0`, `3`, and `10000..=19999` only with an empty identifier, and addressable
+kinds `30000..=39999` with an opaque identifier.
+
+Construction canonicalizes event targets by event id, address targets by
+coordinate, and unique derived kind advisories in ascending order. Duplicate
+normalized targets are rejected. The request enforces the shared content, tag,
+element, aggregate-tag-byte, and compact signed-event budgets before it can
+reach signing. It represents only a kind-`5` protocol request: it does not
+retrieve a target, prove same-author authority, compute an address cutoff,
+suppress content, mutate a store, or make a deletion request itself deletable.
+
+The immutable registry-v7 inventory and addressable-feed-v1 head functions are
+historical protocol inputs to event-store reconciliation v1. The explicit
+`event_contract_registry_v7`, `validate_event_contract_registry_v7`,
+`event_head_candidate_for_nip01_event_v1`, and `select_event_head_v1`
+entrypoints must retain their v7/v1 behavior when a later current registry or
+head algorithm is introduced.
+
+Kind `30402` has a raw, allocation-free marker partition before profile-specific
+tag-shape validation. Presence of `radroots:price_unit` or `radroots:quantity` selects
+the focused FoodAvailability marker family; presence of
+`radroots:primary_bin`, `radroots:bin`, or `radroots:price` selects the richer
+Operational Listing marker family. Focused-only, operational-only, marker-free
+generic NIP-99, and mixed-marker events produce
+`RadrootsClassifiedListingPartition::{FocusedFoodAvailability,
+OperationalListing, GenericNip99, Ambiguous}` respectively. A malformed
+one-element tag still contributes its raw first name, and marker matching is
+case-sensitive. `classify_classified_listing_tags` and the borrowed-slice
+variant inspect neither kind, tag values, nor tag arity.
+
+The FoodAvailability module provides `RadrootsFoodAvailabilityDetails` and
+checked identifier, text, publication timestamp, price, currency, unit,
+quantity, status, image-dimension, and image values. Content contains at least
+one scalar outside Unicode whitespace and U+001C through U+001F, and is bounded
+to 131072 UTF-8 bytes. Identifiers reject whitespace plus Unicode control and
+format characters; title, summary, and location use trimmed, nonempty,
+control-free text bounded to 4096 UTF-8 bytes. Food units are closed to `g`,
+`kg`, `lb`, `oz`, `each`, `dozen`, `bunch`, `punnet`, `bag`, and `basket`.
+Price permits zero; quantity is strictly positive and uses the price unit.
+Image dimensions use two nonzero canonical `u32` decimal values in
+`WIDTHxHEIGHT` form. Details accept at most 64 images, require unique image URLs
+and Blossom digests, and accept only `RadrootsAuthoredImage` values that already
+prove local descriptor-to-byte agreement. Details retain nonzero `published_at`
+and can validate that it is not later than a supplied `created_at`.
+
+These checked details are the exclusive typed input to the focused
+`radroots.food.availability.v1` authoring contract. The event-contract registry
+classifies that kind-`30402` profile as `TypedOnly` and `AdmissionOnly`, so a
+generic unsigned classified listing cannot claim the focused contract. The
+details themselves remain domain values rather than signed events;
+`radroots_event_codec` owns deterministic wire construction, verified inbound
+projection and admission, and strict revision comparison.
+
+An authored image proves local descriptor-to-byte agreement only. Successful
+BUD-02 upload completion and any required raster, retrieval, or availability
+checks remain runtime responsibilities before signing. Neither this domain
+module nor the registry signs, publishes, replicates, or retrieves an event.
+
+The calendar module keeps three different states explicit for NIP-52 kinds
+`31922`, `31923`, `31924`, and `31925`: the complete structural event envelope,
+a tolerant baseline NIP-52 projection, and a strict Radroots-admitted
+projection. The authored types are `RadrootsAuthoredCalendarDateEvent`,
+`RadrootsAuthoredCalendarTimeEvent`, `RadrootsAuthoredCalendar`, and
+`RadrootsAuthoredCalendarEventRsvp`; their inbound counterparts use matching
+`RadrootsParsedNip52*` and `RadrootsAdmitted*` types. Envelope construction
+validates structure, not a matching event id or Schnorr signature. Callers must
+perform those cryptographic checks independently and keep any parsed or
+admitted value bound to the verified envelope and expected kind.
+
+Baseline projections retain the pinned NIP-52 common fields: repeated
+locations, participants, categories, absolute-URI references, kind-`31924`
+calendar-inclusion requests, and deprecated `name` compatibility data in
+addition to `d`, `title`, description, summary, image, and geohash. Date
+events use semantic Gregorian dates and retain observed uppercase-`D` tags as
+uninterpreted extensions. Time events validate unsigned timestamps, exact IANA
+time-zone identifiers, and at least one in-range `D` day while tolerating the
+NIP's non-mandatory start-day and complete-coverage forms. An absent `end_tzid`
+falls back to `start_tzid` when one is present.
+
+Strict authoring and admission add canonical metadata and bounded resource
+rules. Authored common fields include repeated locations, participants,
+categories, references, and calendar-inclusion requests; deprecated `name` is
+not authored. Strict date events reject uppercase `D`, while strict time
+events derive or admit only the complete, ascending sequence of UTC-day
+indices and cover at most 366 days.
+
+Kind `31924` has one calendar-specific contract. It is not decoded or authored
+through either generic NIP-51 list codec. Its NIP-52 detailed description is
+plain-text event content and remains distinct from the optional NIP-51
+`description` tag. It requires one `d` and one `title`, permits optional NIP-51
+`description` and `image` tags, and contains zero or more `a` references to
+kind `31922` or `31923` events. Each reference may have its own relay hint, and
+an empty calendar collection is valid.
+
+Kind `31925` has exactly one required `d`, one `a` event coordinate, and one
+`status`; `e`, `fb`, and `p` are optional singletons. The `a`, `e`, and `p`
+references preserve independent optional relay hints. The `p` tag is an event
+author hint without participant-role semantics, and strict admission requires
+it to match the author in the `a` coordinate. An inbound declined RSVP may
+retain an observed `fb` for diagnostics but exposes no effective free/busy
+state. Authored declined RSVPs cannot carry `fb`.
+
+Strict collection and RSVP identifiers use exactly 22 unpadded base64url
+characters representing 128 bits. The type proves only syntax; the runtime is
+responsible for generating a fresh value for each new identity. Parsing or
+admission does not prove reference existence, revision correspondence, RSVP
+authority, upload completion, or network availability.
+
+Inbound images begin as unverified absolute URIs. Strict admission, including
+kind-`31924` collection images, requires a structural Blossom hash-path URL but
+makes no byte or network claim. Authored calendar images require the shared
+`image/*`, byte-verified Blossom descriptor. That state is not an upload
+receipt: a runtime must require successful BUD-02 upload completion and a
+bounded retrievability check before signing or publishing a media-bearing
+event.
+
+## Field Event Boundary
+
+`radroots_event` includes the public event-layer models needed by Field-style
+farming operations:
+
+ * workspace manifests for discovering the farm group, relay set, media servers,
+ and supported event kinds;
+ * CRDT change envelopes for operation documents such as tasks, work sessions,
+ harvest records, and approvals;
+ * farm file metadata events for media attached to farm documents;
+ * NIP-42 relay auth and NIP-98 HTTP auth payload models;
+ * NIP-29 group metadata, member lists, roles, invites, joins, leaves, and user
+ operations for the supported `9000`, `9001`, `9002`, `9005`, `9007`, `9008`,
+ `9009`, `9021`, `9022`, `39000`, `39001`, `39002`, and `39003` subset.
+
+The NIP-29 group surface uses bare metadata marker tags such as `private`,
+`restricted`, `hidden`, and `closed`, `supported_kinds` declarations, and
+`code` tags for invite and join flows. User management and moderation events
+preserve optional reason content. LiveKit room metadata and live participant
+state are not part of this crate's current group event subset.
+
+Task records, work sessions, harvest records, approvals, and similar Field
+business objects are CRDT document semantics carried by
+`RadrootsFarmCrdtChange`. They are not separate `rr-rs` event families and this
+crate does not enforce private Field workflow authorization.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_event` distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_event` distribution.
diff --git a/crates/event_codec/Cargo.toml b/crates/event_codec/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_event_codec"
+name = "radroots-event-codec"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Event codec layer for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_event_codec"
-readme = "README"
+documentation = "https://docs.rs/radroots-event-codec"
+readme = "README.md"
+
+[lib]
+name = "radroots_event_codec"
[features]
default = ["std"]
@@ -46,5 +49,5 @@ radroots_blossom = { workspace = true, default-features = false, features = [
radroots_test_fixtures = { workspace = true }
serde_json = { workspace = true, features = ["std"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/event_codec/README b/crates/event_codec/README
@@ -1,342 +0,0 @@
-# radroots_event_codec
-
-This is the README for `radroots_event_codec`, which provides canonical event
-codecs and tag builders for the `radroots` core libraries.
-
-## Overview
-
- * canonical decoders for event content, tags, job envelopes, profiles, Field
- event envelopes, NIP-29 group events, and wire representations;
- * tag builder helpers for the same event families exposed by
- `radroots_event`;
- * parsed view and error types for codec-driven validation and routing;
- * optional `serde_json` and `radroots_nostr` integration for JSON and wire
- interop.
-
-With the optional `serde_json` feature, the Profile codec exposes separate
-strict authored and tolerant inbound operations. Strict authoring emits
-bounded, deterministic kind-`0` metadata with empty tags and image-typed,
-byte-verified Blossom media references. Tolerant inbound parsing retains raw
-and residual fields and never upgrades observed media or NIP-05 syntax into
-network verification. Its content parser accepts only bounded kind-`0` content
-from an event whose identifier and signature the caller has already verified.
-With `serde_json,nostr`, `profile::admission::verify_and_admit_profile_event`
-provides that combined boundary and returns metadata bound to a non-forgeable
-signature-verified envelope. Standard tagless kind-`0` events are accepted; no
-Radroots marker is required.
-
-General NIP-01 identifier and Schnorr verification lives in `verification` and
-is independent of the optional `knowledge` decoder. The `nostr` feature exposes
-`RadrootsSignatureVerifiedEvent` and rejects event kinds above `u16::MAX`
-instead of truncating them.
-
-With `serde_json`, `admission::admit_verified_event` is the current central
-contract boundary over an already verified event. It preserves typed admitted
-Profile, root Post, Reply, Comment, DeletionRequest, and FoodAvailability
-values, while other registered events must pass complete registry shape
-validation and return `ContractValidated`. Kind `1` is tested as a root Post
-before the exact thread-excluded candidate may be promoted to Reply. Kind
-`30402` is partitioned as focused Food, Operational Listing, generic NIP-99,
-or ambiguous before any profile validation; a valid excluded Operational
-Listing falls back to the registry, while generic and mixed-marker candidates
-remain distinct failures.
-
-`admission::admit_verified_event_registry_v7` is the immutable historical
-admission graph used by event-store reconciliation v1. It returns the closed
-registry-v7 admitted, unsupported, invalid, or internal-defect decision needed
-for persisted facts without depending on richer current admission enums.
-Later registry revisions must add a new versioned entrypoint rather than
-changing this behavior. Neither admission operation accepts an unverified
-envelope, signs or publishes events, selects event heads, evaluates deletion
-effects, or mutates storage.
-
-The post codec exposes deterministic authored wire builders and a separate
-verified-event projection. Update emits no profile tags, PhotoUpdate emits
-strict ordered NIP-92 `imeta`, and Ask emits one exact `t=radroots-ask` marker
-before optional strict media. The former mutable post encoder and generic tag
-builder are removed. Inbound projection preserves ordinary kind-1 reads,
-excludes any `e`-tagged reply before product classification, and applies Ask,
-PhotoUpdate, Update precedence. Unknown media fields and repeatable fallbacks
-remain ordered; malformed media becomes diagnostic Update unless a valid Ask
-marker takes precedence. Structural inbound URLs remain unverified and no
-network retrieval occurs.
-
-The Reply codec deterministically emits strict marked direct and nested NIP-10
-wire parts from `RadrootsAuthoredNip10Reply`. Its inbound boundary projects
-only signature-and-id verified kind-1 envelopes, accepts marked and deprecated
-positional thread anchors, retains valid supplemental citations, and reports
-malformed advisory relay, author, participant, and citation metadata through
-ordered diagnostics. Root and parent ambiguity or malformed hard anchors
-remain projection failures.
-
-Authored and inbound NIP-10 Reply and NIP-22 Comment relay metadata uses the
-shared `RadrootsNostrRelayHint` profile: exact lowercase `ws://` or `wss://`,
-visible ASCII, canonical lowercase DNS or four-octet IPv4 or bracketed
-pure-hex RFC 5952 IPv6, canonical optional port `1..65535`, and RFC 3986
-path-abempty/query syntax with uppercase `%HH` escapes. IDNA or
-percent-encoded hosts, legacy IPv4, userinfo, fragments, controls,
-backslashes, and normalization-dependent forms are rejected. Inbound
-projection ignores an invalid advisory hint while preserving its exact raw tag
-in ordered diagnostics. Relay syntax remains independent from each event
-profile's 4,096-byte tag-element budget.
-
-The Comment codec implements the strict Radroots
-[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
-kind-`1111` profile for event or address roots of kind `30402`, `31922`, or
-`31923`. It rejects external `I`/`i` authority and kind-`1` roots.
-`authored_nip22_comment_to_wire_parts` emits only canonical order:
-`E,K,P,e,k,p` for a top-level event, `A,K,P,a,e,k,p` for a top-level
-address, and `E,K,P,e,k,p` or `A,K,P,e,k,p` for a nested event or address.
-Event `E` and parent or direct `e` references always have four elements with a
-relay slot and author hint. Address and participant references have two
-elements plus an optional relay; a top-level address Comment's revision `e`
-has no author hint. Direct `k` repeats the root kind and nested `k` is exactly
-`1111`.
-
-`project_verified_nip22_comment_event` accepts only a
-`RadrootsSignatureVerifiedEvent`. It resolves the `E`/`A`, `K`, `P`, `e`/`a`,
-`k`, and selected `p` authority independently of tag order and treats invalid
-cardinality, shapes, noncanonical kinds, unsupported roots, coordinate
-conflicts, and valid-but-conflicting author hints as hard errors. It preserves
-exact raw tags, supplemental unknown and `q` tags, and distinct unselected
-lowercase `p` mentions. Inbound NIP-22 reference event IDs, public keys, and
-coordinate-author hex accept either ASCII hex case; typed values normalize to
-lowercase while raw tags retain the original spelling. Malformed advisory relay
-and participant metadata produce ordered diagnostics without weakening the
-required authority.
-`verify_and_admit_nip22_comment_event` first verifies the NIP-01 id and Schnorr
-signature, then binds that envelope to
-`RadrootsInboundNip22CommentProjection` in
-`RadrootsAdmittedNip22CommentEvent`; it does not verify any referenced event or
-relay.
-
-The exact Comment operation namespace is
-`social.comment.build_authored_draft`,
-`social.comment.project_verified_event`, and
-`social.comment.verify_and_admit_event`. Registry v7 makes
-`radroots.social.comment.v1` `TypedOnly` for authoring and `AdmissionOnly` for
-matching; registry versions `1` through `6` are stale. Content is limited to
-131072 UTF-8 bytes, a Comment to 1024 tags, all tags to 4096 elements including
-tag names, each element to 4096 bytes, aggregate tag bytes to 131072, and
-compact signed wire JSON to 262144 bytes.
-
-The canonical 114-case self-contained Comment corpus is
-`contracts/conformance/vectors/comment/verified_profile.v1.json`; the packaged
-fixture is byte-identical. Projection and admission cases contain fixed signed
-event JSON, while authored cases contain explicit checked inputs and complete
-wire expectations. The runner consumes these records directly and covers
-valid shapes, diagnostics, exact and over-limit budgets, stable error
-precedence, and NIP-01 admission without secret keys, mutation recipes, or
-ambient state.
-
-The Deletion codec implements the request and pure suppression-evaluation
-boundaries of
-[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md).
-`authored_nip09_deletion_request_to_wire_parts` emits canonical two-element
-`e` targets, then `a` targets, then the complete unique ascending set of `k`
-kind advisories. `project_verified_nip09_deletion_request_event` accepts only
-an id-and-signature-verified kind-`5` envelope. It retains exact raw tags,
-deduplicates normalized targets with first-seen provenance, sorts the typed
-target views, and treats malformed `e` or `a` targets as hard errors. Unknown
-tags and trailing target elements remain observable. Advisory `k` shape,
-numeric spelling, duplicates, and address-only conflicts produce stable
-ordered diagnostics; event-target kind correspondence remains unprovable at
-this boundary.
-
-`verify_and_admit_nip09_deletion_request_event` binds the projection to the
-verified request envelope. It does not authorize or apply a deletion, look up
-a target, compare authors, compute an address cutoff, suppress an event, or
-mutate storage.
-
-`evaluate_nip09_suppression` is a separate pure operation over one
-`RadrootsSignatureVerifiedEvent` candidate and admitted deletion requests. It
-returns an explicit `RadrootsNip09SuppressionDecision` with canonical evidence
-without changing any input or touching a store. Kind `5` is immune. Other
-candidates require the request author to equal the candidate author. An exact
-event-id target applies independently of timestamps; a canonical address
-target applies through the inclusive maximum qualifying request timestamp, so
-a later replacement is not suppressed. Advisory `k` values do not affect the
-result, and request order cannot affect the decision.
-
-The reconciliation-v1 entrypoints
-`project_verified_nip09_deletion_request_event_v1`,
-`admit_verified_nip09_deletion_request_event_v1`, and
-`evaluate_nip09_suppression_v1` freeze the exact request and suppression
-behavior consumed by event-store schema version 2. Current compatibility
-entrypoints delegate to these functions; a future algorithm revision must add
-new versioned functions.
-
-The exact operation namespace is
-`social.deletion_request.build_authored_draft`,
-`social.deletion_request.project_verified_event`,
-`social.deletion_request.verify_and_admit_event`, and
-`social.deletion_request.evaluate_suppression`. Registry v7 makes
-`radroots.social.deletion_request.v1` `TypedOnly` for authoring and
-`AdmissionOnly` for matching, with generic kind-`5` authoring and publication
-reserved.
-
-The canonical self-contained 80-case corpus is
-`contracts/conformance/vectors/deletion/verified_profile.v1.json`; the
-packaged fixture is byte-identical. Signed inputs are complete compact event
-JSON, expected projections contain no effect-authority output, and executable
-cases cover canonical authoring, tolerant verified projection, diagnostics,
-exact and over-limit resource budgets, precedence, and admission without
-shipping keys, seeds, generators, or mutation recipes.
-
-The separate
-`contracts/conformance/vectors/deletion/suppression.v1.json` corpus and its
-byte-identical packaged fixture execute the evaluator's same-author, direct
-event, inclusive address-cutoff, later-replacement, pre-target request,
-kind-`5` immunity, and deterministic evidence behavior. These vectors govern a
-decision value only and claim no persistent store effect.
-
-The FoodAvailability codec owns four boundaries for the focused
-`radroots.food.availability.v1` kind-`30402` profile. Strict details plus
-`created_at` produce bounded unsigned wire parts whose tag order is exactly
-`d`, `title`, `summary`, `published_at`, `location`, `price`,
-`radroots:price_unit`, optional `radroots:quantity`, `status`, and repeated
-`image`. Content and decoded tags remain bounded, and compact signed-event size
-is checked after exact JSON escaping. Authored images already prove local
-Blossom descriptor-to-byte agreement, but not upload completion or network
-availability.
-
-Verified inbound projection partitions raw tag names before validation.
-Focused-only candidates are projected, Operational Listing and marker-free
-generic NIP-99 candidates are explicit exclusions, and mixed-marker candidates
-fail as ambiguous. Focused core fields are required; decimal and currency
-spellings are normalized within their wire bounds; optional unowned NIP-99
-tags are ignored; and tags purporting to add commerce, fulfillment,
-provenance, or operational capabilities fail closed. Image observations remain
-ordered structural HTTP(S) data. At most 64 are retained, with stable ordered
-diagnostics for overflow, shape, URL, dimensions, duplicate URL, and duplicate
-Blossom digest. An image diagnostic does not invalidate an otherwise complete
-focused core and never upgrades observed media to an authored typestate.
-
-`verify_and_admit_food_availability_event` first verifies the NIP-01 id and
-Schnorr signature, then binds either the focused projection or an explicit
-non-focused exclusion to that verified envelope. Revision validation accepts
-two independently verified events, requires both to satisfy the exact authored
-wire profile, preserves kind, author, `d`, and `published_at`, and applies the
-NIP-01 newer-event rule: later `created_at`, then lower event id at equal time.
-Successful combined verification and admission uses the optional `nostr`
-feature; without it, signature verification returns the stable unavailable
-error rather than admitting an event. The packaged conformance runner enables
-`serde_json,nostr` for signed JSON event cases.
-These codecs do not sign, publish, replicate, upload, retrieve, or provide
-client behavior.
-
-The NIP-52 calendar codecs expose a deliberate three-stage boundary for kinds
-`31922`, `31923`, `31924`, and `31925`:
-
-1. bounded structural wire or envelope parsing preserves the complete event;
-2. the kind-specific date event, time event, calendar collection, or RSVP
- parser produces a tolerant `RadrootsParsedNip52*` projection and rejects a
- mismatched kind;
-3. the corresponding `admit_radroots_*` operation applies the stricter
- Radroots profile and returns a non-interchangeable admitted type.
-
-Structural envelope construction, the baseline parsers, and the admission
-functions do not verify a Schnorr signature. Before treating inbound data as
-accepted, callers must recompute and compare its NIP-01 id, verify its
-signature against the author, use the parser for its expected kind, and keep
-the typed result bound to that verified envelope. The calendar
-`*_parsed_from_event` convenience functions remain structural wrappers; their
-names do not imply cryptographic verification.
-
-The baseline parser projects repeated NIP-52 `location`, participant `p`,
-category `t`, absolute-URI reference `r`, and kind-`31924` inclusion-request
-`a` tags, plus deprecated `name` compatibility data. It accepts arbitrary
-absolute image URIs. Kind `31922` requires semantic Gregorian dates and retains
-uppercase-`D` tags as uninterpreted extensions. Kind `31923` requires at least
-one in-range `D` day and exact IANA time-zone identifiers. NIP-52 makes
-multi-day coverage a `SHOULD`, so the baseline requires neither the start-day
-index nor complete coverage; duplicate, unordered, and leading-zero numeric
-observations are additionally preserved as parser tolerance and are never
-canonical authored output. An omitted
-`end_tzid` uses `start_tzid` when present.
-
-Strict date admission rejects every uppercase-`D` extension. Strict time
-admission requires canonical decimal timestamps and the exact ascending
-uppercase-`D` sequence for the exclusive interval, bounded to 366 covered
-days. Authored codecs derive that sequence and emit unsigned
-`RadrootsNip01EventWireParts`; signing and transport stay at the owning runtime
-boundary.
-
-Kind `31924` is handled only by the calendar-specific codec. Generic NIP-51
-taxonomy may recognize it as a calendar list, but the generic list-set decoder
-and authoring path and the broader generic list codec reject it. Calendar collection content is the bounded
-plain-text NIP-52 detailed description; it is not substituted from or collapsed
-into the optional NIP-51 `description` tag. The parser requires exactly one
-`d` and `title`, accepts optional singleton `description` and `image` tags, and
-accepts zero or more exact `a` references to kind `31922` or `31923` events.
-An empty collection is valid, and each event reference preserves its own
-optional relay hint. Singleton collection tags have exactly two elements; an
-`a` tag has exactly two elements plus an optional relay element.
-
-Kind `31925` has bounded optional note content and exact singleton
-cardinality: required `d`, `a`, and `status`, with optional `e`, `fb`, and `p`.
-The `a`, `e`, and `p` values preserve independent optional relay hints. The
-`p` tag is parsed as an event-author hint without participant-role semantics;
-strict admission requires it to match the author in the `a` coordinate.
-`d`, `status`, and `fb` tags have exactly two elements, while `a`, `e`, and `p`
-have exactly two elements plus an optional relay element.
-Tolerant parsing preserves `fb` observed on a declined RSVP, but parsed and
-admitted values expose no effective free/busy state for it. Authored declined
-RSVPs never emit `fb`.
-
-Strict collection and RSVP authoring and admission require a canonical
-22-character unpadded base64url 128-bit identifier and canonical reference
-spellings. Identifier syntax does not establish runtime uniqueness. Parsing
-valid identifiers, event ids, author keys, coordinates, and relay hints does
-not establish reference existence, revision correspondence, RSVP authority,
-or relay availability.
-
-Strict inbound admission, including kind-`31924` collection images, upgrades
-an image only to a structural Blossom hash-path URL. It does not prove
-reference approval, bytes, upload completion, or retrievability. Strict
-authored image inputs are approved, byte-verified Blossom `image/*`
-descriptors, but are still not upload receipts. A publishing runtime must
-require successful BUD-02 completion and a bounded retrievability check before
-signing or publishing media-bearing wire parts.
-
-## Field Event Codecs
-
-The Field codec surface validates the public Nostr event substrate exposed by
-`radroots_event`:
-
- * workspace manifests use kind `30078`, JSON content, a schema marker, `d` and
- `h` routing tags, owner tags, relay references, and Field-supported kind
- declarations;
- * CRDT changes use kind `78`, JSON content, `h`, `d`, `a`, optional author `p`,
- and semantic `t` tags, with base64url change payload validation;
- * farm files use kind `1063`, NIP-94-compatible metadata tags, a workspace
- address, a farm group id, an owner document tag, lowercase SHA-256 hashes,
- and optional caption text as content;
- * NIP-42 relay auth and NIP-98 HTTP auth events require empty content and the
- auth tags required by their protocols; NIP-98 `u`, `method`, and `payload`
- tags are parsed as singleton security tags and duplicate occurrences fail
- closed;
- * NIP-29 group codecs preserve the protocol distinction between `h`-routed
- group operations and `d`-routed addressable group state for the supported
- `9000`, `9001`, `9002`, `9005`, `9007`, `9008`, `9009`, `9021`, `9022`,
- `39000`, `39001`, `39002`, and `39003` subset.
-
-These codecs validate event shape, routing tags, hashes, and payload encoding.
-They do not validate private Field task, work-session, harvest, approval, or
-authorization semantics; those remain application and CRDT document concerns.
-The group codecs use bare metadata marker tags such as `private`, `restricted`,
-`hidden`, and `closed`, `supported_kinds` declarations, and `code` tags for
-invite and join flows. They preserve optional reason content on user management
-and moderation events. LiveKit room metadata and live participant state are
-deferred. SDK-owned wasm bindings expose deterministic JSON tag builders for the
-same Field and NIP-29 families.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_event_codec`
-distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_event_codec` distribution.
diff --git a/crates/event_codec/README.md b/crates/event_codec/README.md
@@ -0,0 +1,342 @@
+# radroots-event-codec
+
+This is the README for `radroots_event_codec`, which provides canonical event
+codecs and tag builders for the `radroots` core libraries.
+
+## Overview
+
+ * canonical decoders for event content, tags, job envelopes, profiles, Field
+ event envelopes, NIP-29 group events, and wire representations;
+ * tag builder helpers for the same event families exposed by
+ `radroots_event`;
+ * parsed view and error types for codec-driven validation and routing;
+ * optional `serde_json` and `radroots_nostr` integration for JSON and wire
+ interop.
+
+With the optional `serde_json` feature, the Profile codec exposes separate
+strict authored and tolerant inbound operations. Strict authoring emits
+bounded, deterministic kind-`0` metadata with empty tags and image-typed,
+byte-verified Blossom media references. Tolerant inbound parsing retains raw
+and residual fields and never upgrades observed media or NIP-05 syntax into
+network verification. Its content parser accepts only bounded kind-`0` content
+from an event whose identifier and signature the caller has already verified.
+With `serde_json,nostr`, `profile::admission::verify_and_admit_profile_event`
+provides that combined boundary and returns metadata bound to a non-forgeable
+signature-verified envelope. Standard tagless kind-`0` events are accepted; no
+Radroots marker is required.
+
+General NIP-01 identifier and Schnorr verification lives in `verification` and
+is independent of the optional `knowledge` decoder. The `nostr` feature exposes
+`RadrootsSignatureVerifiedEvent` and rejects event kinds above `u16::MAX`
+instead of truncating them.
+
+With `serde_json`, `admission::admit_verified_event` is the current central
+contract boundary over an already verified event. It preserves typed admitted
+Profile, root Post, Reply, Comment, DeletionRequest, and FoodAvailability
+values, while other registered events must pass complete registry shape
+validation and return `ContractValidated`. Kind `1` is tested as a root Post
+before the exact thread-excluded candidate may be promoted to Reply. Kind
+`30402` is partitioned as focused Food, Operational Listing, generic NIP-99,
+or ambiguous before any profile validation; a valid excluded Operational
+Listing falls back to the registry, while generic and mixed-marker candidates
+remain distinct failures.
+
+`admission::admit_verified_event_registry_v7` is the immutable historical
+admission graph used by event-store reconciliation v1. It returns the closed
+registry-v7 admitted, unsupported, invalid, or internal-defect decision needed
+for persisted facts without depending on richer current admission enums.
+Later registry revisions must add a new versioned entrypoint rather than
+changing this behavior. Neither admission operation accepts an unverified
+envelope, signs or publishes events, selects event heads, evaluates deletion
+effects, or mutates storage.
+
+The post codec exposes deterministic authored wire builders and a separate
+verified-event projection. Update emits no profile tags, PhotoUpdate emits
+strict ordered NIP-92 `imeta`, and Ask emits one exact `t=radroots-ask` marker
+before optional strict media. The former mutable post encoder and generic tag
+builder are removed. Inbound projection preserves ordinary kind-1 reads,
+excludes any `e`-tagged reply before product classification, and applies Ask,
+PhotoUpdate, Update precedence. Unknown media fields and repeatable fallbacks
+remain ordered; malformed media becomes diagnostic Update unless a valid Ask
+marker takes precedence. Structural inbound URLs remain unverified and no
+network retrieval occurs.
+
+The Reply codec deterministically emits strict marked direct and nested NIP-10
+wire parts from `RadrootsAuthoredNip10Reply`. Its inbound boundary projects
+only signature-and-id verified kind-1 envelopes, accepts marked and deprecated
+positional thread anchors, retains valid supplemental citations, and reports
+malformed advisory relay, author, participant, and citation metadata through
+ordered diagnostics. Root and parent ambiguity or malformed hard anchors
+remain projection failures.
+
+Authored and inbound NIP-10 Reply and NIP-22 Comment relay metadata uses the
+shared `RadrootsNostrRelayHint` profile: exact lowercase `ws://` or `wss://`,
+visible ASCII, canonical lowercase DNS or four-octet IPv4 or bracketed
+pure-hex RFC 5952 IPv6, canonical optional port `1..65535`, and RFC 3986
+path-abempty/query syntax with uppercase `%HH` escapes. IDNA or
+percent-encoded hosts, legacy IPv4, userinfo, fragments, controls,
+backslashes, and normalization-dependent forms are rejected. Inbound
+projection ignores an invalid advisory hint while preserving its exact raw tag
+in ordered diagnostics. Relay syntax remains independent from each event
+profile's 4,096-byte tag-element budget.
+
+The Comment codec implements the strict Radroots
+[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
+kind-`1111` profile for event or address roots of kind `30402`, `31922`, or
+`31923`. It rejects external `I`/`i` authority and kind-`1` roots.
+`authored_nip22_comment_to_wire_parts` emits only canonical order:
+`E,K,P,e,k,p` for a top-level event, `A,K,P,a,e,k,p` for a top-level
+address, and `E,K,P,e,k,p` or `A,K,P,e,k,p` for a nested event or address.
+Event `E` and parent or direct `e` references always have four elements with a
+relay slot and author hint. Address and participant references have two
+elements plus an optional relay; a top-level address Comment's revision `e`
+has no author hint. Direct `k` repeats the root kind and nested `k` is exactly
+`1111`.
+
+`project_verified_nip22_comment_event` accepts only a
+`RadrootsSignatureVerifiedEvent`. It resolves the `E`/`A`, `K`, `P`, `e`/`a`,
+`k`, and selected `p` authority independently of tag order and treats invalid
+cardinality, shapes, noncanonical kinds, unsupported roots, coordinate
+conflicts, and valid-but-conflicting author hints as hard errors. It preserves
+exact raw tags, supplemental unknown and `q` tags, and distinct unselected
+lowercase `p` mentions. Inbound NIP-22 reference event IDs, public keys, and
+coordinate-author hex accept either ASCII hex case; typed values normalize to
+lowercase while raw tags retain the original spelling. Malformed advisory relay
+and participant metadata produce ordered diagnostics without weakening the
+required authority.
+`verify_and_admit_nip22_comment_event` first verifies the NIP-01 id and Schnorr
+signature, then binds that envelope to
+`RadrootsInboundNip22CommentProjection` in
+`RadrootsAdmittedNip22CommentEvent`; it does not verify any referenced event or
+relay.
+
+The exact Comment operation namespace is
+`social.comment.build_authored_draft`,
+`social.comment.project_verified_event`, and
+`social.comment.verify_and_admit_event`. Registry v7 makes
+`radroots.social.comment.v1` `TypedOnly` for authoring and `AdmissionOnly` for
+matching; registry versions `1` through `6` are stale. Content is limited to
+131072 UTF-8 bytes, a Comment to 1024 tags, all tags to 4096 elements including
+tag names, each element to 4096 bytes, aggregate tag bytes to 131072, and
+compact signed wire JSON to 262144 bytes.
+
+The canonical 114-case self-contained Comment corpus is
+`contracts/conformance/vectors/comment/verified_profile.v1.json`; the packaged
+fixture is byte-identical. Projection and admission cases contain fixed signed
+event JSON, while authored cases contain explicit checked inputs and complete
+wire expectations. The runner consumes these records directly and covers
+valid shapes, diagnostics, exact and over-limit budgets, stable error
+precedence, and NIP-01 admission without secret keys, mutation recipes, or
+ambient state.
+
+The Deletion codec implements the request and pure suppression-evaluation
+boundaries of
+[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md).
+`authored_nip09_deletion_request_to_wire_parts` emits canonical two-element
+`e` targets, then `a` targets, then the complete unique ascending set of `k`
+kind advisories. `project_verified_nip09_deletion_request_event` accepts only
+an id-and-signature-verified kind-`5` envelope. It retains exact raw tags,
+deduplicates normalized targets with first-seen provenance, sorts the typed
+target views, and treats malformed `e` or `a` targets as hard errors. Unknown
+tags and trailing target elements remain observable. Advisory `k` shape,
+numeric spelling, duplicates, and address-only conflicts produce stable
+ordered diagnostics; event-target kind correspondence remains unprovable at
+this boundary.
+
+`verify_and_admit_nip09_deletion_request_event` binds the projection to the
+verified request envelope. It does not authorize or apply a deletion, look up
+a target, compare authors, compute an address cutoff, suppress an event, or
+mutate storage.
+
+`evaluate_nip09_suppression` is a separate pure operation over one
+`RadrootsSignatureVerifiedEvent` candidate and admitted deletion requests. It
+returns an explicit `RadrootsNip09SuppressionDecision` with canonical evidence
+without changing any input or touching a store. Kind `5` is immune. Other
+candidates require the request author to equal the candidate author. An exact
+event-id target applies independently of timestamps; a canonical address
+target applies through the inclusive maximum qualifying request timestamp, so
+a later replacement is not suppressed. Advisory `k` values do not affect the
+result, and request order cannot affect the decision.
+
+The reconciliation-v1 entrypoints
+`project_verified_nip09_deletion_request_event_v1`,
+`admit_verified_nip09_deletion_request_event_v1`, and
+`evaluate_nip09_suppression_v1` freeze the exact request and suppression
+behavior consumed by event-store schema version 2. Current compatibility
+entrypoints delegate to these functions; a future algorithm revision must add
+new versioned functions.
+
+The exact operation namespace is
+`social.deletion_request.build_authored_draft`,
+`social.deletion_request.project_verified_event`,
+`social.deletion_request.verify_and_admit_event`, and
+`social.deletion_request.evaluate_suppression`. Registry v7 makes
+`radroots.social.deletion_request.v1` `TypedOnly` for authoring and
+`AdmissionOnly` for matching, with generic kind-`5` authoring and publication
+reserved.
+
+The canonical self-contained 80-case corpus is
+`contracts/conformance/vectors/deletion/verified_profile.v1.json`; the
+packaged fixture is byte-identical. Signed inputs are complete compact event
+JSON, expected projections contain no effect-authority output, and executable
+cases cover canonical authoring, tolerant verified projection, diagnostics,
+exact and over-limit resource budgets, precedence, and admission without
+shipping keys, seeds, generators, or mutation recipes.
+
+The separate
+`contracts/conformance/vectors/deletion/suppression.v1.json` corpus and its
+byte-identical packaged fixture execute the evaluator's same-author, direct
+event, inclusive address-cutoff, later-replacement, pre-target request,
+kind-`5` immunity, and deterministic evidence behavior. These vectors govern a
+decision value only and claim no persistent store effect.
+
+The FoodAvailability codec owns four boundaries for the focused
+`radroots.food.availability.v1` kind-`30402` profile. Strict details plus
+`created_at` produce bounded unsigned wire parts whose tag order is exactly
+`d`, `title`, `summary`, `published_at`, `location`, `price`,
+`radroots:price_unit`, optional `radroots:quantity`, `status`, and repeated
+`image`. Content and decoded tags remain bounded, and compact signed-event size
+is checked after exact JSON escaping. Authored images already prove local
+Blossom descriptor-to-byte agreement, but not upload completion or network
+availability.
+
+Verified inbound projection partitions raw tag names before validation.
+Focused-only candidates are projected, Operational Listing and marker-free
+generic NIP-99 candidates are explicit exclusions, and mixed-marker candidates
+fail as ambiguous. Focused core fields are required; decimal and currency
+spellings are normalized within their wire bounds; optional unowned NIP-99
+tags are ignored; and tags purporting to add commerce, fulfillment,
+provenance, or operational capabilities fail closed. Image observations remain
+ordered structural HTTP(S) data. At most 64 are retained, with stable ordered
+diagnostics for overflow, shape, URL, dimensions, duplicate URL, and duplicate
+Blossom digest. An image diagnostic does not invalidate an otherwise complete
+focused core and never upgrades observed media to an authored typestate.
+
+`verify_and_admit_food_availability_event` first verifies the NIP-01 id and
+Schnorr signature, then binds either the focused projection or an explicit
+non-focused exclusion to that verified envelope. Revision validation accepts
+two independently verified events, requires both to satisfy the exact authored
+wire profile, preserves kind, author, `d`, and `published_at`, and applies the
+NIP-01 newer-event rule: later `created_at`, then lower event id at equal time.
+Successful combined verification and admission uses the optional `nostr`
+feature; without it, signature verification returns the stable unavailable
+error rather than admitting an event. The packaged conformance runner enables
+`serde_json,nostr` for signed JSON event cases.
+These codecs do not sign, publish, replicate, upload, retrieve, or provide
+client behavior.
+
+The NIP-52 calendar codecs expose a deliberate three-stage boundary for kinds
+`31922`, `31923`, `31924`, and `31925`:
+
+1. bounded structural wire or envelope parsing preserves the complete event;
+2. the kind-specific date event, time event, calendar collection, or RSVP
+ parser produces a tolerant `RadrootsParsedNip52*` projection and rejects a
+ mismatched kind;
+3. the corresponding `admit_radroots_*` operation applies the stricter
+ Radroots profile and returns a non-interchangeable admitted type.
+
+Structural envelope construction, the baseline parsers, and the admission
+functions do not verify a Schnorr signature. Before treating inbound data as
+accepted, callers must recompute and compare its NIP-01 id, verify its
+signature against the author, use the parser for its expected kind, and keep
+the typed result bound to that verified envelope. The calendar
+`*_parsed_from_event` convenience functions remain structural wrappers; their
+names do not imply cryptographic verification.
+
+The baseline parser projects repeated NIP-52 `location`, participant `p`,
+category `t`, absolute-URI reference `r`, and kind-`31924` inclusion-request
+`a` tags, plus deprecated `name` compatibility data. It accepts arbitrary
+absolute image URIs. Kind `31922` requires semantic Gregorian dates and retains
+uppercase-`D` tags as uninterpreted extensions. Kind `31923` requires at least
+one in-range `D` day and exact IANA time-zone identifiers. NIP-52 makes
+multi-day coverage a `SHOULD`, so the baseline requires neither the start-day
+index nor complete coverage; duplicate, unordered, and leading-zero numeric
+observations are additionally preserved as parser tolerance and are never
+canonical authored output. An omitted
+`end_tzid` uses `start_tzid` when present.
+
+Strict date admission rejects every uppercase-`D` extension. Strict time
+admission requires canonical decimal timestamps and the exact ascending
+uppercase-`D` sequence for the exclusive interval, bounded to 366 covered
+days. Authored codecs derive that sequence and emit unsigned
+`RadrootsNip01EventWireParts`; signing and transport stay at the owning runtime
+boundary.
+
+Kind `31924` is handled only by the calendar-specific codec. Generic NIP-51
+taxonomy may recognize it as a calendar list, but the generic list-set decoder
+and authoring path and the broader generic list codec reject it. Calendar collection content is the bounded
+plain-text NIP-52 detailed description; it is not substituted from or collapsed
+into the optional NIP-51 `description` tag. The parser requires exactly one
+`d` and `title`, accepts optional singleton `description` and `image` tags, and
+accepts zero or more exact `a` references to kind `31922` or `31923` events.
+An empty collection is valid, and each event reference preserves its own
+optional relay hint. Singleton collection tags have exactly two elements; an
+`a` tag has exactly two elements plus an optional relay element.
+
+Kind `31925` has bounded optional note content and exact singleton
+cardinality: required `d`, `a`, and `status`, with optional `e`, `fb`, and `p`.
+The `a`, `e`, and `p` values preserve independent optional relay hints. The
+`p` tag is parsed as an event-author hint without participant-role semantics;
+strict admission requires it to match the author in the `a` coordinate.
+`d`, `status`, and `fb` tags have exactly two elements, while `a`, `e`, and `p`
+have exactly two elements plus an optional relay element.
+Tolerant parsing preserves `fb` observed on a declined RSVP, but parsed and
+admitted values expose no effective free/busy state for it. Authored declined
+RSVPs never emit `fb`.
+
+Strict collection and RSVP authoring and admission require a canonical
+22-character unpadded base64url 128-bit identifier and canonical reference
+spellings. Identifier syntax does not establish runtime uniqueness. Parsing
+valid identifiers, event ids, author keys, coordinates, and relay hints does
+not establish reference existence, revision correspondence, RSVP authority,
+or relay availability.
+
+Strict inbound admission, including kind-`31924` collection images, upgrades
+an image only to a structural Blossom hash-path URL. It does not prove
+reference approval, bytes, upload completion, or retrievability. Strict
+authored image inputs are approved, byte-verified Blossom `image/*`
+descriptors, but are still not upload receipts. A publishing runtime must
+require successful BUD-02 completion and a bounded retrievability check before
+signing or publishing media-bearing wire parts.
+
+## Field Event Codecs
+
+The Field codec surface validates the public Nostr event substrate exposed by
+`radroots_event`:
+
+ * workspace manifests use kind `30078`, JSON content, a schema marker, `d` and
+ `h` routing tags, owner tags, relay references, and Field-supported kind
+ declarations;
+ * CRDT changes use kind `78`, JSON content, `h`, `d`, `a`, optional author `p`,
+ and semantic `t` tags, with base64url change payload validation;
+ * farm files use kind `1063`, NIP-94-compatible metadata tags, a workspace
+ address, a farm group id, an owner document tag, lowercase SHA-256 hashes,
+ and optional caption text as content;
+ * NIP-42 relay auth and NIP-98 HTTP auth events require empty content and the
+ auth tags required by their protocols; NIP-98 `u`, `method`, and `payload`
+ tags are parsed as singleton security tags and duplicate occurrences fail
+ closed;
+ * NIP-29 group codecs preserve the protocol distinction between `h`-routed
+ group operations and `d`-routed addressable group state for the supported
+ `9000`, `9001`, `9002`, `9005`, `9007`, `9008`, `9009`, `9021`, `9022`,
+ `39000`, `39001`, `39002`, and `39003` subset.
+
+These codecs validate event shape, routing tags, hashes, and payload encoding.
+They do not validate private Field task, work-session, harvest, approval, or
+authorization semantics; those remain application and CRDT document concerns.
+The group codecs use bare metadata marker tags such as `private`, `restricted`,
+`hidden`, and `closed`, `supported_kinds` declarations, and `code` tags for
+invite and join flows. They preserve optional reason content on user management
+and moderation events. LiveKit room metadata and live participant state are
+deferred. SDK-owned wasm bindings expose deterministic JSON tag builders for the
+same Field and NIP-29 families.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_event_codec`
+distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_event_codec` distribution.
diff --git a/crates/event_store/contracts/source_maintenance_v1.manifest.json b/crates/event_store/contracts/source_maintenance_v1.manifest.json
@@ -152,8 +152,8 @@
{
"role": "workspace_dependency_authority",
"path": "Cargo.toml",
- "byte_length": 11092,
- "sha256": "812ce187ddab5e4b95e61b0af42333f41fd772d5dfdc76b72093ef33b3dcdf47",
+ "byte_length": 11546,
+ "sha256": "0378afe281a46e02e83e128324efefc3da950abf3e1d3d06c75b628f69810bd2",
"hash_algorithm": "sha256_bytes_v1"
},
{
@@ -236,15 +236,15 @@
{
"role": "predecessor_successor_governance",
"path": "tools/xtask/src/contract/food_availability_projection.rs",
- "byte_length": 194901,
- "sha256": "01fe9546c95244b4197b3d2a6cec3d72e1f7c48f088bc93f168b5e2e4977b048",
+ "byte_length": 195036,
+ "sha256": "d3c567ea3b4ea709723af2d5b6a2dc47993c6ba42c1d62895bd26fa101ebf829",
"hash_algorithm": "sha256_bytes_v1"
},
{
"role": "transitive_predecessor_membership_governance",
"path": "tools/xtask/src/contract/nip09_reconciliation.rs",
- "byte_length": 833937,
- "sha256": "ee111a10b9b51f213d7c18558aaffb3bc4054449ca585be8f8ea1a9dbd464f60",
+ "byte_length": 835585,
+ "sha256": "51d524cb00d2d96dc0da35fc9133c54f2e5328a830d56b8d5e83a7f1e9654ddc",
"hash_algorithm": "sha256_bytes_v1"
},
{
@@ -257,8 +257,8 @@
{
"role": "contract_command_authority",
"path": "tools/xtask/src/contract.rs",
- "byte_length": 499596,
- "sha256": "ece5990259eeacac8bb12a27e89f889f82f8641b958103a0ef97eaa6d2f66fb0",
+ "byte_length": 500480,
+ "sha256": "84739ad8b78bae49ea1f992672101bd83135415042cb22a85dbcbf5c878fc097",
"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 @@
-20d3348c5d1c485c174b45b495331e919e0884cd0372e145e6a9f683acf92afd
+ffb6ec82c763730a69d24ee8e8194e3e0911ee590df58e5fe78a23ae68122652
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\": \"ad4a6c8ae9488fc8033792bc6952af04687f312901c1847d8c668a62913bb642\",\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\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\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\": \"425dc799f392b87f265a6fb81f89c4a1c7a5db8391ab0380970708cb9c66704d\",\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\": \"d526d96ea02be12b4b0aed99e97cfdde17c4474ace67111506a7b900ee78b186\",\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\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\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\": 11092,\n \"sha256\": \"812ce187ddab5e4b95e61b0af42333f41fd772d5dfdc76b72093ef33b3dcdf47\",\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\": 19421,\n \"sha256\": \"4772e041cb20a4963afb2f3159804c777e2f2be61bfdb6ee267e7a7c04258972\",\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\": 33617,\n \"sha256\": \"79296b8f263aa06d17005795e4515f769f064ea6fd971eeb1296e1151debaf20\",\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\": 184407,\n \"sha256\": \"c455d40fc736e3db264f567c7809af7bd897d89be8a33dfb21667a6ef6b8d6c6\",\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\": 394574,\n \"sha256\": \"db57dc3e35e64c7194683142fe55edba853671a829269449dd2273056dfc3a0e\",\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\": 30140,\n \"sha256\": \"210112eeaa6975a3b4fbb97d5c52588f8c6d8d07975e531d39737fd11235de51\",\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\": 51756,\n \"sha256\": \"f8d5b62f0613104aa86658d5bf1baade92c7df83f00ef0cddadd734b9797afca\",\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\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\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\": 194901,\n \"sha256\": \"01fe9546c95244b4197b3d2a6cec3d72e1f7c48f088bc93f168b5e2e4977b048\",\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\": 833937,\n \"sha256\": \"ee111a10b9b51f213d7c18558aaffb3bc4054449ca585be8f8ea1a9dbd464f60\",\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\": 176811,\n \"sha256\": \"cc6125aaeba8d7dab83c2413ca0f1a80d6f9f26876f0117e4c8fceae156304e5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 499596,\n \"sha256\": \"ece5990259eeacac8bb12a27e89f889f82f8641b958103a0ef97eaa6d2f66fb0\",\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\": 14364,\n \"sha256\": \"27096ad2c226c6402313621712db6af23a4fdda5117c3e732cf3a3fdf3d9b434\",\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\": \"ad4a6c8ae9488fc8033792bc6952af04687f312901c1847d8c668a62913bb642\",\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\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\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\": \"425dc799f392b87f265a6fb81f89c4a1c7a5db8391ab0380970708cb9c66704d\",\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\": \"d526d96ea02be12b4b0aed99e97cfdde17c4474ace67111506a7b900ee78b186\",\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\": \"33b93a3c87ce428e8aa6f5e92643c77203d9aa006c53ce96f3562fe6d68ffd23\",\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\": 11546,\n \"sha256\": \"0378afe281a46e02e83e128324efefc3da950abf3e1d3d06c75b628f69810bd2\",\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\": 19421,\n \"sha256\": \"4772e041cb20a4963afb2f3159804c777e2f2be61bfdb6ee267e7a7c04258972\",\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\": 33617,\n \"sha256\": \"79296b8f263aa06d17005795e4515f769f064ea6fd971eeb1296e1151debaf20\",\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\": 184407,\n \"sha256\": \"c455d40fc736e3db264f567c7809af7bd897d89be8a33dfb21667a6ef6b8d6c6\",\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\": 394574,\n \"sha256\": \"db57dc3e35e64c7194683142fe55edba853671a829269449dd2273056dfc3a0e\",\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\": 30140,\n \"sha256\": \"210112eeaa6975a3b4fbb97d5c52588f8c6d8d07975e531d39737fd11235de51\",\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\": 51756,\n \"sha256\": \"f8d5b62f0613104aa86658d5bf1baade92c7df83f00ef0cddadd734b9797afca\",\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\": 38279,\n \"sha256\": \"f326ea57b56d40135f95b6b1e15961f66eed363337180a6d12a5ea903e1a9a29\",\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\": 195036,\n \"sha256\": \"d3c567ea3b4ea709723af2d5b6a2dc47993c6ba42c1d62895bd26fa101ebf829\",\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\": 835585,\n \"sha256\": \"51d524cb00d2d96dc0da35fc9133c54f2e5328a830d56b8d5e83a7f1e9654ddc\",\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\": 176811,\n \"sha256\": \"cc6125aaeba8d7dab83c2413ca0f1a80d6f9f26876f0117e4c8fceae156304e5\",\n \"hash_algorithm\": \"sha256_bytes_v1\"\n },\n {\n \"role\": \"contract_command_authority\",\n \"path\": \"tools/xtask/src/contract.rs\",\n \"byte_length\": 500480,\n \"sha256\": \"84739ad8b78bae49ea1f992672101bd83135415042cb22a85dbcbf5c878fc097\",\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\": 14364,\n \"sha256\": \"27096ad2c226c6402313621712db6af23a4fdda5117c3e732cf3a3fdf3d9b434\",\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 = 14459;
pub(crate) const SOURCE_MAINTENANCE_MANIFEST_SHA256: &str =
- "20d3348c5d1c485c174b45b495331e919e0884cd0372e145e6a9f683acf92afd";
+ "ffb6ec82c763730a69d24ee8e8194e3e0911ee590df58e5fe78a23ae68122652";
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/geonames/Cargo.toml b/crates/geonames/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-geonames"
+description = "GeoNames-backed geocoding for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_geonames"
+
+[lints]
+workspace = true
diff --git a/crates/geonames/README.md b/crates/geonames/README.md
@@ -0,0 +1,7 @@
+# radroots-geonames
+
+GeoNames-backed geocoding for Radroots.
+
+This package root is established for the Release V1 refactor. Dataset models,
+indexes, and deterministic lookup behavior are migrated in the subsequent
+GeoNames checkpoints.
diff --git a/crates/geonames/src/lib.rs b/crates/geonames/src/lib.rs
@@ -0,0 +1 @@
+//! GeoNames-backed geocoding for Radroots.
diff --git a/crates/identity/Cargo.toml b/crates/identity/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_identity"
+name = "radroots-identity"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Identity model for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_identity"
-readme = "README"
+documentation = "https://docs.rs/radroots-identity"
+readme = "README.md"
+
+[lib]
+name = "radroots_identity"
[features]
default = ["std", "json-file", "nip49"]
@@ -44,5 +47,5 @@ zeroize = { workspace = true, optional = true }
[dev-dependencies]
tempfile = { workspace = true }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/identity/README b/crates/identity/README
@@ -1,25 +0,0 @@
-# radroots_identity
-
-This is the README for `radroots_identity`, which provides identity models,
-encrypted file storage, and profile utilities for the `radroots` core
-libraries.
-
-## Overview
-
- * public and private identity, profile, file, and identifier types;
- * username validation, normalization, and parser helpers;
- * default identity path constants and JSON file support behind feature flags;
- * encrypted identity-file and public-profile storage helpers for local runtime
- consumers;
- * optional NIP-49, `secrecy`, and `zeroize` support for protected key
- material.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_identity` distribution
-are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_identity` distribution.
diff --git a/crates/identity/README.md b/crates/identity/README.md
@@ -0,0 +1,25 @@
+# radroots-identity
+
+This is the README for `radroots_identity`, which provides identity models,
+encrypted file storage, and profile utilities for the `radroots` core
+libraries.
+
+## Overview
+
+ * public and private identity, profile, file, and identifier types;
+ * username validation, normalization, and parser helpers;
+ * default identity path constants and JSON file support behind feature flags;
+ * encrypted identity-file and public-profile storage helpers for local runtime
+ consumers;
+ * optional NIP-49, `secrecy`, and `zeroize` support for protected key
+ material.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_identity` distribution
+are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_identity` distribution.
diff --git a/crates/identity/tests/identity.rs b/crates/identity/tests/identity.rs
@@ -21,40 +21,10 @@ use radroots_runtime_paths::{
RadrootsHostEnvironment, RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver,
RadrootsPlatform,
};
-use std::{
- ffi::OsString,
- path::PathBuf,
- sync::{Mutex, OnceLock},
-};
+use std::path::PathBuf;
use test_fixtures::{ApprovedFixtureIdentity, FIXTURE_ALICE, FIXTURE_BOB};
-fn home_env_lock() -> &'static Mutex<()> {
- static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
- LOCK.get_or_init(|| Mutex::new(()))
-}
-
-struct EnvVarGuard {
- key: &'static str,
- previous: Option<OsString>,
-}
-
-impl EnvVarGuard {
- fn remove(key: &'static str) -> Self {
- let previous = std::env::var_os(key);
- unsafe { std::env::remove_var(key) };
- Self { key, previous }
- }
-}
-
-impl Drop for EnvVarGuard {
- fn drop(&mut self) {
- if let Some(value) = self.previous.as_ref() {
- unsafe { std::env::set_var(self.key, value) };
- } else {
- unsafe { std::env::remove_var(self.key) };
- }
- }
-}
+const MISSING_HOME_CHILD: &str = "RADROOTS_IDENTITY_MISSING_HOME_CHILD";
fn fixture_keys(fixture: ApprovedFixtureIdentity) -> nostr::Keys {
let secret = nostr::SecretKey::from_hex(fixture.secret_key_hex).unwrap();
@@ -638,9 +608,29 @@ fn default_path_for_reports_missing_home_dir() {
#[test]
fn load_or_generate_without_explicit_path_propagates_default_path_errors() {
- let _lock = home_env_lock().lock().unwrap();
- let _guard = EnvVarGuard::remove("HOME");
+ let output = std::process::Command::new(std::env::current_exe().unwrap())
+ .args([
+ "--exact",
+ "load_or_generate_without_explicit_path_child",
+ "--nocapture",
+ ])
+ .env_remove("HOME")
+ .env(MISSING_HOME_CHILD, "1")
+ .output()
+ .unwrap();
+ assert!(
+ output.status.success(),
+ "child test failed:\nstdout:\n{}\nstderr:\n{}",
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ );
+}
+#[test]
+fn load_or_generate_without_explicit_path_child() {
+ if std::env::var_os(MISSING_HOME_CHILD).is_none() {
+ return;
+ }
let err = RadrootsIdentity::load_or_generate::<&std::path::Path>(None, false).unwrap_err();
assert!(matches!(err, IdentityError::Paths(_)));
}
diff --git a/crates/nostr/Cargo.toml b/crates/nostr/Cargo.toml
@@ -1,16 +1,22 @@
[package]
-name = "radroots_nostr"
+name = "radroots-nostr"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Nostr integration layer for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_nostr"
-readme = "README"
+documentation = "https://docs.rs/radroots-nostr"
+readme = "README.md"
+
+[lib]
+name = "radroots_nostr"
+
+[lints]
+workspace = true
[features]
default = ["std"]
diff --git a/crates/nostr/README b/crates/nostr/README
@@ -1,143 +0,0 @@
-# radroots_nostr
-
-This is the README for `radroots_nostr`, which provides shared Nostr protocol
-primitives for the `radroots` core libraries.
-
-## Overview
-
- * typed filters, tags, events, relay metadata, parsers, and utility helpers;
- * feature-gated client operations and relay-management helpers for active
- network use;
- * adapters between `radroots_event`, `radroots_event_codec`, and Nostr wire
- representations;
- * strict BUD-11 signed HTTP authorization adapters behind the `blossom`
- feature;
- * optional NIP-11 and NIP-17 support across feature-gated builds.
-
-The `blossom` feature signs kind-24242 authorization events and encodes or
-verifies their `Authorization: Nostr` HTTP values. It does not publish these
-ephemeral authorization events to relays. Pure BUD-11 claim parsing and policy
-validation remain in `radroots_blossom`.
-
-The `events` feature is std-backed. With it, kind-1 root publication is
-available only through typed Update, PhotoUpdate, and Ask builders backed by
-the strict `radroots_event_codec` wire operations. NIP-10 Reply publication is
-separately available through the typed direct or nested Reply model and its
-sealed builder. Authored Replies always emit marked `root` and optional
-`reply` event references plus the required referenced-author `p` tags.
-Verified inbound projection also accepts deprecated positional NIP-10
-references for interoperability and preserves valid supplemental unmarked `e`
-references as citations. Empty marker slots remain absent even when an optional
-fifth-element author hint is present. Missing or malformed advisory
-participant, middle citation, relay, and referenced-author metadata is retained
-as ordered diagnostics instead of erasing an unambiguous Reply. A Reply remains
-thread content and can never enter root-card admission. Reply admission proves
-the Reply event's NIP-01 id and signature; it does not prove that a referenced
-target exists, is kind `1`, or was authored by the declared referenced author.
-
-Strict
-[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
-Comment publication is separately exposed through
-`RadrootsNostrNip22CommentEventBuilder`. Its only input is a checked
-`RadrootsAuthoredNip22Comment`; callers may choose the timestamp and sign or
-publish, but cannot mutate the kind, content, or canonical
-root, parent, kind, and participant tags. The profile admits event or address
-roots only for kinds `30402`, `31922`, and `31923`; it has no external
-`I`/`i` or kind-`1` root surface.
-
-Reply and Comment authoring and verified projection share the portable
-`RadrootsNostrRelayHint` profile rather than the generic relay URL type. It
-accepts only exact lowercase `ws://` or `wss://` visible-ASCII URLs with a
-canonical lowercase DNS, four-octet IPv4, or bracketed pure-hex RFC 5952 IPv6
-authority, an optional canonical port `1..65535`, and RFC 3986
-path-abempty/query syntax using uppercase `%HH` escapes. Rejected inbound hints
-remain verbatim in ordered raw-tag diagnostics. The hint profile does not own
-the event boundary's separate 4,096-byte tag-element budget.
-
-Inbound Comment projection is owned by `radroots_event_codec` and accepts only
-an id-and-signature verified kind-`1111` envelope.
-`RadrootsInboundNip22CommentProjection` retains the order-independent
-authority projection, and `RadrootsAdmittedNip22CommentEvent` keeps it bound to
-the verified envelope. Malformed optional relay or participant metadata
-remains diagnostic, while cardinality, unsupported roots, coordinate
-conflicts, and valid-but-conflicting author hints fail admission. Admission
-does not prove any referenced target or relay. The registry-v7 contract is
-`TypedOnly` for authoring and `AdmissionOnly` for matching, with registry
-versions `1` through `6` stale.
-
-Strict
-[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md)
-deletion-request publication is exposed through the sealed
-`RadrootsNostrNip09DeletionRequestEventBuilder`. Its only input is a checked
-`RadrootsAuthoredNip09DeletionRequest`; callers may choose the timestamp and
-sign or publish, but cannot mutate the kind, content, or canonical `e`, `a`,
-and derived `k` tags. Generic kind-5 builders are rejected before signer
-access. Admission proves only the signed deletion-request event and its typed
-projection. It does not prove target authorship, existence, deletion
-authorization, applicability, relay handling, or any deletion effect.
-
-The former free-form text-note post builder is removed. Generic protocol
-builders reject kind-0 Profile events, every kind-1 event, every kind-5
-deletion request, and kind-1111 Comments at both direct signing and
-client-publication boundaries before signer access. Typed media builders can
-sign or publish only after the owning runtime separately proves successful
-BUD-02 upload completion; their byte-verified descriptors do not attest upload
-completion. The generic net manager intentionally exposes no direct
-PhotoUpdate or media Ask publisher.
-
-The complete governed Comment operation namespace remains
-`social.comment.build_authored_draft`,
-`social.comment.project_verified_event`, and
-`social.comment.verify_and_admit_event`. The typed builder and client
-publication surface consume that strict contract; they do not add a fourth
-wire operation. Comment inputs retain the 131072-byte content, 1024-tag,
-4096-total-element, 4096-byte element, 131072-byte aggregate tag, and
-262144-byte compact signed-wire ceilings proven by the canonical
-self-contained 114-case conformance corpus.
-
-Focused FoodAvailability kind-30402 authoring likewise uses a sealed builder.
-Its `created_at` is fixed during strict construction and cannot be changed
-after wire validation. Generic direct signing and client publication reject
-focused or mixed FoodAvailability/Operational Listing markers before signer
-access; marker-free NIP-99 and operational-only compatibility builders remain
-available. Relaying an already signed event remains a transport operation and
-does not establish typed FoodAvailability authoring.
-
-The opaque generic-builder policy governs Radroots builder signing and client
-publication. It does not redefine the standard NIP-46 `sign_event` method or a
-signer backend's externally supplied unsigned-event operation. Those are
-explicit low-level Nostr interoperability boundaries; their outputs carry no
-Radroots typed product-authoring claim and are not product authoring entry
-points.
-
-Generic protocol events that require an external custody provider finalize
-through `RadrootsNostrExternalSigningRequest`. The opaque request is available
-without the relay-client feature and serializes as a standard unsigned Nostr
-event only after generic typed-authoring reservations pass. It accepts a
-returned event only when the author and canonical event id match the request
-and the complete NIP-01 event verifies. It exposes no raw mutable builder,
-unsigned-event conversion, or unchecked deserialization path.
-
-Strict kind-0 Profile publication uses
-`RadrootsNostrProfileEventBuilder`, constructed only from
-`RadrootsAuthoredProfile`. The sealed wrapper permits timestamp selection and
-local signing or client publication, but no raw kind, content, or tag
-mutation. A media-bearing Profile still requires runtime-owned proof of
-successful BUD-02 upload before it reaches this authoring boundary.
-
-## Portable relay-client lifecycle
-
-With the `client` feature, callers can subscribe and publish to selected relay
-sets and explicitly unsubscribe through Radroots types. The wrapper does not
-create detached listeners or take ownership of subscription lifetime. The
-`client,events` feature combination is qualified for native targets and
-`wasm32-unknown-unknown`.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_nostr` distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_nostr` distribution.
diff --git a/crates/nostr/README.md b/crates/nostr/README.md
@@ -0,0 +1,143 @@
+# radroots-nostr
+
+This is the README for `radroots_nostr`, which provides shared Nostr protocol
+primitives for the `radroots` core libraries.
+
+## Overview
+
+ * typed filters, tags, events, relay metadata, parsers, and utility helpers;
+ * feature-gated client operations and relay-management helpers for active
+ network use;
+ * adapters between `radroots_event`, `radroots_event_codec`, and Nostr wire
+ representations;
+ * strict BUD-11 signed HTTP authorization adapters behind the `blossom`
+ feature;
+ * optional NIP-11 and NIP-17 support across feature-gated builds.
+
+The `blossom` feature signs kind-24242 authorization events and encodes or
+verifies their `Authorization: Nostr` HTTP values. It does not publish these
+ephemeral authorization events to relays. Pure BUD-11 claim parsing and policy
+validation remain in `radroots_blossom`.
+
+The `events` feature is std-backed. With it, kind-1 root publication is
+available only through typed Update, PhotoUpdate, and Ask builders backed by
+the strict `radroots_event_codec` wire operations. NIP-10 Reply publication is
+separately available through the typed direct or nested Reply model and its
+sealed builder. Authored Replies always emit marked `root` and optional
+`reply` event references plus the required referenced-author `p` tags.
+Verified inbound projection also accepts deprecated positional NIP-10
+references for interoperability and preserves valid supplemental unmarked `e`
+references as citations. Empty marker slots remain absent even when an optional
+fifth-element author hint is present. Missing or malformed advisory
+participant, middle citation, relay, and referenced-author metadata is retained
+as ordered diagnostics instead of erasing an unambiguous Reply. A Reply remains
+thread content and can never enter root-card admission. Reply admission proves
+the Reply event's NIP-01 id and signature; it does not prove that a referenced
+target exists, is kind `1`, or was authored by the declared referenced author.
+
+Strict
+[NIP-22](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/22.md)
+Comment publication is separately exposed through
+`RadrootsNostrNip22CommentEventBuilder`. Its only input is a checked
+`RadrootsAuthoredNip22Comment`; callers may choose the timestamp and sign or
+publish, but cannot mutate the kind, content, or canonical
+root, parent, kind, and participant tags. The profile admits event or address
+roots only for kinds `30402`, `31922`, and `31923`; it has no external
+`I`/`i` or kind-`1` root surface.
+
+Reply and Comment authoring and verified projection share the portable
+`RadrootsNostrRelayHint` profile rather than the generic relay URL type. It
+accepts only exact lowercase `ws://` or `wss://` visible-ASCII URLs with a
+canonical lowercase DNS, four-octet IPv4, or bracketed pure-hex RFC 5952 IPv6
+authority, an optional canonical port `1..65535`, and RFC 3986
+path-abempty/query syntax using uppercase `%HH` escapes. Rejected inbound hints
+remain verbatim in ordered raw-tag diagnostics. The hint profile does not own
+the event boundary's separate 4,096-byte tag-element budget.
+
+Inbound Comment projection is owned by `radroots_event_codec` and accepts only
+an id-and-signature verified kind-`1111` envelope.
+`RadrootsInboundNip22CommentProjection` retains the order-independent
+authority projection, and `RadrootsAdmittedNip22CommentEvent` keeps it bound to
+the verified envelope. Malformed optional relay or participant metadata
+remains diagnostic, while cardinality, unsupported roots, coordinate
+conflicts, and valid-but-conflicting author hints fail admission. Admission
+does not prove any referenced target or relay. The registry-v7 contract is
+`TypedOnly` for authoring and `AdmissionOnly` for matching, with registry
+versions `1` through `6` stale.
+
+Strict
+[NIP-09](https://github.com/nostr-protocol/nips/blob/bdfa7e62ef87fcfcb992b1a27aee49d36b0b4f91/09.md)
+deletion-request publication is exposed through the sealed
+`RadrootsNostrNip09DeletionRequestEventBuilder`. Its only input is a checked
+`RadrootsAuthoredNip09DeletionRequest`; callers may choose the timestamp and
+sign or publish, but cannot mutate the kind, content, or canonical `e`, `a`,
+and derived `k` tags. Generic kind-5 builders are rejected before signer
+access. Admission proves only the signed deletion-request event and its typed
+projection. It does not prove target authorship, existence, deletion
+authorization, applicability, relay handling, or any deletion effect.
+
+The former free-form text-note post builder is removed. Generic protocol
+builders reject kind-0 Profile events, every kind-1 event, every kind-5
+deletion request, and kind-1111 Comments at both direct signing and
+client-publication boundaries before signer access. Typed media builders can
+sign or publish only after the owning runtime separately proves successful
+BUD-02 upload completion; their byte-verified descriptors do not attest upload
+completion. The generic net manager intentionally exposes no direct
+PhotoUpdate or media Ask publisher.
+
+The complete governed Comment operation namespace remains
+`social.comment.build_authored_draft`,
+`social.comment.project_verified_event`, and
+`social.comment.verify_and_admit_event`. The typed builder and client
+publication surface consume that strict contract; they do not add a fourth
+wire operation. Comment inputs retain the 131072-byte content, 1024-tag,
+4096-total-element, 4096-byte element, 131072-byte aggregate tag, and
+262144-byte compact signed-wire ceilings proven by the canonical
+self-contained 114-case conformance corpus.
+
+Focused FoodAvailability kind-30402 authoring likewise uses a sealed builder.
+Its `created_at` is fixed during strict construction and cannot be changed
+after wire validation. Generic direct signing and client publication reject
+focused or mixed FoodAvailability/Operational Listing markers before signer
+access; marker-free NIP-99 and operational-only compatibility builders remain
+available. Relaying an already signed event remains a transport operation and
+does not establish typed FoodAvailability authoring.
+
+The opaque generic-builder policy governs Radroots builder signing and client
+publication. It does not redefine the standard NIP-46 `sign_event` method or a
+signer backend's externally supplied unsigned-event operation. Those are
+explicit low-level Nostr interoperability boundaries; their outputs carry no
+Radroots typed product-authoring claim and are not product authoring entry
+points.
+
+Generic protocol events that require an external custody provider finalize
+through `RadrootsNostrExternalSigningRequest`. The opaque request is available
+without the relay-client feature and serializes as a standard unsigned Nostr
+event only after generic typed-authoring reservations pass. It accepts a
+returned event only when the author and canonical event id match the request
+and the complete NIP-01 event verifies. It exposes no raw mutable builder,
+unsigned-event conversion, or unchecked deserialization path.
+
+Strict kind-0 Profile publication uses
+`RadrootsNostrProfileEventBuilder`, constructed only from
+`RadrootsAuthoredProfile`. The sealed wrapper permits timestamp selection and
+local signing or client publication, but no raw kind, content, or tag
+mutation. A media-bearing Profile still requires runtime-owned proof of
+successful BUD-02 upload before it reaches this authoring boundary.
+
+## Portable relay-client lifecycle
+
+With the `client` feature, callers can subscribe and publish to selected relay
+sets and explicitly unsubscribe through Radroots types. The wrapper does not
+create detached listeners or take ownership of subscription lifetime. The
+`client,events` feature combination is qualified for native targets and
+`wasm32-unknown-unknown`.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_nostr` distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_nostr` distribution.
diff --git a/crates/nostr_connect/Cargo.toml b/crates/nostr_connect/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_nostr_connect"
+name = "radroots-nostr-connect"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "NIP-46 protocol model for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_nostr_connect"
-readme = "README"
+documentation = "https://docs.rs/radroots-nostr-connect"
+readme = "README.md"
+
+[lib]
+name = "radroots_nostr_connect"
[dependencies]
nostr = { workspace = true, features = ["nip44"] }
@@ -22,5 +25,5 @@ url = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/nostr_connect/README b/crates/nostr_connect/README
@@ -1,36 +0,0 @@
-# radroots_nostr_connect
-
-This is the README for `radroots_nostr_connect`, which provides NIP-46
-connection and URI models for the `radroots` core libraries.
-
-## Overview
-
- * request and response message types for Nostr Connect exchanges;
- * method, permission, and pending-connection outcome models;
- * bunker and client URI parsing and formatting helpers;
- * portable shared models with optional serialization and TypeScript export
- support.
-
-## Current NIP-46 behavior
-
-`connect` accepts the current one-to-four-parameter wire shape. The fourth
-parameter carries bounded, normalized display metadata (`name`, `url`, and
-`image`), while requested permissions remain the third parameter. Legacy
-one-, two-, and three-parameter requests continue to decode. `nostrconnect://`
-requires an ordered relay set and a non-empty secret; `bunker://` preserves
-ordered relays and keeps its secret optional.
-
-The typed client models support secret-echo connection responses,
-`auth_url` continuation, relay switching, and zero-parameter `logout` with an
-`ack` response. Deterministic protocol cases live in
-`contracts/conformance/vectors/nip46/current_session.v1.json`.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_nostr_connect`
-distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_nostr_connect` distribution.
diff --git a/crates/nostr_connect/README.md b/crates/nostr_connect/README.md
@@ -0,0 +1,36 @@
+# radroots-nostr-connect
+
+This is the README for `radroots_nostr_connect`, which provides NIP-46
+connection and URI models for the `radroots` core libraries.
+
+## Overview
+
+ * request and response message types for Nostr Connect exchanges;
+ * method, permission, and pending-connection outcome models;
+ * bunker and client URI parsing and formatting helpers;
+ * portable shared models with optional serialization and TypeScript export
+ support.
+
+## Current NIP-46 behavior
+
+`connect` accepts the current one-to-four-parameter wire shape. The fourth
+parameter carries bounded, normalized display metadata (`name`, `url`, and
+`image`), while requested permissions remain the third parameter. Legacy
+one-, two-, and three-parameter requests continue to decode. `nostrconnect://`
+requires an ordered relay set and a non-empty secret; `bunker://` preserves
+ordered relays and keeps its secret optional.
+
+The typed client models support secret-echo connection responses,
+`auth_url` continuation, relay switching, and zero-parameter `logout` with an
+`ack` response. Deterministic protocol cases live in
+`contracts/conformance/vectors/nip46/current_session.v1.json`.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_nostr_connect`
+distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_nostr_connect` distribution.
diff --git a/crates/protocol/Cargo.toml b/crates/protocol/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-protocol"
+description = "Versioned wire contracts for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_protocol"
+
+[lints]
+workspace = true
diff --git a/crates/protocol/README.md b/crates/protocol/README.md
@@ -0,0 +1,6 @@
+# radroots-protocol
+
+Versioned cross-process and cross-language schemas for Radroots.
+
+This package root is established for the Release V1 refactor. Its governed
+modules and contracts are migrated in the subsequent protocol checkpoints.
diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs
@@ -0,0 +1 @@
+//! Versioned wire contracts for Radroots.
diff --git a/crates/secrets/Cargo.toml b/crates/secrets/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-secrets"
+description = "Secret material and protected-storage abstractions for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_secrets"
+
+[lints]
+workspace = true
diff --git a/crates/secrets/README.md b/crates/secrets/README.md
@@ -0,0 +1,7 @@
+# radroots-secrets
+
+Secret material and protected-storage abstractions for Radroots.
+
+This package root is established for the Release V1 refactor. Secret types,
+providers, and wrapping policy are migrated in the subsequent secrets
+checkpoints.
diff --git a/crates/secrets/src/lib.rs b/crates/secrets/src/lib.rs
@@ -0,0 +1 @@
+//! Secret material and protected-storage abstractions for Radroots.
diff --git a/crates/signing/Cargo.toml b/crates/signing/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-signing"
+description = "Signing abstractions and authorization policy for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_signing"
+
+[lints]
+workspace = true
diff --git a/crates/signing/README.md b/crates/signing/README.md
@@ -0,0 +1,6 @@
+# radroots-signing
+
+Signing abstractions and authorization policy for Radroots.
+
+This package root is established for the Release V1 refactor. Its signer SPI
+and policy surface are migrated in the subsequent signing checkpoints.
diff --git a/crates/signing/src/lib.rs b/crates/signing/src/lib.rs
@@ -0,0 +1 @@
+//! Signing abstractions and authorization policy for Radroots.
diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-storage"
+description = "Backend-neutral persistence abstractions for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_storage"
+
+[lints]
+workspace = true
diff --git a/crates/storage/README.md b/crates/storage/README.md
@@ -0,0 +1,7 @@
+# radroots-storage
+
+Backend-neutral persistence abstractions for Radroots.
+
+This package root is established for the Release V1 refactor. Storage SPIs,
+models, and atomic operation contracts are migrated in the subsequent storage
+checkpoints.
diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs
@@ -0,0 +1 @@
+//! Backend-neutral persistence abstractions for Radroots.
diff --git a/crates/storage_sqlite/Cargo.toml b/crates/storage_sqlite/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-storage-sqlite"
+description = "SQLite storage backend for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_storage_sqlite"
+
+[lints]
+workspace = true
diff --git a/crates/storage_sqlite/README.md b/crates/storage_sqlite/README.md
@@ -0,0 +1,7 @@
+# radroots-storage-sqlite
+
+SQLite storage backend for Radroots.
+
+This package root is established for the Release V1 refactor. Its backend
+implementation and migration authority are introduced in the subsequent
+SQLite storage checkpoints.
diff --git a/crates/storage_sqlite/src/lib.rs b/crates/storage_sqlite/src/lib.rs
@@ -0,0 +1 @@
+//! SQLite storage backend for Radroots.
diff --git a/crates/sync/Cargo.toml b/crates/sync/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-sync"
+description = "Synchronization orchestration for Radroots"
+version = "0.1.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+authors.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots_sync"
+
+[lints]
+workspace = true
diff --git a/crates/sync/README.md b/crates/sync/README.md
@@ -0,0 +1,6 @@
+# radroots-sync
+
+Synchronization orchestration for Radroots.
+
+This package root is established for the Release V1 refactor. Sync planning,
+execution, and reporting are migrated in the subsequent sync checkpoints.
diff --git a/crates/sync/src/lib.rs b/crates/sync/src/lib.rs
@@ -0,0 +1 @@
+//! Synchronization orchestration for Radroots.
diff --git a/crates/trade/Cargo.toml b/crates/trade/Cargo.toml
@@ -1,16 +1,19 @@
[package]
-name = "radroots_trade"
+name = "radroots-trade"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Trade domain model for Radroots"
repository.workspace = true
homepage.workspace = true
-documentation = "https://docs.rs/radroots_trade"
-readme = "README"
+documentation = "https://docs.rs/radroots-trade"
+readme = "README.md"
+
+[lib]
+name = "radroots_trade"
[features]
default = ["std", "serde", "serde_json"]
@@ -90,5 +93,5 @@ sqlx = { workspace = true, default-features = false, features = [
] }
tokio = { workspace = true, features = ["macros", "rt"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/trade/README b/crates/trade/README
@@ -1,25 +0,0 @@
-# radroots_trade
-
-This is the README for `radroots_trade`, which provides shared trade workflow
-helpers for the `radroots` core libraries.
-
-## Overview
-
- * listing model types such as subtotals and totals built on `radroots_core`
- money, quantity, currency, and unit values;
- * listing publish helpers for canonical address normalization, author checks,
- and event-shaping workflows;
- * one typed Operational Listing semantic reducer shared by unsigned model
- validation and the signature-verified Nostr event boundary;
- * order and public-trade helpers for shared request validation and projection;
- * optional `serde` and `serde_json` support for shared model serialization;
- * portable shared-model support for both `std` and `no_std` builds.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_trade` distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_trade` distribution.
diff --git a/crates/trade/README.md b/crates/trade/README.md
@@ -0,0 +1,25 @@
+# radroots-trade
+
+This is the README for `radroots_trade`, which provides shared trade workflow
+helpers for the `radroots` core libraries.
+
+## Overview
+
+ * listing model types such as subtotals and totals built on `radroots_core`
+ money, quantity, currency, and unit values;
+ * listing publish helpers for canonical address normalization, author checks,
+ and event-shaping workflows;
+ * one typed Operational Listing semantic reducer shared by unsigned model
+ validation and the signature-verified Nostr event boundary;
+ * order and public-trade helpers for shared request validation and projection;
+ * optional `serde` and `serde_json` support for shared model serialization;
+ * portable shared-model support for both `std` and `no_std` builds.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_trade` distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_trade` distribution.
diff --git a/crates/transport/Cargo.toml b/crates/transport/Cargo.toml
@@ -1,15 +1,18 @@
[package]
-name = "radroots_transport"
+name = "radroots-transport"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Transport-neutral delivery model for Radroots"
repository.workspace = true
homepage.workspace = true
-readme = "README"
+readme = "README.md"
+
+[lib]
+name = "radroots_transport"
[features]
default = ["serde"]
@@ -23,5 +26,5 @@ sha2 = { workspace = true, default-features = false }
futures = { workspace = true }
serde_json = { workspace = true, features = ["std"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/transport/README b/crates/transport/README
@@ -1,21 +0,0 @@
-# radroots_transport
-
-This is the README for `radroots_transport`, which provides transport-neutral
-delivery models for the `radroots` core libraries.
-
-## Overview
-
- * transport kind, implementation state, and status model types;
- * target URI, target fingerprint, target set, request, and receipt contracts;
- * deterministic target fingerprints based on transport kind and canonical
- target URI;
- * portable shared models with optional `serde` support.
-
-## Copyright
-
-Except as otherwise noted, all files in the `radroots_transport` distribution are
-
- Copyright (c) 2025 Tyson Lupul
-
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_transport` distribution.
diff --git a/crates/transport/README.md b/crates/transport/README.md
@@ -0,0 +1,21 @@
+# radroots-transport
+
+This is the README for `radroots_transport`, which provides transport-neutral
+delivery models for the `radroots` core libraries.
+
+## Overview
+
+ * transport kind, implementation state, and status model types;
+ * target URI, target fingerprint, target set, request, and receipt contracts;
+ * deterministic target fingerprints based on transport kind and canonical
+ target URI;
+ * portable shared models with optional `serde` support.
+
+## Copyright
+
+Except as otherwise noted, all files in the `radroots_transport` distribution are
+
+ Copyright (c) 2025 Tyson Lupul
+
+For information on usage and redistribution, and for a DISCLAIMER OF ALL
+WARRANTIES, see LICENSE included in the `radroots_transport` distribution.
diff --git a/crates/transport_nostr/Cargo.toml b/crates/transport_nostr/Cargo.toml
@@ -1,15 +1,18 @@
[package]
-name = "radroots_transport_nostr"
+name = "radroots-transport-nostr"
publish = false
-version = "1.0.0-alpha.1"
+version = "0.1.0"
edition.workspace = true
-authors = ["Tyson Lupul <tyson@radroots.org>"]
+authors.workspace = true
rust-version.workspace = true
license.workspace = true
description = "Nostr transport layer for Radroots"
repository.workspace = true
homepage.workspace = true
-readme = "README"
+readme = "README.md"
+
+[lib]
+name = "radroots_transport_nostr"
[features]
default = ["std", "client", "storage", "runtime-tokio"]
@@ -58,5 +61,5 @@ url = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt"] }
-[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] }
+[lints]
+workspace = true
diff --git a/crates/transport_nostr/README b/crates/transport_nostr/README
@@ -1,43 +0,0 @@
-# radroots_transport_nostr
-
-Deterministic Nostr relay transport substrate for exact signed-event publish,
-fetch ingest, and outbox delivery target coordination.
-
-Every fetch path verifies the NIP-01 id and signature before filter matching,
-unique-event budgeting, or returning an event. Repeated event ids preserve
-per-relay observation evidence without consuming the unique-event limit. The
-unique-event limit is bounded at 1,000 so final stored-event visibility can be
-evaluated in one coherent event-store snapshot. A fetch scans at most 64,000
-raw events and 64 MiB of aggregate raw JSON, and rejects any individual raw
-event over 256 KiB before Radroots parses adapter raw JSON. Count and byte
-budgets are charged globally, in adapter order, before Radroots parsing,
-filtering, deduplication, or accepted-event limiting, so malformed and otherwise
-rejected events cannot bypass them. The official SDK adapter enforces the same
-retained-prefix budgets after SDK frame/event decoding and before retaining its
-serialized JSON; the connector's upstream frame parser remains responsible for
-its own first-pass network limits. `Truncated` is distinct from relay `EOSE`,
-including for later target relays that were not queried after a global count or
-byte budget was reached.
-
-Fetch-ingest receipts report verification, contract admission, immutable
-valid-stream eligibility, and current visibility as independent exhaustive
-enums. Verification failure is not a contract-invalid result, and unsupported
-admission does not hide whether the stored event is current, suppressed, or not
-admitted. Persisted events obtain visibility from the event store's central
-authority after the complete accepted fetch batch has been ingested, so event
-receipts and aggregate visibility counts describe final post-batch state rather
-than transient per-item state. Repeated receipt IDs are deduplicated before the
-single snapshot lookup and then mapped back to every receipt. Ephemeral events
-use the explicit `not_persisted` visibility result.
-`admission_code` carries the stable classifier diagnostic when classification
-produces one. Inserted, duplicate, and not-persisted persistence outcomes retain
-separate flags and aggregate counts. Local event-store failures abort the
-operation and remain typed transport errors, so callers can retry without
-confusing storage failure with bad relay input.
-
-`RadrootsRelayUrlPolicy::Public` is for trusted relay configuration. It rejects
-non-canonical and known non-global literal destinations, but hostname checks do
-not pin DNS resolution in the SDK connector and are not an SSRF boundary for
-attacker-controlled relay hostnames. Validate resolved addresses at the network
-boundary or use a connector that pins approved resolutions before accepting
-untrusted relay configuration.
diff --git a/crates/transport_nostr/README.md b/crates/transport_nostr/README.md
@@ -0,0 +1,43 @@
+# radroots-transport-nostr
+
+Deterministic Nostr relay transport substrate for exact signed-event publish,
+fetch ingest, and outbox delivery target coordination.
+
+Every fetch path verifies the NIP-01 id and signature before filter matching,
+unique-event budgeting, or returning an event. Repeated event ids preserve
+per-relay observation evidence without consuming the unique-event limit. The
+unique-event limit is bounded at 1,000 so final stored-event visibility can be
+evaluated in one coherent event-store snapshot. A fetch scans at most 64,000
+raw events and 64 MiB of aggregate raw JSON, and rejects any individual raw
+event over 256 KiB before Radroots parses adapter raw JSON. Count and byte
+budgets are charged globally, in adapter order, before Radroots parsing,
+filtering, deduplication, or accepted-event limiting, so malformed and otherwise
+rejected events cannot bypass them. The official SDK adapter enforces the same
+retained-prefix budgets after SDK frame/event decoding and before retaining its
+serialized JSON; the connector's upstream frame parser remains responsible for
+its own first-pass network limits. `Truncated` is distinct from relay `EOSE`,
+including for later target relays that were not queried after a global count or
+byte budget was reached.
+
+Fetch-ingest receipts report verification, contract admission, immutable
+valid-stream eligibility, and current visibility as independent exhaustive
+enums. Verification failure is not a contract-invalid result, and unsupported
+admission does not hide whether the stored event is current, suppressed, or not
+admitted. Persisted events obtain visibility from the event store's central
+authority after the complete accepted fetch batch has been ingested, so event
+receipts and aggregate visibility counts describe final post-batch state rather
+than transient per-item state. Repeated receipt IDs are deduplicated before the
+single snapshot lookup and then mapped back to every receipt. Ephemeral events
+use the explicit `not_persisted` visibility result.
+`admission_code` carries the stable classifier diagnostic when classification
+produces one. Inserted, duplicate, and not-persisted persistence outcomes retain
+separate flags and aggregate counts. Local event-store failures abort the
+operation and remain typed transport errors, so callers can retry without
+confusing storage failure with bad relay input.
+
+`RadrootsRelayUrlPolicy::Public` is for trusted relay configuration. It rejects
+non-canonical and known non-global literal destinations, but hostname checks do
+not pin DNS resolution in the SDK connector and are not an SSRF boundary for
+attacker-controlled relay hostnames. Validate resolved addresses at the network
+boundary or use a connector that pins approved resolutions before accepting
+untrusted relay configuration.
diff --git a/tools/xtask/src/architecture.rs b/tools/xtask/src/architecture.rs
@@ -286,12 +286,13 @@ fn validate_public_package_metadata(
.values()
.find(|repository| repository.url == workspace.workspace.package.repository)
.ok_or_else(|| "workspace repository has no architecture allocation".to_owned())?;
- let local_packages = repository.packages.iter().collect::<BTreeSet<_>>();
+ let local_packages = repository.packages.iter().cloned().collect::<BTreeSet<_>>();
let all_packages = architecture
.package
.iter()
.map(|package| package.name.as_str())
.collect::<BTreeSet<_>>();
+ let mut found_local_packages = BTreeSet::new();
for member in &workspace.workspace.members {
let manifest_path = workspace_root.join(member).join("Cargo.toml");
@@ -311,11 +312,12 @@ fn validate_public_package_metadata(
if !all_packages.contains(name) {
continue;
}
- if !local_packages.contains(&name.to_owned()) {
+ if !local_packages.contains(name) {
return Err(format!(
"public package {name} belongs to a different canonical repository"
));
}
+ found_local_packages.insert(name.to_owned());
validate_public_manifest_field(
package,
"version",
@@ -391,6 +393,21 @@ fn validate_public_package_metadata(
));
}
}
+ if found_local_packages != local_packages {
+ let missing = local_packages
+ .difference(&found_local_packages)
+ .cloned()
+ .collect::<Vec<_>>()
+ .join(", ");
+ let extra = found_local_packages
+ .difference(&local_packages)
+ .cloned()
+ .collect::<Vec<_>>()
+ .join(", ");
+ return Err(format!(
+ "workspace public package inventory is missing: {missing}; workspace public package inventory has unallocated packages: {extra}"
+ ));
+ }
Ok(())
}
@@ -814,4 +831,15 @@ adr_required = false
assert!(error.contains("must inherit the workspace lint policy"));
let _ = fs::remove_dir_all(root);
}
+
+ #[test]
+ fn public_package_inventory_requires_every_local_package() {
+ let root = test_root("public_package_inventory");
+ fs::write(root.join("Cargo.toml"), complete_workspace_manifest(""))
+ .expect("write workspace manifest");
+ let error = validate_public_package_metadata(&root, &architecture())
+ .expect_err("missing local public package must fail");
+ assert!(error.contains("workspace public package inventory is missing: radroots"));
+ let _ = fs::remove_dir_all(root);
+ }
}
diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs
@@ -3141,6 +3141,7 @@ struct WorkspaceReleaseClassification {
struct CratesReleaseArchitecture {
spec_id: String,
package_count: usize,
+ initial_version: String,
repositories: CratesReleaseRepositories,
package: Vec<CratesReleasePackage>,
}
@@ -3829,27 +3830,46 @@ fn validate_workspace_version_lockstep(
));
}
- let exact_requirement = format!("={contract_version}");
+ let architecture_path = workspace_root.join("docs/specs/radroots_crates_release_v1.toml");
+ let public_versions = if architecture_path.is_file() {
+ let architecture = parse_toml::<CratesReleaseArchitecture>(&architecture_path)?;
+ architecture
+ .repositories
+ .lib
+ .packages
+ .into_iter()
+ .map(|name| (name, architecture.initial_version.clone()))
+ .collect::<BTreeMap<_, _>>()
+ } else {
+ BTreeMap::new()
+ };
let mut governed_packages = BTreeMap::new();
for member in &workspace_manifest.workspace.members {
let package_path = workspace_root.join(member).join("Cargo.toml");
let package = parse_toml::<VersionedPackageCargoManifest>(&package_path)?;
+ let expected_version = public_versions
+ .get(&package.package.name)
+ .map_or(contract_version, String::as_str);
match package.package.version {
- PackageVersionSource::Literal(ref version) if version == contract_version => {}
+ PackageVersionSource::Literal(ref version) if version == expected_version => {}
PackageVersionSource::Literal(version) => {
return Err(format!(
- "workspace member {member} package version {version} must match contract version {contract_version}"
+ "workspace member {member} package version {version} must match governed version {expected_version}"
));
}
PackageVersionSource::Workspace { workspace } => {
return Err(format!(
- "workspace member {member} must set an explicit package version {contract_version}, not version.workspace = {workspace}, so mounted path consumers preserve the public package version"
+ "workspace member {member} must set an explicit package version {expected_version}, not version.workspace = {workspace}, so mounted path consumers preserve the governed package version"
));
}
}
- governed_packages.insert(member.clone(), package.package.name.clone());
+ governed_packages.insert(
+ member.clone(),
+ (package.package.name.clone(), expected_version.to_owned()),
+ );
if package.package.name.starts_with("radroots_") {
+ let exact_requirement = format!("={expected_version}");
let dependency = workspace_manifest
.workspace
.dependencies
@@ -3879,25 +3899,26 @@ fn validate_workspace_version_lockstep(
let Some(path) = dependency.path.as_deref() else {
continue;
};
- if governed_packages.contains_key(path)
- && dependency.version.as_deref() != Some(exact_requirement.as_str())
- {
+ let Some((_, expected_version)) = governed_packages.get(path) else {
+ continue;
+ };
+ let exact_requirement = format!("={expected_version}");
+ if dependency.version.as_deref() != Some(exact_requirement.as_str()) {
return Err(format!(
"workspace path dependency {dependency_name} version must be the exact requirement {exact_requirement}"
));
}
}
- validate_cargo_lock_version_lockstep(workspace_root, contract_version, &governed_packages)
+ validate_cargo_lock_version_lockstep(workspace_root, &governed_packages)
}
fn validate_cargo_lock_version_lockstep(
workspace_root: &Path,
- contract_version: &str,
- governed_packages: &BTreeMap<String, String>,
+ governed_packages: &BTreeMap<String, (String, String)>,
) -> Result<(), String> {
let lock = parse_toml::<CargoLockManifest>(&workspace_root.join("Cargo.lock"))?;
- for (member, package_name) in governed_packages {
+ for (member, (package_name, expected_version)) in governed_packages {
let workspace_entries = lock
.package
.iter()
@@ -3908,9 +3929,9 @@ fn validate_cargo_lock_version_lockstep(
"Cargo.lock must contain exactly one source-free entry for workspace member {member} ({package_name})"
));
}
- if workspace_entries[0].version != contract_version {
+ if workspace_entries[0].version != *expected_version {
return Err(format!(
- "Cargo.lock package {package_name} version {} must match contract version {contract_version}",
+ "Cargo.lock package {package_name} version {} must match governed version {expected_version}",
workspace_entries[0].version
));
}
@@ -12444,7 +12465,7 @@ crates = ["radroots_a"]
.collect::<Vec<_>>()
.join(", ");
let mut architecture = format!(
- "spec_id = \"radroots.crates.release.v1\"\npackage_count = 19\n\n[repositories.lib]\npackages = [\"package-01\"]\n\n[repositories.sdk]\npackages = [{external_toml}]\n"
+ "spec_id = \"radroots.crates.release.v1\"\npackage_count = 19\ninitial_version = \"0.1.0\"\n\n[repositories.lib]\npackages = [\"package-01\"]\n\n[repositories.sdk]\npackages = [{external_toml}]\n"
);
for name in &approved {
architecture.push_str(&format!("\n[[package]]\nname = \"{name}\"\n"));
diff --git a/tools/xtask/src/contract/food_availability_projection.rs b/tools/xtask/src/contract/food_availability_projection.rs
@@ -2638,13 +2638,17 @@ fn validate_blossom_dependency_values(
.and_then(toml::Value::as_table)
.ok_or_else(|| "Cargo.toml must define workspace dependency radroots_blossom".to_owned())?;
if workspace_dependency
- .get("path")
+ .get("package")
.and_then(toml::Value::as_str)
- != Some("crates/blossom")
+ != Some("radroots-blossom")
+ || workspace_dependency
+ .get("path")
+ .and_then(toml::Value::as_str)
+ != Some("crates/blossom")
|| workspace_dependency
.get("version")
.and_then(toml::Value::as_str)
- != Some("=1.0.0-alpha.1")
+ != Some("=0.1.0")
|| workspace_dependency
.get("default-features")
.and_then(toml::Value::as_bool)
diff --git a/tools/xtask/src/contract/nip09_reconciliation.rs b/tools/xtask/src/contract/nip09_reconciliation.rs
@@ -236,7 +236,7 @@ const GOVERNED_DEPENDENCY_TABLE_SHA256: [(&str, &str); 7] = [
),
(
"Cargo.toml#governed-workspace-dependencies",
- "0aa0aeb7988745aad1101c820a340c8ac04a5833c2ec7f7c997b5d9dfac010a3",
+ "2a5e34872160a39daf03ffd15f32fc824e91b2c0253b4bca84c6610b0238ca32",
),
];
@@ -577,7 +577,11 @@ const RUNTIME_DEPENDENCY_ROOTS: [RuntimeDependencyRootSpec; 10] = [
#[derive(Clone, Copy)]
struct CargoPackageFeatureSpec {
+ // The reconciliation manifest predates the Cargo package-name migration and
+ // records the Rust crate identifier. Keep that immutable identifier separate
+ // from the package name validated in the current Cargo manifest.
package: &'static str,
+ cargo_package_name: &'static str,
manifest_path: &'static str,
default_features_enabled: bool,
selected_features: &'static [&'static str],
@@ -587,6 +591,7 @@ struct CargoPackageFeatureSpec {
const CARGO_PACKAGE_FEATURE_SPECS: &[CargoPackageFeatureSpec] = &[
CargoPackageFeatureSpec {
package: "radroots_core",
+ cargo_package_name: "radroots-core",
manifest_path: CORE_CARGO_MANIFEST_RELATIVE,
default_features_enabled: false,
selected_features: &["serde", "std"],
@@ -594,6 +599,7 @@ const CARGO_PACKAGE_FEATURE_SPECS: &[CargoPackageFeatureSpec] = &[
},
CargoPackageFeatureSpec {
package: "radroots_event_store",
+ cargo_package_name: "radroots_event_store",
manifest_path: EVENT_STORE_CARGO_MANIFEST_RELATIVE,
default_features_enabled: true,
selected_features: &["runtime-tokio", "sqlite"],
@@ -601,6 +607,7 @@ const CARGO_PACKAGE_FEATURE_SPECS: &[CargoPackageFeatureSpec] = &[
},
CargoPackageFeatureSpec {
package: "radroots_event_codec",
+ cargo_package_name: "radroots-event-codec",
manifest_path: EVENT_CODEC_CARGO_MANIFEST_RELATIVE,
default_features_enabled: false,
selected_features: &["nostr", "serde", "serde_json", "std"],
@@ -608,6 +615,7 @@ const CARGO_PACKAGE_FEATURE_SPECS: &[CargoPackageFeatureSpec] = &[
},
CargoPackageFeatureSpec {
package: "radroots_event",
+ cargo_package_name: "radroots-event",
manifest_path: EVENT_CARGO_MANIFEST_RELATIVE,
default_features_enabled: false,
selected_features: &["serde", "std"],
@@ -615,6 +623,7 @@ const CARGO_PACKAGE_FEATURE_SPECS: &[CargoPackageFeatureSpec] = &[
},
CargoPackageFeatureSpec {
package: "radroots_blossom",
+ cargo_package_name: "radroots-blossom",
manifest_path: BLOSSOM_CARGO_MANIFEST_RELATIVE,
default_features_enabled: false,
selected_features: &["std"],
@@ -14383,22 +14392,54 @@ xtask = "run -q -p xtask --"
}
let governed_packages = [
- (CORE_CARGO_MANIFEST_RELATIVE, "radroots_core"),
- (EVENT_CARGO_MANIFEST_RELATIVE, "radroots_event"),
- (EVENT_CODEC_CARGO_MANIFEST_RELATIVE, "radroots_event_codec"),
- (BLOSSOM_CARGO_MANIFEST_RELATIVE, "radroots_blossom"),
- (EVENT_STORE_CARGO_MANIFEST_RELATIVE, "radroots_event_store"),
- (TRANSPORT_CARGO_MANIFEST_RELATIVE, "radroots_transport"),
+ (
+ CORE_CARGO_MANIFEST_RELATIVE,
+ "radroots-core",
+ "0.1.0",
+ Some("radroots_core"),
+ ),
+ (
+ EVENT_CARGO_MANIFEST_RELATIVE,
+ "radroots-event",
+ "0.1.0",
+ Some("radroots_event"),
+ ),
+ (
+ EVENT_CODEC_CARGO_MANIFEST_RELATIVE,
+ "radroots-event-codec",
+ "0.1.0",
+ Some("radroots_event_codec"),
+ ),
+ (
+ BLOSSOM_CARGO_MANIFEST_RELATIVE,
+ "radroots-blossom",
+ "0.1.0",
+ Some("radroots_blossom"),
+ ),
+ (
+ EVENT_STORE_CARGO_MANIFEST_RELATIVE,
+ "radroots_event_store",
+ "1.0.0-alpha.1",
+ None,
+ ),
+ (
+ TRANSPORT_CARGO_MANIFEST_RELATIVE,
+ "radroots-transport",
+ "0.1.0",
+ Some("radroots_transport"),
+ ),
];
let mut actual_identities = Vec::new();
- for (relative, expected_package_name) in governed_packages {
+ for (relative, expected_package_name, expected_version, expected_crate_name) in
+ governed_packages
+ {
let manifest = parse_cargo_manifest(workspace_root, relative)?;
let package = manifest
.get("package")
.and_then(toml::Value::as_table)
.ok_or_else(|| format!("{relative} must declare [package]"))?;
if package.get("name").and_then(toml::Value::as_str) != Some(expected_package_name)
- || package.get("version").and_then(toml::Value::as_str) != Some("1.0.0-alpha.1")
+ || package.get("version").and_then(toml::Value::as_str) != Some(expected_version)
|| package
.get("edition")
.and_then(toml::Value::as_table)
@@ -14413,9 +14454,22 @@ xtask = "run -q -p xtask --"
!= Some(true)
{
return Err(format!(
- "{relative} compiler package identity must remain {expected_package_name} 1.0.0-alpha.1 with workspace edition and rust-version"
+ "{relative} compiler package identity must remain {expected_package_name} {expected_version} with workspace edition and rust-version"
));
}
+ match (expected_crate_name, manifest.get("lib")) {
+ (None, None) => {}
+ (Some(expected), Some(lib))
+ if lib.as_table().is_some_and(|lib| {
+ lib.len() == 1
+ && lib.get("name").and_then(toml::Value::as_str) == Some(expected)
+ }) => {}
+ _ => {
+ return Err(format!(
+ "{relative} must preserve its exact Rust crate name without target-path authority"
+ ));
+ }
+ }
if [
"build",
"autolib",
@@ -14434,7 +14488,6 @@ xtask = "run -q -p xtask --"
if [
"build-dependencies",
"target",
- "lib",
"bin",
"example",
"test",
@@ -14671,10 +14724,10 @@ fn describe_cargo_package_features(
.and_then(|package| package.get("name"))
.and_then(toml::Value::as_str)
.ok_or_else(|| format!("{} must declare package.name", spec.manifest_path))?;
- if package_name != spec.package {
+ if package_name != spec.cargo_package_name {
return Err(format!(
"{} package.name must be {}",
- spec.manifest_path, spec.package
+ spec.manifest_path, spec.cargo_package_name
));
}
let feature_table = manifest
@@ -20473,8 +20526,8 @@ pub(crate) fn migration_for_version"#,
let cargo_lock_path = workspace.path().join(CARGO_LOCK_RELATIVE);
let cargo_lock = fs::read_to_string(&cargo_lock_path).expect("Cargo.lock");
let transport_lock_package = r#"[[package]]
-name = "radroots_transport"
-version = "1.0.0-alpha.1"
+name = "radroots-transport"
+version = "0.1.0"
dependencies = [
"futures",
"serde",
@@ -20483,8 +20536,8 @@ dependencies = [
]
"#;
let future_transport_lock_package = r#"[[package]]
-name = "radroots_transport"
-version = "1.0.0-alpha.1"
+name = "radroots-transport"
+version = "0.1.0"
dependencies = [
"futures",
"serde",
diff --git a/tools/xtask/src/coverage.rs b/tools/xtask/src/coverage.rs
@@ -4000,7 +4000,7 @@ test_threads = 0
let out = temp_dir_path("run_crate_runner");
let args = vec![
"--crate".to_string(),
- "radroots_core".to_string(),
+ "radroots-core".to_string(),
"--out".to_string(),
out.display().to_string(),
"--test-threads".to_string(),
@@ -4033,13 +4033,13 @@ test_threads = 0
assert!(
rendered_commands
.iter()
- .filter(|rendered| rendered.contains("report -p radroots_core"))
+ .filter(|rendered| rendered.contains("report -p radroots-core"))
.all(|rendered| rendered.contains("--ignore-filename-regex"))
);
assert!(
rendered_commands
.iter()
- .filter(|rendered| rendered.contains("report -p radroots_core"))
+ .filter(|rendered| rendered.contains("report -p radroots-core"))
.all(|rendered| rendered.contains(COVERAGE_EXTERNAL_IGNORE_FILENAME_REGEX))
);
fs::remove_dir_all(out).expect("remove run crate output dir");
@@ -4049,7 +4049,7 @@ test_threads = 0
fn coverage_ignore_filename_regex_excludes_external_and_sibling_workspace_paths() {
let root = workspace_root();
let ignore_regex =
- coverage_ignore_filename_regex(&root, "radroots_core").expect("build ignore regex");
+ coverage_ignore_filename_regex(&root, "radroots-core").expect("build ignore regex");
assert!(ignore_regex.contains(COVERAGE_EXTERNAL_IGNORE_FILENAME_REGEX));
assert!(ignore_regex.contains("crates/identity"));
assert!(ignore_regex.contains("crates/core/tests"));
@@ -4341,7 +4341,7 @@ test_threads = 0
#[test]
fn run_crate_with_runner_uses_default_output_dir_when_out_is_missing() {
- let args = vec!["--crate".to_string(), "radroots_core".to_string()];
+ let args = vec!["--crate".to_string(), "radroots-core".to_string()];
let mut output_path_seen = false;
let mut runner = |cmd: Command, _: &str| {
let rendered = cmd
@@ -4371,7 +4371,7 @@ test_threads = 0
let out = temp_dir_path("run_crate_runner_fail");
let args = vec![
"--crate".to_string(),
- "radroots_core".to_string(),
+ "radroots-core".to_string(),
"--out".to_string(),
out.display().to_string(),
];
@@ -4384,7 +4384,7 @@ test_threads = 0
write_file(&root.join("blocker"), "x");
let args = vec![
"--crate".to_string(),
- "radroots_core".to_string(),
+ "radroots-core".to_string(),
"--out".to_string(),
root.join("blocker").join("nested").display().to_string(),
];