Snitch
github.comKey Features
Tech Stack
Key Features
Tech Stack
https://www.icir.org/mallman/pubs/APT07/APT07.pdf
> The “SH” state indicates that the remote peer sent a SYN followed by a FIN—however, the monitor never recorded a SYN-ACK from the local peer. At first glance, this would seem to indicate a scanner that is trying to make connection attempts look as real as possible in the hopes of not triggering an alarm. However, such connections can also indicate a vantage point problem whereby the monitor is not observing outgoing traffic from some hosts. While in general the monitor placement at LBNL can observe both incoming and outgoing traffic, there were periods of time where the traffic for some LBNL hosts would partially bypass the monitor. From a measurement perspective this is clearly undesirable.
Just my two snitches.
Many LD_PRELOAD rootkits hide their activity from the system by manipulating the output of libc functions like readdir(), open(), stat(), etc. kernel rootkits can hide whatever they need, but the common functionality is also to hide data from /proc.
That's why netstat, ps, *top or lsof are not reliable tools if the system is compromised. ss is a bit different and is a bit more reliable.
In this case, snitch is written in Go, which doesn't use the libc functions, so probably it'll be able to obtain information from /proc even if hidden by a LD_PRELOAD rootkit.
Another option would be to compile the binary statically.
Anyways, these tools are not meant to unhide malicious traffic or processes, so I think detecting beacons, inspecting traffic, etc, is out of the scope.
Resources:
https://github.com/gustavo-iniguez-goya/decloaker
User-space library rootkits revisited: Are user-space detection mechanisms futile? - https://arxiv.org html/2506.07827v1
The Hidden Threat: Analysis of Linux Rootkit Techniques and Limitations of Current Detection Tools - https://dl.acm.org/doi/10.1145/3688808
https://matheuzsecurity.github.io/hacking/bypass-userland-ho...
In any case, interesting to think of shared libraries (specifically shared libc) as a risk here. Makes sense, but I hadn't thought about it before.
That said, I'm having a hard time doing a threat model where you worry about an attacker only setting LD_PRELOAD but not modifying PATH. The latter is more general and can screw you with all programs (doesn't cover shell builtins, but it's not like those would just be one more step).
https://manpages.debian.org/bookworm/manpages/sock_diag.7.en...
https://github.com/vishvananda/netlink/blob/main/inet_diag.g...
Not many rootkits tamper the netlink channel, so in most cases it's a bit more reliable.
Might need a different name.
(With help from Claude for the second half of the list)
Sure. You can’t start an airline with the common words of united, delta, or american.
It’s limited to a specific industry, though. Here, a trademark for “network monitoring software” named snitch would almost certainly have been granted by the USPTO the first time someone did it. Now it’s probably more complicated.
They should call it "rat" and be done with it.
Besides, "snitch" works for Little Snitch -- I've always found it somehow endearing, although the bare word is unflattering.
- the author of LuLu is a security researcher; he also wrote "The Art of Mac Malware"
- I already bought two versions of Little Snitch and wasn't willing to pay for the third one
- contacting their support left a bitter aftertaste
I have probably also paid for three versions. It’s a great piece of software and they do not require upgrades excessively.
But I will try LuLu. I would rather my security software was OSS.
Textual or similar for a top-like mode would be cool someday
scripts/lsof.sh does lsof from /proc/*: https://github.com/westurner/dotfiles/blob/develop/scripts/l...
1. Can you highlight the currently selected row with a different background?
2. Maybe add optional reverse DNS lookups?
UI libraries have a lot of features for allowing people with disabilities to “read” and interact with the screen in efficient ways
Imagine if everything around us would be designed for blind people.
The idea is to design for all (or as many as feasible), it's not a binary either/or.
Additionally in sysadmin, blind-users are not just some random group, the ability not to use one's eyes is central to the Command Line Interface. You could always in theory get by with just a keyboard and a TTS that reads out the output, it's all based on the STDIO abstractions that are just string streams, completely compatible and accessible to blind, and even deaf users. (Unlike GUIs)
GUI apps are much trickier. They require that the developer implement integration with accessibility frameworks (which vary depending on X11/Wayland) or use a toolkit which does this.
When it comes to GUIs, you have a higher level of abstraction than grid-of-glyphs. By using a GUI toolkit with these abstractions, you can get accessibility (relatively) for free.
Open to having my mind changed though.
So I wouldn't say they are trickier.
I think TUI accessibility generally involves rereading the screen on changes (going by macOS VoiceOver). And if it's built with a GUI-in-the-terminal kit like ncurses, then it might not be able to read anything at all.
I'm not aware of any terminal that manages to build an accessibility tree from a TUI. It's pretty obvious this is an intractable problem since TUI apps operate by simply blasting ANSI codes with cursor moves. Try the combination of any screen reader, any terminal, and any OS of your choice--I don't think there's any combination that allows the screen reader to usefully interpret the TUI. Blind users are totally locked out of TUI apps and can really only use simpler CLI tools in the terminal.
Systemd's obsession with remaking every single wheel in Linux has been aggravating enough. Please don't do it again.
Before systemd presented a generalised interface, there were significant differences in the init and service management systems between the popular Red Hat and Debian families of distros.
Some of these I'm sure make life easier for maintainers. Others just feel like change for the sake of change. Breaking workflows because someone wanted to design a better wheel.
- resolv.conf: systemd-resolved is not unique here in providing a stub resolver and not just NSS functionality (it's been years, but isn't unbound often the same way?). And if you want to have systemd-resolved but not have its stub used in resolv.conf, you're free to do so! Just remove the symlink that is /etc/resolv.conf and replace its contents with whatever you choose.
- cron: systemd timers provide an alternative to cron. You're still allowed to create cron jobs and use cronie (or whatever traditional cron implementation) you like.
- ntpd: leaving aside the fact that most distros (I think?) nowadays use chrony rather than ntpd or systemd-timesyncd, you're likely free to switch to chrony or ntpd depending on your distro. Afaik, this isn't a daemon with deep system integration, and you should be able to plug-and-play without much issue.
- network configuration scripts: What're you comparing systemd-networkd to? NetworkManager? Debian's ifupdown scripts? RH-family's network-scripts? In any case, network management systems tend to be pretty pluggable (much like in the case of your cron daemon). You can even have them live side-by-side, managing different interfaces, e.g. have NetworkManager do WLAN, while systemd-networkd does Ethernet interfaces.
I don't know any of the story behind timedatectl, so I'll avoid opining on that one.
But generally, it really seems like each of these components is as pluggable and freely-choosable by a distro as one could reasonably hope for. And, like you acknowledge, they end up likely getting chosen because it's easier for distro maintainers. Which is kind of a big deal, imo. But if you don't like your distro's choice, it makes sense to complain to your distro.
In general, I think your suggesting that these new-ish (most of which are no longer very new) components were just made for the hell of it, I'd encourage you to look a little deeper into what they offer compared to the incumbents. For starters, they generally work together pretty cohesively, e.g. systemd-networkd and systemd-resolved do some mutual coordination stuff that's pretty nice. Systemd timers have numerous nice properties compared to cron. Etc.
Again, you (or your distro) are free to take or leave these components, since they can be picked on their own. But an analysis of "these new components from the systemd project 1) are forced on me, 2) exist primarily for the sake of change" seems both incorrect and uncharitable.
What you have here isn't a snitch, it's more like a full map of traffic. I don't have any other suggestions unfortunately.
Just my 2c
Like, ss without any options shows such arcane, rarely needed details as send/receive queue size but not the application socket belongs to.
And omits listening sockets which is main use for such tools.
I know picking the right defaults is hard ask but they managed to pick all the wrong defaults.
Being able to use them intuitively trumps ubiquity, speed or features.
If used in scripts, ubiquity and speed can be important. Then again, the output of ss is not ideal for script processing.
Another annoying part is not supporting json or even CSV. Some tools got modernized with it (like iproute2 tool set), but for these you might as well do /proc scraping yourself...
The only one I change is to add `--no-ignore`.
fd, rg and ag all work how I expect them to work and the arguments and order fit in with my expectations for modern cli applications.
They're recursive, they ignore things I don't care about and I can just give them the string I'm looking for, no path, no -name or --recursive etc.
find and grep do similar things but work entirely differently and their args aren't even in the same format.
Generally speaking, you can only have sensible defaults over time if you're able to change the defaults over time. New users and new use-cases come with time, and so what constitutes a "sensible default" changes.
However (and this is a drum I like to bang[0]), because unix tools only deal in usually-text bytestreams without any higher level of abstraction, consumers of those tools end up tightly coupled with how output is presented. Without any separation between data and its representation, the (default) representation is the tool's API. To change the default representation is to make a backwards-incompatible API change. A good example of this is how ps aux truncates longer than like 7 characters.
however this breaks backward compatibility, as you noted. in the golden age of unix it was critical to maintain backward compatibility so that local tooling didn't magically break.
HP-UX seems to have an env var UNIX95 that affects XPG4 compliance in operation/output. Solaris always had a /usr/xpg[46] path (and /usr/ucb). GNU tools have POSIXLY_CORRECT. and so on.
I never liked using any of those because then you're on some other system, or in a break glass situation, and none of the tooling works as you expect. In the today world of a near monoculture of linux, it's fine I guess. And there's no reason today that complex commands like `ss` shouldn't be controllable via env var.
love your blog, thanks for the link.
Thank you!
Configuring configuration via env var is a good historical example. I think that especially works nicely when you Buy An Operating System. You know, one that is created and provided by A Vendor. In principle, the vendor can architect a unified metaconfiguration system, e.g. one or several env vars that align behavior to a standard.
But I dunno if it would work so well to to hypothetically apply that tactic to a modern bazaar-based OS like Linux. Distros do amazing, valuable work to unify things, but modern Linux is basically a zillion software packages in a trench coat. So either the distro carries a zillion patches to have a few env vars, or the distro carries no patches and there are a zillion env vars. Either way, total cost of maintenance explodes.
Maybe when people say "text is the universal interface," they really mean that once you've released a textual interface, the interface becomes universal, unchanging for all time.
And omits listening sockets which is main use for such tools."
IMHO this would be one of the many arguments in favor of compiling from source rather than using "binary packages"^1
https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2...
(
printf '/int state_filter = 0;/a\\\n'
printf 'state_filter = (1 << SS_LISTEN) | (1 << SS_CLOSE);\\\n'
printf 'show_processes++;\\\n'
printf 'show_queues = 0;\\\n'
echo
echo "s/dhalBet/q&/"
printf '/switch (ch) {/a\\\n'
printf "case 'q':show_queues=1;break;\\\n"
echo
)|sed -i -f/dev/stdin misc/ss.c
This changes the default to display all sockets, hide the queues and show the processes using each socketIt also adds adds a -q option to display the queues
1. IMHO this is also an argument against "cloud computing", i.e., using someone else's computers where pre-installed kernels and binary packages are the norm
I think we understand that UX problem much better now than developers did back in the 70s. In general, not just for ss/lsof
For example:
Intent: "create a file"
Command: "touch $FILE"
As it happens, touching a file doesn't mean to create, it was supposed to touch to modify the last access date, like a null op. But now if you want to create a file you do that.
Intent: "Print a file contents to screen" Command: "cat $FILE"
Is this a reference to a feline? some slang for printing or reading? No it's short for concatenate, but if you pass just one argument instead of 2, it prints the concatenation of 1 file and nothing.
Even something as simple as
Intent: "Rename a file" Command: "mv $FILE"
Of ocurse there's the fact that moving a file and renaming the file are very similar if not identical in most FS/OS, but also, the slight change from a word to a proper-name style command already creates a style of command line interaction that was very natural in the 80s, but is now being reinvented with the advent of more powerful language decoding technology. So even:
Intent: "Copy a file" Command: "cp $FILE"
Now to the topic, you can see how my relationship with ss is the mapping:
Intent: "See a list of open ports" Command: "ss -tulnp"
Which I remember mnmemotecnically because it is close to -tulip. This is similar to ps -aux in that the command includes a set of options and I remember it mnemotecnically ("auxiliary" or "auxilio"), and I use the options even when I don't need them, modifying the options from that baseline if needed, like removing "a" to get just the current user's processes.
That said. I don't know if the future is going to be "better" alternatives to old tools, but rather deconstructing or making use of the concept of "binary":"command", running man and --help has never been an optimal solution, and let's be honest, kids nowadays are googling, stackoverflowing and chatgpting their intent in order to get a magical command.
No easy way to improve upon this at the userspace level, the OS model of delegating control to binaries based on a hierarchical command structure is sensible, and "magic", or sharing commands across binaries without a clear ruleset would be too opaque. But I feel that creating new tools while barely revolutionizing the way they work is too small an incremental change, it adds more noise, I'm not sure that ss2 or network-manager instead of wpa_supplicant is a better outcome, now you are just linearly increasing the cognitive demand of new sysadmins linearly with time.
Sorry to be a bummer.
Even in operating systems as distant as Android, we still have the phenomenon of using proper_names instead of natural names.
If you want a taxi or a cab, you don't ask your OS to get you a taxi or cab, you ask it to use the Uber binary.
In the 2000s it wasn't clear that this was going to be the case, the famous example of the pets.com domain was a wrong bet that natural names would somehow be important.
Instead natural names are only important when used through an obscure privately controlled algorithm like Google or StackoverFlow or ChatGPT, if you want to say "flights to Greece" instead of "Oobloo greece", you need a magical black box in the middle.
go install github.com/karol-broda/snitch@latest
I get this error message: go: github.com/karol-broda/snitch@latest: version constraints conflict:
github.com/karol-broda/snitch@v0.1.8: parsing go.mod:
module declares its path as: snitch
but was required as: github.com/karol-broda/snitchI find it a bit interesting that Go even allows you to declare `module barename` in go.mod even though it loves breaking so many things if you do so. I sometimes try doing it for completely private projects but I always just declare some URL in the end, it's a weird anti-pattern in my opinion.
`brew install snitch`
$ brew info snitch
==> snitch: stable 0.1.8 (bottled), HEAD
Prettier way to inspect network connections
https://github.com/karol-broda/snitch
Installed
/opt/homebrew/Cellar/snitch/0.1.8 (9 files, 8.4MB) \*
Poured from bottle using the formulae.brew.sh API on 2025-12-23 at 15:32:41
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/s/snitch.rb
License: MIT
==> Dependencies
Build: go
==> Options
--HEAD
Install HEAD versionIt was created by Laurent Constantin (https://linuxsecurity.com/features/introduction-to-netwox-an...) for his own needs and hence the TUI/GUI is not polished. But it is simple, direct and gets the job done which is what is important. And it is a mature tool (hence no need for active maintenance) available in all Linux distros.
That said though, I'm not going to install snitch. The thing about ss is that it's already there, on every server I manage.
Thanks for sharing
Not affiliated with Hacker News or Y Combinator. We simply enrich the public API with analytics.