Skip to content

Domains and the proxy

kind: port
name: web
spec:
preferred: 3010
domains: [runy.localhost, https://runy.umka.day]

Each entry is one of two forms:

entry served by published as
a bare host, runy.localhost the proxy itself http://runy.localhost, with :<proxy port> appended unless it listens on 80
a URL, https://runy.umka.day (http or https, an optional port, no path) the proxy, by host, behind whatever terminates TLS in front of it the entry itself, lowercased, no trailing slash

domain: x is sugar for domains: [x]; giving both is an error. The status publishes urls (one per entry, in spec order) and url (the first), and runy ps prints 3010 → http://runy.localhost, https://runy.umka.day.

The port registers host → 127.0.0.1:<value> in the daemon’s route table when it is applied and releases the hosts when it stops. A host another resource already holds fails the apply with an Error condition naming the holder, and nothing of that apply is registered. A re-apply moves the hosts it keeps, adds the new ones and drops the rest.

Requests are HTTP/1.1, routed by the Host header. Method, path and query go to localhost:<value> with the hop-by-hop headers removed and X-Forwarded-Host, X-Forwarded-Proto and X-Forwarded-For added. Bodies stream through both ways, so server-sent events and large downloads work. A request with Upgrade: websocket is upgraded on a 101 and both sides are copied into each other, so dev servers’ hot reload works through the proxy.

An unknown host gets a 404 page listing the known URLs and their resources. A known host whose backend does not answer gets a 502 page naming the port, its namespace and workspace, and its condition, which usually means the process is still starting.

The proxy is the daemon’s second listener, --proxy-addr of runy daemon start, default 0.0.0.0:80. macOS refuses an unprivileged bind below 1024 on 127.0.0.1 but allows it on the wildcard address, so the proxy accepts loopback clients only: any other peer gets 403 and is closed.

If the bind fails (another server on port 80) the daemon logs a warning and serves its API without the proxy. The ports still publish their URLs, and runy ns ends with proxy: not listening on 0.0.0.0:80: <error>.

The proxy does not resolve names. *.localhost resolves to the loopback in Chrome and Firefox without setup; any other domain needs a record pointing at 127.0.0.1.

For https, put a tunnel in front: Cloudflare’s, with an ingress of *.umka.day → http://127.0.0.1:80, terminates TLS and forwards plain HTTP with the original Host, which is what the URL form of an entry is for. The proxy sees Host: runy.umka.day from a loopback client and the URL people open is https.