Pinned Pinned Coming to the Mac App Store
The Pinned app icon: a white download arrow beside an amber upload arrow.

Your real speed, up and down, in the menu bar.

Pinned reads the byte counters your Mac already keeps and turns them into a live download and upload readout, on whatever connection is actually carrying your traffic. Open the panel and it also names the app moving the bytes, counts your data cap against your real billing cycle, and measures whether the link still holds up under load.

Coming to the Mac App Store

Not submitted yet. Follow the build at bhd.om.

Other menu bar monitors measure ten things and show you a dashboard. Pinned answers four questions completely: how fast, which app, how much this cycle, and is the link usable.

Fig. 1  /  the readout

22 pt bar

The status item is drawn at a guaranteed fixed width. Measured identical from 0 B/s to 1.4 Gbps, so it never jitters and never shoves the items beside it along the bar.

1 s
Sample interval for the rate, with timer tolerance so the wakeup can be coalesced
64-bit
Kernel byte counters, so they do not wrap and lie on a fast link
0
Sockets opened to watch throughput or attribute it to an app. Only the responsiveness test sends traffic, and only when you press it
0.3–0.4%
CPU with the panel closed, measured on Apple silicon

What it measures

It counts what already moved. It does not go and fetch anything.

macOS keeps a running byte count for every network interface on your Mac. Pinned asks the kernel for that list once a second, through sysctl(NET_RT_IFLIST2), and reports the difference between one reading and the next. That difference is your speed. That is the entire measurement.

Those particular counters are 64-bit. The easier interface to reach for hands back 32-bit counters, which wrap every 4.29 GB. On a fast link that can be a few minutes, and every wrap would land on screen as a nonsense spike.

Pinned opens no sockets of its own to do this. No probe, no test file, no server at the other end, nothing added to the very traffic it is trying to describe.

Which interface it reads follows the system default route, re-checked on every tick. Unplug Ethernet and the readout is on Wi-Fi. Tether to a phone and it is on the phone. There is nothing to configure, and a reading it cannot trust is reported as zero rather than guessed at.

Signal path

1
Kernel counters
Per-interface bytes in and out, 64-bit, already maintained by macOS
2
One reading a second
sysctl(NET_RT_IFLIST2), no elevated privileges required
3
Difference
This reading minus the last one, over the elapsed time
4
The readout
Down and up, in the menu bar, at a fixed width

Follows the default route

Wi-Fi Ethernet Hotspot VPN tunnel

Per-app bandwidth

Which app is moving the bytes, right now.

A rate on its own tells you that something is happening. It does not tell you what. Pinned breaks the live figure down by process, so "why is my connection busy" has an answer rather than a suspicion.

Fig. 2  /  by process

Measured on the developer's Mac

  1. cloudflared 4.42 GB
  2. claude 163.1 MB
  3. node 54.6 MB
  4. mongod 52.2 MB

The usual way to attribute traffic to a process is to install a privileged helper. The commercial alternative ships a root LaunchDaemon: it loads when your Mac loads, and it stays resident whether or not you ever open the menu. That is a permanent piece of your system, running as root, in exchange for a list you glance at now and then.

Pinned does not do that. Per-app attribution runs inside the sandbox, in the app itself. No privileged helper, no root daemon, no admin password, and no permission prompt at any point.

The cost model follows from that. Pinned samples per app at a 3 second floor, and only while the panel is open. Close the panel and the work stops: independently verified at zero subprocesses while closed. A feature you are not looking at should not be running.

What it costs

Privilege
None. Sandboxed, with no helper installed outside the app.
Prompt
No admin password at install, and no permission dialog to approve.
Interval
A 3 second floor between per-app samples.
Panel open
Sampling runs.
Panel closed
Nothing. Zero subprocesses, independently verified.
Removal
Drag the app to the Trash. There is nothing else to uninstall.

Two limits worth stating plainly. A 3 second poll cannot see a transfer that starts and finishes between two samples, so a short burst can be missed entirely. And per-app figures are gathered at the socket level, which means they do not sum exactly to the interface total.

Data caps

Counted on your billing cycle, not the calendar month.

"How much of my allowance is left, and am I going to make it to the end of the cycle" is a question a network monitor should answer. Most of them do not.

Set your cap and tell Pinned the day your carrier's cycle starts. Every total is then counted against that window, so a cycle beginning on the 12th runs from the 12th to the 11th, which is what your bill already does.

The awkward cases are handled rather than ignored. A cycle day of 31 uses 28 February and 30 April, and lands back on the 31st in the months that have one. A cycle should never silently skip a month because the calendar was short.

When you are on course to overrun, Pinned says so in words instead of leaving you to do the arithmetic:

At this rate you reach your cap on Thursday 4 September.

Totals are also kept per network, so you can see how much went over the hotel Wi-Fi and how much went over the phone hotspot, which is usually the one that costs real money. Alerts fire at the cap thresholds you choose, on the same hold-and-cooldown rules as every other alert.

Cycle handling

Cap
Your allowance, in your own units.
Cycle day
The day your carrier's month starts. 1 to 31.
Short months
28 February, 30 April. A cycle day past the end of a month clamps to the last day of it.
Projection
The date you reach the cap at your current rate, written as a sentence.
Per network
Totals split by the network that carried them.
Alerts
At the share of the cap you choose, with a cooldown.

Responsiveness

A link can be fast and still be unusable for a call.

Latency measured on an idle link is nearly useless. What ruins a video call is latency under load, and the only way to know that number is to put the link under load and measure it there.

Your download figure describes how much water the pipe carries. A call cares about how long one drop waits in the queue. When a large transfer fills the buffers in your router, every small packet has to line up behind it, so the connection stays fast and the call falls apart at the same time. That is bufferbloat, and on this connection it is 53 ms of delay that only exists when the link is busy.

Pinned saturates the link on demand, probes while it is saturated, and reports the loaded figure, the idle figure, the gap between them and the round trips per minute. Four numbers, and the one that matters is the gap.

When it runs

Trigger
You press the button. That is the only way it starts.
Schedule
There is none. It never runs on its own, and never in the background.
Why
The test deliberately saturates your connection to get the number, so it is not something an app should do behind you.
Reports
Idle latency, loaded latency, bufferbloat and RPM.

Specification

Everything it does, and nothing it does not.

01
Menu barThe part you see all day
Layout
One row or two.
Icon style
Arrows, triangles, dots, letters, or no icon at all.
Directions
Download only, upload only, or both.
Typography
Units on or off. Font size to taste.
Colour
Monochrome, or your own colours per direction.
Sparkline
Optional, inline, beside the numbers.
Idle
Dims when nothing is moving, so a quiet Mac looks quiet.
Width
Guaranteed fixed. The item never jitters as the rate crosses a unit boundary, and never pushes your other menu bar items around.
02
PanelOne click away
Live
Current download and upload rate.
Chart
An opposed area chart: download above the line, upload below. Linear, logarithmic, or a fixed maximum.
Per app
Which process is moving the bytes, ranked and live, sampled at a 3 second floor while the panel is open. No helper, no prompt.
Usage
A usage chart for the current billing cycle, with the share of your cap used and the per-network split.
Totals
This session, and the current period.
Quality
Latency, jitter and packet loss, plus the responsiveness test on a button.
Interface
MAC address, MTU, link speed, IPv4, IPv6 and DNS.
Wi-Fi
SSID, BSSID, RSSI, noise, channel, band, width, security and PHY mode.
Download Upload
Linear, auto peak
03
AlertsOnly when it matters

Notify me when a value crosses a threshold and stays there for a duration I choose. A one-second dip is not an event.

Watchable
Download speed, upload speed, latency, or how much of your data cap is used.
Cap thresholds
At the share of your cycle's allowance you pick, counted on the cycle, not the calendar month.
Also
Connection lost.
Condition
Above or below a threshold, held for a duration you set.
Cooldown
Every rule has one, so a flapping link cannot spam you.
04
Data usageYour billing cycle, not the month
Session
Since the app started, or since you last reset it.
Period
Daily, weekly or monthly totals.
Cap
Your allowance, counted against the day your carrier's cycle starts, not the first of the month.
Short months
A cycle day of 31 uses 28 February and 30 April.
Projection
The date you reach the cap at your current rate, in a sentence.
Per network
How much went over each network you used.
Rollover
On the real boundary, not a rolling window counted back from now.
05
UnitsBoth camps are served
Unit
Bytes or bits. MB/s or Mbps.
Base
1000 or 1024. Network convention or storage convention, your choice.

Light on your Mac

A monitor that costs you something is a bad trade.

0.3–0.4% CPU
With the panel closed
36MB
Resident memory

Both figures are measurements, not targets. Taken on an Apple silicon Mac running macOS 26, with the panel closed and Pinned sampling the rate once a second. Opening the panel adds the per-app sampler at a 3 second floor, and closing it takes that back to nothing. Your own numbers will move a little with your settings and your hardware.

Privacy

Nothing leaves your Mac, and there is no small print under that sentence.

A network monitor sits in a position of unusual trust. Here is exactly what Pinned is allowed to do.

Sandboxed
Pinned runs inside the macOS App Sandbox. The throughput counters need no elevated privilege at all: reading them is an ordinary kernel query.
No admin password
No background helper, no root daemon, no kernel extension, nothing installed outside the app itself. Drag it to the Trash and it is gone.
Per app, with no prompt
Naming the app behind the traffic is done inside the sandbox, by the app itself. It needs no privileged helper, no root daemon and no permission prompt, so macOS never asks you to approve anything for it. It samples at a 3 second floor only while the panel is open, and nothing at all while it is closed.
No telemetry
No analytics, no crash reporting service, no usage counting, no account. Pinned does not report on you, because it does not report at all.
Public IP lookup is off
Off by default. Leave it off, and leave the responsiveness test unpressed, and Pinned makes no outbound request at all. Turn it on and it fetches one thing, your public address, at a refresh interval you pick.
The test is a button
The responsiveness test is the one feature that deliberately sends traffic, because it has to saturate the link to measure it. So it only ever runs when you press it. No schedule, no background run, and no quiet saturation of your connection while you are on a call.
Location, only for the name
macOS withholds the name of your Wi-Fi network from any app without location access. So if you want the SSID in the details panel, Pinned has to ask. That is the only reason it ever asks, nothing is stored or sent, and if you decline, every rate, total, chart and interface detail still works.

Requirements

macOS 13 or later
Apple silicon and Intel