app-server: add Unix socket transport (#18255)

## Summary
- add unix:// app-server transport backed by the shared codex-uds crate
- reuse the websocket connection loop for axum and tungstenite-backed
streams
- add codex app-server proxy to bridge stdio clients to the control
socket
- tolerate Windows UDS backends that report a missing rendezvous path as
connection refused before binding

## Tests
- cargo test -p codex-app-server
control_socket_acceptor_forwards_websocket_text_messages_and_pings
- cargo test -p codex-app-server
- just fmt
- just fix -p codex-app-server
- git -c core.fsmonitor=false diff --check
This commit is contained in:
Ruslan Nigmatullin
2026-04-23 11:09:25 -07:00
committed by GitHub
parent c2423f42d1
commit 8a0ab3fc13
13 changed files with 668 additions and 63 deletions
+19 -8
View File
@@ -31,6 +31,7 @@ use crate::transport::OutboundConnectionState;
use crate::transport::TransportEvent;
use crate::transport::auth::policy_from_settings;
use crate::transport::route_outgoing_envelope;
use crate::transport::start_control_socket_acceptor;
use crate::transport::start_remote_control;
use crate::transport::start_stdio_connection;
use crate::transport::start_websocket_acceptor;
@@ -93,6 +94,7 @@ mod transport;
pub use crate::error_code::INPUT_TOO_LARGE_ERROR_CODE;
pub use crate::error_code::INVALID_PARAMS_ERROR_CODE;
pub use crate::transport::AppServerTransport;
pub use crate::transport::app_server_control_socket_path;
pub use crate::transport::auth::AppServerWebsocketAuthArgs;
pub use crate::transport::auth::AppServerWebsocketAuthSettings;
pub use crate::transport::auth::WebsocketAuthCliMode;
@@ -542,7 +544,7 @@ pub async fn run_main_with_transport(
let graceful_signal_restart_enabled = !single_client_mode;
let mut app_server_client_name_rx = None;
match transport {
match &transport {
AppServerTransport::Stdio => {
let (stdio_client_name_tx, stdio_client_name_rx) = oneshot::channel::<String>();
app_server_client_name_rx = Some(stdio_client_name_rx);
@@ -553,9 +555,18 @@ pub async fn run_main_with_transport(
)
.await?;
}
AppServerTransport::UnixSocket { socket_path } => {
let accept_handle = start_control_socket_acceptor(
socket_path.clone(),
transport_event_tx.clone(),
transport_shutdown_token.clone(),
)
.await?;
transport_accept_handles.push(accept_handle);
}
AppServerTransport::WebSocket { bind_address } => {
let accept_handle = start_websocket_acceptor(
bind_address,
*bind_address,
transport_event_tx.clone(),
transport_shutdown_token.clone(),
policy_from_settings(&auth)?,
@@ -660,7 +671,7 @@ pub async fn run_main_with_transport(
config_warnings,
session_source,
auth_manager,
rpc_transport: analytics_rpc_transport(transport),
rpc_transport: analytics_rpc_transport(&transport),
remote_control_handle: Some(remote_control_handle),
}));
let mut thread_created_rx = processor.thread_created_receiver();
@@ -772,7 +783,7 @@ pub async fn run_main_with_transport(
.process_request(
connection_id,
request,
transport,
&transport,
Arc::clone(&connection_state.session),
)
.await;
@@ -892,12 +903,12 @@ pub async fn run_main_with_transport(
Ok(())
}
fn analytics_rpc_transport(transport: AppServerTransport) -> AppServerRpcTransport {
fn analytics_rpc_transport(transport: &AppServerTransport) -> AppServerRpcTransport {
match transport {
AppServerTransport::Stdio => AppServerRpcTransport::Stdio,
AppServerTransport::WebSocket { .. } | AppServerTransport::Off => {
AppServerRpcTransport::Websocket
}
AppServerTransport::UnixSocket { .. }
| AppServerTransport::WebSocket { .. }
| AppServerTransport::Off => AppServerRpcTransport::Websocket,
}
}