From 6211cbbe5ce6bd954ba9a0a7674c0963607e0242 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 14 Jul 2026 14:52:07 +0200 Subject: [PATCH] fix(supervisor): keep instance id stable across reconnects Signed-off-by: Evan Lezar --- .../src/supervisor_session.rs | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/crates/openshell-supervisor-process/src/supervisor_session.rs b/crates/openshell-supervisor-process/src/supervisor_session.rs index 594d865599..05ac3bc8eb 100644 --- a/crates/openshell-supervisor-process/src/supervisor_session.rs +++ b/crates/openshell-supervisor-process/src/supervisor_session.rs @@ -255,6 +255,9 @@ async fn run_session_loop( ) { let mut backoff = INITIAL_BACKOFF; let mut attempt: u64 = 0; + // `instance_id` identifies this supervisor process, not an individual + // connection attempt, so reconnects must reuse it. + let instance_id = uuid::Uuid::new_v4().to_string(); loop { attempt += 1; @@ -265,6 +268,7 @@ async fn run_session_loop( &ssh_socket_path, netns_fd, expected_ssh_peer_pid, + &instance_id, ) .await { @@ -295,6 +299,7 @@ async fn run_single_session( ssh_socket_path: &std::path::Path, netns_fd: Option, expected_ssh_peer_pid: Option, + instance_id: &str, ) -> Result<(), Box> { // Connect to the gateway. The same `Channel` is used for both the // long-lived control stream and all data-plane `RelayStream` calls, so @@ -310,12 +315,11 @@ async fn run_single_session( let outbound = tokio_stream::wrappers::ReceiverStream::new(rx); // Send hello as the first message. - let instance_id = uuid::Uuid::new_v4().to_string(); tx.send(SupervisorMessage { - payload: Some(supervisor_message::Payload::Hello(SupervisorHello { - sandbox_id: sandbox_id.to_string(), - instance_id: instance_id.clone(), - })), + payload: Some(supervisor_message::Payload::Hello(build_supervisor_hello( + sandbox_id, + instance_id, + ))), }) .await .map_err(|_| "failed to queue hello")?; @@ -383,6 +387,13 @@ async fn run_single_session( } } +fn build_supervisor_hello(sandbox_id: &str, instance_id: &str) -> SupervisorHello { + SupervisorHello { + sandbox_id: sandbox_id.to_string(), + instance_id: instance_id.to_string(), + } +} + fn handle_gateway_message( msg: &GatewayMessage, sandbox_id: &str, @@ -710,6 +721,16 @@ fn normalize_tcp_target_host(target: &TcpRelayTarget) -> Result mod target_tests { use super::*; + #[test] + fn supervisor_hello_reuses_caller_owned_instance_id() { + let instance_id = uuid::Uuid::new_v4().to_string(); + let first = build_supervisor_hello("sandbox-1", &instance_id); + let second = build_supervisor_hello("sandbox-1", &instance_id); + + assert_eq!(first.instance_id, instance_id); + assert_eq!(second.instance_id, first.instance_id); + } + fn tcp(host: &str, port: u32) -> TcpRelayTarget { TcpRelayTarget { host: host.to_string(),