package httpx import ( "net" "strings" ) // IsLoopbackHost reports whether a request Host ("host" or "host:port") names a // loopback origin. // // It gates the passkey nudge (SPEC-006 §8): a passkey registered at // `127.0.0.1` binds the Relying Party ID `127.0.0.1` and is UNUSABLE at the // public domain (O-13/D-041), so the nudge is suppressed on loopback. An // empty/unknown host counts as loopback — fail closed, so an origin we cannot // classify never triggers the nudge. func IsLoopbackHost(hostport string) bool { loopback, known := classifyHost(hostport) return !known || loopback } // classifyHost reports whether hostport ("host", "host:port", or a bare IP) is // loopback, and whether it could be classified at all. // // "Unknown" is only an empty/blank value: a NAME that is not `localhost` is // known — and it is not loopback. func classifyHost(hostport string) (loopback, known bool) { host := strings.TrimSpace(hostport) if host == "" { return false, false } if h, _, err := net.SplitHostPort(host); err == nil { host = h } host = strings.Trim(strings.TrimSpace(host), "[]") if host == "" { return false, false } if strings.EqualFold(host, "localhost") || strings.EqualFold(host, "localhost.") { return true, true } if ip := net.ParseIP(host); ip != nil { return ip.IsLoopback(), true } // A name we cannot map to loopback: known, and not loopback. return false, true }