Domains and the proxy
kind: portname: webspec: 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.
Routing
Section titled “Routing”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 listener
Section titled “The listener”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>.
DNS and TLS
Section titled “DNS and TLS”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.

