maestro-web
Standalone HTTP server that hosts the Maestro browser UI and reverse-proxies
to a co-located maestrod.
browser ──HTTP──▶ maestro-web (:8080) ──HTTP/WS──▶ maestrod (:7070)
│
└── / serves the embedded SPA (web/dist)
/api/... proxied unmodified
/api/attach/:id, /api/events proxied as WebSocket
Trust model
maestro-web does not authenticate anyone. Whoever can reach the bind address
can use it. Pick the bind accordingly:
| Bind | Reach | Use when |
|---|---|---|
127.0.0.1:8080 |
Local users on this host | Default. Same as tmux. |
<wg-ip>:8080 |
Devices on your WireGuard mesh | Phone/tablet over VPN. |
<lan-ip>:8080 |
Devices on your trusted LAN | Home / lab networks. |
| Public IP | Don't | — add real auth first. |
Config
The binary reads ~/.config/maestro/config.toml. The [web] section:
[web]
bind_addr = "127.0.0.1:8080" # default
daemon_url = "http://127.0.0.1:7070" # default
# dev_proxy_target = "http://127.0.0.1:5173" # only when developing the frontend
Env-var overrides (all optional):
MAESTRO_WEB_BIND_ADDRMAESTRO_WEB_DAEMON_URLMAESTRO_WEB_DEV_PROXY_TARGETMAESTRO_CONFIG— override the unified config-file path.
Build
The SPA bundle lives in ../../web and is embedded into the binary at compile
time via include_dir!. Build the frontend first:
pnpm -C web install
pnpm -C web build
cargo build -p maestro-web --release
Or, from the repo root:
just web
A fresh checkout compiles even without pnpm build (an empty web/dist
exists in-tree); the binary just serves a placeholder until the bundle is
built.
Run
target/release/maestro-web
Then point a browser at http://127.0.0.1:8080/.
Development workflow for the frontend
Use Vite directly so you get HMR. From web/:
pnpm dev
Vite serves on :5173 and proxies /api/* straight to maestrod at
127.0.0.1:7070 (override with MAESTRO_DAEMON_URL=...). You don't need to
rebuild the Rust binary while iterating on the frontend.