Skip to content

Commit fa5ddce

Browse files
committed
feat: add Noise rendezvous provider API
Co-authored-by: Codex noreply@openai.com
1 parent 742aeb4 commit fa5ddce

7 files changed

Lines changed: 171 additions & 28 deletions

File tree

codex-rs/core-api/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,14 @@ pub use codex_core::resolve_installation_id;
4747
pub use codex_core::skills::SkillsManager;
4848
pub use codex_core::thread_store_from_config;
4949
pub use codex_exec_server::EnvironmentManager;
50+
pub use codex_exec_server::ExecServerError;
5051
pub use codex_exec_server::ExecServerRuntimePaths;
52+
pub use codex_exec_server::NoiseChannelIdentity;
53+
pub use codex_exec_server::NoiseChannelPublicKey;
54+
pub use codex_exec_server::NoiseRendezvousConnectArgs;
55+
pub use codex_exec_server::NoiseRendezvousConnectBundle;
56+
pub use codex_exec_server::NoiseRendezvousConnectProvider;
57+
pub use codex_exec_server::SharedNoiseRendezvousConnectProvider;
5158
pub use codex_extension_api::empty_extension_registry;
5259
pub use codex_features::Feature;
5360
pub use codex_features::Features;

codex-rs/exec-server/src/client.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use crate::ProcessId;
2323
use crate::client_api::ExecServerClientConnectOptions;
2424
use crate::client_api::ExecServerTransportParams;
2525
use crate::client_api::HttpClient;
26+
use crate::client_api::NoiseRendezvousConnectArgs;
27+
use crate::client_api::NoiseRendezvousConnectBundle;
2628
use crate::client_api::RemoteExecServerConnectArgs;
2729
use crate::client_api::StdioExecServerConnectArgs;
2830
use crate::connection::JsonRpcConnection;
@@ -143,6 +145,26 @@ impl RemoteExecServerConnectArgs {
143145
}
144146
}
145147

148+
impl NoiseRendezvousConnectArgs {
149+
/// Builds one secure connection attempt with the standard client timeouts.
150+
///
151+
/// `bundle` must be freshly issued for this physical connection attempt.
152+
pub fn new(
153+
bundle: NoiseRendezvousConnectBundle,
154+
harness_identity: crate::NoiseChannelIdentity,
155+
client_name: String,
156+
) -> Self {
157+
Self {
158+
bundle,
159+
harness_identity,
160+
client_name,
161+
connect_timeout: CONNECT_TIMEOUT,
162+
initialize_timeout: INITIALIZE_TIMEOUT,
163+
resume_session_id: None,
164+
}
165+
}
166+
}
167+
146168
pub(crate) struct SessionState {
147169
wake_tx: watch::Sender<u64>,
148170
events: ExecProcessEventLog,
@@ -237,6 +259,7 @@ impl LazyRemoteExecServerClient {
237259
if matches!(
238260
&self.transport_params,
239261
ExecServerTransportParams::WebSocketUrl { .. }
262+
| ExecServerTransportParams::NoiseRendezvous { .. }
240263
) =>
241264
{
242265
ExecServerClient::connect_for_transport(self.transport_params.clone()).await?

codex-rs/exec-server/src/client_api.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashMap;
22
use std::path::PathBuf;
3+
use std::sync::Arc;
34
use std::time::Duration;
45

56
use futures::future::BoxFuture;
@@ -88,6 +89,23 @@ impl std::fmt::Debug for NoiseRendezvousConnectArgs {
8889
}
8990
}
9091

92+
/// Supplies fresh registry-authorized material for Noise rendezvous connections.
93+
///
94+
/// Implementations preserve one endpoint-local harness identity while fetching
95+
/// a fresh atomic connect bundle for every physical connection attempt. A
96+
/// failed secure connection must remain a failure; providers must not fall back
97+
/// to an unauthenticated transport.
98+
pub trait NoiseRendezvousConnectProvider: Send + Sync {
99+
/// Environment ID this provider is authorized to connect to.
100+
fn environment_id(&self) -> &str;
101+
102+
/// Returns fresh arguments for one physical connection attempt.
103+
fn connect_args(&self) -> BoxFuture<'_, Result<NoiseRendezvousConnectArgs, ExecServerError>>;
104+
}
105+
106+
/// Shared provider used by reconnect-capable remote environments.
107+
pub type SharedNoiseRendezvousConnectProvider = Arc<dyn NoiseRendezvousConnectProvider>;
108+
91109
/// Stdio connection arguments for a command-backed exec-server.
92110
#[derive(Debug, Clone, PartialEq, Eq)]
93111
pub(crate) struct StdioExecServerConnectArgs {
@@ -107,20 +125,52 @@ pub(crate) struct StdioExecServerCommand {
107125
}
108126

109127
/// Parameters used to connect to a remote exec-server environment.
110-
#[derive(Debug, Clone, PartialEq, Eq)]
128+
#[derive(Clone)]
111129
pub(crate) enum ExecServerTransportParams {
112130
WebSocketUrl {
113131
websocket_url: String,
114132
connect_timeout: Duration,
115133
initialize_timeout: Duration,
116134
},
135+
NoiseRendezvous {
136+
provider: SharedNoiseRendezvousConnectProvider,
137+
},
117138
#[allow(dead_code)]
118139
StdioCommand {
119140
command: StdioExecServerCommand,
120141
initialize_timeout: Duration,
121142
},
122143
}
123144

145+
impl std::fmt::Debug for ExecServerTransportParams {
146+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147+
match self {
148+
Self::WebSocketUrl {
149+
websocket_url,
150+
connect_timeout,
151+
initialize_timeout,
152+
} => f
153+
.debug_struct("WebSocketUrl")
154+
.field("websocket_url", websocket_url)
155+
.field("connect_timeout", connect_timeout)
156+
.field("initialize_timeout", initialize_timeout)
157+
.finish(),
158+
Self::NoiseRendezvous { provider } => f
159+
.debug_struct("NoiseRendezvous")
160+
.field("environment_id", &provider.environment_id())
161+
.finish(),
162+
Self::StdioCommand {
163+
command,
164+
initialize_timeout,
165+
} => f
166+
.debug_struct("StdioCommand")
167+
.field("command", command)
168+
.field("initialize_timeout", initialize_timeout)
169+
.finish(),
170+
}
171+
}
172+
}
173+
124174
impl ExecServerTransportParams {
125175
pub(crate) fn websocket_url(websocket_url: String) -> Self {
126176
Self::WebSocketUrl {

codex-rs/exec-server/src/client_transport.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ impl ExecServerClient {
4444
})
4545
.await
4646
}
47+
crate::client_api::ExecServerTransportParams::NoiseRendezvous { provider } => {
48+
let args = provider.connect_args().await?;
49+
// Keep the configured environment and the freshly authorized
50+
// bundle bound together before opening the websocket.
51+
if args.bundle.environment_id != provider.environment_id() {
52+
return Err(ExecServerError::Protocol(
53+
"Noise rendezvous provider returned a different environment id".to_string(),
54+
));
55+
}
56+
Self::connect_noise_rendezvous(args).await
57+
}
4758
crate::client_api::ExecServerTransportParams::StdioCommand {
4859
command,
4960
initialize_timeout,

codex-rs/exec-server/src/environment.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::ExecServerError;
99
use crate::ExecServerRuntimePaths;
1010
use crate::ExecutorFileSystem;
1111
use crate::HttpClient;
12+
use crate::SharedNoiseRendezvousConnectProvider;
1213
use crate::client::LazyRemoteExecServerClient;
1314
use crate::client::http_client::ReqwestHttpClient;
1415
use crate::client_api::ExecServerTransportParams;
@@ -282,6 +283,37 @@ impl EnvironmentManager {
282283
.insert(environment_id, Arc::new(environment));
283284
Ok(())
284285
}
286+
287+
/// Adds or replaces a named remote environment that connects through an
288+
/// authenticated, end-to-end encrypted rendezvous stream.
289+
///
290+
/// The provider is retained so every reconnect obtains fresh authorization.
291+
/// This transport never falls back to the URL-only remote environment path.
292+
pub fn upsert_noise_environment(
293+
&self,
294+
environment_id: String,
295+
provider: SharedNoiseRendezvousConnectProvider,
296+
) -> Result<(), ExecServerError> {
297+
if environment_id.is_empty() {
298+
return Err(ExecServerError::Protocol(
299+
"environment id cannot be empty".to_string(),
300+
));
301+
}
302+
if environment_id != provider.environment_id() {
303+
return Err(ExecServerError::Protocol(
304+
"Noise environment id does not match connection provider".to_string(),
305+
));
306+
}
307+
let environment = Environment::remote_with_transport(
308+
ExecServerTransportParams::NoiseRendezvous { provider },
309+
self.local_runtime_paths.clone(),
310+
);
311+
self.environments
312+
.write()
313+
.unwrap_or_else(std::sync::PoisonError::into_inner)
314+
.insert(environment_id, Arc::new(environment));
315+
Ok(())
316+
}
285317
}
286318

287319
/// Concrete execution/filesystem environment selected for a session.
@@ -420,6 +452,7 @@ impl Environment {
420452
websocket_url: exec_server_url,
421453
..
422454
} => Some(exec_server_url.clone()),
455+
ExecServerTransportParams::NoiseRendezvous { .. } => None,
423456
ExecServerTransportParams::StdioCommand { .. } => None,
424457
};
425458
let client = LazyRemoteExecServerClient::new(remote_transport.clone());

codex-rs/exec-server/src/environment_toml.rs

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct EnvironmentToml {
4848
initialize_timeout_sec: Option<Duration>,
4949
}
5050

51-
#[derive(Clone, Debug, PartialEq, Eq)]
51+
#[derive(Clone, Debug)]
5252
struct TomlEnvironmentProvider {
5353
default: EnvironmentDefault,
5454
include_local: bool,
@@ -577,18 +577,26 @@ mod tests {
577577
)
578578
.expect("provider");
579579

580+
let ExecServerTransportParams::StdioCommand {
581+
command,
582+
initialize_timeout,
583+
} = &provider.environments[0].1
584+
else {
585+
panic!("expected stdio transport");
586+
};
580587
assert_eq!(
581-
provider.environments[0].1,
582-
ExecServerTransportParams::StdioCommand {
583-
command: StdioExecServerCommand {
584-
program: "ssh".to_string(),
585-
args: Vec::new(),
586-
env: HashMap::new(),
587-
cwd: Some(config_dir.path().join("workspace")),
588-
},
589-
initialize_timeout: DEFAULT_REMOTE_EXEC_SERVER_INITIALIZE_TIMEOUT,
588+
command,
589+
&StdioExecServerCommand {
590+
program: "ssh".to_string(),
591+
args: Vec::new(),
592+
env: HashMap::new(),
593+
cwd: Some(config_dir.path().join("workspace")),
590594
}
591595
);
596+
assert_eq!(
597+
*initialize_timeout,
598+
DEFAULT_REMOTE_EXEC_SERVER_INITIALIZE_TIMEOUT
599+
);
592600
}
593601

594602
#[test]
@@ -614,26 +622,35 @@ mod tests {
614622
})
615623
.expect("provider");
616624

625+
let ExecServerTransportParams::WebSocketUrl {
626+
websocket_url,
627+
connect_timeout,
628+
initialize_timeout,
629+
} = &provider.environments[0].1
630+
else {
631+
panic!("expected websocket transport");
632+
};
633+
assert_eq!(websocket_url, "ws://127.0.0.1:8765");
634+
assert_eq!(*connect_timeout, Duration::from_secs(12));
635+
assert_eq!(*initialize_timeout, Duration::from_secs(34));
636+
637+
let ExecServerTransportParams::StdioCommand {
638+
command,
639+
initialize_timeout,
640+
} = &provider.environments[1].1
641+
else {
642+
panic!("expected stdio transport");
643+
};
617644
assert_eq!(
618-
provider.environments[0].1,
619-
ExecServerTransportParams::WebSocketUrl {
620-
websocket_url: "ws://127.0.0.1:8765".to_string(),
621-
connect_timeout: Duration::from_secs(12),
622-
initialize_timeout: Duration::from_secs(34),
623-
}
624-
);
625-
assert_eq!(
626-
provider.environments[1].1,
627-
ExecServerTransportParams::StdioCommand {
628-
command: StdioExecServerCommand {
629-
program: "ssh".to_string(),
630-
args: Vec::new(),
631-
env: HashMap::new(),
632-
cwd: None,
633-
},
634-
initialize_timeout: Duration::from_secs(56),
645+
command,
646+
&StdioExecServerCommand {
647+
program: "ssh".to_string(),
648+
args: Vec::new(),
649+
env: HashMap::new(),
650+
cwd: None,
635651
}
636652
);
653+
assert_eq!(*initialize_timeout, Duration::from_secs(56));
637654
}
638655

639656
#[test]

codex-rs/exec-server/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ pub use client_api::ExecServerClientConnectOptions;
3535
pub use client_api::HttpClient;
3636
pub use client_api::NoiseRendezvousConnectArgs;
3737
pub use client_api::NoiseRendezvousConnectBundle;
38+
pub use client_api::NoiseRendezvousConnectProvider;
3839
pub use client_api::RemoteExecServerConnectArgs;
40+
pub use client_api::SharedNoiseRendezvousConnectProvider;
3941
pub use codex_file_system::CopyOptions;
4042
pub use codex_file_system::CreateDirectoryOptions;
4143
pub use codex_file_system::ExecutorFileSystem;

0 commit comments

Comments
 (0)