The dashboard
Running lyno in an interactive terminal without -headless opens a live view
of the system. It is the primary operational interface: there is no web UI, and
the management API is read-mostly and off by default.
▐ Lyno PBX ▌ v1.4.2 0.0.0.0:5060 · 4/5 online · 1/1 trunks up · uptime 6h12m
Overview Calls 2 Endpoints 5 Trunks 1 Security 1 Logs tenant acme
╭──────────────────────────────────────────────────────────────────────────────────────────╮
│ active calls endpoints trunks calls finished banned ips │
│ 2 4/5 1/1 148 1 │
│ in progress registered reachable 15 missed 412 rejected │
│ │
│ Tenants │
│ TENANT DOMAIN ONLINE CALLS DIDS │
│ acme acme.pbx.example.com 3/3 2 2 active │
│ globex globex.pbx.example.com 1/2 0 1 idle │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
tab switch · t tenant · / filter · ? help · q quit
The banned-addresses tile only appears once there is something to report. An always-present "0 banned" is a number nobody reads.
The header
Always visible, and always describing what is currently on screen rather than the whole system — see filters below.
| Field | Meaning |
|---|---|
host:port | The address the PBX is listening on. |
n/m online | Endpoints with a live registration, out of all configured accounts. |
n/m trunks up | Trunks that are reachable and, where configured, registered. |
uptime | Time since the process started. |
The six views
| # | View | Shows |
|---|---|---|
| 1 | Overview | Tiles, a per-tenant summary and recent calls. |
| 2 | Calls | Calls in progress, plus finished ones. |
| 3 | Endpoints | Every account, its registration state, contact and user agent. |
| 4 | Trunks | Trunk state, registration and the addresses inbound calls are accepted from. |
| 5 | Security | The source addresses the guard is holding at arm's length. |
| 6 | Logs | The live log, scrollable, with a level floor. |
The tab bar carries a count badge per view, so Calls 2 means two calls are
visible under the current filters.
There is no queue view and no do-not-disturb view. Queue depth and feature state are not surfaced anywhere at run time; the log is the only place a queued call appears.
Overview
Four tiles, then a tenant table, then recent calls.
The calls since start tile breaks down as answered · missed · live, and the
breakdown adds up to the total — a glance never leaves you wondering where the
missing calls went.
The tenant table answers the two questions an operator asks first: is anyone online, and is anything happening.
Calls
Calls currently in progress, with tenant, direction, from, to, state and duration. State colours are consistent everywhere: a colour means the same thing on every screen.
Endpoints
One row per configured account. An account that never registered still appears, which is how you tell "the phone is off" from "the account does not exist". Includes the contact address actually being used — the source address seen at REGISTER, not the advertised Contact.
Trunks
Per trunk: reachability, registration state and expiry, and the discovered INBOUND FROM addresses. This is the first place to look when inbound calls are rejected while outbound works.
Security
One row per address the guard is tracking:
SOURCE STATE REASON STRIKES FAILURES EXPIRES DROPPED TENANT
198.51.100.9 banned auth failures 2 7 18m 412 acme
The security guard is not enabled and Nothing is being blocked are different
messages on purpose — the first means security.enabled is off.
A ban that could not be attributed to a tenant survives the tenant filter, the same way a shared trunk does. An attacker is not a customer, and hiding that row would hide the one thing an operator filtering for trouble is looking for.
These numbers are polled once a second rather than pushed over the event bus, because the bus drops events under exactly the flood they describe.
See The security guard.
Logs
The live log, newest at the bottom. Scrolling back with ↑ pauses following;
the footer says so and ↓ resumes. L cycles the level floor
(DEBUG → INFO → WARN → ERROR, starting at INFO) when info is too noisy.
Keys
| Key | Action |
|---|---|
tab / shift+tab | Next or previous view |
← → / h l | Same as tab / shift+tab |
1 … 6 | Jump straight to a view |
↑ ↓ / k j | Move the row cursor, or scroll the log |
x / d | Act on the selected row — see below |
r | Trunks only: re-resolve the provider addresses now |
t | Cycle the tenant filter |
/ | Filter by text — enter applies, esc clears |
L | Cycle the log level floor |
esc | Clear all filters |
? | Help |
q / ctrl+c | Quit, stopping the PBX |
Acting on the running system
The dashboard is not only a viewer. x stages a destructive action on the
selected row, and it always asks first, naming what it is about to do:
hang up 100 → 101 (acme)? y confirm · any other key cancel
y, Y or enter confirms; any other key cancels.
| View | x does |
|---|---|
| Calls | Hang up the selected call. |
| Endpoints | Drop the endpoint's registration. |
| Security | Lift the ban on the selected address. |
| Trunks | Nothing — use r to re-resolve, which is not destructive and needs no confirmation. |
Dropping a registration clears an endpoint's bindings so the phone registers again, which is the usual way to clear a stuck registration. Re-resolving a trunk repeats the DNS discovery immediately instead of waiting for the five-minute refresh. Lifting a ban is there because banning is the feature most likely to shut out a real customer at three in the morning, and "restart the PBX" is a worse answer than a keypress.
Hanging up is asynchronous on purpose: a BYE to a peer that has not
acknowledged the call can block for a full transaction timeout of about 32
seconds while still reporting success, and the dashboard cannot freeze for that
long. The call disappearing from the list is what confirms the outcome.
The same four actions are on the management API behind the operate
scope.
Filters are global
This is the design decision worth internalising: the tenant filter and the
text filter narrow every view at once. Pressing t until the badge reads
tenant acme means the header counts, the tiles, the call list, the endpoints
and the logs all describe acme only.
Active filters are shown as badges next to the tab bar precisely because they
change what every number on screen means. esc clears both.
Finished calls are kept
The event bus reports state changes; the dashboard turns them into history and counters. That is why a call that failed thirty seconds ago is still on the Overview under "Recent calls" — without it, a failed call vanished the moment it ended, which made the dashboard useless for finding out what just happened.
Counters and history live in memory and reset when the process restarts. The durable record is the call log, which is written to disk from a different code path for exactly that reason.
Quitting
q cancels the PBX context and exits. There is no way to detach the dashboard
and leave the PBX running — for a long-lived service, run with -headless under
systemd or Docker and read the logs there.