{"record":{"id":"7152f206f3980652","repo":"Hmbown/CodeWhale","slug":"refusing-unauthenticated-app-server-bind-on-non-lo","errorCode":null,"errorMessage":"refusing unauthenticated app-server bind on non-loopback address","messagePattern":"refusing unauthenticated app-server bind on non-loopback address","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/app-server/src/lib.rs","lineNumber":882,"sourceCode":"        runtime_bridge: Arc::new(Mutex::new(None)),\n        runtime_thread_map: Arc::new(Mutex::new(HashMap::new())),\n        stdio_thread_hints: Arc::new(Mutex::new(HashMap::new())),\n        in_flight_turns: Arc::new(Mutex::new(HashMap::new())),\n    })\n}\n\nfn resolve_auth_token(options: &AppServerOptions) -> Result<Option<String>> {\n    let configured = options.auth_token.as_ref().map(|token| token.trim());\n    if let Some(token) = configured\n        && token.is_empty()\n    {\n        bail!(\"app-server auth token cannot be empty\");\n    }\n    let has_explicit_token = configured.is_some();\n\n    if options.insecure_no_auth {\n        if !options.listen.ip().is_loopback() {\n            bail!(\"refusing unauthenticated app-server bind on non-loopback address\");\n        }\n        eprintln!(\"warning: app-server HTTP auth disabled by --insecure-no-auth\");\n        return Ok(None);\n    }\n\n    if !has_explicit_token && !options.listen.ip().is_loopback() {\n        bail!(\n            \"refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN\"\n        );\n    }\n\n    let token = configured\n        .map(str::to_string)\n        .unwrap_or_else(|| format!(\"cwapp_{}\", Uuid::new_v4().simple()));\n    for line in app_server_auth_status_lines(has_explicit_token) {\n        eprintln!(\"{line}\");\n    }\n    Ok(Some(token))","sourceCodeStart":864,"sourceCodeEnd":900,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/app-server/src/lib.rs#L864-L900","documentation":"The app-server refuses to start with HTTP authentication disabled (`--insecure-no-auth`) while binding to a non-loopback address. Disabling auth exposes the server's full control API (threads, prompts, attachments) without any token, so it is only tolerated on 127.0.0.1/::1. This is a deliberate fail-loud guard against an unsafe deployment configuration.","triggerScenarios":"Launching the app-server with `--insecure-no-auth` (or the equivalent options.insecure_no_auth flag) while `options.listen` resolves to a non-loopback IP such as 0.0.0.0 or an external interface address.","commonSituations":"Developers trying to expose the app-server to a container sibling, LAN device, or Docker port map who pass `--insecure-no-auth` together with `--listen 0.0.0.0:<port>` instead of configuring a token.","solutions":["Bind to a loopback address (e.g. `--listen 127.0.0.1:<port>`) if only local access is needed.","Remove `--insecure-no-auth` and supply an explicit token via `--auth-token <token>` or the `CODEWHALE_APP_SERVER_TOKEN` env var.","If remote access truly must be unauthenticated (never recommended), tunnel it through SSH/localhost forwarding instead of binding non-loopback."],"exampleFix":"// before\napp-server --insecure-no-auth --listen 0.0.0.0:3927\n// after\napp-server --auth-token \"$MY_TOKEN\" --listen 0.0.0.0:3927","handlingStrategy":"validation","validationCode":"// Rust-style pre-check before launching\nfn ensure_safe_no_auth(listen: std::net::IpAddr) -> Result<(), String> {\n    if listen.is_loopback() { Ok(()) } else { Err(format!(\"{} is non-loopback; --insecure-no-auth is not allowed\", listen)) }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never combine --insecure-no-auth with a 0.0.0.0 or LAN bind in scripts/deployment templates.","Prefer explicit --auth-token even for local use so the same launch line works everywhere.","Lint startup scripts for the insecure-no-auth flag."],"tags":["security","authentication","cli","configuration"],"backgroundTag":"conflicting-config-options","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}