Introducing ThrottleKit Lens — which axis throttled me?
A built-in, zero-dependency terminal dashboard that shows, live and per key, which of rate, concurrency, or cost bound each denial — the one view no other rate-limiter dashboard can render.
Every rate-limiter dashboard can tell you that a request was denied. None of them can tell you why: which of the axes you’re governing actually bound it. When you limit on rate, concurrency, and cost at once, “denied” is three different problems wearing the same colour, and you debug the wrong one.
ThrottleKit Lens answers the question directly. It’s a built-in, zero-dependency
terminal dashboard that ships inside throttlekit-server:
throttlekit-server --config ./throttlekit.yaml --tui
The hero view: binding-axis attribution
The headline panel is binding-axis attribution. For every denial, it shows which axis bound it: rate, concurrency, or cost (or the joint-LP policy lane). It reads straight off the same in-process telemetry the limiter already computes, so it adds no hot-path store reads and never changes a decision. A throwing observer can’t break admission; the tap is synchronous, exception-swallowing, and O(1).
Around it are eight tabbed views: throughput and top denied keys, per-policy latency
(avg / p50 / p99), the Cost Room for token budgets, weighted-fair-escrow tenant shares,
non-consuming capacity forecasts, concurrency-guard health, and a live denial feed with
exact per-axis numbers. Press 1–8 or Tab to switch; p pauses, q quits.
Why the terminal
Lens started as a browser dashboard and we pulled it back to the terminal on purpose. A
TUI has no build step, no port to expose, no auth surface, and no dependency to audit. It
runs where your server already runs. It’s opt-in via --tui and wants an interactive
TTY; a non-TTY just warns and serves the gRPC door without it.
Reading it remotely
A dashboard you can only see on one box isn’t an ops tool. So the same state is readable from anywhere:
- the Monitor door: a read-only gRPC service (
GetSnapshot/ a live filtered denialWatch) you can call from any language, loopback-only until you set--monitor-secret; - Prometheus:
--metrics-portserves/metrics(per-policy allow/deny, the per-axisthrottlekit_denied_by_axis_total, observed ceiling, p50/p99) and a/healthzprobe; - gRPC health: the standard
grpc.health.v1.Healthservice on the same port, so Kubernetes probes and service meshes work out of the box.
For fully headless shops, the binding-axis signal also exports over OpenTelemetry as
throttlekit.denies_by_axis{lane} straight into Grafana.
Lens is part of throttlekit-server.
Point it at a config, add --tui, and watch which axis is actually throttling you.