e0c5a5cba3
terdut-server v0.10.2 serves a web UI you sign in to with a password, and every user starts without one. Until now the only way to give somebody their first password was a curl call with an API key. p in Users sets the selected user's password. The form asks for the current password only in the one case the server checks it: you are changing your own password and already have one. The client has no other way to know who its key belongs to, so opening the form calls GET /api/me first and shows the fields once that answers. Setting someone else's password sends no current_password at all, rather than an empty one. Length (at least 10) and the repeated entry are checked before anything is sent, mirroring the server's rule so a typo costs no round trip. The server stays authoritative: a wrong current password comes back as its own 403 message on the dashboard. The status line says the user's other web sessions were signed out, because the server does that on every password change. API keys are not affected. Older servers have no /api/me. The client now returns a typed StatusError carrying the status code, so a 404 there reads as "needs terdut-server v0.10.2 or later" rather than a bare "server returned 404". Its Error() text is unchanged, so every existing message reads as before. Requires terdut-server v0.10.2 only for this form. Everything else works against the same servers as before.
201 lines
7.1 KiB
Markdown
201 lines
7.1 KiB
Markdown
# terdut-tui
|
||
|
||
A terminal user interface for [terdut-server](https://git.ryuvia.com/niklas/terdut-server). Communicates with the server over its REST API.
|
||
|
||
Written in Go using [Bubbletea](https://github.com/charmbracelet/bubbletea).
|
||
|
||
## Features
|
||
|
||
- **Incident queue** — open incidents with severity, status, assignee and age, auto-refreshing
|
||
- **Incident actions** — acknowledge, assign, snooze, note, resolve and archive
|
||
- **Timeline** — the full history of an incident, system events, pages and notes together
|
||
- **Alert feed** — the raw read-only alerts underneath, each linked to its incident
|
||
- **On-call schedule** — visual calendar of who is on duty, assign and remove entries
|
||
- **Statistics** — MTTA and MTTR, plus alert frequency by name, hour and day
|
||
- **User management** — add and remove users, manage API keys, set each user's ntfy topic
|
||
|
||
> Requires terdut-server **v0.4.0 or later**. Earlier servers have no incidents API;
|
||
> use terdut-tui v0.3.x with those.
|
||
|
||
## Alerts and incidents
|
||
|
||
The server keeps two objects and this client follows that split:
|
||
|
||
- An **alert** is Alertmanager's record — firing or resolved, and read-only here.
|
||
- An **incident** is the work item. It is what you acknowledge, assign, snooze,
|
||
discuss and resolve, and it is where all the actions live.
|
||
|
||
Incidents are correlated by the `groupKey` Alertmanager already computed from your
|
||
`group_by` configuration, so several alerts commonly share one incident.
|
||
|
||
Two behaviours worth knowing before you press a key:
|
||
|
||
- **Resolving is final.** The server treats a manual resolve as terminal: a later
|
||
occurrence opens a *new* incident rather than reopening this one, and if the alert
|
||
underneath never stops firing the incident stays closed. The TUI asks for
|
||
confirmation before doing it.
|
||
- **Snooze is the "not now" button.** It hides an incident from the default queue
|
||
without closing it, and expires on its own.
|
||
|
||
## Push notifications
|
||
|
||
When the server is configured for ntfy, an incident that opens pages whoever is
|
||
on call. Each user has their own topic, shown as a column in the Users section
|
||
and edited with `t`. A user with no topic falls back to the server's shared
|
||
fallback topic, which carries **no Acknowledge button** — the topic is shared, so
|
||
a button on it would let any subscriber acknowledge as somebody else.
|
||
|
||
Every delivery lands on the incident's timeline: `Notified <user> (triggered)`
|
||
when ntfy accepted the page, and `Notification to <user> failed` when it ran out
|
||
of retries. That second one is the one to look for when nobody's phone rang.
|
||
|
||
Editing topics needs terdut-server **v0.6.0 or later**; the timeline entries need
|
||
**v0.7.0 or later**. Against an older server the topic column stays empty and
|
||
editing one reports the server's 404.
|
||
|
||
## Installation
|
||
|
||
Download the latest release binary for your platform from the [releases page](https://git.ryuvia.com/niklas/terdut-tui/releases), or build from source:
|
||
|
||
```bash
|
||
go install git.ryuvia.com/niklas/terdut-tui@latest
|
||
```
|
||
|
||
## Configuration
|
||
|
||
Create `~/.config/terdut-tui/config.yaml`:
|
||
|
||
```yaml
|
||
server_url: https://terdut.example.com
|
||
api_key: <your-api-key>
|
||
refresh_interval: 30 # seconds, optional
|
||
theme: gruvbox-dark # optional, this is the default
|
||
```
|
||
|
||
The API key is generated in terdut-server. See the server documentation for how to bootstrap a user and issue an API key.
|
||
|
||
## Themes
|
||
|
||
Two themes ship with the client: `gruvbox-dark` (the default) and
|
||
`gruvbox-light`. Both colour foregrounds only — the terminal supplies the
|
||
background — so pick the one that matches the background you already run.
|
||
|
||
To make your own, drop a file in `~/.config/terdut-tui/themes/` and name it in
|
||
`theme:`. `extends` inherits a built-in, so a file only has to list what it
|
||
changes:
|
||
|
||
```yaml
|
||
# ~/.config/terdut-tui/themes/mine.yaml
|
||
extends: gruvbox-dark
|
||
primary: "#d3869b"
|
||
accent: "#fabd2f"
|
||
```
|
||
|
||
A file may shadow a built-in name — `themes/gruvbox-dark.yaml` is how you tweak
|
||
the default without renaming it.
|
||
|
||
Without `extends`, every token must be set. The twelve are:
|
||
|
||
| Token | Where it shows |
|
||
|---|---|
|
||
| `primary` | header, active tab, selected row, cursors |
|
||
| `on_primary` | text drawn *on* `primary` — the active tab and selected row |
|
||
| `text` | incident titles and other emphasis |
|
||
| `muted` | secondary text, dividers, footer, table headers |
|
||
| `accent` | status line, acknowledged incidents, the by-day chart |
|
||
| `firing` | firing alerts, triggered incidents, the by-hour chart |
|
||
| `resolved` | resolved alerts and incidents, the top-alerts chart |
|
||
| `error` | error banners |
|
||
| `sev_critical`, `sev_error`, `sev_warning`, `sev_info` | the `severity` label |
|
||
|
||
Values are hex (`#83a598` or `#abc`) or an ANSI palette index (`0`–`255`) if you
|
||
would rather follow your terminal's own colours. Colours are downsampled
|
||
automatically on 256- and 16-colour terminals, and `NO_COLOR` is honoured.
|
||
|
||
## Usage
|
||
|
||
```
|
||
terdut-tui start the TUI
|
||
terdut-tui --version print version
|
||
terdut-tui --self-update update to the latest release
|
||
```
|
||
|
||
### Keybindings
|
||
|
||
Global:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `j` / `↓` | Move down |
|
||
| `k` / `↑` | Move up |
|
||
| `tab` / `shift+tab` | Next / previous section |
|
||
| `enter` | Open detail |
|
||
| `esc` | Go back |
|
||
| `r` | Refresh |
|
||
| `f` | Cycle filter |
|
||
| `q` | Quit |
|
||
|
||
The sections, in `tab` order: Incidents · Alerts · Stats · Archived · Schedule · Users.
|
||
|
||
Incidents section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `f` | Cycle: open → triggered → acknowledged → resolved → snoozed |
|
||
| `x` | Archive (resolved incidents only) |
|
||
|
||
Incident detail:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `a` / `A` | Acknowledge / clear acknowledgement |
|
||
| `R` | Resolve — asks to confirm, and is final |
|
||
| `s` | Assign to a user |
|
||
| `z` / `Z` | Snooze for a duration / un-snooze |
|
||
| `c` | Add a note |
|
||
| `[` / `]` | Select a note |
|
||
| `d` | Delete the selected note (your own only) |
|
||
| `x` | Archive / un-archive |
|
||
|
||
Alerts section (read-only):
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `f` | Cycle: firing → resolved → all → archived |
|
||
| `i` | In detail: jump to the alert's incident |
|
||
|
||
Stats section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `j` / `k`, `pgup` / `pgdn` | Scroll |
|
||
|
||
Schedule section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `+` / `W` | Assign a day / a whole week |
|
||
| `d` | Remove the assignment |
|
||
| `←` / `→` | Shift the week window |
|
||
|
||
One person holds a given day. Assigning over days somebody else already has
|
||
asks first — naming them and how many days are being taken — and moves the whole
|
||
selection at once when you accept, so reassigning a week is one confirmation
|
||
rather than seven deletions. Taking somebody's shift needs terdut-server
|
||
**v0.8.0 or later**; against an older server the assignment is refused with
|
||
`date already assigned`.
|
||
|
||
Users section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `n` | Create a user |
|
||
| `t` | Edit the user's ntfy topic — submit empty to clear it |
|
||
| `d` | Delete a user |
|
||
| `k` | API keys for the selected user |
|
||
| `p` | Set the selected user's web UI password — asks for the current one when it is your own |
|
||
|
||
In Users, `k` and `d` act on the selected row, so move with `↑`/`↓` there rather
|
||
than `k`. Setting passwords needs terdut-server **v0.10.2 or later**, the first
|
||
with a web UI.
|