A small, smooth activity monitor for your Mac, with graff in the corner.
Built on nanoui, in Harness's Codegraff colors.
Download · Get started · Pages · What it reads · Using it · Credits
nanomon shows every process, the machine's CPU, memory, GPU, disk and network over the last minute to hour, and what each app is using. It works out of the box: no helper, no daemon, no admin rights. Its colors are amber, rust, sage, slate and violet on warm charcoal or cream, set in Geist and JetBrains Mono.
macOS (Apple silicon, macOS 13+):
download nanomon,
open the disk image, and drag nanomon to Applications. Each
release lists the image's
SHA-256. Releases are built by
.github/workflows/release.yml, and
sh macos/package.sh builds the same disk image locally.
Linux (GTK 3 and installed TrueType fonts): download the
x86_64 archive
or arm64 archive,
extract it, and run ./nanomon. Each archive has a .sha256 file on the
release. On Debian/Ubuntu, install libgtk-3-0 (or libgtk-3-0t64) and
fonts-dejavu-core if needed. Building from source works too:
Signed macOS releases check GitHub for updates automatically. Since v0.1.11, nanomon uses nanolight's quiet restart: it installs a verified update while out of sight and returns in the background, restoring windows and view preferences. Paused monitoring and pending actions defer automatic restart. The sidebar also offers Check for updates and Restart to update. Linux release builds install verified updates on quit or an explicit restart; source builds do not replace themselves. MCP sessions never auto-restart.
Coming from ad-hoc-signed v0.1.9? Install a signed v0.1.10+ manually once.
See updates and release verification. A release becomes
latest only after signatures, notarization, checksums and the real updater
have been checked headlessly.
nanomon builds on its own. It fetches nanoui, pinned by commit in
build.zig.zon:
git clone https://github.com/justrach/nanomon
cd nanomon
zig build run
zig build test # the model, ps's answers, the table, every page through ui.Tester, real samplesThe tests write a picture of every page to /tmp/nanomon-*.png.
Working on nanoui at the same time? In a --recursive clone of
nanoui, nanomon lives under apps/.
Build it there against the nanoui next to it, with your changes, using
--fork:
cd nanoui/apps/nanomon
zig build run --fork=../..Since v0.1.10, nanomon mcp is a headless stdio server using the same
sampler as the window. Agents can query system stats, filter/sort/paginate
processes, inspect a PID, and read timestamped history from their session.
It never opens a window, listens on a port, or allows process control.
Build it with zig build, then launch the installed app: the first GUI
launch offers Connect agents for detected Claude Code, Claude Desktop,
Cursor, Gemini CLI, Windsurf and Codex profiles. One click adds nanomon without
replacing other servers/settings. Not now remembers your choice; the
sidebar's Agent connections… button lets you revisit it. On macOS, move
the app to Applications first—copying a DMG cannot run an install hook.
Terminal/installer equivalents (after placing the binary at its final path):
./zig-out/bin/nanomon mcp install --dry-run
./zig-out/bin/nanomon mcp install
./zig-out/bin/nanomon mcp status
./zig-out/bin/nanomon mcp uninstall # undo only nanomon's matching entriesSetup backs up changed configs, skips malformed/custom/disabled entries, and
never silently opts agents in. Codex uses its official CLI to preserve TOML.
Alternatively, configure any MCP client with the absolute executable path
and "args": ["mcp"]. The GUI need not be running. Sampling is on demand
(no idle polling); history belongs to that session, not an existing GUI window.
Normal MCP startup never modifies client configs.
See MCP setup, tools, scales and security for the client configuration and test commands. Use v0.1.10 or newer; v0.1.9 binaries do not include MCP or agent setup.
- Overview: a tile for each part of the machine, with a small graph; the busiest processes; this Mac in a few lines.
- Processes: the full table, with All/My/System processes, CPU, Memory and Disk columns, search, an inspector and a quit sheet. Tick a row's box (or ⌘-click, or ⇧-click for a run of rows; the header's box ticks them all) to mark processes, then quit them together. The sheet names them first. Processes that aren't yours stay marked, and nanomon never signals itself, launchd or the kernel.
- graff: the amber bubble in the corner asks graff about the machine
("What's slowing my Mac down?", "Should I quit the 3 marked?"). It runs
graff -p --no-local-toolsfrom an empty temporary directory, so graff can answer but not act. graff sees the system's figures and the busiest and largest processes. If it names processes to quit, only your own are offered, as "Mark N to quit"; quitting them is still your call. Asking uses your graff account like any other graff run. - CPU: the processor's load over a cell graph (system rust under user amber); each core's load with fast and frugal cores apart; load averages against the core count, tasks, threads and uptime.
- Memory: app, wired and compressed memory stacked over time, the pressure, and the largest processes.
- GPU, Disk, Network: the load or rates over time, totals since boot, and the processes that wrote the most.
- Apps: processes grouped by the app they belong to (Chrome and its helpers), with summed CPU, memory and threads.
- About This Mac: chip, cores, graphics, memory, system and uptime.
Graphs span 1 minute to 1 hour, and you can pause them. Hover a graph to read the values in any column.
On macOS nanomon also sits in the menu bar, beside the clock. It shows CPU, memory, the network's rates and the chip's temperature, in the menu bar's own color. Click it to open a panel with:
- the CPU now, over its last minute in dots
- user, system, and how hot it runs
- every core as a ring, frugal and fast cores apart
- the five busiest processes
- the GPU's load, memory, temperature and renderer as dials
- load averages and uptime
Open nanomon brings up the window. The gear chooses what the menu bar shows and quits nanomon. Closing the window leaves nanomon running in the menu bar.
Activity Monitor gets its figures from a private system daemon (sysmond, through libsysmon), which reads every process. nanomon uses only public interfaces:
- Processes (macOS):
proc_listallpidslists them.PROC_PIDTBSDSHORTINFOgives each one's parent, user and short name, andproc_pidpathits program. A process inside an app bundle shows the app's name, as Activity Monitor does ("Google Chrome Helper (Renderer)"). - CPU time and memory:
proc_pid_rusagegives these for the processes nanomon may read (the user's own, which is most of them). Memory is the physical footprint, the figure Activity Monitor shows. For the system's processes, nanomon asks/bin/ps(setuid, astopis) every third sample. It reads the answer as it arrives without waiting, and fills it in once it's there. ps reports resident memory for these, not the footprint. - The machine:
host_processor_infogives the CPU ticks per core.host_statistics64gives memory: app memory (anonymous, not purgeable), wired, compressed, and cached files (file-backed and purgeable). - Threads, idle wake ups and disk (macOS): task info and rusage, for the processes nanomon may read. Activity Monitor's daemon reads them for every process; nanomon shows a dash for the rest.
- Swap, memory pressure and the disks:
vm.swapusage,kern.memorystatus_level, and each block storage driver's statistics in IOKit. - The rest of the machine (macOS):
- Each core's load:
host_processor_info, with fast and frugal cores told apart byhw.perflevel*. - Load averages:
getloadavg. - Every task and thread: the default processor set's statistics.
- Thermal state: NSProcessInfo.
- Temperatures: the chip's sensors as IOKit's HID event system lists them. These are the CPU dies, the GPU's where the chip names them, and the rest of the SoC. They need no admin rights.
- The GPU: its accelerator's statistics in IOKit.
- The network: each interface's 64-bit counters from its MIB data. The routing socket gives only 32 bits to anyone but root.
- Each core's load:
- Linux:
/proc/<pid>/stat,statm,statusandio, plus/proc/stat(each core too),/proc/meminfo,/proc/diskstats,/proc/loadavg,/proc/uptimeand/proc/net/dev. Processes whose disk counters are restricted by Linux's ptrace permissions still appear; unavailable counters stay at zero, without logging a stack trace. There's no GPU page data there. - Windows: not yet. The app builds and says so.
% CPU is a share of one core over the last interval (two seconds), as Activity Monitor counts it, so a busy process can pass 100%. Some Linux monitors (including XFCE Task Manager in whole-machine mode) divide this by the number of logical CPUs instead: nanomon's 25% on four logical CPUs is 6.25% on that scale. Compare the same interval and scale; nanomon's machine and per-core graphs each run from 0 to 100%. The Processes page shows the per-core scale below its CPU table.
- Sampling only while visible. The view samples when it's due and asks
for its next frame then (
c.frameIn). A hidden or covered window samples nothing. - Smooth updates. Rows are keyed by process ID, so a row's % CPU eases to each new value as its process moves through the table, and bars ease too.
- Graphs draw only what changes. A graph follows the pointer only while
you hover it (
c.wants_moves), and redraws its readout once when the hovered column changes. - Icons a few at a time. App icons are made once per app, at most six per frame, so the first screenful doesn't stall.
- A virtualized table. Only the rows in view are built (
ui.list), so two thousand processes cost what a screenful does.
There's no energy page: per-process energy and temperature come only from Apple's private interfaces.
- Pages: click the sidebar, or press ⌘1 to ⌘9.
- Pause: the ⏸ button beside the range, or ⇧⌘P.
- Sort: click a column; click it again to reverse the order.
- Search: type to filter by name, user or process ID (⌘F).
- Select: click a row, or move with ↑ and ↓.
- Inspect: double-click a row, press ⓘ or ⌘I.
- Quit a process: ⓧ or ⌥⌘Q. You're asked first. Quit sends SIGTERM, which asks the process to close; Force Quit sends SIGKILL.
- Close: Escape closes the open sheet or menu, then clears the selection.
The headless real-page benchmark checks the 8.33 ms budget for 120 Hz without opening a window. Top-process rankings are memoized between source changes; GUI temperature I/O runs on a single event-driven worker, while MCP remains synchronous. Lean checks cache/filter/publication models; Zig tests exercise selection, rendering and worker lifecycle. See performance measurements and slow-frame profiling for reproducible commands, all retained tails, and the proof boundaries. Sustained 120 FPS is not established.
src/main.zig the window and the menu bar
src/menubar.zig the menu bar readout (a template image) and its panel (macOS)
src/view_popover.zig the panel under the menu bar
src/app.zig state: sampling, order, search, selection, quitting
src/model.zig processes, the system, sorting, formatting
src/sample.zig a sample: CPU percentages, history, names, users
src/mcp.zig read-only MCP session: JSON-RPC, negotiation, discovery, calls
src/mcp_stdio.zig headless stdio and on-demand sampling (no event loop or GUI)
src/mcp_tools.zig validated monitoring queries and JSON views
src/mcp_catalog.zig tool schemas and descriptions
src/tests_mcp.zig protocol, data views and real headless sampling tests
src/sample_mac.zig libproc, Mach and sysctl
src/icons.zig app icons for the table (macOS)
src/ps.zig /bin/ps for the processes nanomon may not read
src/sample_linux.zig /proc
src/sample_proc_linux.zig bounded virtual-file reads, including read-time permission errors
src/sample_none.zig elsewhere
src/sample_sys_mac.zig chip, cores, load, uptime, threads, thermal, network
src/sample_iokit_mac.zig the disks and the GPU, from IOKit
src/sample_temp_mac.zig temperatures, from IOKit's HID sensors
src/sample_sys_linux.zig the same from /proc
src/view.zig the window: sidebar, page, sheets, keys
src/view_side.zig the sidebar
src/view_kit.zig headers, cards, figures, bars, ranges, the cell graph
src/view_overview.zig the overview
src/view_procs.zig the processes page
src/view_table.zig the process table
src/view_cpu.zig CPU
src/view_memory.zig memory and GPU
src/view_io.zig disk and network
src/view_apps.zig apps
src/view_about.zig About This Mac
src/view_sheet.zig the filter menu, the inspector, the quit sheets
src/view_graff.zig the graff bubble and what it tells graff
src/graff.zig running graff one-shot, reading its answer
src/tests_select.zig marking, quitting together, the bubble (a fake graff)
src/theme.zig the colors, light and dark
src/tests.zig tests
src/tests_menubar.zig the panel and the readout
nanomon's own code is licensed under the MIT License. The parts derived
from CodeGraff (its colors, and the graff features, which work only with graff)
keep CodeGraff's original modified AGPLv3, kept verbatim in
third_party/licenses; see
third-party notices.
- nanoui: windows, layout, text, the GPU and CPU renderers, and the Tester that draws every page offscreen. nanoui itself owes its design to mygo by egoist, and to GPUI by Zed Industries.
- egoist's activity monitor designs: the sidebar of pages with live figures, and graphs drawn as columns of dots, which nanomon draws as square cells.
- Apple's Activity Monitor: what's worth showing, and how: memory split into app, wired, compressed and cached, % CPU as a share of one core, and the quit sheet. rea helped show how Activity Monitor gets its figures.
- Harness: the Codegraff colors, and graff, the agent in the bubble.
Built in Zig 0.17 on nanoui · MIT · third-party notices


