Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c4289ce
feat(extensions): add Unix socket transport for StreamableHttp extens…
wpfleger96 Mar 3, 2026
f4af365
fix: make TLS opt-out in goosed agent via GOOSE_TLS env var
wpfleger96 Mar 6, 2026
99564e9
fix: thread TLS scheme through TunnelManager
wpfleger96 Mar 6, 2026
9eeac23
Merge remote-tracking branch 'origin/main' into wpfleger/tls
wpfleger96 Mar 6, 2026
5716182
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 6, 2026
52ee2b5
Merge remote-tracking branch 'origin/wpfleger/tls' into wpfleger/sock…
wpfleger96 Mar 6, 2026
944f675
fix(extensions):
wpfleger96 Mar 6, 2026
53517de
fix(extensions): add OAuth retry to Unix socket client setup
wpfleger96 Mar 6, 2026
057a044
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 6, 2026
6c9dad3
fix(extensions): support Unix socket error type in OAuth auth detection
wpfleger96 Mar 6, 2026
02320d0
fix(extensions): preserve user headers on Unix socket OAuth retry
wpfleger96 Mar 6, 2026
b5487d8
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 9, 2026
c1d7153
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 9, 2026
322d0b5
fix(extensions): use spawn_blocking for abstract socket connect
wpfleger96 Mar 10, 2026
93c2b69
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 16, 2026
94b8d10
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 23, 2026
b884cd1
fix: use `std::io::Error::other()` to satisfy clippy `io_other_error`…
wpfleger96 Mar 23, 2026
9854fef
fix: resolve clippy redundant_closure and expand env vars in socket f…
wpfleger96 Mar 23, 2026
403b39b
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 23, 2026
b3c49b9
Merge remote-tracking branch 'origin/main' into wpfleger/socket-support
wpfleger96 Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/goose-acp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn mcp_server_to_extension_config(mcp_server: McpServer) -> Result<ExtensionConf
.into_iter()
.map(|h| (h.name, h.value))
.collect(),
socket: None,
timeout: None,
bundled: Some(false),
available_tools: vec![],
Expand Down Expand Up @@ -1403,6 +1404,7 @@ mod tests {
"Authorization".into(),
"Bearer ghp_xxxxxxxxxxxx".into()
)]),
socket: None,
timeout: None,
bundled: Some(false),
available_tools: vec![],
Expand Down
1 change: 1 addition & 0 deletions crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ fn configure_streamable_http_extension() -> anyhow::Result<()> {
envs: Envs::new(envs),
env_keys,
headers,
socket: None,
description,
timeout: Some(timeout),
bundled: None,
Expand Down
3 changes: 3 additions & 0 deletions crates/goose-cli/src/recipes/secret_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ mod tests {
bundled: None,
available_tools: Vec::new(),
headers: HashMap::new(),
socket: None,
},
ExtensionConfig::Stdio {
name: "slack-mcp".to_string(),
Expand Down Expand Up @@ -246,6 +247,7 @@ mod tests {
bundled: None,
available_tools: Vec::new(),
headers: HashMap::new(),
socket: None,
},
ExtensionConfig::Stdio {
name: "service-b".to_string(),
Expand Down Expand Up @@ -305,6 +307,7 @@ mod tests {
bundled: None,
available_tools: Vec::new(),
headers: HashMap::new(),
socket: None,
}]),
sub_recipes: Some(vec![SubRecipe {
name: "child-recipe".to_string(),
Expand Down
4 changes: 4 additions & 0 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ impl CliSession {
envs: Envs::new(HashMap::new()),
env_keys: Vec::new(),
headers: HashMap::new(),
socket: None,
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
timeout: Some(timeout),
bundled: None,
Expand Down Expand Up @@ -2034,6 +2035,7 @@ mod tests {
envs: Envs::default(),
env_keys: vec![],
headers: HashMap::new(),
socket: None,
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
timeout: Some(300),
bundled: None,
Expand All @@ -2049,6 +2051,7 @@ mod tests {
envs: Envs::default(),
env_keys: vec![],
headers: HashMap::new(),
socket: None,
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
timeout: Some(300),
bundled: None,
Expand All @@ -2064,6 +2067,7 @@ mod tests {
envs: Envs::default(),
env_keys: vec![],
headers: HashMap::new(),
socket: None,
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
timeout: Some(300),
bundled: None,
Expand Down
41 changes: 26 additions & 15 deletions crates/goose-server/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub async fn run() -> Result<()> {
let secret_key =
std::env::var("GOOSE_SERVER__SECRET_KEY").unwrap_or_else(|_| "test".to_string());

let app_state = state::AppState::new().await?;
let app_state = state::AppState::new(settings.tls).await?;

let cors = CorsLayer::new()
.allow_origin(Any)
Expand All @@ -55,16 +55,6 @@ pub async fn run() -> Result<()> {
.layer(cors);

let addr = settings.socket_addr();
let tls_setup = self_signed_config().await?;

let handle = Handle::new();
let shutdown_handle = handle.clone();
tokio::spawn(async move {
shutdown_signal().await;
shutdown_handle.graceful_shutdown(None);
});

info!("listening on https://{}", addr);

let tunnel_manager = app_state.tunnel_manager.clone();
tokio::spawn(async move {
Expand All @@ -76,10 +66,31 @@ pub async fn run() -> Result<()> {
gateway_manager.check_auto_start().await;
});

axum_server::bind_rustls(addr, tls_setup.config)
.handle(handle)
.serve(app.into_make_service())
.await?;
if settings.tls {
let tls_setup = self_signed_config().await?;

let handle = Handle::new();
let shutdown_handle = handle.clone();
tokio::spawn(async move {
shutdown_signal().await;
shutdown_handle.graceful_shutdown(None);
});

info!("listening on https://{}", addr);

axum_server::bind_rustls(addr, tls_setup.config)
.handle(handle)
.serve(app.into_make_service())
.await?;
} else {
let listener = tokio::net::TcpListener::bind(addr).await?;

info!("listening on http://{}", addr);

axum::serve(listener, app)
.with_graceful_shutdown(async { shutdown_signal().await })
.await?;
}

if goose::otel::otlp::is_otlp_initialized() {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Expand Down
8 changes: 8 additions & 0 deletions crates/goose-server/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub struct Settings {
pub host: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default = "default_tls")]
pub tls: bool,
}

impl Settings {
Expand All @@ -28,6 +30,7 @@ impl Settings {
// Server defaults
.set_default("host", default_host())?
.set_default("port", default_port())?
.set_default("tls", default_tls())?
// Layer on the environment variables
.add_source(
Environment::with_prefix("GOOSE")
Expand Down Expand Up @@ -74,6 +77,10 @@ fn default_port() -> u16 {
3000
}

fn default_tls() -> bool {
true
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -83,6 +90,7 @@ mod tests {
let server_settings = Settings {
host: "127.0.0.1".to_string(),
port: 3000,
tls: true,
};
let addr = server_settings.socket_addr();
assert_eq!(addr.to_string(), "127.0.0.1:3000");
Expand Down
2 changes: 1 addition & 1 deletion crates/goose-server/src/routes/action_required.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ mod tests {

#[tokio::test(flavor = "multi_thread")]
async fn test_tool_confirmation_endpoint() {
let state = AppState::new().await.unwrap();
let state = AppState::new(true).await.unwrap();

let app = routes(state);

Expand Down
2 changes: 1 addition & 1 deletion crates/goose-server/src/routes/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ mod tests {

#[tokio::test(flavor = "multi_thread")]
async fn test_reply_endpoint() {
let state = AppState::new().await.unwrap();
let state = AppState::new(true).await.unwrap();

let app = routes(state);

Expand Down
4 changes: 2 additions & 2 deletions crates/goose-server/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ pub struct AppState {
}

impl AppState {
pub async fn new() -> anyhow::Result<Arc<AppState>> {
pub async fn new(tls: bool) -> anyhow::Result<Arc<AppState>> {
register_builtin_extensions(goose_mcp::BUILTIN_EXTENSIONS.clone());

let agent_manager = AgentManager::instance().await?;
let tunnel_manager = Arc::new(TunnelManager::new());
let tunnel_manager = Arc::new(TunnelManager::new(tls));
let gateway_manager = Arc::new(GatewayManager::new(agent_manager.clone())?);

Ok(Arc::new(Self {
Expand Down
9 changes: 6 additions & 3 deletions crates/goose-server/src/tunnel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,25 @@ pub struct TunnelManager {
restart_tx: Arc<RwLock<Option<mpsc::Sender<()>>>>,
watchdog_handle: Arc<RwLock<Option<tokio::task::JoinHandle<()>>>>,
lock_file: Arc<std::sync::Mutex<Option<File>>>,
scheme: String,
}

impl Default for TunnelManager {
fn default() -> Self {
Self::new()
Self::new(true)
}
}

impl TunnelManager {
pub fn new() -> Self {
pub fn new(tls: bool) -> Self {
TunnelManager {
state: Arc::new(RwLock::new(TunnelState::Idle)),
info: Arc::new(RwLock::new(None)),
lapstone_handle: Arc::new(RwLock::new(None)),
restart_tx: Arc::new(RwLock::new(None)),
watchdog_handle: Arc::new(RwLock::new(None)),
lock_file: Arc::new(std::sync::Mutex::new(None)),
scheme: if tls { "https" } else { "http" }.to_string(),
}
}

Expand Down Expand Up @@ -229,7 +231,7 @@ impl TunnelManager {
tunnel_secret,
server_secret,
agent_id,
"https",
&self.scheme,
self.lapstone_handle.clone(),
restart_tx,
)
Expand Down Expand Up @@ -317,6 +319,7 @@ impl TunnelManager {
restart_tx: self.restart_tx.clone(),
watchdog_handle: self.watchdog_handle.clone(),
lock_file: self.lock_file.clone(),
scheme: self.scheme.clone(),
}
}

Expand Down
9 changes: 9 additions & 0 deletions crates/goose/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ pulldown-cmark = "0.13.0"
llama-cpp-2 = { version = "0.1.137", features = ["sampler"] }
encoding_rs = "0.8.35"

# Unix domain socket HTTP transport for StreamableHttp extensions
[target.'cfg(unix)'.dependencies]
hyper = { version = "1", features = ["client", "http1"] }
hyper-util = { version = "0.1", features = ["tokio"] }
http-body-util = "0.1"
sse-stream = "0.2"
bytes = { workspace = true }
http = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["wincred"] }

Expand Down
Loading
Loading