gateway
turnout gateway <start|run|stop>The gateway has two kinds of doors. Per app, it listens on localhost - one port per app, assigned by turnout when the app is registered - and forwards every request to the server the app is currently bound to; how it treats cookies and redirects is described in The Dev Gateway. On top of that, one front door answers by name: http://myapp.localhost reaches the dev server turnout started for myapp.
The name is the address to use. The per-app port is the spare: the way in when the front door cannot open, and the URL the app’s own variable carries.
start / stop
Section titled “start / stop”turnout gateway start # background process; ports and addresses are printedturnout gateway stop # stop itGateway started (pid 24180). web: http://localhost:7100 api: http://localhost:7101Front door: http://localhost web: http://web.localhost api: http://api.localhoststart launches the gateway detached and returns once its first port answers. The gateway is the first job: it records itself once its ports are bound - so status never reports a gateway that died on the way up - and shows in turnout ps like any other. Its output goes to a log, turnout logs gateway; when it cannot start, the end of that log is printed with the error. Two things are refused before anything is spawned, each naming the port and the app: a port already taken by another process, and a port two apps share (app add and app edit no longer let that happen, a hand-edited catalog still can). Bindings changed with turnout use are picked up automatically - the gateway re-reads them on every request.
stop ends the recorded process - the same as turnout stop gateway. If that process is already gone - killed from outside, died on its own, or the machine rebooted - the stale record is cleared and the command succeeds; a pid the system has since given to another program is recognised as not the gateway and left alone.
turnout gateway run # foreground, Ctrl+C to stopThe same server in the foreground - handy for watching it work. start spawns exactly this. A foreground gateway is recorded too, so dev, open and ps know its door; its output stays in the terminal.
The front door
Section titled “The front door”Dev servers take their port in the order they start - Vite hands out 5173, then 5174 - so “which app is on which port” is a fresh question every morning. The front door answers it by name: the gateway listens on one well-known port and routes by host, myapp.localhost to the dev server turnout started for myapp. Every name under .localhost resolves to loopback (RFC 6761), so nothing is added to a hosts file.
- Port. 80 when the machine hands it out - Windows and macOS do; Linux asks for privileges - and 7000 otherwise, in which case addresses carry the port:
http://myapp.localhost:7000.TURNOUT_FRONT_PORTpins the door to a port;0keeps it shut. A door that cannot open is a warning at start, not a failed gateway: the per-app stand proxies are the daily flow. - Where the dev server is. The port behind a name is the app’s dev port: assigned from
5100-5199on the firstturnout dev, or pinned withturnout app edit myapp --dev-port PORT.devhands it to the server asPORTand as{port}in the command line - seeturnout dev. - Who is not running. A name whose dev server is not answering gets a page saying so, with the command to run - not a reset connection. A name that is no app gets a 404 naming
turnout app list. - Redirects and WebSocket. A dev server redirecting to its own
localhost:PORTis redirected back to the name; WebSocket upgrades (Vite’s HMR) travel through the door too. No cookie jar on this door: the browser talks to the app by name and keeps the app’s own cookies itself. - Browsers. Chrome, Edge and Firefox resolve
*.localhostthemselves. Safari asks the system resolver, which on macOS does not - add127.0.0.1 myapp.localhostto/etc/hoststhere.
turnout open myapp opens the address; status, app list and app show print it.
Behavior notes
Section titled “Behavior notes”- Cookie jar. Stand cookies never reach the browser; they live in the gateway, in a separate jar per app+server pair. Switching stands swaps jars, so sessions survive switching. The jar is in-memory: restarting the gateway means logging in to the stand again.
- Redirects. Absolute
Locationheaders pointing at the stand are rewritten tolocalhost, so the browser never escapes. - TLS. Connections to the stand honor the server’s TLS policy (
--insecureaccepts self-signed certificates). The gateway itself serves plain HTTP on localhost. - WebSocket. Upgrade requests are proxied too: the gateway opens a matching connection to the stand (jar cookies attached,
wssfor https servers, TLS per policy) and pumps frames both ways. - Errors. If the stand is down you get a plain-text
502from the gateway saying which stand is unreachable - not a cryptic proxy failure.