{"record":{"id":"63c951c61ce8dbdf","repo":"JuliusBrussee/caveman","slug":"listen-address-q-is-not-loopback-standalone-prox","errorCode":null,"errorMessage":"listen address %q is not loopback; standalone proxy has no inbound authentication","messagePattern":"listen address %q is not loopback; standalone proxy has no inbound authentication","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/config/config.go","lineNumber":133,"sourceCode":"\t\treturn Config{}, err\n\t}\n\treturn cfg, nil\n}\n\n// validateListen keeps standalone's unauthenticated BYOK proxy local to one\n// operator. Binding an empty, wildcard, or non-loopback host would expose every\n// configured provider credential to the network with no inbound authentication.\nfunc validateListen(listen string) error {\n\thost, port, err := net.SplitHostPort(strings.TrimSpace(listen))\n\tif err != nil || port == \"\" {\n\t\treturn fmt.Errorf(\"listen address %q must be loopback host:port\", listen)\n\t}\n\tif strings.EqualFold(host, \"localhost\") {\n\t\treturn nil\n\t}\n\tip := net.ParseIP(host)\n\tif ip == nil || !ip.IsLoopback() {\n\t\treturn fmt.Errorf(\"listen address %q is not loopback; standalone proxy has no inbound authentication\", listen)\n\t}\n\treturn nil\n}\n\nfunc (c Config) withDefaults() Config {\n\tif label := env.String(\"CAVEMAN_LABEL\", \"\"); label != \"\" {\n\t\tc.Label = label\n\t}\n\tif c.Label == \"\" {\n\t\tc.Label = \"local\"\n\t}\n\tif mode := env.String(\"CAVEMAN_MODE\", \"\"); mode != \"\" {\n\t\tc.Mode = mode\n\t}\n\tif listen := env.String(\"CAVEMAN_LISTEN\", \"\"); listen != \"\" {\n\t\tc.Listen = listen\n\t}\n\tif sub := env.String(\"CAVEMAN_SUBSCRIPTION_COMPRESS\", \"\"); sub != \"\" {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/config/config.go#L115-L151","documentation":"validateListen parsed a host:port but the host is not loopback. The standalone caveman proxy forwards every configured provider credential with no inbound authentication, so binding to '', '0.0.0.0', '::', a LAN IP, or a resolvable non-loopback hostname is refused by design (config.go comment states this exposure directly).","triggerScenarios":"listen = ':8080' (empty host), '0.0.0.0:8080', '192.168.1.10:8080', 'myhost.local:8080', or '[::]:8080'. 'localhost' and 127.0.0.0/8 and ::1 pass; everything else hits this error.","commonSituations":"Trying to run the proxy in a container and reach it from the host; wanting a teammate or another machine to share one proxy; DNS name that resolves to loopback is still rejected because only the literal string/IP is checked.","solutions":["Bind to '127.0.0.1:8080' (or 'localhost:8080') and access it from the same machine","For containers, use host networking (--network host) so loopback works, or run the proxy on the host itself","To share the proxy safely, front it with an authenticated reverse proxy (e.g. nginx with TLS + auth) that proxies to the loopback listener","Do not attempt to bypass this check; it protects unauthenticated provider credentials"],"exampleFix":"// before\nlisten: \":8080\"        // empty host -> Error[1061]\nlisten: \"0.0.0.0:8080\" // wildcard -> Error[1061]\n\n// after\nlisten: \"127.0.0.1:8080\"","handlingStrategy":"validation","validationCode":"func isLoopbackHost(host string) bool {\n    if strings.EqualFold(host, \"localhost\") {\n        return true\n    }\n    ip := net.ParseIP(host)\n    return ip != nil && ip.IsLoopback()\n}\n\nif !isLoopbackHost(host) {\n    // refuse to start; suggest 127.0.0.1 instead of widening the bind\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never ship ':port' or '0.0.0.0' defaults — the proxy is unauthenticated BYOK","In containers prefer host networking over wildcard binds","Front an authenticated reverse proxy when off-box access is truly needed"],"tags":["config","security","network","byok"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}