Features

Everything a lookout needs. Nothing he doesn’t.

Gabier is deliberately small. Here is the full list of what it does, and a few things it refuses to do.

01 · Real time

Within a second of the login

Gabier streams the sshd journal (journalctl --follow) or tails a log file across rotations. There is no polling interval to tune: a line is read the moment sshd writes it, and the webhook POST — with retries — never blocks reading the next one.

journalctl -fu gabier
09:03:14.114 sshd: Accepted publickey for root from 203.0.113.42 …
09:03:14.118 gabier: event accepted  user=root ip=203.0.113.42
09:03:14.391 gabier: discord ok  273 ms
/etc/gabier/config.toml
[events]
accepted     = true
failed       = true
invalid_user = true
closed       = false   # doubles the volume for little value

[throttle]
window_secs  = 300     # per source IP
burst        = 1       # then count and summarise

02 · Anti-spam

A scan cannot drown the real login

This is the only genuinely hard part, and it is solved. Per source IP, the first burst failures go out immediately; the rest are counted and summarised in one message at the end of the window. Successful logins are never grouped — that is the event you are watching for.

Tuning the window

03 · Channels

Discord and Slack, by webhook

No bot to invite, no OAuth dance, no token with broad scopes. A webhook URL is all Gabier needs, and it can post to both at once — if one is down, the other still gets through. Keep the secret out of the config entirely with GABIER_DISCORD_WEBHOOK.

Discord

Rich embed, coloured by event. Custom username. Ping a user or a role, and allowed_mentions is locked down so a log line can never trigger an @everyone.

Slack

Coloured attachment with a plain-text fallback for push. Slack's own mention syntax: <@U…>, <!subteam^S…>, <!channel>.

Telegram · ntfy · email

Not written yet, but cheap to add: a payload() and a Sink variant. Retries, size limits and ping clamping already live in hook.rs.

systemd/gabier.service
[Service]
User=gabier
SupplementaryGroups=systemd-journal
ProtectSystem=strict
ProtectHome=yes
NoNewPrivileges=yes
CapabilityBoundingSet=
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=yes
SystemCallFilter=@system-service
MemoryMax=64M
TasksMax=32

04 · Hardened

Least privilege, out of the box

A tool that watches your logins should not be a new way in. The installer creates a dedicated account with no shell and no home; the unit gives it a read-only filesystem, no capabilities, no executable memory, a syscall allow-list and a 64 MB ceiling. It can read the journal and open outbound HTTPS. That is the whole attack surface.

05 · Footprint

One binary, a few megabytes

Rust and rustls, no OpenSSL, no runtime, roots embedded — so there is nothing to install on the VPS alongside it. Release builds are stripped with LTO. Idle, it sits on a journal pipe and costs nothing.

Binary
[X] MB
Memory ceiling
64 MB
Runtime deps
0

Where Gabier sits

Between the bash one-liner you never finished and the SIEM you do not want to run for one VPS.

Gabier compared with a hand-written shell script and a full monitoring stack
Shell script + curl Gabier Full monitoring stack
Setup time An evening, then forever 2 minutes A weekend
Log rotation, journald Usually broken Handled Handled
Survives a port-22 scan Floods the channel Burst summaries Yes, with tuning
Runs as root Usually No, dedicated account Often, as an agent
Resources Tiny 64 MB ceiling A dedicated host
Dashboards, history, search No No, on purpose Yes
Good for One box, one afternoon Personal VPS, side projects, a handful of servers Fleets and compliance

What Gabier will not do

Block anyone

It watches and reports. Banning is fail2ban's job, and the two run well side by side.

Store history

Your journal already does. Gabier keeps no database and no state across restarts.

Phone home

The only outbound connections are your webhooks — plus ipwho.is, if you opt in to geolocation.

Ready to put Pince on watch?

Two minutes, one webhook.