Manage shared local *.localhost routing with a single proxy on port 80.
Without this, each repo can start its own proxy and race for port 80.
f domains centralizes ownership with one engine at a time.
State lives in:
~/.config/flow/local-domains/routes.json- runtime artifacts under
~/.config/flow/local-domains/
docker(default): shared nginx container (flow-local-domains-proxy)native(experimental): local C++ daemon (domainsd-cpp)
Select engine per command:
f domains --engine docker up
f domains --engine native upOr via env:
export FLOW_DOMAINS_ENGINE=nativef domains list
f domains add linsa.localhost 127.0.0.1:3481
f domains rm linsa.localhost
f domains up
f domains down
f domains doctor
f domains --engine native up
f domains --engine native down
f domains --engine native doctorf domains up- starts shared proxy on
:80 - fails fast if another process/container owns port
80
- starts shared proxy on
f domains add- validates
hostends with.localhost - validates target format
host:port - refuses overwrite unless
--replace - reloads proxy if already running
- validates
f domains doctor- shows route count
- shows current owner of port
80 - highlights conflict ownership
- Requires
clang++to buildtools/domainsd-cpp/domainsd.cpp. - Current scope is HTTP/1.1 host routing with WebSocket upgrade passthrough and upstream keepalive pooling.
- Native daemon has built-in overload shedding (
503) and upstream timeout protection (504on connect timeout). - HTTP/2/TLS are not implemented yet.
- See
docs/local-domains-domainsd-cpp-spec.md. - myflow-specific setup:
docs/myflow-localhost-runbook.md. - Lifecycle integration: configure
[lifecycle.domains]and usef up/f down.
If native startup fails with Permission denied on 127.0.0.1:80, install launchd socket mode once:
cd ~/code/flow
sudo ./tools/domainsd-cpp/install-macos-launchd.shThen run:
f domains --engine native upThis keeps routing fully native and avoids Docker overhead while still using port 80.
You can tune the native daemon at startup via environment variables:
FLOW_DOMAINS_NATIVE_MAX_ACTIVE_CLIENTS=128
FLOW_DOMAINS_NATIVE_UPSTREAM_CONNECT_TIMEOUT_MS=10000
FLOW_DOMAINS_NATIVE_UPSTREAM_IO_TIMEOUT_MS=15000
FLOW_DOMAINS_NATIVE_CLIENT_IO_TIMEOUT_MS=30000
FLOW_DOMAINS_NATIVE_POOL_MAX_IDLE_PER_KEY=8
FLOW_DOMAINS_NATIVE_POOL_MAX_IDLE_TOTAL=256
FLOW_DOMAINS_NATIVE_POOL_IDLE_TIMEOUT_MS=15000
FLOW_DOMAINS_NATIVE_POOL_MAX_AGE_MS=120000Instead of per-repo docker proxy tasks:
[[tasks]]
name = "domains-up"
command = "f domains add myapp.localhost 127.0.0.1:3000 && f domains up"This keeps one proxy process for all repos and avoids accidental domain hijacking.