{"record":{"id":"e2dcd720f319c52c","repo":"Hmbown/CodeWhale","slug":"codewhale-web-is-loopback-only-and-must-bind-to-12-e2dcd7","errorCode":null,"errorMessage":"Codewhale web is loopback-only and must bind to 127.0.0.1","messagePattern":"Codewhale web is loopback-only and must bind to 127\\.0\\.0\\.1","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_api.rs","lineNumber":835,"sourceCode":"        workspace,\n        manager_config,\n        plugin_registry,\n    )?);\n    Ok((manager, workshop_activation))\n}\n\n/// Start the runtime API server.\npub async fn run_http_server(\n    config: Config,\n    workspace: PathBuf,\n    plugin_discovery: Arc<crate::plugins::PluginDiscoveryContext>,\n    options: RuntimeApiOptions,\n) -> Result<()> {\n    if options.port == 0 {\n        bail!(\"Port must be > 0\");\n    }\n    if options.web && options.host != \"127.0.0.1\" {\n        bail!(\"Codewhale web is loopback-only and must bind to 127.0.0.1\");\n    }\n    if options.web && options.insecure_no_auth {\n        bail!(\"Codewhale web requires Runtime authentication; remove --insecure\");\n    }\n\n    let task_cfg = TaskManagerConfig::from_runtime(\n        &config,\n        workspace.clone(),\n        config.default_text_model.clone(),\n        Some(options.workers),\n    );\n    let (runtime_threads, _workshop_activation) = open_runtime_threads_for_server(\n        &config,\n        workspace.clone(),\n        RuntimeThreadManagerConfig::from_task_data_dir(task_cfg.data_dir.clone()),\n        plugin_discovery.registry_for_workspace(&workspace),\n    )?;\n    let task_manager =","sourceCodeStart":817,"sourceCodeEnd":853,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/runtime_api.rs#L817-L853","documentation":"When the Codewhale web UI is enabled (options.web), run_http_server enforces that the bind host is exactly 127.0.0.1. The web surface is designed loopback-only: exposing it to the network is refused at startup rather than relying on the operator to bind safely. Any --host other than 127.0.0.1 combined with web mode triggers this bail.","triggerScenarios":"Starting the server with web enabled and --host 0.0.0.0, a LAN IP, or a hostname such as localhost; passing a custom host for the plain API while also enabling --web.","commonSituations":"Operators try to serve the web UI from a container or remote VM by binding 0.0.0.0; docker port-mapping setups that require a wildcard bind; users passing 'localhost' expecting it to be accepted (only the literal 127.0.0.1 is).","solutions":["Keep web mode on the default loopback bind: set host to exactly 127.0.0.1.","To reach the web UI remotely, keep the loopback bind and tunnel (SSH port-forward) instead of widening the bind.","If you only meant to run the non-web runtime API on another interface, drop --web so the loopback restriction no longer applies (subject to your own exposure policy)."],"exampleFix":"# before\ncodewhale serve --web --host 0.0.0.0 --port 8080   # bails: loopback-only\n\n# after\nssh -L 8080:127.0.0.1:8080 user@host &\ncodewhale serve --web --host 127.0.0.1 --port 8080   # open http://127.0.0.1:8080 locally","handlingStrategy":"validation","validationCode":"// Rust: enforce the web-mode binding contract at the call site\nif options.web {\n    anyhow::ensure!(\n        options.host == \"127.0.0.1\",\n        \"web mode requires host 127.0.0.1, got {}\",\n        options.host\n    );\n}\nrun_http_server(config, workspace, discovery, options).await?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default --host to 127.0.0.1 in web mode and warn when a user overrides it.","Use SSH port-forwarding (ssh -L 8080:127.0.0.1:8080) for remote access instead of widening the bind.","In containers, publish the loopback port rather than rebinding to 0.0.0.0."],"tags":["server","security","bind-address","web-ui","rust"],"backgroundTag":"invalid-host-binding","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}