The module path, the CI pipeline and the self-updater all named GitHub. They now name the Gitea instance everything else already runs on. The workflows are rewritten rather than translated, for the reason recorded in ci.yaml: Gitea's runner image is ubuntu:22.04, whose nodejs is Node 12, so no JS action runs there -- actions/checkout@v4 dies with a SyntaxError before doing anything. Every step is shell and checkout is a plain clone, which this public repo needs no credential for. upload-artifact/download-artifact are JS actions too, and there is no artifact store here, so the job that builds the binaries is the job that publishes them. internal/updater keeps its release and asset types unchanged: Gitea's release payload carries the same tag_name, and its attachments the same name and browser_download_url, so only the URL, the Accept header and one error string move. The asset naming in release.yaml is load-bearing for that matching. This does strand already-installed binaries, which still poll api.github.com. The GitHub repository is left in place and untouched, so they report themselves up to date rather than erroring; its last release is the bridge, and crossing it is a one-time manual download.
4.0 KiB
terdut-tui
TUI client for terdut-server, a Prometheus Alertmanager receiver and incident manager. Requires server v0.4.0+.
Domain model
The server splits alerts from incidents, and this client mirrors it:
- Alert — Alertmanager's record. Firing or resolved, read-only, no workflow state.
- Incident — the work item: triggered → acknowledged → resolved, with an
assignee, snooze, notes and an append-only timeline. Many alerts to one incident,
correlated by Alertmanager's
groupKey.
All user actions target incidents. Two server behaviours the UI has to respect: manual resolve is terminal (hence the confirmation prompt), and snooze is the non-destructive "not now" alternative.
Tech stack
- Go 1.25+
- Bubbletea — TUI framework (strict Elm architecture)
- Lipgloss — styles (all in
internal/tui/styles.go, never inline) - Bubbles — table, textinput, help components
Project layout
main.go CLI entry point: flags, config load, health check, start TUI
internal/api/client.go REST API client — one method per endpoint
internal/config/config.go Config loader (~/.config/terdut-tui/config.yaml)
internal/tui/ Bubbletea UI
model.go Model struct, mode/section constants, Init(), tea.Cmd constructors
update.go Update() — dispatch only, no API calls inline
view.go View() — pure rendering
keys.go keyMap (bubbles/key pattern)
styles.go All lipgloss styles
internal/updater/updater.go Self-update via GitHub Releases
Architecture rules
- Never call API inside
Update()— returntea.Cmdinstead; the runtime runs it async. View()is pure — no side effects, no state mutations.- All state in
Model— no globals. - All styles in
styles.go— never use lipgloss inline inview.go.
Config
Location: ~/.config/terdut-tui/config.yaml
server_url: https://terdut.example.com
api_key: <64-char hex key>
refresh_interval: 30 # seconds, optional, default 30
The API key is a one-time secret generated by terdut-server (POST /api/users/{id}/api-keys).
Running
go run .
go run . --version
go run . --self-update
Building
go build -ldflags="-X main.version=v0.1.0" -o terdut-tui .
Sections
Incidents (the queue, and the default) · Alerts (raw read-only feed) ·
Stats (MTTA/MTTR and alert frequency charts) ·
Archived (archived incidents) · Schedule · Users
Development stages
| Stage | Feature |
|---|---|
| 1 | Scaffold, config, health check, placeholder TUI |
| 2 | Alert dashboard with auto-refresh and stats |
| 3 | Alert detail: acknowledge, comment, statistics charts |
| 4 | On-call schedule calendar view |
| 5 | User management and API key lifecycle |
| 6 | Incidents: queue, timeline, ack/assign/snooze/resolve, MTTA/MTTR |
Memory (GrayMatter)
This project has persistent agent memory via the graymatter MCP tools:
memory_search(agent_id,query) — call at the start of a task when prior context might matter.memory_add(agent_id,text) — call whenever you learn something durable: user preferences, decisions, conventions, gotchas.memory_reflect(action,agent,text/target) — update or forget stale facts. ⚠ takesagent, notagent_id.checkpoint_save/checkpoint_resume(agent_id) — snapshot/restore session state before major refactors or across restarts.
Use a stable agent_id of the form <project>-<role> (e.g. myapp-backend). Store conclusions, not conversation logs. Err on the side of remembering.