Starf: an activity monitor that never invents a number
macOS declines to describe about a quarter of the processes on an ordinary machine. Most monitors print a zero. Starf prints — and this is why that one character is the whole product.
Open Activity Monitor on any Mac and scroll down the process list. Somewhere below the browsers and the editors you'll find a long tail of processes — WindowServer, mds_stores, half of /System/Library — and beside a surprising number of them a tidy 0.0.
Some of those zeros are true. Many of them are not. macOS declines to describe about a quarter of the processes on an ordinary machine; on the laptop Starf was built on, 341 of 1,245. The kernel simply answers no when asked for their counters. And what most monitors do with a no is print a zero, because a zero fits in the column.
Starf doesn't. It prints —. That one character is the whole product, and this is a note about why it matters and what follows from it.
The name
Starf is Old Norse for work — labour, the doing of a thing. In Icelandic today it's simply a job: ég er í starfi — I'm in work. An activity monitor watches what a machine is working on, and a name that says only that seemed right. It also happens to be a word about doing rather than seeming, which is the distinction the rest of this article is about.
The rule
An unavailable counter is not a zero.
That's the sentence at the top of Starf's documentation, and it was the rule of the Swift reference implementation Starf was ported from. Everything else in the application is that rule applied.
A process macOS refuses to describe stays in the list, with — in every column the kernel would not fill. A 0.0 was measured. A — never was.
The same distinction runs through everything downstream:
Exports. An unavailable counter is an empty CSV cell and a JSON null. Never a 0, because a 0 in a spreadsheet gets summed.
Sorting. Unavailable values sort below every real value in both directions. Sort by CPU descending and the — rows are at the bottom; sort ascending and they're still at the bottom. An unavailable counter is not a small one.
Subtree totals. Switch to Tree mode and numeric columns become Σ — a process plus every descendant. Where a member wouldn't report, the total shows ≥: a known lower bound, not a made-up sum.
Error messages. A process the kernel refuses (EPERM) is reported as refused. A process that has exited (ESRCH) is reported as exited. Conflating them is the easiest mistake in the codebase, and it was made exactly once — the first per-thread probe against WindowServer reported it as exited while it was running perfectly well. Every message now says which.
Why it matters
Because a monitor is something you make decisions from. If it says a process is idle, you leave it alone. If it says your CPU is at 26% and the top process is at 18.5%, you believe the other 7.5% is spread thinly across the rest. When a quarter of the list is silently zero, that belief is wrong, and you don't know it's wrong, because nothing on screen looks unusual.
The fix isn't to try harder to read those processes — macOS won't let an unprivileged app do it, and Starf doesn't ask for elevated privileges, install a helper tool or touch SIP. The fix is to stop pretending. Show what was measured; mark what wasn't; let the person decide what to make of the gap.
That's a less impressive-looking screen than one full of numbers. It's also the only honest one.
How it works, with the numbers
Starf is Rust reading the kernel and Svelte drawing. The Rust side talks to the same interfaces ps, top and lsof use — proc_pidinfo, proc_pid_rusage, host_processor_info, IOKit for the GPU — and every figure has been checked against a known load or another tool. The Verification page in the docs carries the comparisons. A few:
Process CPU, against ps. Two yes processes, each pinned to a core:
Starf ps
yes (pid 1) 100.0% 99.1%
yes (pid 2) 100.0% 98.9%
Starf's figure is exact because every rate is a difference between two readings divided by the time actually measured between them — never by the interval that was asked for. ps divides by something slightly different, and lands slightly under.
Host CPU, against the processor count. On sixteen logical processors, user + system + idle sums to 1600.00%. Not approximately. The card at the top of the CPU tab shows load as a share of total capacity, so it always reads out of 100.
GPU, against a Metal compute load. Device utilisation read 99% while the per-process figures summed to 99.9%. The renderer and tiler meters are drawn as independent meters rather than a stacked bar, because they overlap and must never be summed — a bar that stacked them would show 180% and look busy.
Open files, against lsof. Descriptor for descriptor. Per-process network, against nettop. Byte for byte. Subtree totals, against an independent Python implementation that recomputes every Σ from the raw process list and compares.
And where two tools disagree, the docs say which one is right. Memory-map region counts differ from vmmap because vmmap runs with access Starf doesn't have and splits adjacent regions differently; the totals agree with the kernel's own figure, and the comparison is written down.
A PID is not an identity
Here's a bug most monitors carry quietly. macOS reuses process IDs. A process exits, a new one starts, and it may well get the same number. If your rate calculation is keyed by PID alone, the new process inherits the old one's baseline and its first reading shows a wild rate — or a negative one.
Starf keys everything that has to survive across samples by PID and start time together: rate baselines, GPU observed time, tree expansion state (so a recycled PID doesn't arrive collapsed), per-thread baselines. And before it signals a process or reads its diagnostics, it re-checks that the PID still names the same process with the same start time and owner — then checks again after the read, so a process that exits mid-walk cannot have its successor's open files attributed to it.
What it refuses to show
Some of the most useful lines in Starf's documentation are about things it does not do.
Apple's per-process Energy Impact. A private metric computed from counters no unprivileged process can read. Starf doesn't implement it and dresses nothing up as it. The Energy tab shows CPU counters and says so, plainly, in the table.
Total system power draw. Needs SMC access. Starf shows the battery's own charge and discharge flow — cycle count, charge against design capacity, voltage, current, temperature, the adapter's rating — and names it for what it is. On mains, that figure falls to zero while the machine keeps drawing from the adapter, and the label says so.
Apple's Battery Health percentage. Apple computes theirs differently from the controller's raw figure. Showing ours under their name would be wrong, so it isn't.
A processor's core type when the device tree doesn't say. It keeps a neutral label. Guessing "Performance" because it's probably Performance is exactly the kind of small lie the whole application exists to avoid.
Each of these could have been a feature. Each would have been a number somebody planned around.
Nothing transmitted
Starf makes one promise about the network: it doesn't use it. No telemetry, no crash reports, no analytics, no account.
There's one exception, and it's asked for before it happens. On first launch, before a single byte has gone anywhere:
Watch for new versions? Starf has not used the network, and will not without an answer here.
Say no and it never opens a socket. Say yes and once a day it fetches one small static file — a plain GET, no identifier, no version number, no query string, a fixed User-Agent of Starf with nothing distinguishing. What comes back is treated as untrusted: the download address is compiled into the app and never read from that file, so a hijacked host can at worst report a wrong version number. It cannot send anyone to a download of its choosing.
The licence follows the same rule
Starf is free. Every measurement, every tab, every diagnostics page works without a licence, now and later.
After seven days of use, and then at most once every five days, a sheet appears at launch asking whether Starf has been useful enough to be worth a small one-off payment. The three buttons on it are the same size and weight — declining is not a lesser act, and a reminder that disguised its own dismissal would be worse than none. A licence removes that sheet. That's all it does.
A licence is $39, once. It's a line of text — an Ed25519 signature over your email address — verified against a public key compiled into the app. It's checked on your machine and nowhere else, because a licence check that phoned home would break the nothing transmitted promise for everyone, licensed or not. It shows Licensed to you@example.com, so a key is visibly someone's.
And yes: an offline key can be copied. The docs say so, and say that this is accepted rather than fought. Machine binding and activation servers cost real complexity and inconvenience honest buyers most. People who were going to pay, pay.
Try it
Download Starf from elyracode.com/starf — a signed, notarized disk image for Apple silicon, macOS 13 or later. Open the CPU tab, sort by % CPU, and scroll to the bottom. The rows that read — are the processes your other monitor has been calling idle.
They may well be idle. Now you know that nobody actually checked.