Real-time web UI for monitoring CAKE SQM statistics on Linux and OpenWrt routers.
Built for embedded hardware where every allocation matters: Fiber v3 keeps HTTP overhead minimal, zerolog emits structured logs with near-zero allocations, and a dedicated pkg/util package centralises all allocation-heavy string operations so they can be audited and swapped in one place. The result is a single static binary that streams live per-tier CAKE stats to any browser with negligible CPU and memory cost.
- How it looks like
- Status
- Features
- Requirements
- Design Notes
- Build
- Usage
- Limitations & Next Steps
- Contributing
- License
- Stable — all parser unit tests passing, binary builds clean for 14 target platforms. See Releases for the current version.
- Automatically discovers all CAKE qdiscs via
tc -s qdisc, includingcake_mq(multi-queue CAKE introduced in Linux 7.0 and backported to OpenWrt): sub-queue statistics are aggregated into a single logical interface entry so TX throughput, latency and drop graphs work correctly on multi-queue NICs - Parses every CAKE field:
thresh,target,interval,pk_delay,av_delay,sp_delay,backlog,pkts,bytes,way_inds,way_miss,way_cols,drops,marks,ack_drop,sp_flows,bk_flows,un_flows,max_len,quantum - Correctly handles diffserv modes:
diffserv3,diffserv4,diffserv8,besteffort,precedence; also parses the separatefwmark MASKtin-override parameter - Two-word tier names are joined correctly (e.g.
"Best Effort") - Real-time push via Server-Sent Events — no WebSocket, no polling jitter
- Built on Fiber v3 with zerolog for structured logs
- Default poll interval 100ms for near-instant UI updates (adjustable via
-interval) - Single static binary — no runtime dependencies
- Web UI: dark TUI aesthetic (
#2D3C59bg, JetBrains Mono, zero hover animations) - Responsive for desktop and mobile (sticky first column, horizontal scroll on small screens)
- Per-interface live sparklines (TX throughput, avg latency, drops/s) with current-value labels
- Tap/click any sparkline bar to open a full-screen history modal with three uPlot time-series charts
- Server-side ring buffer retains history across page reloads (configurable via
-historyflag)
- Linux kernel with
tc+sch_cakemodule loaded, or OpenWrt withkmod-sched-cake - Go 1.25+ (build only; not needed at runtime)
- Third-party libraries used during build/services:
- Fiber v3 – HTTP framework
- zerolog – structured logging
- easyjson – JSON code generation;
pkg/types/types_easyjson.gois checked in and generated via//go:generate easyjson -allinpkg/types/types.go. CI installs theeasyjsonbinary and re-runsgo generate ./...on every build to keep the generated file in sync. - rtnetlink – optional netlink client (not currently used; included in go.mod for future event‑based polling)
- Modular architecture: code is split into
pkg/parser,pkg/history,pkg/server,pkg/log,pkg/types, andpkg/util, with the CLI entrypoint undercmd/cake-stats.pkg/utilcentralises all allocation-heavy string/byte helpers (split, trim, parse, zero-copy byte↔string conversions) so every other package imports one place instead of duplicatingstrconv/stringscall sites. This keeps the core logic reusable and simplifies testing. - Zero-allocation philosophy: hot paths avoid heap allocations by using
sync.Poolfor temporary buffers, easyjson-generated marshalers (MarshalEasyJSON/UnmarshalEasyJSONinpkg/types/types_easyjson.go) that skip reflection entirely, zero-copyunsafe-backed byte↔string conversions inpkg/util, and pre‑computed byte slices. The 100 ms poll loop is designed to run with minimal GC pressure. - Ring buffer history: a thread-safe circular buffer stores past snapshots; clients receive both current data and historical samples after reconnects or page loads.
- Polling strategy: defaults to 100 ms for near-instant updates; interval is command-line configurable. The codebase contains scaffolding and a placeholder comment for an optional rtnetlink-based watcher, but the current release still relies on regular
tcinvocations. - Server-Sent Events: statistics are broadcast over SSE. A pool of reusable message buffers reduces allocations when many clients connect.
- Fiber & zerolog: Fiber v3 provides a lightweight HTTP server with built‑in recovery middleware; zerolog supplies compact, structured log output.
- Single static binary: the project builds to one statically-linked executable, suitable for OpenWrt.
- Testing and documentation:
pkg/parser,pkg/history, andpkg/utilinclude unit tests;pkg/historyalso includes a benchmark (BenchmarkHistoryRecord). Dependencies are kept to a minimum to ease audits.
git clone https://github.com/galpt/cake-stats
cd cake-stats
go test ./... # prints ok for each package with tests
go build -ldflags "-s -w -X main.Version=1.0.0" -o cake-stats ./cmd/cake-statsNote
pkg/types/types_easyjson.go is checked in, so a normal go build works without extra tools. To regenerate it after editing pkg/types/types.go, install easyjson and re-run codegen:
go install github.com/mailru/easyjson/easyjson@latest
go generate ./...CI does this automatically on every run.
Cross-compile for a MIPS OpenWrt router:
CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat \
go build -ldflags "-s -w -X main.Version=1.0.0" -o cake-stats-linux-mips ./cmd/cake-statsPre-built binaries for all common platforms are attached to every GitHub Release.
./cake-stats # serves on http://0.0.0.0:11112
./cake-stats -port 8080 # custom port
./cake-stats -interval 2s # poll tc every 2 seconds (default 100ms)
./cake-stats -history 3600 # retain 1 hour of history (default 300 = 5 min)
./cake-stats -host 127.0.0.1 # listen only on loopback
./cake-stats -version # print version and exitOpen http://<router-ip>:11112 in a browser.
sh install.sh # auto-detects arch, downloads latest binary
sh install.sh --port 11112 --interval 1sNote
The installer automatically enables and starts the init.d service.
The OpenWrt init script is installed with START=99 so it comes up late in boot, registers with procd immediately, and waits through a brief overlay-mount race before exec-ing /usr/bin/cake-stats. A matching iface hotplug fallback also starts it later if netifd finishes after the rc.d boot window. The installer fails immediately if enable or start does not succeed. If you install by hand make sure to run:
/etc/init.d/cake-stats enable # create the rc.d symlink
/etc/init.d/cake-stats start # verify it launches immediatelyTo verify the router-side install after a reboot, copy verify-openwrt-install.sh to the router and run:
sh verify-openwrt-install.shServices that are not enabled will not start after a reboot.
sudo sh install.shsh uninstall.sh # prompts for confirmation
sh uninstall.sh --force # no prompts| Endpoint | Description |
|---|---|
GET / |
Web UI (HTML) |
GET /api/stats |
Current stats snapshot (JSON) |
GET /api/history |
Full ring-buffer history per interface (JSON), used to seed sparklines on page load |
GET /events |
SSE stream — emits updated JSON on every poll interval |
- Still polls using
tc; a kernel‑level rtnetlink watcher is included as an option but not yet the default. - No built‑in authentication or HTTPS; expose only on trusted networks or pair with a reverse proxy.
- UI is intentionally minimal.
- RAM footprint may vary. Depending on kernel malloc behaviour, architecture and how many clients are connected the value can be anywhere from about 4 MB up to a dozen megabytes.
- Cross‑platform builds are produced, but CAKE itself is Linux‑only; Windows/FreeBSD binaries do not collect real data.
go test ./... # run unit tests
go vet ./... # static analysisPRs welcome. Please keep external dependencies at zero.



