Skip to main content
Logs answers three different questions that get asked in the same evening. Each is a tab. The first two are scoped to an organisation, so a tenant’s own admin reaches them. The third is not — a log line has no tenant against it — so it is the operator’s, and the tab is simply absent rather than failing after the click.

Sign-ins

Every attempt at every door, accepted or refused, with the address it came from and what was typed.
Most refusals are one door’s credential typed into another. An agent’s email at the panel, a SIP password at the desk. The screen says so under the table, because it is the answer often enough to be worth printing.
A refusal carries the reason in words rather than as a code, and the useful ones are the ones that are not about a wrong password:
  • No PIN set up for that extension — the agent has never been given one.
  • The agent is on no team — their sign-in has nothing to scope to. Add them to a team.
  • The agent is on several teams and none was chosen — reset the PIN and pick one.
  • The handset gave the server’s IP as its domain, and that extension number exists in more than one organisation. Set the handset’s SIP domain to its own organisation’s.
  • The control plane could not check — not a wrong credential. Something was down.
  • Too many attempts — refused without even checking.
Filter by result and by door.

Changes

Every create, update, replace and delete, with who did it, what the request was in plain words, and whether it worked. Reads are not recorded. A failed change is, with its status and the error, which is often the more interesting row: somebody tried and was refused.
Secrets never land here. A password, a PIN, an API key, a provider credential, a webhook’s signing secret and an HTTP connection’s headers are written as «set» — the field name is recorded, the value never is. An audit log that faithfully stored secrets would be a second place they live, and the encryption everywhere else exists precisely so there is only one.
The log is written in middleware rather than by each handler, so there is no route that somebody forgot to add it to. It never fails a request either: a database that is briefly slow must not turn a customer’s successful change into an error.

System log

The daemon’s journal and the switch’s log, in one place, for the operator who would otherwise need a shell on the box. Filter by source, by level, and by what a line contains, then Apply — it is not applied as you type, because each fetch reads the journal and the tail of a file. Newest first is the default, because the line explaining what just happened is the one you came for. Choose oldest first to read a sequence in the order it happened; a registration or a startup is unreadable backwards.
A source that could not be read says so, in amber, with the reason. It is never drawn as an empty list. “Could not ask” and “nothing happened” are opposite facts, and a calm empty panel for a box that is refusing every call would be the failure this screen exists to end.
Reading the daemon’s journal needs the daemon’s user in the systemd-journal group; reading the switch’s log needs read permission on the file. Both are named in the amber box when they are missing.

Permissions

audit:read is an organisation admin’s as well as an operator’s, and each sees only their own tenant’s rows. logs:read is the operator’s alone.