A native macOS menu-bar utility for live, per-app network monitoring — with cross-session totals, code-signature trust badges, and an on-hover historical graph. No accounts, no telemetry, no background services. Just a small status item that quietly tells you who's using your bandwidth.
- Live menu-bar speed —
↓ 1.2 MB/s | ↑ 305 KB/s, refreshed every second. - Per-app breakdown — click the menu bar item for a popover with two collapsible sections:
- All-Time (across sessions) — cumulative bytes per app, persisted between launches.
- Currently Using — apps actively moving bytes right now, with a 60-second rolling sparkline.
- Hover-to-expand graph — hover any sparkline for ~2 seconds and a floating chart panel opens with a date-range picker (Live / 5 min / 15 min / 1 hour). Closes itself when you move the mouse away.
- Code-signature trust badges — each row shows whether the binary is
Trusted(valid Apple / Developer ID signature) orUntrusted(ad-hoc / unsigned). System binaries default to trusted. - System vs User tag — every row prefixed with
System:orUser:so daemons don't get confused with apps you installed. - Ignore list — right-click any app → Hide from Currently Using. Restore from the popover gear menu.
- Per-app data budgets — set a session data limit; get a notification when it's crossed.
- Launch at login — opt-in toggle (not on by default).
All data lives on your machine. The app makes zero network connections of its own.
The app spawns /usr/bin/nettop -P -n -L 1 once per second, parses the CSV output, and computes per-PID byte deltas. PIDs are grouped by bundle identifier so multi-process apps (Chrome helpers, Electron renderers) collapse to a single row. The previous architecture tried to keep one long-lived nettop process, which doesn't work — nettop block-buffers stdout when piped and only emits CSV when given -L. Per-cycle spawning costs ~60 ms each tick (idle CPU stays near 0%).
Code signatures are checked once per bundle path via SecStaticCodeCheckValidity and cached. Lifetime byte counts are accumulated into a JSON file under ~/Library/Application Support/NetworkUsageMonitor/lifetime.json and saved every 30 seconds plus on quit.
| macOS | 13 Ventura or later (Swift Charts) |
| CPU | Intel or Apple Silicon |
| Toolchain (for building) | Swift 5.9+ (Xcode 15 or Command Line Tools) |
No external Swift packages. The app is not sandboxed — it needs to invoke nettop and read installed-application metadata via NSWorkspace, both of which the App Sandbox blocks. That's why this can't ship on the Mac App Store; download from GitHub Releases (or Homebrew) instead.
Grab NetworkMonitor.app.zip from the Releases page, unzip, and drag NetworkMonitor.app into /Applications.
First launch: Right-click the app → Open → confirm. (Standard Gatekeeper prompt for unnotarized builds. The app is ad-hoc-signed; if you'd like a notarized release, see Distribution below.)
brew install --cask network-monitor(See Distribution for how to get the cask accepted into homebrew-cask.)
git clone https://github.com/SNGWN/Network-Speed.git
cd Network-Speed
./Scripts/build_app.sh --run # builds + launches
# or
./Scripts/build_app.sh --install # builds + copies to /Applications + launchesThe script handles compilation, bundle assembly, ad-hoc signing, and (optionally) installation.
- Left-click the menu bar item — opens the popover.
- Right-click (or control-click) — Launch-at-Login toggle + Quit.
- Click any section header — collapses or expands that section.
- Right-click any app row — set data budget, or hide from Currently Using.
- Hover a sparkline for 2 seconds — opens the expanded graph window with a date-range picker. Move the mouse away to dismiss.
- Gear icon in the popover header — restore ignored apps, quit.
Network-Speed/
├── Package.swift
├── Scripts/
│ ├── build_app.sh # build + sign + (optional) install/run
│ ├── Info.plist # bundle metadata
│ └── NetworkUsageMonitor.entitlements
├── Sources/NetworkUsageMonitor/
│ ├── NetworkUsageMonitorApp.swift # @main + AppDelegate + popover plumbing
│ ├── Managers/
│ │ ├── NetworkMonitor.swift # per-cycle nettop spawner
│ │ ├── NettopLineParser.swift # pure CSV-row parser
│ │ ├── NetworkViewModel.swift # SwiftUI publishers
│ │ ├── ProcessTracker.swift # per-PID → per-bundle aggregation
│ │ ├── ProcessClassifier.swift # System/User + Trust verdicts
│ │ ├── LifetimeUsageStore.swift # persistent all-time totals
│ │ ├── SpeedHistoryStore.swift # in-memory rolling 1-hour buffer
│ │ ├── IgnoreListManager.swift # user-hidden apps
│ │ ├── BudgetManager.swift # per-app session limits
│ │ ├── BudgetNotifier.swift # threshold-crossed notifications
│ │ └── LaunchAtLoginManager.swift
│ ├── Models/
│ │ ├── AppNetworkUsage.swift
│ │ └── NetworkSpeed.swift
│ ├── Utilities/FormatUtility.swift
│ └── Views/
│ ├── PopoverRootView.swift # status-item popover
│ └── GraphPanel.swift # hover-expand floating chart
└── Tests/NetworkUsageMonitorTests/
├── NettopLineParserTests.swift
├── ProcessTrackerTests.swift
├── BudgetManagerTests.swift
└── FormatUtilityTests.swift
- History capped at 1 hour. The hover-expand graph reads from an in-memory ring buffer (3,600 samples per app at 1 Hz). Once you quit, that history is gone — only the cumulative totals persist. A disk-backed long-history store is a sensible next step.
- No retroactive counters. macOS doesn't expose per-process bytes the kernel has counted before our app launched, so the All-Time totals only include bytes observed while the monitor was running.
- Cold-start cost. First snapshot after launch hashes each running binary's signature. Expect a brief ~0.3-1 second CPU spike, then idle.
- No App Store distribution. See Requirements.
Bug reports, fixes, and feature ideas are all welcome. Please open an issue first for anything larger than a small fix so we can sort scope. See CONTRIBUTING.md for the basics.
If you're a maintainer (or planning to fork-and-ship), read DISTRIBUTION.md — covers Apple Developer ID signing, notarization for Gatekeeper, GitHub Releases automation, and submitting a Homebrew Cask formula.
MIT — see LICENSE.