Rename maglev-frontend → maglevd-frontend; v0.9.1; API RX/TX pulse

Rename the web dashboard binary to maglevd-frontend and move it to
/usr/sbin (it's a daemon and belongs with maglevd). The systemd unit
name stays vpp-maglev-frontend.service since that prefix is the
package name. Manpage, README, user-guide, and debian packaging all
updated in lockstep; bump to 0.9.1 for the first real release.

All frontend env vars are now prefixed MAGLEV_FRONTEND_ so a single
/etc/default/vpp-maglev can be shared with maglevd without collisions.
Every flag has an env equivalent for Docker use. MAGLEV_FRONTEND_USER
and MAGLEV_FRONTEND_PASSWORD still gate the /admin surface.

VPPInfoPanel now pulses "API: ↑↓" indicators in the zippy title
whenever a vpp-api-send / vpp-api-recv log event arrives on the SSE
stream for the scoped maglevd — 250ms blue flash, re-triggerable,
with the two arrows tightly kerned via negative letter-spacing.
This commit is contained in:
2026-04-13 00:13:47 +02:00
parent 1191b3d994
commit 35643fd774
20 changed files with 380 additions and 83 deletions

View File

@@ -91,6 +91,7 @@ command, with examples and operational notes — see the user guide at:
https://git.ipng.ch/ipng/vpp-maglev/docs/user-guide.md
.RE
.SH SEE ALSO
.BR maglevd (8)
.BR maglevd (8),
.BR maglevd\-frontend (8)
.SH AUTHOR
Pim van Pelt <pim@ipng.ch>

View File

@@ -1,14 +1,14 @@
.TH MAGLEV\-FRONTEND 8 "April 2026" "vpp\-maglev" "System Administration"
.TH MAGLEVD\-FRONTEND 8 "April 2026" "vpp\-maglev" "System Administration"
.SH NAME
maglev\-frontend \- web dashboard for one or more running maglevd instances
maglevd\-frontend \- web dashboard for one or more running maglevd instances
.SH SYNOPSIS
.B maglev\-frontend
.B maglevd\-frontend
\fB\-server\fR \fIaddr\fR[,\fIaddr\fR...]
[\fB\-listen\fR \fIaddr\fR]
[\fB\-log\-level\fR \fIlevel\fR]
[\fB\-version\fR]
.SH DESCRIPTION
.B maglev\-frontend
.B maglevd\-frontend
is a single\-binary web dashboard that connects to one or more running
.BR maglevd (8)
instances over gRPC and renders a live view of frontends, backends,
@@ -21,7 +21,7 @@ one or more maglevds with
is enough to serve the dashboard.
.PP
For each configured maglevd,
.B maglev\-frontend
.B maglevd\-frontend
maintains:
.IP \(bu 2
A long\-lived
@@ -45,16 +45,36 @@ A 5\-second health probe that surfaces maglevd connection drops
quickly and flips the scope\-selector indicator dot red.
.PP
Browsers connect to
.B maglev\-frontend
.B maglevd\-frontend
over HTTP. State is hydrated once via REST and then kept live via a
Server\-Sent Events stream. Short SSE disconnects (nginx idle timeout,
wifi flap, laptop wake) are handled silently via a 30\-second replay
ring buffer; longer outages fall through to a full refetch. The SPA
is stateless on reload so refreshing the page at any time returns a
consistent view.
.PP
The frontend exposes two base paths:
.B /view/
is the read\-only dashboard and serves without authentication;
.B /admin/
is a basic\-auth\-protected variant of the same SPA that exposes
lifecycle mutations (pause / resume / enable / disable a backend,
set configured weight within a pool). The admin surface is only
mounted when both
.B MAGLEV_FRONTEND_USER
and
.B MAGLEV_FRONTEND_PASSWORD
are set to non\-empty values at startup; otherwise
.B /admin/
returns 404 and the SPA hides the admin\-toggle button entirely.
.SH OPTIONS
Each flag may also be supplied via an environment variable (shown in
parentheses); the flag takes precedence.
parentheses); the flag takes precedence when both are set. All env
vars are prefixed with
.B MAGLEV_FRONTEND_
so a single env file can be shared with
.BR maglevd (8)
without variables leaking across processes.
.TP
.BI \-server " addr[,addr...]"
Comma\-separated list of maglevd gRPC addresses. Required. Each
@@ -62,11 +82,11 @@ entry is in
.I host:port
form; a short display name is derived from the hostname label (for
IP literals the full address is used).
.RI "(env: " MAGLEV_SERVERS )
.RI "(env: " MAGLEV_FRONTEND_SERVERS )
.TP
.BI \-listen " addr"
HTTP bind address for the dashboard.
.RI "(default: " :8080 "; env: " MAGLEV_LISTEN )
.RI "(default: " :8080 "; env: " MAGLEV_FRONTEND_LISTEN )
.TP
.BI \-log\-level " level"
Structured\-log verbosity:
@@ -76,19 +96,19 @@ Structured\-log verbosity:
or
.BR error .
Affects
.B maglev\-frontend 's
.B maglevd\-frontend 's
own logs, not the log level it subscribes to on the upstream maglevd
(which is always
.BR debug
so the probe heartbeat can animate).
.RI "(default: " info "; env: " MAGLEV_LOG_LEVEL )
.RI "(default: " info "; env: " MAGLEV_FRONTEND_LOG_LEVEL )
.TP
.B \-version
Print version, commit hash, and build date, then exit.
.SH HTTP ENDPOINTS
.TP
.I /view/
Static SPA (HTML, JS, CSS, assets).
Static SPA (HTML, JS, CSS, assets). Read\-only.
.TP
.I /view/api/maglevds
JSON array describing the configured maglevds and their current
@@ -101,22 +121,39 @@ Full JSON state snapshot for every maglevd.
Full JSON state snapshot for a single maglevd.
.TP
.I /view/api/version
Build version, commit hash, and build date.
Build version, commit hash, and build date, plus an
.B admin_enabled
flag the SPA uses to decide whether to show the admin toggle.
.TP
.I /view/api/events
Server\-Sent Events stream. Long\-lived HTTP/1.1 chunked response
fanning out log, backend, frontend, maglevd\-status, and vpp\-status
events to every connected browser. Supports
.B Last\-Event\-ID
replay from a 30\-second / 2000\-event ring buffer.
replay from a 30\-second / 2000\-event ring buffer, plus a
.B resync
control event emitted after every maglevd config reload so the SPA
re\-hydrates from the now\-fresh server cache.
.TP
.I /healthz
Liveness endpoint; returns 200 if the HTTP server is up.
.TP
.I /admin/
Placeholder for a future basic\-auth mutation surface. Currently
returns
.B 501 Not Implemented .
SPA shell served behind basic auth when
.B MAGLEV_FRONTEND_USER
and
.B MAGLEV_FRONTEND_PASSWORD
are configured. Returns 404 when they're not.
.TP
.I "/admin/api/{maglevd}/backend/{name}/{action}"
Backend lifecycle POST. Action is
.BR pause ", " resume ", " enable ", or " disable .
Returns the fresh backend snapshot as JSON.
.TP
.I "/admin/api/{maglevd}/frontend/{fe}/pool/{pool}/backend/{name}/weight"
Weight change POST. Body is
.B {"weight": 0\-100, "flush": bool} .
Returns the fresh frontend snapshot as JSON.
.SH REVERSE PROXY NOTES
The SSE stream has a handful of operational requirements that every
reverse proxy must satisfy:
@@ -124,7 +161,7 @@ reverse proxy must satisfy:
Disable buffering on the events endpoint. Nginx honours
.B X\-Accel\-Buffering: no
(sent by
.BR maglev\-frontend )
.BR maglevd\-frontend )
but a global
.B proxy_buffering off;
in the server block is the more robust answer.
@@ -136,36 +173,72 @@ to at least
so the stream isn't torn down between the 15\-second
.B :\ ping
heartbeats that
.B maglev\-frontend
.B maglevd\-frontend
sends.
.IP \(bu 2
Do not wrap the events endpoint in a gzip/brotli middleware — response
compression buffers until its window fills and destroys the live\-stream
property.
.SH ENVIRONMENT
All environment variables are prefixed with
.B MAGLEV_FRONTEND_
so this daemon can share
.I /etc/default/vpp-maglev
(or a container env file) with
.BR maglevd (8)
— whose env vars use only the shorter
.B MAGLEV_
prefix — without cross\-contamination.
.TP
.B MAGLEV_SERVERS
.B MAGLEV_FRONTEND_SERVERS
Default value of
.BR \-server .
.TP
.B MAGLEV_LISTEN
.B MAGLEV_FRONTEND_LISTEN
Default value of
.BR \-listen .
.TP
.B MAGLEV_LOG_LEVEL
.B MAGLEV_FRONTEND_LOG_LEVEL
Default value of
.BR \-log\-level .
.TP
.B MAGLEV_FRONTEND_USER
HTTP basic\-auth username for
.BR /admin/ .
When set together with
.B MAGLEV_FRONTEND_PASSWORD
the admin surface is enabled; when either is missing or empty the
admin surface is hidden entirely (the SPA doesn't render the admin
toggle button and
.B /admin/
itself returns 404).
.TP
.B MAGLEV_FRONTEND_PASSWORD
HTTP basic\-auth password for
.BR /admin/ .
See
.B MAGLEV_FRONTEND_USER
above.
.TP
.B MAGLEV_FRONTEND_ARGS
Extra command\-line arguments picked up by the systemd unit's
.B ExecStart
line. Not read directly by the process — the unit expands it before
exec\-ing the binary.
.SH FILES
.TP
.I /etc/default/vpp-maglev
Environment file sourced by the systemd unit before starting
.BR maglev\-frontend .
.BR maglevd\-frontend .
The same file is shared with
.BR maglevd (8);
the
.B MAGLEV_FRONTEND_ARGS
variable there is passed on the command line to
.B maglev\-frontend .
.BR maglevd\-frontend ,
and
.B MAGLEV_FRONTEND_USER / MAGLEV_FRONTEND_PASSWORD
are read from the process environment.
.SH SEE ALSO
.BR maglevd (8),
.BR maglevc (1)

View File

@@ -130,6 +130,7 @@ https://git.ipng.ch/ipng/vpp-maglev/docs/user-guide.md
.RE
.SH SEE ALSO
.BR maglevc (1),
.BR maglevd\-frontend (8),
.BR vpp (8)
.SH AUTHOR
Pim van Pelt <pim@ipng.ch>

View File

@@ -241,3 +241,83 @@ keyword. The `?` character is not added to the input line.
Commands and keywords support **prefix matching**: typing `sh ba` is equivalent
to `show backends`, and `sh ba nginx0` is equivalent to `show backends nginx0`.
---
## maglevd-frontend
`maglevd-frontend` is an optional web dashboard that connects to one or
more running `maglevd` instances over gRPC and renders a live view of
frontends, backends, health checks, and VPP load-balancer state. It is
a single Go binary with the SolidJS SPA embedded via `//go:embed`; no
runtime file dependencies.
Installed by the Debian package to `/usr/sbin/maglevd-frontend` but
**not** enabled by default — the operator opts in via:
```sh
systemctl enable --now vpp-maglev-frontend
```
The systemd unit (`vpp-maglev-frontend.service`) reads its arguments
from `/etc/default/vpp-maglev` via `MAGLEV_FRONTEND_ARGS`. The same
env file is shared with `maglevd`; all `maglevd-frontend`-specific
variables are prefixed with `MAGLEV_FRONTEND_` so there's no overlap.
### Flags
| Flag | Environment variable | Default | Description |
|---|---|---|---|
| `--server` | `MAGLEV_FRONTEND_SERVERS` | *(required)* | Comma-separated list of `host:port` maglevd addresses. |
| `--listen` | `MAGLEV_FRONTEND_LISTEN` | `:8080` | HTTP bind address. |
| `--log-level` | `MAGLEV_FRONTEND_LOG_LEVEL` | `info` | Structured-log verbosity for `maglevd-frontend`'s own logs. |
| `--version` | — | — | Print version, commit hash, and build date, then exit. |
In addition to flags, two env-only variables control the admin surface:
| Environment variable | Purpose |
|---|---|
| `MAGLEV_FRONTEND_USER` | HTTP basic-auth username for `/admin/`. |
| `MAGLEV_FRONTEND_PASSWORD` | HTTP basic-auth password for `/admin/`. |
When **both** are set and non-empty the admin surface is mounted and
the SPA's "admin…" toggle becomes visible. When either is missing or
empty the `/admin/` route returns 404 and the SPA hides the toggle —
`/view/` is always reachable read-only.
### HTTP surface
- **`/view/`** — static SPA (dashboard). No authentication.
- **`/view/api/state`**, **`/view/api/state/{name}`** — full JSON
snapshot for every maglevd, or one maglevd.
- **`/view/api/maglevds`** — configured maglevds and connection status.
- **`/view/api/version`** — build info + `admin_enabled` flag.
- **`/view/api/events`** — Server-Sent Events stream; log, backend,
frontend, maglevd-status, vpp-status events with
`Last-Event-ID` replay from a 30-second / 2000-event ring buffer.
- **`/healthz`** — liveness; returns 200 if the HTTP server is up.
- **`/admin/`** — SPA shell behind basic auth (when configured).
- **`POST /admin/api/{maglevd}/backend/{name}/{action}`** — backend
lifecycle action. `action` is `pause`, `resume`, `enable`, or
`disable`. Returns the fresh backend snapshot as JSON.
- **`POST /admin/api/{maglevd}/frontend/{fe}/pool/{pool}/backend/{name}/weight`**
— weight change. Body: `{"weight": 0-100, "flush": bool}`. When
`flush=true`, VPP's flow table for the backend is cleared;
otherwise only the new-buckets map is updated and existing
sessions keep reaching the backend until they finish.
### Reverse-proxy requirements (SSE)
Nginx, HAProxy, or any proxy in front of `maglevd-frontend` must:
- Disable buffering on the events endpoint. `X-Accel-Buffering: no`
is sent by the server; a global `proxy_buffering off;` in the
nginx server block is the more robust answer.
- Raise `proxy_read_timeout` to at least 300s so the stream isn't
torn down between the 15-second `: ping` heartbeats the server
sends.
- Not wrap the events endpoint in any gzip/brotli middleware —
response compression buffers until its window fills and destroys
the live-stream property.
See `maglevd-frontend(8)` for the full reference.