feat(maestro): registry relays Forward{Create,Kill} RPC for cross-daemon ops (NAP-327)
Adds the home-daemon → registry control-link RPC that brokers a cross-daemon
session create/kill without putting any session bytes on the registry: the
owning daemon still does the tmux spawn, the registry just relays request and
reply.
- protocol: `DaemonOutbound::{ForwardCreate,ForwardKill}` +
`DaemonInbound::{ForwardCreateResult,ForwardKillResult}` (+ round-trip tests).
- registry: `Registry::{forward_create,forward_kill}` reuse the existing
`create_session`/`kill_session` R→D machinery and route the reply back to the
home daemon; transport faults map to stable `ErrorInfo` codes. Wired into the
`WS /daemon` dispatch.
Next: daemon-side broker handle + local_api wiring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,23 @@ pub enum DaemonOutbound {
|
||||
reply_to: ReplyTo,
|
||||
session_id: Uuid,
|
||||
},
|
||||
/// Home daemon asks the registry to relay a session create to the daemon
|
||||
/// that should own it (`request.daemon_id`). Control-plane only: the
|
||||
/// registry forwards the request to the owner over its control link and
|
||||
/// relays the owner's reply — no session bytes ever pass through the
|
||||
/// registry, so the data-plane stays daemon-to-daemon. Reply:
|
||||
/// [`DaemonInbound::ForwardCreateResult`]. (NAP-327)
|
||||
ForwardCreate {
|
||||
reply_to: ReplyTo,
|
||||
request: CreateSessionRequest,
|
||||
},
|
||||
/// Home daemon asks the registry to relay a kill to the daemon that owns
|
||||
/// `id`. Reply: [`DaemonInbound::ForwardKillResult`]. (NAP-327)
|
||||
ForwardKill {
|
||||
reply_to: ReplyTo,
|
||||
daemon_id: String,
|
||||
id: Uuid,
|
||||
},
|
||||
/// Target daemon answers a [`DaemonInbound::RelaySetup`]: whether it will
|
||||
/// accept relay traffic for the requested session. Registry only allocates
|
||||
/// relay capacity when `accepted == true`.
|
||||
@@ -170,6 +187,18 @@ pub enum DaemonInbound {
|
||||
reply_to: ReplyTo,
|
||||
result: RpcResult<AttachCandidates>,
|
||||
},
|
||||
/// Registry's reply to [`DaemonOutbound::ForwardCreate`] — the owning
|
||||
/// daemon's create result, relayed back to the home daemon. `Err` covers
|
||||
/// "owner offline", the owner's own spawn failure, etc. (NAP-327)
|
||||
ForwardCreateResult {
|
||||
reply_to: ReplyTo,
|
||||
result: RpcResult<Session>,
|
||||
},
|
||||
/// Registry's reply to [`DaemonOutbound::ForwardKill`]. (NAP-327)
|
||||
ForwardKillResult {
|
||||
reply_to: ReplyTo,
|
||||
result: RpcResult<()>,
|
||||
},
|
||||
/// Registry signals a target daemon that a home daemon wants to attach via
|
||||
/// the registry-mediated relay (T3). Target evaluates `allow_inbound` and
|
||||
/// replies with [`DaemonOutbound::RelayAck`].
|
||||
@@ -731,6 +760,66 @@ mod tests {
|
||||
assert_eq!(msg, round_trip(&msg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daemon_outbound_forward_create_round_trip_and_tag() {
|
||||
let msg = DaemonOutbound::ForwardCreate {
|
||||
reply_to: Uuid::from_u128(51),
|
||||
request: CreateSessionRequest {
|
||||
daemon_id: "konishi".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
command: "bash".into(),
|
||||
env: HashMap::new(),
|
||||
metadata: HashMap::new(),
|
||||
worktree: None,
|
||||
},
|
||||
};
|
||||
let v = serde_json::to_value(&msg).unwrap();
|
||||
assert_eq!(v["type"], "ForwardCreate");
|
||||
assert_eq!(v["request"]["daemon_id"], "konishi");
|
||||
assert_eq!(msg, round_trip(&msg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daemon_outbound_forward_kill_round_trip_and_tag() {
|
||||
let msg = DaemonOutbound::ForwardKill {
|
||||
reply_to: Uuid::from_u128(52),
|
||||
daemon_id: "konishi".into(),
|
||||
id: Uuid::from_u128(99),
|
||||
};
|
||||
let v = serde_json::to_value(&msg).unwrap();
|
||||
assert_eq!(v["type"], "ForwardKill");
|
||||
assert_eq!(v["daemon_id"], "konishi");
|
||||
assert_eq!(msg, round_trip(&msg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daemon_inbound_forward_results_round_trip() {
|
||||
let create_ok = DaemonInbound::ForwardCreateResult {
|
||||
reply_to: Uuid::from_u128(51),
|
||||
result: RpcResult::Ok {
|
||||
value: sample_session(),
|
||||
},
|
||||
};
|
||||
let create_err = DaemonInbound::ForwardCreateResult {
|
||||
reply_to: Uuid::from_u128(51),
|
||||
result: RpcResult::Err {
|
||||
error: ErrorInfo {
|
||||
code: "daemon_not_connected".into(),
|
||||
message: "owner offline".into(),
|
||||
},
|
||||
},
|
||||
};
|
||||
let kill_ok = DaemonInbound::ForwardKillResult {
|
||||
reply_to: Uuid::from_u128(52),
|
||||
result: RpcResult::Ok { value: () },
|
||||
};
|
||||
let v = serde_json::to_value(&create_ok).unwrap();
|
||||
assert_eq!(v["type"], "ForwardCreateResult");
|
||||
for m in [create_ok, create_err, kill_ok] {
|
||||
assert_eq!(m, round_trip(&m));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn daemon_outbound_relay_ack_round_trip_and_tag() {
|
||||
let msg = DaemonOutbound::RelayAck {
|
||||
|
||||
Reference in New Issue
Block a user