cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit aeda0daab1770993d9741adf93e659c769e53149
parent 0a408fb04b8542b0d9368ea7933bbc6aab7b754e
Author: triesap <tyson@radroots.org>
Date:   Sat, 11 Jul 2026 08:10:14 +0000

mesh: derive policy check from mesh model

- add the direct radroots_mesh dependency for preview policy inspection
- report structured mesh policy denial fields from the shared admission model
- render deny reason and compression in terminal mesh policy output
- cover mesh.policy.check JSON output and dependency classification guard updates

Diffstat:
MCargo.lock | 8++++++++
MCargo.toml | 1+
Msrc/out/terminal/renderers/runtime.rs | 2++
Msrc/runtime/mesh.rs | 32++++++++++++++++++++++++++------
Msrc/runtime/sdk.rs | 7+++++++
Msrc/view/runtime.rs | 8++++++++
Mtests/target_cli.rs | 30++++++++++++++++++++++++++++++
7 files changed, 82 insertions(+), 6 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -3625,6 +3625,7 @@ dependencies = [ "radroots_identity", "radroots_local_events", "radroots_log", + "radroots_mesh", "radroots_nostr", "radroots_nostr_accounts", "radroots_nostr_connect", @@ -3752,6 +3753,13 @@ dependencies = [ ] [[package]] +name = "radroots_mesh" +version = "0.1.0-alpha.2" +dependencies = [ + "radroots_transport", +] + +[[package]] name = "radroots_nostr" version = "0.1.0-alpha.2" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml @@ -34,6 +34,7 @@ radroots_events_codec = { path = "../lib/crates/events_codec", features = ["nost radroots_identity = { path = "../lib/crates/identity" } radroots_local_events = { path = "../lib/crates/local_events" } radroots_log = { path = "../lib/crates/log" } +radroots_mesh = { path = "../lib/crates/mesh", default-features = false } radroots_nostr_accounts = { path = "../lib/crates/nostr_accounts", features = ["os-keyring"] } radroots_nostr = { path = "../lib/crates/nostr", features = ["client", "events"] } radroots_nostr_connect = { path = "../lib/crates/nostr_connect" } diff --git a/src/out/terminal/renderers/runtime.rs b/src/out/terminal/renderers/runtime.rs @@ -279,6 +279,8 @@ fn mesh_status_document(envelope: &OutputEnvelope, result: &Value) -> TerminalDo common::push_path_field(&mut document, "Implementation", result, &["implementation"]); common::push_bool_field(&mut document, "Usable", result, &["usable_for_delivery"]); common::push_path_field(&mut document, "Decision", result, &["decision"]); + common::push_path_field(&mut document, "Deny Reason", result, &["deny_reason"]); + common::push_path_field(&mut document, "Compression", result, &["compression"]); common::push_path_field(&mut document, "Message", result, &["message"]); document } diff --git a/src/runtime/mesh.rs b/src/runtime/mesh.rs @@ -1,3 +1,7 @@ +use radroots_mesh::{ + RADROOTS_MESH_PREVIEW_DENIAL_MESSAGE, RadrootsMeshAdmissionInput, RadrootsMeshPayloadPolicy, + RadrootsMeshPrivacyClass, RadrootsMeshScope, +}; use serde_json::Value as JsonValue; use toml::{Value, map::Map}; @@ -45,21 +49,37 @@ pub fn status(config: &RuntimeConfig) -> MeshStatusView { configured: scope != "disabled", implementation: "preview_unavailable".to_owned(), usable_for_delivery: false, - message: "Reticulum mesh preview is explicit and unavailable for real delivery".to_owned(), + message: RADROOTS_MESH_PREVIEW_DENIAL_MESSAGE.to_owned(), } } pub fn policy_check(config: &RuntimeConfig) -> MeshPolicyCheckView { + let policy = RadrootsMeshPayloadPolicy::preview_unavailable(); + let privacy_class = RadrootsMeshPrivacyClass::PublicEvent; + let input = RadrootsMeshAdmissionInput::new(RadrootsMeshScope::Local, privacy_class, 1, 1); + let decision = policy.evaluate(&input); + let deny_reason = decision + .deny_reason() + .map(|reason| reason.label()) + .unwrap_or("none"); + MeshPolicyCheckView { state: "ready".to_owned(), source: MESH_SOURCE.to_owned(), scope: config.mesh.scope.as_str().to_owned(), - policy: "reticulum_preview_delivery".to_owned(), + policy: policy.policy_id().to_owned(), transport: "reticulum".to_owned(), - usable_for_delivery: false, - decision: "reject_delivery_attempt".to_owned(), - message: "Reticulum preview never falls back to Nostr and cannot deliver real events" - .to_owned(), + usable_for_delivery: decision.usable_for_delivery(), + decision: decision.label().to_owned(), + deny_reason: deny_reason.to_owned(), + privacy_class: privacy_class.label().to_owned(), + payload_bytes: input.payload_bytes, + frame_bytes: input.frame_bytes, + max_payload_bytes: policy.max_payload_bytes, + max_frame_bytes: policy.max_frame_bytes, + compression: policy.compression.label().to_owned(), + custom_scopes_enabled: policy.custom_scopes_enabled, + message: decision.message().to_owned(), } } diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs @@ -870,6 +870,13 @@ mod tests { }, DirectRrRsDependency { section: "dependencies", + name: "radroots_mesh", + owner: "cli-mesh-preview-policy", + reason: "canonical Reticulum preview admission policy and structured delivery denial reporting", + lifecycle: "retain while CLI exposes mesh preview status and policy inspection", + }, + DirectRrRsDependency { + section: "dependencies", name: "radroots_nostr", owner: "cli-signer-and-event-runtime", reason: "remote signer relay transport, account event conversion, and direct publish command transport", diff --git a/src/view/runtime.rs b/src/view/runtime.rs @@ -3389,6 +3389,14 @@ pub struct MeshPolicyCheckView { pub transport: String, pub usable_for_delivery: bool, pub decision: String, + pub deny_reason: String, + pub privacy_class: String, + pub payload_bytes: u64, + pub frame_bytes: u64, + pub max_payload_bytes: u64, + pub max_frame_bytes: u64, + pub compression: String, + pub custom_scopes_enabled: bool, pub message: String, } diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -1827,6 +1827,36 @@ fn mesh_status_reports_reticulum_preview_unusable_state_without_fallback() { } #[test] +fn mesh_policy_check_reports_structured_preview_denial() { + let sandbox = RadrootsCliSandbox::new(); + + let value = sandbox.json_success(&["--format", "json", "mesh", "policy", "check"]); + let result = &value["result"]; + + assert_eq!(value["operation_id"], "mesh.policy.check"); + assert_eq!(result["scope"], "disabled"); + assert_eq!(result["policy"], "reticulum_preview_delivery"); + assert_eq!(result["transport"], "reticulum"); + assert_eq!(result["usable_for_delivery"], false); + assert_eq!(result["decision"], "denied"); + assert_eq!(result["deny_reason"], "preview_unavailable"); + assert_eq!(result["privacy_class"], "public_event"); + assert_eq!(result["payload_bytes"], 1); + assert_eq!(result["frame_bytes"], 1); + assert_eq!(result["max_payload_bytes"], 0); + assert_eq!(result["max_frame_bytes"], 0); + assert_eq!(result["compression"], "disabled"); + assert_eq!(result["custom_scopes_enabled"], false); + assert_contains(&result["message"], "Reticulum mesh preview is explicit"); + assert!( + !result["message"] + .as_str() + .expect("message") + .contains("Nostr") + ); +} + +#[test] fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces() { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));