Skip to content

D1023 — The client refuses a cleartext, non-loopback server URL

Decision. The client transport refuses every request to a non-loopback http:// URL unless the operator opted in with allow_insecure_http: true (set by connect --allow-insecure-http) or CARTOGRAPHER_ALLOW_INSECURE_HTTP=1. Loopback is decided by one predicate, client.IsLoopbackHost (net.ParseIP(...).IsLoopback() plus localhost).

Why. Over cleartext the bearer token is exposed and nothing authenticates the responses (instructions, manifests); only a signing_keys pin protects what gets installed. A warning is never read in a hook-driven sync, and the exposure repeats at every sync, so existing users are refused with an error naming the two fixes. It costs a behaviour break for whoever runs a plain-http remote (LAN host, proxy without TLS).

Alternatives rejected. - A check per command: call sites are many and a new one would silently bypass it; the invariant lives in the transport. - A warning only: not read in unattended runs. - A flag with no persisted key: it would not survive the next unattended sync. - Auto-upgrading http to https or certificate pinning: guessing a scheme can send the token to the wrong place. - Keeping the literal localhost/127.0.0.1/::1 lists: they refuse 127.0.0.2 and IPv4-mapped loopback.

A redirect is held to the same rule: every client http.Client refuses a redirect whose target fails the check, and any https to http downgrade, because Go keeps Authorization on same-host redirects.

Consequences. Any new code that builds a client request must go through client.MCPClient (or CheckServerURL) and chain WithInsecureHTTP(cfg.InsecureHTTPAllowed()) at construction. Breaking for non-loopback http:// users: shipped as feat(client)!.