commit d6e486361eaec450ddf8d9c1a76e4000197a72b3
parent 1b4b602f38885f829ac14266500de0be64171927
Author: triesap <tyson@radroots.org>
Date: Fri, 8 May 2026 03:32:25 +0000
cli: clarify signer session binding
- point radrootsd publish errors at signer.remote_nip46
- keep explicit signer-session flags rejected publicly
- cover farm publish missing binding copy
- align listing publish copy with the public surface
Diffstat:
3 files changed, 72 insertions(+), 6 deletions(-)
diff --git a/src/runtime/farm.rs b/src/runtime/farm.rs
@@ -596,7 +596,7 @@ fn dry_run_publish_view(
profile_idempotency_key,
farm_idempotency_key,
"unconfigured",
- "radrootsd farm publish dry-run requires `signer_session_id` input or a signer.remote_nip46 capability binding with signer_session_ref",
+ "radrootsd farm publish dry-run requires a signer.remote_nip46 capability binding with signer_session_ref",
));
};
if config.rpc.bridge_bearer_token.is_none() {
@@ -743,7 +743,7 @@ fn publish_via_radrootsd(
profile_idempotency_key,
farm_idempotency_key,
"unconfigured",
- "radrootsd farm publish requires `signer_session_id` input or a signer.remote_nip46 capability binding with signer_session_ref",
+ "radrootsd farm publish requires a signer.remote_nip46 capability binding with signer_session_ref",
));
};
if config.rpc.bridge_bearer_token.is_none() {
diff --git a/src/runtime/listing.rs b/src/runtime/listing.rs
@@ -914,7 +914,7 @@ fn mutate(
listing_addr,
event_draft.event,
"unconfigured",
- "radrootsd listing publish dry-run requires `signer_session_id` input or a signer.remote_nip46 capability binding with signer_session_ref",
+ "radrootsd listing publish dry-run requires a signer.remote_nip46 capability binding with signer_session_ref",
));
};
Some(signer_session_id)
@@ -1065,7 +1065,7 @@ fn mutate_via_radrootsd(
listing_addr,
event_draft.event,
"unconfigured",
- "radrootsd listing publish requires `signer_session_id` input or a signer.remote_nip46 capability binding with signer_session_ref",
+ "radrootsd listing publish requires a signer.remote_nip46 capability binding with signer_session_ref",
));
};
if config.rpc.bridge_bearer_token.is_none() {
diff --git a/tests/target_cli.rs b/tests/target_cli.rs
@@ -253,6 +253,18 @@ fn help_lists(stdout: &str, command: &str) -> bool {
})
}
+fn assert_public_signer_session_binding_message(value: &Value) {
+ let message = value["errors"][0]["message"]
+ .as_str()
+ .expect("error message");
+ assert!(message.contains("signer.remote_nip46"));
+ assert!(message.contains("signer_session_ref"));
+ assert!(
+ !message.contains("signer_session_id"),
+ "public CLI message should not reference unavailable explicit session input: {message}"
+ );
+}
+
#[test]
fn removed_global_flags_are_rejected_publicly() {
for args in [
@@ -835,6 +847,60 @@ signer_session_ref = "session_test"
}
#[test]
+fn radrootsd_farm_publish_missing_signer_binding_points_to_capability_binding() {
+ let sandbox = RadrootsCliSandbox::new();
+ sandbox.json_success(&["--format", "json", "account", "create"]);
+ sandbox.json_success(&[
+ "--format",
+ "json",
+ "farm",
+ "create",
+ "--name",
+ "Binding Farm",
+ "--location",
+ "farmstand",
+ "--country",
+ "US",
+ "--delivery-method",
+ "pickup",
+ ]);
+ sandbox.write_app_config("[publish]\nmode = \"radrootsd\"\n");
+
+ let dry_run_output = sandbox
+ .command()
+ .env("RADROOTS_RPC_BEARER_TOKEN", "bridge_test")
+ .args(["--format", "json", "--dry-run", "farm", "publish"])
+ .output()
+ .expect("run radrootsd farm publish dry-run");
+ let dry_run: Value = serde_json::from_slice(&dry_run_output.stdout).expect("json output");
+
+ assert!(!dry_run_output.status.success());
+ assert_eq!(dry_run["operation_id"], "farm.publish");
+ assert_eq!(dry_run["errors"][0]["code"], "signer_unconfigured");
+ assert_public_signer_session_binding_message(&dry_run);
+
+ let live_output = sandbox
+ .command()
+ .env("RADROOTS_RPC_BEARER_TOKEN", "bridge_test")
+ .args([
+ "--format",
+ "json",
+ "--approval-token",
+ "approve",
+ "farm",
+ "publish",
+ ])
+ .output()
+ .expect("run radrootsd farm publish");
+ let live: Value = serde_json::from_slice(&live_output.stdout).expect("json output");
+
+ assert!(!live_output.status.success());
+ assert_eq!(live["operation_id"], "farm.publish");
+ assert_eq!(live["errors"][0]["code"], "signer_unconfigured");
+ assert_public_signer_session_binding_message(&live);
+}
+
+#[test]
fn radrootsd_listing_writes_dry_run_use_draft_identity_without_local_account() {
for operation in ["publish", "update", "archive"] {
let sandbox = RadrootsCliSandbox::new();
@@ -1073,7 +1139,7 @@ fn radrootsd_listing_publish_bypasses_relay_signer_preflight() {
assert_eq!(value["operation_id"], "listing.publish");
assert_eq!(value["errors"][0]["code"], "signer_unconfigured");
assert_eq!(value["errors"][0]["detail"]["class"], "signer");
- assert_contains(&value["errors"][0]["message"], "signer_session_id");
+ assert_public_signer_session_binding_message(&value);
assert!(
!value["errors"][0]["message"]
.as_str()
@@ -1126,7 +1192,7 @@ fn radrootsd_publish_mode_routes_listing_update() {
assert_eq!(value["result"], Value::Null);
assert_eq!(value["errors"][0]["code"], "signer_unconfigured");
assert_eq!(value["errors"][0]["detail"]["class"], "signer");
- assert_contains(&value["errors"][0]["message"], "signer_session_id");
+ assert_public_signer_session_binding_message(&value);
}
#[test]