diff --git a/CHANGELOG.md b/CHANGELOG.md index c436d9e44..c3d60630a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to Switchyard are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- The standalone `switchyard-server` now binds to `127.0.0.1` by default. + Existing deployments that accept remote clients must add `--host 0.0.0.0`. + The provided Docker image and systemd service already set this explicitly. + ## [0.3.0] Switchyard 0.3.0 builds on the native server and Rust library introduced in diff --git a/Dockerfile b/Dockerfile index eb6815eea..8d58719c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,4 +28,4 @@ ENV HOME=/tmp USER 1000:1000 EXPOSE 4000 -ENTRYPOINT ["switchyard-server"] +ENTRYPOINT ["switchyard-server", "--host", "0.0.0.0"] diff --git a/benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml b/benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml index 85df8b4cb..d750a58c1 100644 --- a/benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml +++ b/benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml @@ -29,7 +29,7 @@ # OpenRouter so the file runs as written with an OpenRouter key. Treat the figures above # as this pair's operating point, not a guarantee for every serving stack. # -# switchyard-server --config benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml --port 4000 +# switchyard-server --config benchmark/routing-profiles/deepswe-v11-advisor-gate-luna-sol.toml --host 0.0.0.0 --port 4000 schema_version = 1 diff --git a/benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml b/benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml index 24197ac62..40b17a403 100644 --- a/benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml +++ b/benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml @@ -29,7 +29,7 @@ # run as written with an `OPENROUTER_API_KEY`. Absolute results may vary by serving # stack. # -# switchyard-server --config benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml --port 4000 +# switchyard-server --config benchmark/routing-profiles/deepswe-v11-plan-execute-luna-sol.toml --host 0.0.0.0 --port 4000 schema_version = 1 diff --git a/benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml b/benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml index 5a17d5f3b..60815cb14 100644 --- a/benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml +++ b/benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml @@ -38,7 +38,7 @@ # a local configuration reference. # # Run: -# switchyard-server --config benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml --port 4000 +# switchyard-server --config benchmark/routing-profiles/deepswe-v11-stage-router-luna-sol.toml --host 0.0.0.0 --port 4000 # See benchmark/README.md, section "DeepSWE v1.1", for the Pier command. schema_version = 1 diff --git a/crates/switchyard-server/README.md b/crates/switchyard-server/README.md index 152dbfe02..c4667f4a0 100644 --- a/crates/switchyard-server/README.md +++ b/crates/switchyard-server/README.md @@ -4,6 +4,11 @@ and Anthropic Messages endpoints. A TOML file explicitly defines the LLM clients, targets, and algorithm routes served by the process. +The standalone binary listens on `127.0.0.1` unless `--host` is set. Pass +`--host 0.0.0.0` to accept remote clients. The provided Docker image and systemd +service opt into that address explicitly. A later `--host` argument overrides +the container's default. + ```toml # routes.toml schema_version = 1 diff --git a/crates/switchyard-server/src/cli.rs b/crates/switchyard-server/src/cli.rs index 231d4e2bc..479a0f0e6 100644 --- a/crates/switchyard-server/src/cli.rs +++ b/crates/switchyard-server/src/cli.rs @@ -13,7 +13,7 @@ use switchyard_server::{ ServerRunOptions, ServerState, TlsOptions, run_server, }; -const DEFAULT_HOST: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); +const DEFAULT_HOST: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); const DEFAULT_PORT: u16 = 4000; /// Command-line arguments accepted by the Rust server binary. @@ -29,7 +29,7 @@ pub(crate) struct ServerArgs { config: PathBuf, /// Host address to bind. - #[arg(long, default_value_t = DEFAULT_HOST)] + #[arg(long, default_value_t = DEFAULT_HOST, overrides_with = "host")] host: IpAddr, /// Port to bind. @@ -101,3 +101,32 @@ pub(crate) async fn run(args: ServerArgs) -> ServerResult<()> { let (state, options) = args.into_runtime()?; run_server(state, options).await } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_host_is_loopback() { + let args = ServerArgs::try_parse_from(["switchyard-server", "--config", "routes.toml"]) + .expect("valid arguments"); + assert_eq!(args.host, IpAddr::V4(Ipv4Addr::LOCALHOST)); + } + + #[test] + fn explicit_host_overrides_the_container_default() { + for host in ["127.0.0.1", "::1", "0.0.0.0"] { + let args = ServerArgs::try_parse_from([ + "switchyard-server", + "--host", + "0.0.0.0", + "--config", + "routes.toml", + "--host", + host, + ]) + .expect("valid explicit host"); + assert_eq!(args.host, host.parse::().expect("IP address")); + } + } +} diff --git a/dev-server/switchyard.service b/dev-server/switchyard.service index 51962a9a1..288c934d2 100644 --- a/dev-server/switchyard.service +++ b/dev-server/switchyard.service @@ -13,6 +13,7 @@ LoadCredential=tls-cert.pem:/etc/switchyard/cert.pem # %d is where systemd put those files ExecStart=/usr/local/bin/switchyard-server \ -p 443 \ + --host 0.0.0.0 \ --config %d/config.toml \ --tls-key %d/tls-key.pem \ --tls-cert %d/tls-cert.pem diff --git a/docs/cli_reference.md b/docs/cli_reference.md index e5da32455..214c987dd 100644 --- a/docs/cli_reference.md +++ b/docs/cli_reference.md @@ -14,7 +14,7 @@ switchyard-server --config [options] | Option | Default | Purpose | |---|---|---| | `--config PATH` | Required | TOML file defining LLM clients, targets, and algorithm routes. | -| `--host HOST` | `0.0.0.0` | Address on which the server listens. | +| `--host HOST` | `127.0.0.1` | Address on which the server listens. Use `0.0.0.0` for remote clients. | | `-p, --port PORT` | `4000` | Port on which the server listens. | | `--backlog BACKLOG` | `65535` | TCP listen backlog configured before accepting traffic. | | `--shutdown-timeout SHUTDOWN_TIMEOUT` | `30s` | Maximum time active requests may drain during shutdown. | diff --git a/docs/getting_started.md b/docs/getting_started.md index 66e1106ba..6dcd02f83 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -128,6 +128,11 @@ switchyard-server --config routes.toml \ --host 127.0.0.1 --port 4000 ``` +The standalone binary binds to `127.0.0.1` by default. For remote clients, +pass `--host 0.0.0.0` explicitly and secure access to the server. The provided +Docker image and systemd service set this option to preserve network access. +A later `--host` argument overrides the image's default. + Any client that speaks OpenAI Chat Completions, Anthropic Messages, or OpenAI Responses API can connect. The route `id` is the model name clients use.