Web UI
featherbit ships a node-graph policy editor as a React SPA (React 19, TypeScript, Vite, React Flow) embedded in the gateway binary — no separate web server. Open the admin port in a browser:
open http://localhost:9090


How it is served
The UI is compiled into the binary at build time (ui/dist/, embedded via rust-embed) and served by the admin server as the fallback for any path not matched by the admin API. Unknown paths fall back to index.html so client-side routes resolve within the SPA.
The static assets themselves are served without authentication; the SPA's own calls to the admin API carry HTTP Basic credentials (see Admin API).
Editor workflow
- Select a route from the sidebar to open its routing policy on the canvas.
- Add plugins from the plugin drawer (Add Node). The palette is populated from
GET /api/pluginsand offers every node type the gateway can build. - Wire nodes by dragging from output ports to input ports — green ports are
success, red ports areerror. A complete pipeline runs fromlistener.outthrough the plugin chain toclient.in. - Configure a node by clicking it: the inspector panel shows a schema-driven form for that plugin type's config keys. Types without a declared form get a raw-JSON config editor instead.
- Save Policy to deploy: the UI writes the policy through the admin API, which validates, recompiles, and hot-swaps the route graphs — no restart.
- Toggle dark/light mode with the theme button.




Node positions on the canvas are stored in each node's position field in the policy; the graph engine ignores them, and they are omitted from serialized output when unset.
Headless mode
The UI is optional. It is only a client of the admin API, and it edits exactly the same data that lives in gateway.yaml — a policy saved from the canvas and a policy written by hand in YAML are interchangeable. Everything the UI does can be done with the YAML files plus hot-reload, or with the Admin API directly.
Omitting the admin section from system.yaml disables the admin server entirely (no API, no UI); the data plane still runs from the YAML configuration.
There are two mechanisms to run without the UI:
- Runtime: Set
admin.ui_enabled: falseinsystem.yamlkeeps the admin REST API available but returns 404 for UI paths. A restart is required for this setting to take effect. - Compile time: The
-headlessDocker image tags (latest-headless,edge-headless,X.Y.Z-headless) omit the UI entirely at build time, reducing binary size and attack surface.
Building the UI
The gateway build embeds whatever is in ui/dist/, so build the frontend first:
cd ui && npm install && npm run build && cd ..
cargo build
cargo build embeds ui/dist/ as it finds it — it does not rebuild the frontend, and it will happily embed a stale bundle without a warning. After changing anything under ui/src/, re-run npm run build before cargo build, or the binary keeps serving the previous UI.
For UI development, npm run dev starts a dev server with HMR that proxies /api to the gateway admin port.
Screenshots in these docs
The UI screenshots on this site are captured from the real binary, not mocked up. website/screenshots/capture.mjs boots the gateway against a posed demo config, drives the editor with Playwright, and writes both light and dark variants to static/img/ui/:
cargo build --release
cd website && npx playwright install chromium && node screenshots/capture.mjs
Re-run it after a UI change so the docs images do not drift from the product.