Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
165c22f
feat: self-signed HTTPS for goosed server
aharvard Feb 10, 2026
a87cec8
feat: serve MCP app guest HTML from real URL instead of srcdoc
aharvard Feb 11, 2026
8d8fc92
fix: scope self-signed cert trust to localhost via net.fetch
aharvard Feb 12, 2026
526c8d8
fix: move certificate-error handler to top level
aharvard Feb 12, 2026
4ece61f
fix: add setCertificateVerifyProc for net.fetch self-signed cert support
aharvard Feb 12, 2026
b4c0dd2
fix: update tunnel proxy and external backend to use HTTPS
aharvard Feb 19, 2026
3e5e953
Merge origin/main into aharvard/self-signed-https
aharvard Feb 19, 2026
bc82490
fix: make tunnel proxy scheme configurable for HTTPS support
aharvard Feb 19, 2026
399193e
fix: accept self-signed certs in Node.js integration tests
aharvard Feb 19, 2026
96acdb4
fix: await background extension loading in read_resource and call_tool
aharvard Feb 19, 2026
5172071
fix: use https:// for default GOOSE_API_HOST to match HTTPS server
aharvard Feb 19, 2026
05a2a68
feat: pin self-signed cert fingerprint for secure localhost TLS
aharvard Feb 20, 2026
1f3224a
fix: address remaining review feedback from jh-block
aharvard Feb 20, 2026
d9c66d8
fix: always accept localhost in certificate-error, pin fingerprint on…
aharvard Feb 20, 2026
a29f343
fix: use aws_lc_rs crypto provider in tunnel proxy to match tls.rs
aharvard Feb 20, 2026
6d6a142
fix: pin cert fingerprint in renderer certificate-error handler
aharvard Feb 25, 2026
8221aa6
merge: resolve conflict with origin/main in agent.rs
aharvard Feb 25, 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
63 changes: 60 additions & 3 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion crates/goose-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ rand = "0.9.2"
hex = "0.4.3"
socket2 = "0.6.1"
fs2 = { workspace = true }
rustls = { version = "0.23", features = ["ring"] }
rustls = { version = "0.23", features = ["aws_lc_rs"] }
uuid = { workspace = true }
once_cell = { workspace = true }
dirs = { workspace = true }
rcgen = "0.13"
axum-server = { version = "0.8.0", features = ["tls-rustls"] }
aws-lc-rs = "1.16.0"

[target.'cfg(windows)'.dependencies]
winreg = { version = "0.55.0" }
Expand Down
1 change: 1 addition & 0 deletions crates/goose-server/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub async fn check_token(
if request.uri().path() == "/status"
|| request.uri().path() == "/mcp-ui-proxy"
|| request.uri().path() == "/mcp-app-proxy"
|| request.uri().path() == "/mcp-app-guest"
{
return Ok(next.run(request).await);
}
Expand Down
21 changes: 16 additions & 5 deletions crates/goose-server/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use crate::configuration;
use crate::state;
use anyhow::Result;
use axum::middleware;
use axum_server::Handle;
use goose_server::auth::check_token;
use goose_server::tls::self_signed_config;
use tower_http::cors::{Any, CorsLayer};
use tracing::info;

// Graceful shutdown signal
#[cfg(unix)]
async fn shutdown_signal() {
use tokio::signal::unix::{signal, SignalKind};
Expand Down Expand Up @@ -53,8 +54,17 @@ pub async fn run() -> Result<()> {
))
.layer(cors);

let listener = tokio::net::TcpListener::bind(settings.socket_addr()).await?;
info!("listening on {}", listener.local_addr()?);
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 @@ -66,8 +76,9 @@ pub async fn run() -> Result<()> {
gateway_manager.check_auto_start().await;
});

axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
axum_server::bind_rustls(addr, tls_setup.config)
.handle(handle)
.serve(app.into_make_service())
.await?;

if goose::otel::otlp::is_otlp_initialized() {
Expand Down
1 change: 1 addition & 0 deletions crates/goose-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod error;
pub mod openapi;
pub mod routes;
pub mod state;
pub mod tls;
pub mod tunnel;

// Re-export commonly used items
Expand Down
25 changes: 24 additions & 1 deletion crates/goose-server/src/routes/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,16 @@ async fn update_working_dir(
Ok(StatusCode::OK)
}

async fn ensure_extensions_loaded(state: &AppState, session_id: &str) {
if let Some(_results) = state.take_extension_loading_task(session_id).await {
tracing::debug!(
"Awaited background extension loading for session {} before serving request",
session_id
);
state.remove_extension_loading_task(session_id).await;
}
}

#[utoipa::path(
post,
path = "/agent/read_resource",
Expand All @@ -866,6 +876,8 @@ async fn read_resource(
) -> Result<Json<ReadResourceResponse>, StatusCode> {
use rmcp::model::ResourceContents;

ensure_extensions_loaded(&state, &payload.session_id).await;

let agent = state
.get_agent_for_route(payload.session_id.clone())
.await?;
Expand All @@ -879,7 +891,16 @@ async fn read_resource(
CancellationToken::default(),
)
.await
.map_err(|_e| StatusCode::INTERNAL_SERVER_ERROR)?;
.map_err(|e| {
tracing::error!(
"read_resource failed for session={}, uri={}, extension={}: {:?}",
payload.session_id,
payload.uri,
payload.extension_name,
e
);
StatusCode::INTERNAL_SERVER_ERROR
})?;
Comment thread
aharvard marked this conversation as resolved.

let content = read_result
.contents
Expand Down Expand Up @@ -936,6 +957,8 @@ async fn call_tool(
State(state): State<Arc<AppState>>,
Json(payload): Json<CallToolRequest>,
) -> Result<Json<CallToolResponse>, StatusCode> {
ensure_extensions_loaded(&state, &payload.session_id).await;

let agent = state
.get_agent_for_route(payload.session_id.clone())
.await?;
Expand Down
Loading
Loading