A macOS menu bar app that provides intelligent, grouped views of memory (RAM) usage. Unlike Activity Monitor, Total Recall groups processes by logical application using built-in knowledge of how apps like Chrome, VS Code, Docker, Claude Code, and system services manage their process hierarchies.
The number Activity Monitor gives you is an app's footprint — what it is charged for, not what it occupies. macOS compresses pages it thinks you are done with and writes others out to swap, and on a loaded machine the gap between the two runs to gigabytes. Total Recall's whole reason to exist is showing you that gap, so it spends its entire palette on it — two colors, and no others:
| Means | Where it comes from | |
|---|---|---|
| 🟦 In RAM | Pages really sitting in physical memory right now | residentSize |
| 🟧 Compressed / swapped | Memory macOS has squeezed out of physical RAM — compressed in place or written to disk | Each process's compressed pages from its VM regions, with shared ones split between the processes mapping them, kept current by the kernel's per-process count (task_vm_info.compressed); physFootprint - residentSize for other users' processes |
Nothing else in the app is colored, so there is no palette to memorize. The Memory River names its two halves down the left gutter in those same two colors, and carries a written key beneath the bar whenever you are not hovering a segment; the detail panel repeats it against the actual numbers. A group with a deep amber stub is not costing you RAM right now — but going back to it means waiting on decompression or swap I/O, which is what "my machine has plenty of free memory and still feels slow" usually turns out to be.
- Smart process grouping: Chrome processes grouped by profile, Electron apps by bundle, Claude Code by workspace, Xcode Simulator devices with the apps running on them broken out, system daemons with human-readable explanations
- Memory River: proportional stacked bar across all your RAM — apps on the left, then segments measured from the kernel's own page counts: "Other" (app memory in no group, such as processes Total Recall can't inspect), "Kernel" (wired memory, never compressed or swapped), "Compressor" (the RAM the compressor pool occupies; hover to see how much it holds uncompressed), and "Available" (free pages plus file cache macOS can drop on demand, the same figure Activity Monitor reports). Apps are sized by their proportional share of RAM: memory a process shares with others, like system libraries, is split between them instead of counted once per process, so the apps add up to real app memory. The bar splits at a midline: blue segment widths above it are that in-RAM memory, and below it each app hangs an amber stub for what has been compressed or swapped out. Because width is already in-RAM memory, stub depth makes each rectangle's area the memory it holds — a deep stub is an app much larger than it looks, and two stubs of equal area hold equal memory wherever they sit in the bar. Hovering an app gives its in-RAM, compressed, and shared figures; with the Compressor drawn as its own segment, hovering it gives how much it holds and how much of that has been written to swap (the summary bar always shows both). A toolbar toggle ("Compressor in apps", on by default) shares the Compressor out to the apps instead: each gets an estimated slice of the pool's RAM in proportion to its compressed memory, so the bar shows apps rather than one large pool — each stub then holds only the part estimated to be in swap, at its uncompressed size, labelled "Swapped (estimated)". The readout marks both estimates with "~", since how much of any one app's compressed memory is in RAM rather than swap isn't something macOS reports. Groups too small to draw to scale fold into one "small groups" segment with its own stub (they keep their own rows in the list, and hovering the segment highlights them). A left gutter names the two halves in their own colors; the line beneath the bar carries the key until you hover a segment, when it becomes that segment's readout
- Swap activity: a group reading back from swap faster than 1 MB/s gets an amber "↓ 9 MB/s" badge on its row, in its river readout, and in the detail panel — the kernel's own per-process swap-in counter, so you see which app is waiting on disk right now
- Memory composition bars: per-process breakdown of resident (in RAM) vs compressed/swapped, in the same two colors, with both figures in the tooltip
- VM region breakdown: the Regions tab in the detail panel walks a single-process app's virtual address space and shows categories (
__TEXT, Heap, Anonymous, Stack, File-backed) with virtual size and resident pages; system and other-user processes show a clear access-denied explanation - Per-group sparklines: each group row charts its memory footprint over the last ~2 minutes, so a leak's slow ramp, a GC sawtooth, or a one-off step-change is recognizable by shape at a glance
- Menu bar presence: memory pressure indicator + used/total display, with compressed and swap totals in the dropdown
- Trend indicators: see which apps are growing or shrinking over time
- Sort by footprint or resident: understand total impact vs what's actually in RAM
- Instance merging: toggle between merged view (all Chrome instances as one) and separate view
- Safe kill actions: PID-verified termination with system process protection and audit logging
- Working directory context: see which directory Claude Code sessions are running in
- Process identification: resolves
node,python3, volta shims to what they're actually running (TypeScript Server, Webpack, MCP Server, etc.)
- macOS 26 (Tahoe) or later
- Xcode 17+ (for building from source)
- Apple Silicon or Intel Mac
Download the latest TotalRecall-<version>-arm64.dmg from Releases, open it, and drag Total Recall to Applications.
Total Recall is ad-hoc signed but not notarized (I don't have a paid Apple Developer account). The first time you launch it, macOS will show:
"Apple could not verify 'Total Recall.app' is free of malware..."
To allow it:
- Double-click the app → click Done on the warning.
- Open System Settings → Privacy & Security.
- Scroll to the security section — you'll see "'Total Recall' was blocked..." with an Open Anyway button.
- Click Open Anyway and authenticate.
- Launch the app again — you'll get one more confirmation dialog; click Open Anyway.
You only need to do this once. Alternatively, from Terminal:
xattr -d com.apple.quarantine "/Applications/Total Recall.app"git clone https://github.com/alecf/totalrecall.git
cd totalrecall
swift build
swift run TotalRecallThe diagnostic tool outputs the full classified process tree to the terminal:
swift run TotalRecallDiagswift testProcessMonitor (actor, background thread)
→ SystemProbe (libproc/sysctl/Mach API wrappers)
→ ClassifierRegistry → ChromeClassifier, ElectronClassifier,
ClaudeCodeClassifier, SimulatorClassifier,
SystemServicesClassifier, GenericClassifier
→ Returns [ProcessGroup] + SystemMemoryInfo
AppState (@MainActor, @Observable)
→ Receives classified groups, computes trends
→ Drives SwiftUI views
Views (SwiftUI)
→ MenuBarExtra(.menu) — compact menu bar dropdown
→ Window — Memory River, group list, detail panel
| Tier | API | Cost (886 PIDs) | Strategy |
|---|---|---|---|
| 0 | proc_listallpids |
0.17ms | Every cycle |
| 1 | proc_pid_rusage |
2.6ms | Every cycle |
| 2 | proc_pidinfo + proc_pidpath |
2.8ms | Cached per PID |
| 3 | KERN_PROCARGS2 |
9.2ms | Cached per PID |
| 4 | task_name_for_pid + TASK_VM_INFO (compressed, swap-ins) |
~12ms | Every cycle, same-user processes |
| 5 | PROC_PIDREGIONINFO walk (proportional share) |
~2s | 100ms budget per refresh, cached per PID |
Tiers 0–3 take ~15ms for 886 processes (0.3% of a 5-second interval). Tier 4 reads the kernel's per-process compressed count and lifetime swap-in counter; a task name port is enough, and macOS hands one out for any process owned by the same user without root or entitlements. Tier 5 walks each process's VM regions to split shared pages — resident and compressed — between the processes mapping them; it spends at most 100ms per refresh (2% of a core), stalest and never-measured processes first, with a larger budget until every process has been measured once, and skips processes younger than 15 seconds.
- Physical footprint (
phys_footprint): the primary metric, same as Activity Monitor's "Memory" column - Resident: pages currently in physical RAM — drawn in blue everywhere, labelled "In RAM"
- Non-resident: compressed in-place or swapped to disk — drawn in amber everywhere, labelled "Compressed". For same-user processes it is the process's compressed pages from the region walk, with each shared object's compressed pages split between the processes mapping it so every page counts once, moved between walks by the kernel's own compressed count (
task_vm_info.compressed, the figure Activity Monitor's "Compressed Memory" column shows); other users' processes fall back tophysFootprint - residentSize, which undercounts when resident size includes shared mappings. The count includes compressed pages macOS has written to swap: it swaps whole compressor segments mixing many processes' pages, so the in-RAM vs on-disk split exists only system-wide, not per process. A per-process figure can therefore only name one of them; it names the compressor because that is the half macOS reaches first, and because it is the word Activity Monitor uses for the same memory. The wording lives inTheme.residentLabel/Theme.nonResidentLabelso every view says it the same way - Swap activity: the kernel's lifetime swap-in counter (
task_vm_info.ledger_swapins), differenced between refreshes, gives an exact per-process rate of reads back from swap - Shared memory: deduplicated via RSHRD heuristic for group totals
- Proportional resident (PSS): private pages plus each shared object's resident pages split evenly across the processes mapping it — what sizes the Memory River's app segments, so they sum to real app memory instead of counting a shared library once per process
TotalRecall/ — App source
Models/ — ProcessSnapshot, ProcessGroup, SystemMemoryInfo
DataLayer/ — SystemProbe, ProcessMonitor, RedactionFilter, ProcessActions
Profiles/ — ProcessClassifier protocol + 6 classifiers
Theme/ — Colors, typography, spacing
Views/ — SwiftUI views
Utilities/ — Formatting, diagnostics, river layout, swap activity
TotalRecallDiag/ — CLI diagnostic tool
TotalRecallTests/ — Tests with synthetic fixtures
tools/ — Benchmark scripts
docs/ — PRD, research, plans
site/ — Marketing website (React + Vite) + Sparkle appcast
Released builds use Sparkle to check for new versions daily and offer them via the menu bar. The appcast feed lives at https://alecf.github.io/totalrecall/appcast.xml; updates are verified with EdDSA before installation. See docs/sparkle-setup.md for the one-time key generation steps required before the first signed release.
This project is in early development. Issues and PRs welcome.
MIT
