commit d335ed8c66204e144959055905298503fba6071a
parent 8f8eb36aaaea091bd4b7c9aa3bafdc005cfd34a5
Author: triesap <tyson@radroots.org>
Date: Thu, 9 Jul 2026 17:42:02 +0000
transport: emit v3 publish capabilities
- return the v3 transport publish capability DTO from JSON-RPC
- assert capability responses use transport, configured, and implementation
- guard capability rows against old transport_kind and implementation_state fields
- verify daemon formatting, check, and transport publish tests
Diffstat:
3 files changed, 48 insertions(+), 6 deletions(-)
diff --git a/src/transport/jsonrpc/methods/mod.rs b/src/transport/jsonrpc/methods/mod.rs
@@ -140,11 +140,17 @@ mod tests {
assert!(response.get().contains("\"scoped_bearer_token\""));
assert!(response.get().contains("\"signed_event_ingress\":true"));
assert!(response.get().contains("\"transports\":["));
- assert!(response.get().contains("\"transport_kind\":\"reticulum\""));
assert!(
response
.get()
- .contains("\"implementation_state\":\"preview_unavailable\"")
+ .contains("\"api_version\":\"radrootsd.transport_publish.v3\"")
+ );
+ assert!(response.get().contains("\"transport\":\"reticulum\""));
+ assert!(response.get().contains("\"configured\":true"));
+ assert!(
+ response
+ .get()
+ .contains("\"implementation\":\"preview_unavailable\"")
);
assert!(response.get().contains("\"usable_for_delivery\":false"));
assert!(
diff --git a/src/transport/jsonrpc/methods/transport_publish.rs b/src/transport/jsonrpc/methods/transport_publish.rs
@@ -37,7 +37,7 @@ fn register_capabilities(
registry.track(METHOD_CAPABILITIES);
module.register_async_method(METHOD_CAPABILITIES, |_params, ctx, extensions| async move {
require_publish_principal(&extensions)?;
- Ok::<TransportPublishCapabilities, RpcError>(TransportPublishCapabilities::v2(
+ Ok::<TransportPublishCapabilities, RpcError>(TransportPublishCapabilities::v3(
ctx.state.transport_publish.config.max_event_bytes,
ctx.state.transport_publish.config.max_targets_per_request,
))
diff --git a/tests/source_boundary.rs b/tests/source_boundary.rs
@@ -212,8 +212,10 @@ fn transport_publish_capabilities_expose_per_transport_readiness() {
);
for required in [
"transport.publish.capabilities",
- r#"\"transport_kind\":\"reticulum\""#,
- r#"\"implementation_state\":\"preview_unavailable\""#,
+ r#"\"api_version\":\"radrootsd.transport_publish.v3\""#,
+ r#"\"transport\":\"reticulum\""#,
+ r#"\"configured\":true"#,
+ r#"\"implementation\":\"preview_unavailable\""#,
r#"\"usable_for_delivery\":false"#,
"RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE",
] {
@@ -229,7 +231,9 @@ fn transport_publish_capabilities_expose_per_transport_readiness() {
.as_path(),
);
for required in [
- "pub implementation_state: TransportPublishImplementationState,",
+ "pub transport: String,",
+ "pub configured: bool,",
+ "pub implementation: TransportPublishImplementation,",
"pub usable_for_delivery: bool,",
"RADROOTS_RETICULUM_UNAVAILABLE_MESSAGE",
] {
@@ -238,6 +242,23 @@ fn transport_publish_capabilities_expose_per_transport_readiness() {
"transport publish protocol must retain capability field `{required}`"
);
}
+ let capability_source = source_window(
+ protocol_source.as_str(),
+ "pub struct TransportPublishTransportCapability",
+ "#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"serde\", serde(rename_all = \"snake_case\"))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum TransportPublishDeliveryPolicyName",
+ );
+ for forbidden in [
+ "pub transport_kind: String,",
+ concat!(
+ "pub implementation",
+ "_state: TransportPublishImplementationState,"
+ ),
+ ] {
+ assert!(
+ !capability_source.contains(forbidden),
+ "transport publish capability rows must not retain removed field `{forbidden}`"
+ );
+ }
}
#[test]
@@ -302,6 +323,21 @@ fn relative_path(root: &Path, path: &Path) -> String {
.replace('\\', "/")
}
+fn source_window<'source>(
+ source: &'source str,
+ start_marker: &str,
+ end_marker: &str,
+) -> &'source str {
+ let start = source
+ .find(start_marker)
+ .unwrap_or_else(|| panic!("source must contain start marker `{start_marker}`"));
+ let end = source[start..]
+ .find(end_marker)
+ .map(|index| start + index)
+ .unwrap_or_else(|| panic!("source must contain end marker `{end_marker}`"));
+ &source[start..end]
+}
+
fn contains_forbidden_concept(source: &str, pattern: &str) -> bool {
source.match_indices(pattern).any(|(index, _)| {
let before = source[..index].chars().next_back();