Skip to main content
Showcase

Argus: Small software, deliberately

Argus is a TUI-based unified control plane for agent sandboxing written in Rust, but this post isn't about agents or sandboxing. It's about what happens when every dependency, every default, and every line has to earn its place.

For the people who build CLI/TUI dev tools, and for anyone who just likes seeing how small software stays small.

In Motion

The whole lifecycle (start, draw, keypress, shutdown) fits in less than half a human blink.

Why this matters

Argus is a TUI-based unified control plane for agent sandboxing written in Rust. But this post isn't about agents or sandboxing. It's about what happens when every dependency, every default, and every line of code has to earn its place.

If you build CLI/TUI dev tools, or you just like seeing how small software stays small, this is for you.

Most modern dev tools accrete weight by accident. A logging framework here, an async runtime there, an argument parser, a config loader, three serialization formats. One day you ship a 50 MB binary that takes 400 ms to draw its first frame and spins your fan at idle.

Argus picks the other path. Each “no” is deliberate. Each megabyte saved is a vote for the user's laptop battery, their patience, and their trust.

“Small tools punch above their weight when every NO is deliberate.”

The Benchmarks

Measured, not estimated. Numbers that match what your eye sees.

9.3ms
Time to first frame
Faster than your monitor can refresh once
68ms
Spawn to exit
Start, draw, keypress, shutdown. Half a human blink.
9.3MB
Peak RAM
A single Chrome tab on google uses 20–30× more
0.28%
Idle CPU
It doesn't make your fan spin
3.7MB
Binary size
3.1 MB stripped. Smaller than an iPhone photo.
<1%
Variance
Across runs. Predictable, every time

Binary Size, in Context

The tools you trust on your $PATH every day. And Argus.

k9s
134 MB
kubectl
55 MB
gh
34 MB
Argus
3.7 MB

Argus is ~10× smaller than gh and ~36× smaller than k9s.

How It Stays Small

One foreground thread. One background worker. A channel between them. That's the entire concurrency model.

UI Thread

Render on change

Polls the keyboard with a 50 ms timeout. Only calls terminal.draw() when input arrives, the terminal resizes, or the worker reports state actually changed. No 60 Hz loop. No wasted frames.

std::sync::mpsc

Just a channel

The standard library's own channel. No tokio. No crossbeam. No async ceremony. Jobs flow one way, results flow back.

←  jobs · results  →
Worker Thread

All the slow stuff

HTTP calls, polling for IPs, SSH probes, long-running provision steps. It blocks freely, but only itself. The UI never waits on it.

No shared state. No locks. No data races. The mpsc-worker pattern is a Pareto-optimal point that almost no Rust tutorial teaches, and it gives you everything an async runtime gives you for a fraction of the cost.

Every “No” Was Deliberate

Each dependency had to pass a single test: do I really need this? Most didn't.

No async runtime

Tokio adds megabytes and a colored-function tax. The whole app fits in one foreground thread plus one worker.

No CLI parser

There are no flags. Argus has one job and runs it. clap is half a megabyte you don't owe anyone.

No logging framework

Errors bubble up with anyhow context. Live logs stream to the UI. tracing/log are not in the build graph.

No async HTTP

ureq is a few hundred KB of pure sync. reqwest pulls in tokio, hyper, h2, rustls, and you'd never notice the cost until you wonder why your binary is 30 MB.

No 60 Hz redraw loop

Most TUIs repaint 60 times a second whether anything changed or not. Argus paints when something actually moved. That's why idle CPU rounds to zero.

No shared state

The UI thread and the worker thread never touch the same memory. They communicate by passing messages on a channel. No locks. No data races. No bugs of that shape.

Building a CLI or TUI?

The discipline that built Argus is the same discipline that builds developer tools people actually keep installed. Let's talk about yours.