{"record":{"id":"8706e1e0133e54db","repo":"gastownhall/beads","slug":"addr-q-binds-beyond-loopback-which-requires","errorCode":null,"errorMessage":"--addr %q binds beyond loopback, which requires --allow-non-loopback (and, with it, --auth-token-file)","messagePattern":"--addr %q binds beyond loopback, which requires --allow-non-loopback \\(and, with it, --auth-token-file\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/httpapi/server.go","lineNumber":451,"sourceCode":"// Hostnames are refused, \"localhost\" included. A name is not a listener\n// specification — it resolves to whatever the host's resolver says today, so\n// the operator cannot tell from the flag which interfaces they just opened.\n// Unix sockets are not supported at all; they fail here because they do not\n// parse as host:port.\nfunc ValidateBindAddr(addr string, allowNonLoopback bool) (net.IP, error) {\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"--addr %q must be HOST:PORT with a numeric IP literal host (unix sockets are not supported): %w\", addr, err)\n\t}\n\tif _, err := strconv.ParseUint(port, 10, 16); err != nil {\n\t\treturn nil, fmt.Errorf(\"--addr %q: port must be a number from 0 to 65535 (0 picks an ephemeral port)\", addr)\n\t}\n\tip := net.ParseIP(host)\n\tif ip == nil {\n\t\treturn nil, fmt.Errorf(\"--addr %q: host must be a numeric IP literal, not a name — use 127.0.0.1 rather than localhost\", addr)\n\t}\n\tif !ip.IsLoopback() && !allowNonLoopback {\n\t\treturn nil, fmt.Errorf(\"--addr %q binds beyond loopback, which requires --allow-non-loopback (and, with it, --auth-token-file)\", addr)\n\t}\n\treturn ip, nil\n}\n\n// Listen validates the configuration, binds the listener, and reports the\n// bound address on stdout and the startup state on stderr. It does not accept\n// anything until Serve runs.\n//\n// There is no lock file, pid file or discovery file: bd serve is\n// operator-invoked and the TCP bind IS the mutual exclusion, so a second\n// instance on the same fixed port fails here with the operating system's own\n// address-in-use error. (Under the ephemeral default that exclusion does not\n// exist — N instances simply run on N ports — which is why fixed ports are the\n// deployment recommendation.)\nfunc Listen(cfg Config) (*Server, error) {\n\tif err := checkDatabaseSource(cfg); err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/httpapi/server.go#L433-L469","documentation":"ValidateBindAddr rejects binding to a non-loopback IP unless the caller explicitly opts in with allowNonLoopback (the --allow-non-loopback flag, which requires --auth-token-file). This is a security guard against accidentally exposing the server on a public interface without authentication.","triggerScenarios":"Calling ValidateBindAddr with a loopback=false IP such as 0.0.0.0, 192.168.1.5, or :: while allowNonLoopback is false.","commonSituations":"Users bind to 0.0.0.0 to share the server on a LAN or in a container and are surprised by the auth requirements; automated deployments pass a pod IP without enabling the flag.","solutions":["Bind to a loopback address like 127.0.0.1:PORT if only local access is needed","If external access is required, pass --allow-non-loopback together with --auth-token-file","Use a reverse proxy/tunnel (e.g. SSH port forward) instead of binding beyond loopback"],"exampleFix":"// before\n--addr 0.0.0.0:8080\n// after\n--addr 0.0.0.0:8080 --allow-non-loopback --auth-token-file /path/to/token","handlingStrategy":"validation","validationCode":"ip, _ := net.ResolveIPAddr(\"ip\", hostOnly(addr))\nif ip != nil && !ip.IP.IsLoopback() {\n    // must enable allowNonLoopback + auth token\n}","typeGuard":null,"tryCatchPattern":"if _, err := httpapi.ValidateBindAddr(addr, allowNonLoopback); err != nil {\n    log.Fatalf(\"refusing to start: %v\", err)\n}","preventionTips":["Default to loopback binds; only widen deliberately","When binding non-loopback, always pair --allow-non-loopback with --auth-token-file","Prefer loopback + tunnel/reverse proxy over direct exposure"],"tags":["security","validation","cli","network"],"backgroundTag":"non-loopback-bind-rejected","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}