JuliusBrussee/caveman · error

listen address %q is not loopback; standalone proxy has no i

Error message

listen address %q is not loopback; standalone proxy has no inbound authentication

What it means

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).

Source

Thrown at proxy/internal/config/config.go:133

		return Config{}, err
	}
	return cfg, nil
}

// validateListen keeps standalone's unauthenticated BYOK proxy local to one
// operator. Binding an empty, wildcard, or non-loopback host would expose every
// configured provider credential to the network with no inbound authentication.
func validateListen(listen string) error {
	host, port, err := net.SplitHostPort(strings.TrimSpace(listen))
	if err != nil || port == "" {
		return fmt.Errorf("listen address %q must be loopback host:port", listen)
	}
	if strings.EqualFold(host, "localhost") {
		return nil
	}
	ip := net.ParseIP(host)
	if ip == nil || !ip.IsLoopback() {
		return fmt.Errorf("listen address %q is not loopback; standalone proxy has no inbound authentication", listen)
	}
	return nil
}

func (c Config) withDefaults() Config {
	if label := env.String("CAVEMAN_LABEL", ""); label != "" {
		c.Label = label
	}
	if c.Label == "" {
		c.Label = "local"
	}
	if mode := env.String("CAVEMAN_MODE", ""); mode != "" {
		c.Mode = mode
	}
	if listen := env.String("CAVEMAN_LISTEN", ""); listen != "" {
		c.Listen = listen
	}
	if sub := env.String("CAVEMAN_SUBSCRIPTION_COMPRESS", ""); sub != "" {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Bind to '127.0.0.1:8080' (or 'localhost:8080') and access it from the same machine
  2. For containers, use host networking (--network host) so loopback works, or run the proxy on the host itself
  3. To share the proxy safely, front it with an authenticated reverse proxy (e.g. nginx with TLS + auth) that proxies to the loopback listener
  4. Do not attempt to bypass this check; it protects unauthenticated provider credentials

Example fix

// before
listen: ":8080"        // empty host -> Error[1061]
listen: "0.0.0.0:8080" // wildcard -> Error[1061]

// after
listen: "127.0.0.1:8080"
Defensive patterns

Strategy: validation

Validate before calling

func isLoopbackHost(host string) bool {
    if strings.EqualFold(host, "localhost") {
        return true
    }
    ip := net.ParseIP(host)
    return ip != nil && ip.IsLoopback()
}

if !isLoopbackHost(host) {
    // refuse to start; suggest 127.0.0.1 instead of widening the bind
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/63c951c61ce8dbdf. Report an issue: GitHub.