Yet another fetch: written in go, targeted to server operators
Find a file
master 7494401d1d
DOCS: document the project for handoff
README covered none of build, install, configuration, or behaviour. It now
carries sample output, the section table, and the reasoning behind the
non-obvious choices -- why a missing unit reports "not found", why a hung
mount reports "unreachable", why some mounts are filtered, and why last
login means last remote login. Those are the parts a new maintainer would
otherwise read as bugs.

config.example.toml documents every key with its default and what the key
actually colours, instead of listing a bare subset.

cattpuccin.toml was an unreferenced scratch file using a different schema
from the one servfetch reads. Its palette is folded into
config.example.toml as a commented Macchiato alternative, mapped onto the
real key names, with a note that it replaces the block above rather than
being uncommented alongside it.

CHANGES.md was a todo list under a changelog's name. It becomes TODO.md,
with the completed Mocha-defaults item dropped and the gaps found while
working on this added: no tests and parsers that hardcode their paths,
serial mount reads, long paths overflowing the fixed column widths, and
wtmp's coming deprecation.

CLAUDE.md is brought back in line with the code and gains the invariants
worth preserving: prefer a degraded reading over a hidden one, put a
timeout on anything remote, warn on config problems rather than aborting.

go mod tidy -- every dependency had been marked // indirect.

Verified: the example config and its Macchiato variant both load with no
warnings on stderr.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 23:34:22 +02:00
cmd/servfetch FIX: bound statfs, validate config, clean up the OS field 2026-08-18 23:29:52 +02:00
internal FIX: bound statfs, validate config, clean up the OS field 2026-08-18 23:29:52 +02:00
.gitignore Init commit 2026-03-22 10:38:38 +01:00
CLAUDE.md DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00
config.example.toml DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00
go.mod DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00
go.sum DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00
LICENSE Initial commit 2026-03-22 10:06:21 +01:00
README.md DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00
TODO.md DOCS: document the project for handoff 2026-08-18 23:34:22 +02:00

servfetch

Yet another fetch, written in Go, aimed at server operators rather than desktops. No ASCII art: it prints who you are, what the box is doing, how full the disks are, and whether the units you care about are running.

Linux only. It reads /proc, /var/log/wtmp and systemd over D-Bus.

╭──────────────────────────╮
│ master@nerdge-04         │
│ last login: Aug 18 08:11 │
│ from 10.10.0.42          │
╰──────────────────────────╯
System
  OS            cachyos (7.1.8-1-cachyos-bore)
  Uptime        15h 17m
  CPU           16 cores  (7.2%)
  RAM           13Gi/30Gi  (42%)
  IP Address    10.10.0.103/24  (br0)
  Gateway       10.10.0.1
  DNS           10.10.77.201

Storage
  /                     162Gi/924Gi  (18%)
  /home                 232Gi/2Ti  (12%)

  /mnt/backups          180Gi/834Gi  (22%) [smb]
  /mnt/media            2Ti/14Ti  (18%) [smb]

Services
  ●  docker
  ○  sshd  (inactive)
  ?  doesnotexist  (not found)

Build

go build -o bin/servfetch ./cmd/servfetch/
./bin/servfetch

Install it somewhere on PATH and call it from ~/.profile or /etc/profile.d/ to get it on login.

Configure

Configuration is optional. Without a config file the built-in defaults apply.

mkdir -p ~/.config/servfetch
cp config.example.toml ~/.config/servfetch/config.toml

config.example.toml documents every key. Values you leave out keep their default, so the file only needs what you want to change. Unknown keys and misspelled section names are reported on stderr instead of being ignored, so a config that appears to do nothing will tell you why.

The four sections, any of which can be dropped via display.sections:

Section Shows
title user@host and the last SSH login, in a box
system OS, uptime, CPU, RAM, IP, gateway, DNS
storage local mounts, then network mounts, with usage thresholds
services the systemd units named in services.units

What it reports, and what it does not

A few behaviours are deliberate, because the alternative is a confident wrong answer:

  • Units that do not exist say so. systemd happily hands out a stub for an unknown unit, and that stub reports inactive. A typo'd or uninstalled unit shows as not found, and a masked one as masked, rather than as merely stopped.
  • Dead network mounts say so. Reading usage off a hung NFS or SMB mount would block forever, so each mount gets 2 seconds; one that does not answer is printed as unreachable instead of quietly dropped from the list.
  • Only real filesystems are listed. Pseudo and virtual filesystems are skipped, btrfs subvolumes are collapsed to one entry per device, and mounts that report no capacity at all (gvfs, desktop portals, AppImages) are left out.
  • Last login means last remote login. wtmp entries are only accepted when the host field parses as an IP, which filters out local logins and tmux sessions. If nothing qualifies you get last login: unknown.

Layout

Strict two phases, collect then render, with no rendering in the collectors and no data gathering in the renderers.

cmd/servfetch/      orchestration: load config, collect, render, print
internal/config/    TOML loading, defaults, validation
internal/collector/ data gathering, one file per section
internal/renderer/  lipgloss output, one file per section

See CLAUDE.md for how to add a section.

Licence

MIT. See LICENSE.