commit 6343ebe1310bde309a5bfe63ff86f104dd0545d1
parent 1e0b18f6d0d0dfe38d694595b928c506694b7af7
Author: triesap <tyson@radroots.org>
Date: Thu, 9 Jul 2026 17:24:28 +0000
transport: align CLI status contract
- serialize CLI transport and sync status rows with canonical transport fields
- add Reticulum preview scope and inert agent endpoint config
- remove old SDK target-policy helper usage from CLI runtimes
Diffstat:
10 files changed, 331 insertions(+), 227 deletions(-)
diff --git a/src/cli/input.rs b/src/cli/input.rs
@@ -337,6 +337,16 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
Value::String(behavior.as_str().to_owned()),
);
}
+ insert_string(
+ &mut input,
+ "reticulum_preview_scope",
+ &args.reticulum_preview_scope,
+ );
+ insert_string(
+ &mut input,
+ "reticulum_preview_agent_endpoint",
+ &args.reticulum_preview_agent_endpoint,
+ );
insert_string(&mut input, "proxy_url", &args.proxy_url);
insert_path(&mut input, "proxy_token_file", &args.proxy_token_file);
insert_string(
diff --git a/src/cli/transport.rs b/src/cli/transport.rs
@@ -35,6 +35,10 @@ pub struct TransportProfileSetArgs {
pub nostr_relay: Vec<String>,
#[arg(long = "reticulum-preview-behavior", value_enum)]
pub reticulum_preview_behavior: Option<ReticulumPreviewBehaviorArg>,
+ #[arg(long = "reticulum-preview-scope")]
+ pub reticulum_preview_scope: Option<String>,
+ #[arg(long = "reticulum-preview-agent-endpoint")]
+ pub reticulum_preview_agent_endpoint: Option<String>,
#[arg(long = "proxy-url")]
pub proxy_url: Option<String>,
#[arg(long = "proxy-token-file", value_name = "PATH")]
diff --git a/src/out/terminal/renderers/runtime.rs b/src/out/terminal/renderers/runtime.rs
@@ -186,7 +186,7 @@ fn push_transport_status_table(document: &mut TerminalDocument, result: &Value,
.flatten()
.map(|transport| {
TerminalTableRow::new(vec![
- common::string(transport, &["transport_kind"]).unwrap_or_default(),
+ common::string(transport, &["transport"]).unwrap_or_default(),
common::string(transport, &["profile_id"]).unwrap_or_default(),
common::bool_path(transport, &["configured"])
.map(|value| if value { "yes" } else { "no" }.to_owned())
diff --git a/src/runtime/config.rs b/src/runtime/config.rs
@@ -7,6 +7,7 @@ use std::path::PathBuf;
use radroots_runtime::{parse_bool_value, parse_strict_env_file, parse_u64_value};
use radroots_runtime_paths::RadrootsPathResolver;
use radroots_secret_vault::{RadrootsHostVaultPolicy, RadrootsSecretBackend};
+use radroots_transport::{RADROOTS_RETICULUM_PREVIEW_SCOPE_ID, RadrootsTransportMeshScopeId};
use radroots_transport_nostr::{
RadrootsRelayTransportError, RadrootsRelayUrl, RadrootsRelayUrlPolicy,
};
@@ -45,6 +46,10 @@ const ENV_CLI_TRANSPORT_PROFILE: &str = "RADROOTS_CLI_TRANSPORT_PROFILE";
const ENV_CLI_TRANSPORT_NOSTR_RELAY_URLS: &str = "RADROOTS_CLI_TRANSPORT_NOSTR_RELAY_URLS";
const ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_BEHAVIOR: &str =
"RADROOTS_CLI_TRANSPORT_RETICULUM_PREVIEW_BEHAVIOR";
+const ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_SCOPE: &str =
+ "RADROOTS_CLI_TRANSPORT_RETICULUM_PREVIEW_SCOPE";
+const ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_AGENT_ENDPOINT: &str =
+ "RADROOTS_CLI_TRANSPORT_RETICULUM_PREVIEW_AGENT_ENDPOINT";
const ENV_CLI_TRANSPORT_PROXY_URL: &str = "RADROOTS_CLI_TRANSPORT_PROXY_URL";
const ENV_CLI_TRANSPORT_PROXY_TOKEN_FILE: &str = "RADROOTS_CLI_TRANSPORT_PROXY_TOKEN_FILE";
const ENV_CLI_TRANSPORT_PROXY_TOKEN_SECRET_ID: &str =
@@ -69,6 +74,8 @@ const SUPPORTED_ENV_FILE_KEYS: &[&str] = &[
ENV_CLI_TRANSPORT_PROFILE,
ENV_CLI_TRANSPORT_NOSTR_RELAY_URLS,
ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_BEHAVIOR,
+ ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_SCOPE,
+ ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_AGENT_ENDPOINT,
ENV_CLI_TRANSPORT_PROXY_URL,
ENV_CLI_TRANSPORT_PROXY_TOKEN_FILE,
ENV_CLI_TRANSPORT_PROXY_TOKEN_SECRET_ID,
@@ -252,6 +259,8 @@ pub struct TransportConfig {
pub source: TransportConfigSource,
pub nostr_relay_urls: Vec<String>,
pub reticulum_preview_behavior: ReticulumPreviewBehavior,
+ pub reticulum_preview_scope: String,
+ pub reticulum_preview_agent_endpoint: Option<String>,
pub proxy: ProxyTransportConfig,
}
@@ -262,6 +271,8 @@ impl TransportConfig {
source: TransportConfigSource::Defaults,
nostr_relay_urls: Vec::new(),
reticulum_preview_behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts,
+ reticulum_preview_scope: RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned(),
+ reticulum_preview_agent_endpoint: None,
proxy: ProxyTransportConfig::default(),
}
}
@@ -275,6 +286,8 @@ impl TransportConfig {
source: TransportConfigSource::Defaults,
nostr_relay_urls,
reticulum_preview_behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts,
+ reticulum_preview_scope: RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned(),
+ reticulum_preview_agent_endpoint: None,
proxy: ProxyTransportConfig::default(),
}
}
@@ -538,6 +551,8 @@ struct NostrTransportFileConfig {
#[serde(default, deny_unknown_fields)]
struct ReticulumPreviewFileConfig {
behavior: Option<String>,
+ scope: Option<String>,
+ agent_endpoint: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
@@ -986,6 +1001,28 @@ fn resolve_transport_config(
})
.transpose()?
.unwrap_or(ReticulumPreviewBehavior::RejectDeliveryAttempts);
+ let reticulum_preview_scope =
+ env_value(env, env_file, &[ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_SCOPE])
+ .map(|value| {
+ parse_reticulum_preview_scope(
+ ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_SCOPE,
+ value.as_str(),
+ )
+ })
+ .transpose()?
+ .unwrap_or_else(|| RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned());
+ let reticulum_preview_agent_endpoint = env_value(
+ env,
+ env_file,
+ &[ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_AGENT_ENDPOINT],
+ )
+ .map(|value| {
+ parse_reticulum_preview_agent_endpoint(
+ ENV_CLI_TRANSPORT_RETICULUM_PREVIEW_AGENT_ENDPOINT,
+ value.as_str(),
+ )
+ })
+ .transpose()?;
return Ok(TransportConfig {
profile,
source: TransportConfigSource::Environment,
@@ -996,6 +1033,8 @@ fn resolve_transport_config(
.transpose()?
.unwrap_or_default(),
reticulum_preview_behavior,
+ reticulum_preview_scope,
+ reticulum_preview_agent_endpoint,
proxy: resolve_transport_proxy_env(env, env_file)?,
});
}
@@ -1021,6 +1060,8 @@ fn resolve_transport_config(
source: TransportConfigSource::Defaults,
nostr_relay_urls: Vec::new(),
reticulum_preview_behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts,
+ reticulum_preview_scope: RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned(),
+ reticulum_preview_agent_endpoint: None,
proxy: ProxyTransportConfig::default(),
})
}
@@ -1044,6 +1085,8 @@ fn resolve_transport_file_config(
source,
nostr_relay_urls: Vec::new(),
reticulum_preview_behavior: ReticulumPreviewBehavior::RejectDeliveryAttempts,
+ reticulum_preview_scope: RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned(),
+ reticulum_preview_agent_endpoint: None,
proxy: ProxyTransportConfig::default(),
});
};
@@ -1068,11 +1111,36 @@ fn resolve_transport_file_config(
})
.transpose()?
.unwrap_or(ReticulumPreviewBehavior::RejectDeliveryAttempts);
+ let reticulum_preview_scope = transport
+ .reticulum_preview
+ .as_ref()
+ .and_then(|preview| preview.scope.as_deref())
+ .map(|value| {
+ parse_reticulum_preview_scope(
+ format!("{source_label}.reticulum_preview.scope").as_str(),
+ value,
+ )
+ })
+ .transpose()?
+ .unwrap_or_else(|| RADROOTS_RETICULUM_PREVIEW_SCOPE_ID.to_owned());
+ let reticulum_preview_agent_endpoint = transport
+ .reticulum_preview
+ .as_ref()
+ .and_then(|preview| preview.agent_endpoint.as_deref())
+ .map(|value| {
+ parse_reticulum_preview_agent_endpoint(
+ format!("{source_label}.reticulum_preview.agent_endpoint").as_str(),
+ value,
+ )
+ })
+ .transpose()?;
Ok(TransportConfig {
profile,
source,
nostr_relay_urls,
reticulum_preview_behavior,
+ reticulum_preview_scope,
+ reticulum_preview_agent_endpoint,
proxy: resolve_transport_proxy_file(transport.proxy.as_ref(), source_label)?,
})
}
@@ -1909,6 +1977,30 @@ fn parse_reticulum_preview_behavior(
}
}
+fn parse_reticulum_preview_scope(source: &str, value: &str) -> Result<String, RuntimeError> {
+ RadrootsTransportMeshScopeId::parse(value)
+ .map(|scope| scope.as_str().to_owned())
+ .map_err(|error| RuntimeError::Config(format!("{source} is invalid: {error}")))
+}
+
+fn parse_reticulum_preview_agent_endpoint(
+ source: &str,
+ value: &str,
+) -> Result<String, RuntimeError> {
+ if value.is_empty()
+ || value != value.trim()
+ || value
+ .chars()
+ .any(|ch| ch.is_ascii_control() || ch.is_ascii_whitespace())
+ || value.find(':').is_none()
+ {
+ return Err(RuntimeError::Config(format!(
+ "{source} must be a non-empty URI-like endpoint without whitespace"
+ )));
+ }
+ Ok(value.to_owned())
+}
+
fn resolve_account_secret_backend(
env: &dyn Environment,
env_file: &EnvFileValues,
@@ -2871,7 +2963,7 @@ RADROOTS_CLI_LOGGING_STDOUT=true
fs::create_dir_all(&app_config_dir).expect("app config dir");
fs::write(
app_config_dir.join("config.toml"),
- "[transport]\nprofile = \"hybrid\"\n\n[transport.nostr]\nrelay_urls = [\"wss://relay.user\", \"wss://relay.backup\"]\n\n[transport.reticulum_preview]\nbehavior = \"defer_delivery_plans\"\n",
+ "[transport]\nprofile = \"hybrid\"\n\n[transport.nostr]\nrelay_urls = [\"wss://relay.user\", \"wss://relay.backup\"]\n\n[transport.reticulum_preview]\nbehavior = \"defer_delivery_plans\"\nscope = \"farmers_market\"\nagent_endpoint = \"reticulum-agent:local\"\n",
)
.expect("write user config");
@@ -2893,6 +2985,14 @@ RADROOTS_CLI_LOGGING_STDOUT=true
resolved.transport.reticulum_preview_behavior,
super::ReticulumPreviewBehavior::DeferDeliveryPlans
);
+ assert_eq!(resolved.transport.reticulum_preview_scope, "farmers_market");
+ assert_eq!(
+ resolved
+ .transport
+ .reticulum_preview_agent_endpoint
+ .as_deref(),
+ Some("reticulum-agent:local")
+ );
}
#[test]
diff --git a/src/runtime/order.rs b/src/runtime/order.rs
@@ -175,7 +175,7 @@ fn trade_satisfaction_policy(mode: PublishMode) -> Result<SatisfactionPolicy, Ru
}
fn trade_target_policy() -> TargetPolicy {
- TargetPolicy::use_transport_profile()
+ TargetPolicy::default_profile()
}
fn trade_privacy_confirmation(confirm_public_note: bool) -> PrivacyPreflightConfirmation {
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -16,12 +16,12 @@ use radroots_nostr_connect::prelude::{
RadrootsNostrConnectClientTarget, RadrootsNostrConnectError, RadrootsNostrConnectUri,
};
use radroots_sdk::{
- HybridProfile, NostrProfile, NostrRelayUrlPolicy, ProxyProfile, RadrootsClient,
+ HybridProfile, MeshScopeId, NostrProfile, NostrRelayUrlPolicy, ProxyProfile, RadrootsClient,
RadrootsClientBuilder, RadrootsSdkError, RadrootsSdkLocalKeySigner,
RadrootsSdkMycNip46RequestPolicy, RadrootsSdkMycNip46Signer, RadrootsSdkNip46Transport,
RadrootsSdkNip46TransportFuture, RadrootsSdkSignerProvider, RadrootsSdkStorageConfig,
- ReticulumPreviewBehavior as SdkReticulumPreviewBehavior, ReticulumPreviewProfile, TargetPolicy,
- TransportProfile,
+ ReticulumPreviewAgentEndpoint, ReticulumPreviewBehavior as SdkReticulumPreviewBehavior,
+ ReticulumPreviewProfile, TargetPolicy, TransportProfile,
};
use radroots_transport_nostr::{
RadrootsNostrClientFetchAdapter, RadrootsRelayFetchRequest, RadrootsRelayFetchedEventsReceipt,
@@ -638,7 +638,7 @@ pub fn sdk_nostr_relay_url_policy(config: &RuntimeConfig) -> NostrRelayUrlPolicy
}
pub fn sdk_target_policy(_config: &RuntimeConfig) -> TargetPolicy {
- TargetPolicy::use_transport_profile()
+ TargetPolicy::default_profile()
}
fn sdk_transport_profile(config: &RuntimeConfig) -> Result<TransportProfile, RuntimeError> {
@@ -653,7 +653,7 @@ fn sdk_transport_profile(config: &RuntimeConfig) -> Result<TransportProfile, Run
Ok(TransportProfile::nostr(profile))
}
TransportProfileKind::ReticulumPreview => Ok(TransportProfile::reticulum_preview(
- sdk_reticulum_preview_profile(config),
+ sdk_reticulum_preview_profile(config)?,
)),
TransportProfileKind::Hybrid => {
let nostr = NostrProfile::new(
@@ -663,7 +663,7 @@ fn sdk_transport_profile(config: &RuntimeConfig) -> Result<TransportProfile, Run
.map_err(|error| RuntimeError::Config(error.to_string()))?;
Ok(TransportProfile::hybrid(HybridProfile::new(
nostr,
- sdk_reticulum_preview_profile(config),
+ sdk_reticulum_preview_profile(config)?,
)))
}
TransportProfileKind::Proxy => {
@@ -674,7 +674,9 @@ fn sdk_transport_profile(config: &RuntimeConfig) -> Result<TransportProfile, Run
}
}
-fn sdk_reticulum_preview_profile(config: &RuntimeConfig) -> ReticulumPreviewProfile {
+fn sdk_reticulum_preview_profile(
+ config: &RuntimeConfig,
+) -> Result<ReticulumPreviewProfile, RuntimeError> {
let behavior = match config.transport.reticulum_preview_behavior {
ReticulumPreviewBehavior::RejectDeliveryAttempts => {
SdkReticulumPreviewBehavior::RejectDeliveryAttempts
@@ -683,7 +685,18 @@ fn sdk_reticulum_preview_profile(config: &RuntimeConfig) -> ReticulumPreviewProf
SdkReticulumPreviewBehavior::DeferDeliveryPlans
}
};
- ReticulumPreviewProfile::preview_unavailable().with_behavior(behavior)
+ let scope = MeshScopeId::parse(config.transport.reticulum_preview_scope.as_str())
+ .map_err(|error| RuntimeError::Config(error.to_string()))?;
+ let mut profile = ReticulumPreviewProfile::preview_unavailable()
+ .with_behavior(behavior)
+ .with_scope(scope);
+ if let Some(agent_endpoint) = config.transport.reticulum_preview_agent_endpoint.as_ref() {
+ profile = profile.with_agent_endpoint(
+ ReticulumPreviewAgentEndpoint::parse(agent_endpoint.as_str())
+ .map_err(|error| RuntimeError::Config(error.to_string()))?,
+ );
+ }
+ Ok(profile)
}
pub(crate) fn validate_proxy_bearer_token(config: &RuntimeConfig) -> Result<(), RuntimeError> {
@@ -1221,6 +1234,9 @@ mod tests {
);
config.transport.profile = TransportProfileKind::Hybrid;
config.transport.reticulum_preview_behavior = ReticulumPreviewBehavior::DeferDeliveryPlans;
+ config.transport.reticulum_preview_scope = "farmers_market".to_owned();
+ config.transport.reticulum_preview_agent_endpoint =
+ Some("reticulum-agent:local".to_owned());
let sdk_config = CliSdkConfig::from_runtime_config(&config).expect("sdk config");
@@ -1235,6 +1251,18 @@ mod tests {
profile.reticulum_preview().behavior().as_str(),
"defer_delivery_plans"
);
+ assert_eq!(
+ profile.reticulum_preview().scope().as_str(),
+ "farmers_market"
+ );
+ assert_eq!(
+ profile
+ .reticulum_preview()
+ .agent_endpoint()
+ .expect("agent endpoint")
+ .as_str(),
+ "reticulum-agent:local"
+ );
}
#[test]
diff --git a/src/runtime/sync.rs b/src/runtime/sync.rs
@@ -23,8 +23,8 @@ use radroots_sdk::{
use radroots_sql_core::{SqlExecutor, SqliteExecutor};
use radroots_transport::{
RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
- RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportReadinessState,
- RadrootsTransportStatus, RadrootsTransportTarget,
+ RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportStatus,
+ RadrootsTransportTarget,
};
use radroots_transport_nostr::{
RadrootsRelayFetchFailure, RadrootsRelayFetchedEventsReceipt, RadrootsRelayTransportError,
@@ -605,17 +605,13 @@ fn sdk_transport_status_view(
status: &radroots_sdk::SyncTransportStatusSummary,
) -> SyncTransportStatusView {
SyncTransportStatusView {
- transport_kind: status.transport_kind.clone(),
+ transport: status.transport.clone(),
profile_id: status.profile_id.clone(),
endpoint_uri: status.endpoint_uri.clone(),
- configured: sync_transport_status_configured(&status.readiness),
- implementation: status.implementation_state.clone(),
- usable_for_delivery: status.publish_usable,
- message: sync_transport_status_message(
- &status.transport_kind,
- status.publish_usable,
- status.redacted_message.as_deref(),
- ),
+ configured: status.configured,
+ implementation: status.implementation.clone(),
+ usable_for_delivery: status.usable_for_delivery,
+ message: status.message.clone(),
}
}
@@ -949,8 +945,10 @@ fn sync_transport_statuses(config: &RuntimeConfig) -> Vec<SyncTransportStatusVie
TransportProfileKind::LocalOnly => vec![sync_transport_status_view(
RadrootsTransportStatus::new(
RadrootsTransportKind::Local,
- RadrootsTransportImplementationState::Available,
- RadrootsTransportReadinessState::Ready,
+ true,
+ RadrootsTransportImplementationState::Real,
+ false,
+ "Local-only profile writes only to local state",
)
.with_profile_id(profile_id),
)],
@@ -978,20 +976,17 @@ fn sync_transport_statuses(config: &RuntimeConfig) -> Vec<SyncTransportStatusVie
vec![sync_transport_status_view(
RadrootsTransportStatus::new(
RadrootsTransportKind::Proxy,
+ auth_configured,
+ RadrootsTransportImplementationState::Real,
+ auth_configured,
if auth_configured {
- RadrootsTransportImplementationState::Available
- } else {
- RadrootsTransportImplementationState::Misconfigured
- },
- if auth_configured {
- RadrootsTransportReadinessState::Ready
+ "Proxy transport delegates delivery to the configured endpoint"
} else {
- RadrootsTransportReadinessState::Misconfigured
+ "Proxy transport requires a configured token file or token secret id"
},
)
.with_profile_id(profile_id)
- .with_endpoint_uri(config.transport.proxy.url.as_str())
- .with_publish_usable(auth_configured),
+ .with_endpoint_uri(config.transport.proxy.url.as_str()),
)]
}
}
@@ -1000,99 +995,46 @@ fn sync_transport_statuses(config: &RuntimeConfig) -> Vec<SyncTransportStatusVie
fn nostr_transport_status(profile_id: &str, targets_configured: bool) -> RadrootsTransportStatus {
RadrootsTransportStatus::new(
RadrootsTransportKind::Nostr,
+ targets_configured,
+ RadrootsTransportImplementationState::Real,
+ targets_configured,
if targets_configured {
- RadrootsTransportImplementationState::Available
- } else {
- RadrootsTransportImplementationState::Misconfigured
- },
- if targets_configured {
- RadrootsTransportReadinessState::Ready
+ "Nostr relay transport is configured for delivery"
} else {
- RadrootsTransportReadinessState::Misconfigured
+ "Nostr transport requires configured Nostr relay targets"
},
)
.with_profile_id(profile_id)
- .with_publish_usable(targets_configured)
- .with_fetch_usable(targets_configured)
}
fn reticulum_preview_transport_status(profile_id: &str) -> RadrootsTransportStatus {
RadrootsTransportStatus::new(
RadrootsTransportKind::Reticulum,
+ true,
RadrootsTransportImplementationState::PreviewUnavailable,
- RadrootsTransportReadinessState::PreviewUnavailable,
+ false,
+ RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
)
.with_profile_id(profile_id)
.with_endpoint_uri(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI)
- .with_redacted_message(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE)
}
fn sync_transport_status_view(status: RadrootsTransportStatus) -> SyncTransportStatusView {
- let configured = transport_status_configured(&status);
- let usable_for_delivery = status.publish_usable;
- let message = transport_status_message(&status);
SyncTransportStatusView {
- transport_kind: status.kind.canonical_label(),
+ transport: status.kind.canonical_label(),
profile_id: status.profile_id,
endpoint_uri: status.endpoint_uri,
- configured,
- implementation: transport_implementation_state_label(status.implementation_state)
- .to_owned(),
- usable_for_delivery,
- message,
+ configured: status.configured,
+ implementation: transport_implementation_label(status.implementation).to_owned(),
+ usable_for_delivery: status.usable_for_delivery,
+ message: status.message,
}
}
-fn transport_status_configured(status: &RadrootsTransportStatus) -> bool {
- matches!(
- status.readiness,
- RadrootsTransportReadinessState::Ready
- | RadrootsTransportReadinessState::PreviewUnavailable
- )
-}
-
-fn sync_transport_status_configured(readiness: &str) -> bool {
- matches!(readiness, "ready" | "preview_unavailable")
-}
-
-fn transport_status_message(status: &RadrootsTransportStatus) -> String {
- if let Some(message) = &status.redacted_message {
- return message.clone();
- }
- sync_transport_status_message(&status.kind.canonical_label(), status.publish_usable, None)
-}
-
-fn sync_transport_status_message(
- transport_kind: &str,
- usable_for_delivery: bool,
- redacted_message: Option<&str>,
-) -> String {
- if let Some(message) = redacted_message {
- return message.to_owned();
- }
- match transport_kind {
- "local" => "Local-only profile writes only to local state".to_owned(),
- "nostr" if usable_for_delivery => {
- "Nostr relay transport is configured for delivery".to_owned()
- }
- "nostr" => "Nostr transport requires configured Nostr relay targets".to_owned(),
- "reticulum" => RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned(),
- "mesh" => "Mesh transport status is not available".to_owned(),
- "proxy" if usable_for_delivery => {
- "Proxy transport delegates delivery to the configured endpoint".to_owned()
- }
- "proxy" => "Proxy transport requires a configured token file or token secret id".to_owned(),
- _ => "Transport status is not available".to_owned(),
- }
-}
-
-fn transport_implementation_state_label(
- state: RadrootsTransportImplementationState,
-) -> &'static str {
+fn transport_implementation_label(state: RadrootsTransportImplementationState) -> &'static str {
match state {
- RadrootsTransportImplementationState::Available => "available",
- RadrootsTransportImplementationState::Disabled => "disabled",
- RadrootsTransportImplementationState::Misconfigured => "misconfigured",
+ RadrootsTransportImplementationState::Real => "real",
+ RadrootsTransportImplementationState::Mock => "mock",
RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable",
}
}
@@ -1927,13 +1869,13 @@ mod tests {
assert_eq!(view.configured_transport_target_count, 0);
assert!(view.configured_transport_targets.is_empty());
assert_eq!(view.transport_statuses.len(), 1);
- assert_eq!(view.transport_statuses[0].transport_kind, "nostr");
+ assert_eq!(view.transport_statuses[0].transport, "nostr");
assert_eq!(
view.transport_statuses[0].profile_id.as_deref(),
Some("nostr")
);
assert!(!view.transport_statuses[0].configured);
- assert_eq!(view.transport_statuses[0].implementation, "misconfigured");
+ assert_eq!(view.transport_statuses[0].implementation, "real");
assert!(!view.transport_statuses[0].usable_for_delivery);
assert!(view.target_transport_endpoints.is_empty());
assert_eq!(
@@ -1965,9 +1907,9 @@ mod tests {
"reticulum"
);
assert_eq!(view.transport_statuses.len(), 2);
- assert_eq!(view.transport_statuses[0].transport_kind, "nostr");
+ assert_eq!(view.transport_statuses[0].transport, "nostr");
assert!(view.transport_statuses[0].usable_for_delivery);
- assert_eq!(view.transport_statuses[1].transport_kind, "reticulum");
+ assert_eq!(view.transport_statuses[1].transport, "reticulum");
assert!(view.transport_statuses[1].configured);
assert_eq!(
view.transport_statuses[1].implementation,
@@ -2003,15 +1945,15 @@ mod tests {
"reticulum"
);
assert_eq!(view.transport_statuses.len(), 2);
- assert_eq!(view.transport_statuses[0].transport_kind, "nostr");
+ assert_eq!(view.transport_statuses[0].transport, "nostr");
assert_eq!(
view.transport_statuses[0].profile_id.as_deref(),
Some("hybrid")
);
assert!(!view.transport_statuses[0].configured);
- assert_eq!(view.transport_statuses[0].implementation, "misconfigured");
+ assert_eq!(view.transport_statuses[0].implementation, "real");
assert!(!view.transport_statuses[0].usable_for_delivery);
- assert_eq!(view.transport_statuses[1].transport_kind, "reticulum");
+ assert_eq!(view.transport_statuses[1].transport, "reticulum");
assert!(view.transport_statuses[1].configured);
assert_eq!(
view.transport_statuses[1].implementation,
@@ -2034,7 +1976,7 @@ mod tests {
assert_eq!(snapshot.configured_transport_target_count, 2);
assert_eq!(snapshot.configured_transport_targets.len(), 2);
assert_eq!(snapshot.transport_statuses.len(), 2);
- assert_eq!(snapshot.transport_statuses[1].transport_kind, "reticulum");
+ assert_eq!(snapshot.transport_statuses[1].transport, "reticulum");
assert_eq!(
snapshot.transport_statuses[1].implementation,
"preview_unavailable"
@@ -2154,24 +2096,22 @@ mod tests {
],
transport_statuses: vec![
SyncTransportStatusSummary {
- transport_kind: "nostr".to_owned(),
+ transport: "nostr".to_owned(),
profile_id: Some("hybrid".to_owned()),
endpoint_uri: None,
- implementation_state: "available".to_owned(),
- readiness: "ready".to_owned(),
- publish_usable: true,
- fetch_usable: true,
- redacted_message: None,
+ configured: true,
+ implementation: "real".to_owned(),
+ usable_for_delivery: true,
+ message: "Nostr relay transport is configured for delivery".to_owned(),
},
SyncTransportStatusSummary {
- transport_kind: "reticulum".to_owned(),
+ transport: "reticulum".to_owned(),
profile_id: Some("hybrid".to_owned()),
endpoint_uri: Some(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned()),
- implementation_state: "preview_unavailable".to_owned(),
- readiness: "preview_unavailable".to_owned(),
- publish_usable: false,
- fetch_usable: false,
- redacted_message: Some(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned()),
+ configured: true,
+ implementation: "preview_unavailable".to_owned(),
+ usable_for_delivery: false,
+ message: RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned(),
},
],
};
@@ -2539,14 +2479,13 @@ mod tests {
})
.collect(),
transport_statuses: vec![SyncTransportStatusSummary {
- transport_kind: "nostr".to_owned(),
+ transport: "nostr".to_owned(),
profile_id: Some("nostr".to_owned()),
endpoint_uri: None,
- implementation_state: "available".to_owned(),
- readiness: "ready".to_owned(),
- publish_usable: true,
- fetch_usable: true,
- redacted_message: None,
+ configured: true,
+ implementation: "real".to_owned(),
+ usable_for_delivery: true,
+ message: "Nostr relay transport is configured for delivery".to_owned(),
}],
},
}
@@ -2588,14 +2527,13 @@ mod tests {
endpoint_fingerprint: "1".repeat(64),
}],
transport_statuses: vec![SyncTransportStatusSummary {
- transport_kind: "reticulum".to_owned(),
+ transport: "reticulum".to_owned(),
profile_id: Some("reticulum_preview".to_owned()),
endpoint_uri: Some(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI.to_owned()),
- implementation_state: "preview_unavailable".to_owned(),
- readiness: "preview_unavailable".to_owned(),
- publish_usable: false,
- fetch_usable: false,
- redacted_message: Some(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned()),
+ configured: true,
+ implementation: "preview_unavailable".to_owned(),
+ usable_for_delivery: false,
+ message: RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned(),
}],
},
}
diff --git a/src/runtime/transport.rs b/src/runtime/transport.rs
@@ -6,9 +6,9 @@ use radroots_sdk::{
SyncStatusRequest,
};
use radroots_transport::{
- RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
- RadrootsTransportImplementationState, RadrootsTransportKind, RadrootsTransportReadinessState,
- RadrootsTransportStatus,
+ RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI, RADROOTS_RETICULUM_PREVIEW_SCOPE_ID,
+ RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE, RadrootsTransportImplementationState,
+ RadrootsTransportKind, RadrootsTransportStatus,
};
use serde_json::Value as JsonValue;
use toml::{Value, map::Map};
@@ -53,8 +53,17 @@ pub fn set_profile(
"reticulum_preview" => {
let behavior = string_input(input, "reticulum_preview_behavior")
.unwrap_or("reject_delivery_attempts");
+ let scope = string_input(input, "reticulum_preview_scope")
+ .unwrap_or(RADROOTS_RETICULUM_PREVIEW_SCOPE_ID);
let mut preview = Map::new();
preview.insert("behavior".to_owned(), Value::String(behavior.to_owned()));
+ preview.insert("scope".to_owned(), Value::String(scope.to_owned()));
+ if let Some(agent_endpoint) = string_input(input, "reticulum_preview_agent_endpoint") {
+ preview.insert(
+ "agent_endpoint".to_owned(),
+ Value::String(agent_endpoint.to_owned()),
+ );
+ }
transport.insert("reticulum_preview".to_owned(), Value::Table(preview));
}
"hybrid" => {
@@ -66,6 +75,8 @@ pub fn set_profile(
}
let behavior = string_input(input, "reticulum_preview_behavior")
.unwrap_or("reject_delivery_attempts");
+ let scope = string_input(input, "reticulum_preview_scope")
+ .unwrap_or(RADROOTS_RETICULUM_PREVIEW_SCOPE_ID);
let mut nostr = Map::new();
nostr.insert(
"relay_urls".to_owned(),
@@ -73,6 +84,13 @@ pub fn set_profile(
);
let mut preview = Map::new();
preview.insert("behavior".to_owned(), Value::String(behavior.to_owned()));
+ preview.insert("scope".to_owned(), Value::String(scope.to_owned()));
+ if let Some(agent_endpoint) = string_input(input, "reticulum_preview_agent_endpoint") {
+ preview.insert(
+ "agent_endpoint".to_owned(),
+ Value::String(agent_endpoint.to_owned()),
+ );
+ }
transport.insert("nostr".to_owned(), Value::Table(nostr));
transport.insert("reticulum_preview".to_owned(), Value::Table(preview));
}
@@ -118,6 +136,8 @@ pub fn set_profile(
kind,
string_array_input(input, "nostr_relays"),
string_input(input, "reticulum_preview_behavior").map(str::to_owned),
+ reticulum_preview_scope_for_profile(kind, input),
+ string_input(input, "reticulum_preview_agent_endpoint").map(str::to_owned),
string_input(input, "proxy_url").map(str::to_owned),
string_input(input, "proxy_token_file").map(str::to_owned),
string_input(input, "proxy_token_secret_id").map(str::to_owned),
@@ -272,6 +292,8 @@ fn active_profile_view(config: &RuntimeConfig) -> TransportProfileView {
None,
None,
None,
+ None,
+ None,
"configured",
),
TransportProfileKind::Nostr => profile_view_from_parts(
@@ -281,6 +303,8 @@ fn active_profile_view(config: &RuntimeConfig) -> TransportProfileView {
None,
None,
None,
+ None,
+ None,
if config.transport.nostr_relay_urls.is_empty() {
"unconfigured"
} else {
@@ -297,6 +321,8 @@ fn active_profile_view(config: &RuntimeConfig) -> TransportProfileView {
.as_str()
.to_owned(),
),
+ Some(config.transport.reticulum_preview_scope.clone()),
+ config.transport.reticulum_preview_agent_endpoint.clone(),
None,
None,
None,
@@ -312,6 +338,8 @@ fn active_profile_view(config: &RuntimeConfig) -> TransportProfileView {
.as_str()
.to_owned(),
),
+ Some(config.transport.reticulum_preview_scope.clone()),
+ config.transport.reticulum_preview_agent_endpoint.clone(),
None,
None,
None,
@@ -331,6 +359,8 @@ fn active_profile_view(config: &RuntimeConfig) -> TransportProfileView {
"proxy",
Vec::new(),
None,
+ None,
+ None,
Some(config.transport.proxy.url.clone()),
config
.transport
@@ -357,6 +387,8 @@ fn profile_view_from_parts(
profile_id: &str,
nostr_relays: Vec<String>,
reticulum_preview_behavior: Option<String>,
+ reticulum_preview_scope: Option<String>,
+ reticulum_preview_agent_endpoint: Option<String>,
proxy_url: Option<String>,
proxy_token_file: Option<String>,
proxy_token_secret_id: Option<String>,
@@ -405,6 +437,8 @@ fn profile_view_from_parts(
message: message.to_owned(),
nostr_relays,
reticulum_preview_behavior,
+ reticulum_preview_scope,
+ reticulum_preview_agent_endpoint,
proxy_url,
proxy_token_source,
proxy_token_file,
@@ -451,41 +485,39 @@ fn transport_statuses_from_parts(
fn local_transport_status(profile_id: &str) -> RadrootsTransportStatus {
RadrootsTransportStatus::new(
RadrootsTransportKind::Local,
- RadrootsTransportImplementationState::Available,
- RadrootsTransportReadinessState::Ready,
+ true,
+ RadrootsTransportImplementationState::Real,
+ false,
+ "Local-only profile writes only to local state",
)
.with_profile_id(profile_id)
- .with_redacted_message("Local-only profile writes only to local state")
}
fn nostr_transport_status(profile_id: &str, targets_configured: bool) -> RadrootsTransportStatus {
RadrootsTransportStatus::new(
RadrootsTransportKind::Nostr,
+ targets_configured,
+ RadrootsTransportImplementationState::Real,
+ targets_configured,
if targets_configured {
- RadrootsTransportImplementationState::Available
- } else {
- RadrootsTransportImplementationState::Misconfigured
- },
- if targets_configured {
- RadrootsTransportReadinessState::Ready
+ "Nostr relay transport is configured for delivery"
} else {
- RadrootsTransportReadinessState::Misconfigured
+ "Nostr transport requires configured Nostr relay targets"
},
)
.with_profile_id(profile_id)
- .with_publish_usable(targets_configured)
- .with_fetch_usable(targets_configured)
}
fn reticulum_preview_transport_status(profile_id: &str) -> RadrootsTransportStatus {
RadrootsTransportStatus::new(
RadrootsTransportKind::Reticulum,
+ true,
RadrootsTransportImplementationState::PreviewUnavailable,
- RadrootsTransportReadinessState::PreviewUnavailable,
+ false,
+ RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE,
)
.with_profile_id(profile_id)
.with_endpoint_uri(RADROOTS_RETICULUM_PREVIEW_ENDPOINT_URI)
- .with_redacted_message(RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE)
}
fn proxy_transport_status(
@@ -495,19 +527,16 @@ fn proxy_transport_status(
) -> RadrootsTransportStatus {
let mut status = RadrootsTransportStatus::new(
RadrootsTransportKind::Proxy,
+ token_ready,
+ RadrootsTransportImplementationState::Real,
+ token_ready,
if token_ready {
- RadrootsTransportImplementationState::Available
- } else {
- RadrootsTransportImplementationState::Misconfigured
- },
- if token_ready {
- RadrootsTransportReadinessState::Ready
+ "Proxy transport delegates delivery to the configured endpoint"
} else {
- RadrootsTransportReadinessState::Misconfigured
+ "Proxy transport requires a configured token file or token secret id"
},
)
- .with_profile_id(profile_id)
- .with_publish_usable(token_ready);
+ .with_profile_id(profile_id);
if let Some(proxy_url) = proxy_url {
status = status.with_endpoint_uri(proxy_url);
}
@@ -515,61 +544,33 @@ fn proxy_transport_status(
}
fn transport_runtime_status_view(status: RadrootsTransportStatus) -> TransportRuntimeStatusView {
- let configured = transport_status_configured(&status);
- let usable_for_delivery = status.publish_usable;
- let message = transport_status_message(&status);
TransportRuntimeStatusView {
- transport_kind: status.kind.canonical_label(),
+ transport: status.kind.canonical_label(),
profile_id: status.profile_id,
endpoint_uri: status.endpoint_uri,
- configured,
- implementation: transport_implementation_state_label(status.implementation_state)
- .to_owned(),
- usable_for_delivery,
- message,
+ configured: status.configured,
+ implementation: transport_implementation_label(status.implementation).to_owned(),
+ usable_for_delivery: status.usable_for_delivery,
+ message: status.message,
}
}
-fn transport_status_configured(status: &RadrootsTransportStatus) -> bool {
- matches!(
- status.readiness,
- RadrootsTransportReadinessState::Ready
- | RadrootsTransportReadinessState::PreviewUnavailable
- )
-}
-
-fn transport_status_message(status: &RadrootsTransportStatus) -> String {
- if let Some(message) = &status.redacted_message {
- return message.clone();
- }
- match status.kind {
- RadrootsTransportKind::Local => "Local-only profile writes only to local state".to_owned(),
- RadrootsTransportKind::Nostr if status.publish_usable => {
- "Nostr relay transport is configured for delivery".to_owned()
- }
- RadrootsTransportKind::Nostr => {
- "Nostr transport requires configured Nostr relay targets".to_owned()
- }
- RadrootsTransportKind::Reticulum => RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE.to_owned(),
- RadrootsTransportKind::Mesh => "Mesh transport status is not available".to_owned(),
- RadrootsTransportKind::Proxy if status.publish_usable => {
- "Proxy transport delegates delivery to the configured endpoint".to_owned()
- }
- RadrootsTransportKind::Proxy => {
- "Proxy transport requires a configured token file or token secret id".to_owned()
- }
- RadrootsTransportKind::Custom(_) => "Custom transport status is not available".to_owned(),
+fn transport_implementation_label(state: RadrootsTransportImplementationState) -> &'static str {
+ match state {
+ RadrootsTransportImplementationState::Real => "real",
+ RadrootsTransportImplementationState::Mock => "mock",
+ RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable",
}
}
-fn transport_implementation_state_label(
- state: RadrootsTransportImplementationState,
-) -> &'static str {
- match state {
- RadrootsTransportImplementationState::Available => "available",
- RadrootsTransportImplementationState::Disabled => "disabled",
- RadrootsTransportImplementationState::Misconfigured => "misconfigured",
- RadrootsTransportImplementationState::PreviewUnavailable => "preview_unavailable",
+fn reticulum_preview_scope_for_profile(profile_id: &str, input: &OperationData) -> Option<String> {
+ match profile_id {
+ "reticulum_preview" | "hybrid" => Some(
+ string_input(input, "reticulum_preview_scope")
+ .unwrap_or(RADROOTS_RETICULUM_PREVIEW_SCOPE_ID)
+ .to_owned(),
+ ),
+ _ => None,
}
}
diff --git a/src/view/runtime.rs b/src/view/runtime.rs
@@ -3281,6 +3281,10 @@ pub struct TransportProfileView {
#[serde(skip_serializing_if = "Option::is_none")]
pub reticulum_preview_behavior: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
+ pub reticulum_preview_scope: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub reticulum_preview_agent_endpoint: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub proxy_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proxy_token_source: Option<String>,
@@ -3312,7 +3316,7 @@ pub struct TransportProfileSummaryView {
#[derive(Debug, Clone, Serialize)]
pub struct TransportRuntimeStatusView {
- pub transport_kind: String,
+ pub transport: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub profile_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -3551,7 +3555,7 @@ pub struct SyncTransportTargetView {
#[derive(Debug, Clone, Serialize)]
pub struct SyncTransportStatusView {
- pub transport_kind: String,
+ pub transport: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub profile_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
diff --git a/tests/target_cli.rs b/tests/target_cli.rs
@@ -93,6 +93,7 @@ fn configure_nostr_transport(sandbox: &RadrootsCliSandbox, relay_url: &str) {
fn assert_no_removed_transport_status_fields(status: &Value) {
for field in [
+ "transport_kind",
"implementation_state",
"readiness",
"publish_usable",
@@ -1426,10 +1427,7 @@ fn transport_profile_reticulum_preview_output_is_transport_specific() {
assert_eq!(result["profile_delivery_usable"], false);
assert!(result.get("transport_kind").is_none());
assert!(result.get("implementation_state").is_none());
- assert_eq!(
- result["transport_statuses"][0]["transport_kind"],
- "reticulum"
- );
+ assert_eq!(result["transport_statuses"][0]["transport"], "reticulum");
assert_eq!(
result["transport_statuses"][0]["implementation"],
"preview_unavailable"
@@ -1445,6 +1443,7 @@ fn transport_profile_reticulum_preview_output_is_transport_specific() {
);
assert_no_removed_transport_status_fields(&result["transport_statuses"][0]);
assert_eq!(result["reticulum_preview_behavior"], "defer_delivery_plans");
+ assert_eq!(result["reticulum_preview_scope"], "local_preview");
assert_eq!(result["message"], RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE);
assert!(
!result["message"]
@@ -1480,9 +1479,9 @@ fn transport_profile_local_only_output_uses_profile_and_local_status_fields() {
assert!(result.get("transport_kind").is_none());
assert!(result.get("implementation_state").is_none());
assert_eq!(statuses.len(), 1);
- assert_eq!(local["transport_kind"], "local");
+ assert_eq!(local["transport"], "local");
assert_eq!(local["profile_id"], "local_only");
- assert_eq!(local["implementation"], "available");
+ assert_eq!(local["implementation"], "real");
assert_eq!(local["configured"], true);
assert_eq!(local["usable_for_delivery"], false);
assert_eq!(
@@ -1508,6 +1507,10 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
"wss://relay.example.com",
"--reticulum-preview-behavior",
"defer-delivery-plans",
+ "--reticulum-preview-scope",
+ "farmers_market",
+ "--reticulum-preview-agent-endpoint",
+ "reticulum-agent:local",
]);
assert_eq!(value["operation_id"], "transport.profile.set");
@@ -1527,12 +1530,12 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
2
);
assert_eq!(
- value["result"]["transport_statuses"][0]["transport_kind"],
+ value["result"]["transport_statuses"][0]["transport"],
"nostr"
);
assert_eq!(
value["result"]["transport_statuses"][0]["implementation"],
- "available"
+ "real"
);
assert_eq!(value["result"]["transport_statuses"][0]["configured"], true);
assert_eq!(
@@ -1540,7 +1543,7 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
true
);
assert_eq!(
- value["result"]["transport_statuses"][1]["transport_kind"],
+ value["result"]["transport_statuses"][1]["transport"],
"reticulum"
);
assert_eq!(
@@ -1558,6 +1561,11 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
value["result"]["reticulum_preview_behavior"],
"defer_delivery_plans"
);
+ assert_eq!(value["result"]["reticulum_preview_scope"], "farmers_market");
+ assert_eq!(
+ value["result"]["reticulum_preview_agent_endpoint"],
+ "reticulum-agent:local"
+ );
let value = sandbox.json_success(&["--format", "json", "transport", "profile", "get"]);
assert_eq!(value["result"]["profile_id"], "hybrid");
@@ -1569,6 +1577,11 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
value["result"]["reticulum_preview_behavior"],
"defer_delivery_plans"
);
+ assert_eq!(value["result"]["reticulum_preview_scope"], "farmers_market");
+ assert_eq!(
+ value["result"]["reticulum_preview_agent_endpoint"],
+ "reticulum-agent:local"
+ );
let config = fs::read_to_string(sandbox.root().join("config/apps/cli/config.toml"))
.expect("read app config");
@@ -1576,6 +1589,8 @@ fn transport_profile_set_hybrid_persists_nostr_and_reticulum_preview_config() {
assert!(config.contains("[transport.nostr]"));
assert!(config.contains("[transport.reticulum_preview]"));
assert!(config.contains("behavior = \"defer_delivery_plans\""));
+ assert!(config.contains("scope = \"farmers_market\""));
+ assert!(config.contains("agent_endpoint = \"reticulum-agent:local\""));
}
#[test]
@@ -1596,11 +1611,11 @@ fn transport_status_hybrid_output_reports_nostr_and_reticulum_rows() {
assert_eq!(result["active_profile"]["profile_kind"], "hybrid");
assert_eq!(result["active_profile"]["profile_delivery_usable"], true);
assert_eq!(transports.len(), 2);
- assert_eq!(nostr["transport_kind"], "nostr");
- assert_eq!(nostr["implementation"], "available");
+ assert_eq!(nostr["transport"], "nostr");
+ assert_eq!(nostr["implementation"], "real");
assert_eq!(nostr["configured"], true);
assert_eq!(nostr["usable_for_delivery"], true);
- assert_eq!(reticulum["transport_kind"], "reticulum");
+ assert_eq!(reticulum["transport"], "reticulum");
assert_eq!(reticulum["implementation"], "preview_unavailable");
assert_eq!(reticulum["configured"], true);
assert_eq!(reticulum["usable_for_delivery"], false);
@@ -1624,9 +1639,9 @@ fn transport_status_nostr_output_uses_canonical_transport_row() {
assert_eq!(result["active_profile"]["profile_kind"], "nostr");
assert_eq!(result["active_profile"]["profile_delivery_usable"], true);
assert_eq!(transports.len(), 1);
- assert_eq!(nostr["transport_kind"], "nostr");
+ assert_eq!(nostr["transport"], "nostr");
assert_eq!(nostr["profile_id"], "nostr");
- assert_eq!(nostr["implementation"], "available");
+ assert_eq!(nostr["implementation"], "real");
assert_eq!(nostr["configured"], true);
assert_eq!(nostr["usable_for_delivery"], true);
assert_no_removed_transport_status_fields(nostr);
@@ -1653,7 +1668,7 @@ fn transport_status_reticulum_preview_output_reports_unusable_preview_state() {
"reticulum_preview"
);
assert_eq!(result["active_profile"]["profile_delivery_usable"], false);
- assert_eq!(active["transport_kind"], "reticulum");
+ assert_eq!(active["transport"], "reticulum");
assert_eq!(active["implementation"], "preview_unavailable");
assert_eq!(active["configured"], true);
assert_eq!(active["usable_for_delivery"], false);
@@ -1876,6 +1891,7 @@ fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces()
"pub struct TransportOutboxStatusView",
);
for required in [
+ "pub transport: String,",
"pub configured: bool,",
"pub implementation: String,",
"pub usable_for_delivery: bool,",
@@ -1887,6 +1903,7 @@ fn transport_source_boundary_rejects_removed_relay_and_publish_proxy_surfaces()
);
}
for forbidden in [
+ "pub transport_kind:",
"pub implementation_state:",
"pub readiness:",
"pub publish_usable:",
@@ -1951,6 +1968,7 @@ fn sync_transport_status_source_boundary_rejects_retired_relay_shaped_generic_ou
"pub configured_transport_target_count: usize,",
"pub configured_transport_targets: Vec<SyncTransportTargetView>,",
"pub transport_statuses: Vec<SyncTransportStatusView>,",
+ "pub transport: String,",
"pub configured: bool,",
"pub implementation: String,",
"pub usable_for_delivery: bool,",
@@ -1972,6 +1990,7 @@ fn sync_transport_status_source_boundary_rejects_retired_relay_shaped_generic_ou
"pub struct TransportTargetFailureView",
);
for forbidden in [
+ "pub transport_kind:",
"pub implementation_state:",
"pub readiness:",
"pub publish_usable:",
@@ -2330,12 +2349,12 @@ fn transport_profile_set_proxy_persists_token_file_without_printing_token_materi
assert_eq!(value["result"]["state"], "configured");
assert_eq!(value["result"]["profile_delivery_usable"], true);
assert_eq!(
- value["result"]["transport_statuses"][0]["transport_kind"],
+ value["result"]["transport_statuses"][0]["transport"],
"proxy"
);
assert_eq!(
value["result"]["transport_statuses"][0]["implementation"],
- "available"
+ "real"
);
assert_eq!(value["result"]["transport_statuses"][0]["configured"], true);
assert_eq!(
@@ -2393,12 +2412,12 @@ fn transport_profile_set_proxy_persists_token_secret_id_without_printing_token_m
assert_eq!(value["result"]["state"], "configured");
assert_eq!(value["result"]["profile_delivery_usable"], true);
assert_eq!(
- value["result"]["transport_statuses"][0]["transport_kind"],
+ value["result"]["transport_statuses"][0]["transport"],
"proxy"
);
assert_eq!(
value["result"]["transport_statuses"][0]["implementation"],
- "available"
+ "real"
);
assert_eq!(value["result"]["transport_statuses"][0]["configured"], true);
assert_eq!(