Repository navigation
Releases: eslamfaisal/mac-cleaner
Release list
v1.6.0 — Audit remediation: safer deletes, verified duplicates, hardened server
A security and correctness release. Six areas of the app were audited — the delete path, the local HTTP surface, how every location is classified, performance, the interface, and the build pipeline. 38 findings were raised and each was re-checked against the real code by an independent reviewer trying to disprove it; 31 survived and are fixed here.
Nothing about how you use the app has changed, except one thing noted at the bottom if you run it from source.
Files that could have been lost
The inside of app bundles, Logic projects, VMs and document packages is no longer offered. macOS shows a .app, .logicx, .sparsebundle, .key or .pvm as a single file, but underneath each is a folder. The scanner used to walk into them and list their contents as separate items — so a 300 MB audio take inside a Logic project appeared as "a big media file", and deleting it left the project opening with a silently missing track. Worse, two apps built on the same Electron version contain byte-identical framework binaries, so they were clustering as duplicates behind a one-click "delete every copy but one". Each package is now a single row: all of it or none of it.
Duplicates are now proven identical before one is deleted. Sets were decided from 384 KB sampled out of each file — the first 256 KB, 64 KB from the middle, 64 KB from the end — and the row then stated "identical content" as fact. On a 4 GB file that is 0.009% of it. Two files can match at all three points and differ everywhere in between: VM disk images cloned from a template, preallocated database files, partly-downloaded files. This was reproduced with two 8 MB files differing at a single byte 3 MB in — the app called them identical and offered one for permanent deletion. Now the entire contents of both files are read and compared before anything is removed, and a copy that only looked identical is refused with the reason shown.
The suggested copy to keep was the wrong one. The keeper was whichever copy was newest, but a re-downloaded file is always the newest — so Downloads/2024-lease.pdf was kept and Documents/Contracts/2024-lease.pdf was the one offered for deletion. The filed copy now wins over one sitting in Downloads, Desktop or the Trash, and a name ending in "copy" or "(2)" loses.
Lightroom. The app offered every folder in your Lightroom directory while describing all of them as "generated JPEG previews that Lightroom regenerates" — including the catalog's edit data (masking, healing, AI denoise), your catalog backups, and cloud-synced originals. None of those come back. Only actual previews are offered now; smart previews and catalog backups get their own rows with honest descriptions.
A Downloads folder synced to Dropbox or iCloud. If ~/Downloads or ~/Desktop is a link into a cloud folder — a common "keep my Downloads in Dropbox" setup — deleting from it removed the file from the cloud, and from every other device. The app has always refused to touch cloud folders; one type of row skipped the check. Fixed.
Emptying the Trash could stop halfway. A file with two dots in its name (Talk with....mp4) was rejected as an invalid path, which aborted the rest of the operation — some files gone, others untouched, one confusing error. Related: one locked or in-use file no longer abandons everything after it, and a partly-completed cleanup is now reported as failed rather than counted as a success.
Things the app claimed without checking
- Old JetBrains / Android Studio folders were declared dead purely by comparing version numbers in folder names — so running IntelliJ 2024.3 alongside 2025.2 (normal when a project needs an older version) got you "nothing reads it any more" about the IDE you had open, and deleting reset it to factory defaults. It now checks whether the version has actually been used in the last 90 days. Local file history is never deleted.
- App data was labelled "left behind after an uninstall" for apps that were still installed, if the app lived in a subfolder of Applications (Bitdefender, and similar), or if it writes to a subfolder rather than its top-level folder. On this machine that was 13 of 185 folders.
- "Downloads you have not opened in 3 months" was measured by when files were last changed. Opening a file does not change it. The wording now says what it measures.
- A scan that could not read everything looked complete. A long scan of a large folder was killed after 45 seconds and the progress bar still reached 100%. The timer now watches whether work is actually progressing, and anything skipped or refused by macOS is reported next to the item count.
Security
- The dashboard is no longer embeddable in another web page. It could be loaded invisibly inside a hostile page, where your own clicks would drive it — as far as a chain that empties your Trash permanently.
- Every request now needs the session token. Loading the page and reading the full inventory of your files previously needed nothing at all.
- Force-quitting the app really stops it. Quitting mid-cleanup used to leave the background process deleting to completion, with Full Disk Access, after the window was gone — and its port stayed open until you rebooted.
- A permission prompt can no longer freeze the app. Checking for Full Disk Access could occupy every available file-system thread, after which nothing that touches disk would start again — deletes included.
- The bundled Node runtime is verified before it is signed. It was downloaded with no integrity check at all, then signed with the developer certificate and notarized. It is now confirmed to be Apple-signed by the Node.js Foundation, or the build refuses to continue.
- Thumbnail previews are cached in a private folder instead of a world-readable temporary one, and are cleaned up.
Interface
- The risky label failed accessibility contrast (3.3:1) — the most consequential tier was the hardest to read. Every colour pair now passes WCAG AA.
- Checkboxes rendered as light-grey squares on the dark background, and disabled ones looked identical to enabled ones.
- The confirm dialog now takes keyboard focus properly, starting on Cancel — never on the delete button — and you can no longer tab into the page behind it and change the selection while it is open.
- Scan progress is announced to screen readers; every row's controls are named after that row rather than all reading "Select item".
- The window no longer overflows between roughly 760 and 1065 pixels wide.
- Disk figures now use the same units as Finder, so the gauge agrees with System Settings instead of reading about 7% low.
Speed
- A large cleanup no longer breaks the live connection at the moment it starts — which used to leave the app saying "Cleaning…" forever with no summary.
- The list stopped rewriting itself four times a second when nothing had changed: identical redraws now touch the page zero times.
- Files with no preview are no longer re-rendered on every redraw.
If you run from source
npm start now prints a URL containing a one-time token — open that, not http://127.0.0.1:4545, which is refused on purpose. Your browser exchanges the token for a cookie immediately and the address bar returns to normal. The .app handles this by itself; nothing changes for you if you use the DMG.
v1.5.0 — Normal mode: a Mac cleaner for everyone, not just developers
Mac Cleaner now has two modes. Normal mode is a brand-new interface for anyone who is not a developer: twelve plain-language categories, Move-to-Trash by default, and every row explained in a sentence. Developer mode is the tool you already know, unchanged. You pick once on first launch and can switch any time from the toolbar.
Universal build: one download runs natively on Apple Silicon and Intel.
Normal mode
- Twelve categories instead of twenty-six technical ones: System Junk, Trash, Large & Old Files, Duplicate Files, Downloads & Installers, Unused Apps & Leftovers, App Caches, Mail & Message Attachments, Photos/Music & Media, Backups & Updates, Cloud Files on This Mac, and one collapsed card for everything developer-related.
- Move to Trash by default, so a mistake is one "Put Back" away. The counter says what is in the Trash versus what is actually freed — emptying the Trash is what returns the space, and the app now says so.
- A receipt after every clean: what went, what failed, and one click to the failures.
- Keep list: pin any row you never want offered again. It stays kept across scans, and one chip brings them all back.
Where your space actually went
- Cloud Files on This Mac — iCloud Drive, Dropbox, OneDrive and Google Drive files kept on the disk. Often the largest folder on a normal Mac, and previously invisible. Read-only: freeing them is done from Finder, and the app explains how.
- Mail's downloaded messages are listed (read-only) instead of a category promising "Mail" and showing a few hundred megabytes.
- Screensaver and wallpaper videos macOS downloads in the background (20–65 GB on many Macs).
- Old app updates left behind by Sparkle, Google's updater, Microsoft AutoUpdate and Zoom.
- The Trash on every disk, not just the boot drive.
- iPhone and iPad backups now say which device and when, instead of a 40-character identifier.
Duplicate files, rebuilt
- Finds duplicates from 1 MB (was 5 MB), so photos and documents saved twice show up.
- A thumbnail for every set — click for a bigger preview. A duplicate photo is judged with your eyes, not its filename.
- Each set says what deleting the extras frees, not what the set occupies.
- One copy is always kept. The set checkbox skips the newest file, the confirm dialog marks it, and the server refuses any request that would delete every copy of a set.
See exactly what you are deleting
Every row shows its real path on its own line, and clicking it opens the folder in Finder with the file selected. The confirmation dialog lists paths too.
Permissions
Full Disk Access now explains what is locked ("56 places on your disk cannot be measured yet"), and offers a Quit & Reopen button — macOS only applies the permission to an app that starts after you grant it, which is the step people got stuck on. A fresh Mac with no Safari, Mail or backup data is no longer misread as "already granted".
Fixes
Two independent audit rounds found — and this release fixes — a set of defects worth naming:
- The headline number was wrong. Space that can never be freed (your Photos library, Time Machine snapshots) was counted in "you can free up". It is now reported separately, and every total in the app follows one rule.
- Ticking a whole duplicates category could delete every copy of every set.
- The main scan could walk inside a Photos or Final Cut library and offer its internals as ordinary files.
- Rescanning doubled every total because the previous scan's rows were never cleared.
- "Select all safe" did nothing while a safety filter was active, and froze the interface on large selections (measured 96 ms per frame, now 0.7 ms).
- A single unreadable folder could freeze the app. Three causes, all fixed: iCloud enumeration on the main thread, hashing a cloud placeholder (which makes macOS download it first), and any remaining stall — a watchdog now skips a folder that takes longer than 45 seconds and keeps going.
- iPhone backup labels, Zoom's data row, uninstalled-app leftovers and JetBrains caches all reported wrong sizes or vanished entirely; each is fixed.
v1.4.2 — Signed and notarized: no more Gatekeeper warning
No more Gatekeeper warning, and no Node.js to install. Downloads are now signed with an Apple Developer ID certificate and notarized by Apple: double-click and it opens.
What changed
Previous releases were ad-hoc signed, so macOS blocked the first launch. The workaround the docs gave — right-click → Open — stopped working in macOS 15; there was no path left except System Settings → Privacy & Security → Open Anyway. That's fixed.
Three things it took:
- Sign inside-out instead of
codesign --deep, which is deprecated and can't apply per-binary entitlements. - New
app/entitlements-node.plist. The hardened runtime that notarization requires blocks V8 from allocating executable memory, so withoutallow-jitandallow-unsigned-executable-memorythe bundled Node aborts on launch. - Sign the DMG itself. Stapling a notarization ticket onto an unsigned disk image isn't enough — Gatekeeper still reads it as
no usable signature.
The .app is notarized and stapled before it goes into the DMG, so it stays valid when dragged out of the DMG or launched with no network.
Upgrading from 1.4.1
Grant Full Disk Access to Mac Cleaner again after replacing the app. macOS ties that permission to the code signature, and the signature changed from ad-hoc to Developer ID. This is a one-time step — future updates keep the same identity, so the grant sticks from now on.
Verify it yourself
spctl -a -vvv "/Applications/Mac Cleaner.app"
# accepted
# source=Notarized Developer ID
# origin=Developer ID Application: Eslam Faisal (CDRX96YDNZ)Downloads
| File | For |
|---|---|
Mac.Cleaner.dmg |
Universal — Apple Silicon and Intel |
Mac.Cleaner-AppleSilicon.dmg |
Apple Silicon only (smaller) |
Mac.Cleaner-Intel.dmg |
Intel only (smaller) |
No behaviour changes to scanning or deletion in this release.
v1.4.1 — Filters you can see, safe totals that add up
Follow-up to v1.4.0 — two fixes for numbers that didn't reconcile on screen.
Filters are now visible and clearable
The safety tiles are a toggle filter and scroll out of view in a category, so a group could read 0 items (3 filtered out) while its card showed a size, with nothing naming the filter that hid them.
- A chip appears in the controls row whenever items are hidden:
1,286 hidden by: safe · ≥ 10 MB — clear ✕— one click resets the safety, size and text filters together. - The per-group label is now
(3 filtered out — show), clickable, and its tooltip names the active filters.
The safe total now explains itself
safe 77.3 GB next to Select all safe (35.3 GB) looked like a bug. It isn't — bulk-select deliberately skips build artifacts inside recently-touched projects, the Trash (irreversible) and read-only items. Now it says so:
77.3 GB · safe
35.3 GB in one click — 41.9 GB in active projects, 117 MB in Trash (permanent) must be picked by hand
Selection behaviour is unchanged — only the explanation is new.
Everything from v1.4.0 (old & unused apps, superseded IDE versions, uninstall leftovers, creative categories) is included.
Downloads
| File | For |
|---|---|
Mac.Cleaner.dmg |
Universal — Apple Silicon and Intel (recommended) |
Mac.Cleaner-AppleSilicon.dmg |
Apple Silicon only (smaller) |
Mac.Cleaner-Intel.dmg |
Intel only (smaller) |
macOS 12+. Ad-hoc signed — right-click → Open on first launch.
v1.4.0 — Old & unused apps, uninstall leftovers, creative cleanup
Mac Cleaner now cleans up after apps, not just caches — the apps you stopped opening, the old versions of the ones you kept, and the data left behind by everything you uninstalled.
⏳ Applications — Old & Unused
- Apps you stopped using. Three signals are combined per bundle: Spotlight (
kMDItemLastUsedDate, version, bundle id), the mtime of~/Library/Preferences/<bundle-id>.plist, and the running-process list. An app is listed only when every signal is 6+ months old — and a running app is never listed. - Duplicate installs. Same bundle id installed twice — the older copy is listed, the newer one is named in the reason.
- Superseded IDE versions. Android Studio and JetBrains IDEs keep one settings/cache folder per version (
AndroidStudio2025.2.3,IntelliJIdea2024.3,DataGrip2025.3…) and never remove them after an upgrade. Only folders that are not the newest of their product are listed, badged with the version that replaced them. - Old JetBrains Toolbox builds — the previous builds Toolbox keeps after updating.
- Forgotten macOS installers —
Install macOS *.appis 12–16 GB and usually sits there long after the upgrade ran.
👻 Leftovers from Uninstalled Apps
Dragging an app to the Trash leaves its ~/Library/Application Support and ~/Library/Containers data behind forever. Those folders are now matched against the names and bundle ids of every installed app, and listed when nothing claims them. Matching is deliberately loose so ambiguous names stay out; macOS daemons and folders touched in the last 60 days are skipped.
🎬 Cleanup for the rest of the Mac world
- Creative — Video · Photo · Audio · Design: Adobe media cache (Premiere / After Effects / Audition), DaVinci Resolve render cache and proxies, Final Cut backups, Camera Raw cache, Lightroom previews, Capture One, Photoshop autorecover, Sketch, Figma, Blender, Logic and GarageBand sound libraries.
- Games: Steam libraries, shader cache and partial downloads.
- Personal: Podcast downloads, Apple Books downloads, Messages attachments.
- App caches: Teams, Word, Outlook, Slack, OneDrive, Google Drive, plus an iCloud Drive explainer.
26 categories total.
Safety
/Applications became deletable, so the server-side validator was tightened for it: only whole .app bundles at the top level of /Applications pass — nothing else under that path can be deleted, and the existing symlink / realpath TOCTOU checks still run on every target. Apple's own apps and the running Mac Cleaner bundle are never listed. As always: scanning is read-only, deletion is Trash-first, and nothing goes without your click.
Downloads
| File | For |
|---|---|
Mac.Cleaner.dmg |
Universal — Apple Silicon and Intel (recommended) |
Mac.Cleaner-AppleSilicon.dmg |
Apple Silicon only (smaller) |
Mac.Cleaner-Intel.dmg |
Intel only (smaller) |
macOS 12+. The build is ad-hoc signed — right-click → Open on first launch.
Mac Cleaner v1.3.0
Universal binary — one app for every Mac 🎉
The main DMG is now a Universal 2 binary: native code for Apple Silicon (M1/M2/M3/M4) and Intel in one app. macOS picks the right one automatically — no Rosetta needed.
Which download?
| Asset | Runs on | Size |
|---|---|---|
Mac.Cleaner.dmg (recommended) |
Every Mac (macOS 12+) | ~80 MB |
Mac.Cleaner-AppleSilicon.dmg |
Apple Silicon only | ~41 MB |
Mac.Cleaner-Intel.dmg |
Intel only | ~41 MB |
Not sure? → About This Mac ("Chip: Apple M…" = Apple Silicon, "Processor: Intel…" = Intel) — or just take the universal DMG.
Also in this release
build-app.sh --arch universal|arm64|x64|all— build any variant yourself- Fixed: pre-1.3.0 DMGs were Apple Silicon-only ("Bad CPU type in executable" on Intel)
- README: new Apple Silicon vs Intel guide + troubleshooting
- Landing page: SEO overhaul (Open Graph, Twitter cards, JSON-LD, sitemap, robots.txt)
First launch: releases are ad-hoc signed — right-click the app → Open → Open (once).
Mac Cleaner v1.2.0
Now for every Mac user — not just developers.
⬇ Download & install
Download Mac.Cleaner.dmg below (41 MB, Apple Silicon, macOS 12+), drag to Applications.
First launch: right-click → Open → Open (ad-hoc signed), or
xattr -d com.apple.quarantine "/Applications/Mac Cleaner.app".
✨ New in 1.2.0
- 👯 Duplicate Files — identical files (same size and checksum) clustered into sets. One click selects every copy except the newest, so you always keep one.
- 🐋 Biggest Files — every file ≥ 50 MB across your whole home folder, now including Movies, Music and Pictures, sorted largest-first. Photos/Music libraries are shown but protected (read-only).
- 🏠 Personal & Media — screenshots piling up on the Desktop, Downloads untouched for 90+ days, Mail attachment copies.
- Smarter big-file safety: Downloads leftovers are caution (re-downloadable), personal files risky, media libraries never deletable.
Still: 100% local · zero dependencies · always manual deletion · per-boot token · open source (MIT).
🔐 Verify
shasum -a 256 Mac.Cleaner.dmg
# 4d7de6c1b81f551d5dc58d23fd441ac346ec54b1505e4f0a081de114f81a40dc
Full docs: README · Site: https://eslamfaisal.github.io/mac-cleaner/
Mac Cleaner v1.1.0
Mac Cleaner — find what is eating your disk, delete only what you choose.
A zero-dependency, 100% local disk cleaner for developer Macs. Native app and web dashboard, same code.
⬇ Download & install
- Download Mac.Cleaner.dmg below (41M, Apple Silicon, macOS 12+).
- Open it and drag Mac Cleaner into Applications.
- First launch: right-click the app → Open → Open. (Ad-hoc signed — no paid Apple certificate — so Gatekeeper asks once.)
Terminal alternative:xattr -d com.apple.quarantine "/Applications/Mac Cleaner.app" - Optional: grant Full Disk Access to Mac Cleaner when prompted — unlocks Safari/Mail/backup scanning. The app walks you through it and detects the grant live.
Intel Mac? Run from source (
npm start) or build with an x64 Node:NODE_BIN=/path/to/node ./build-app.sh.
🧭 How to use
- Start Scan — category cards and the disk gauge fill in live.
- Browse the overview; each card shows size, item count and a safety split (🟢 safe / 🟡 caution / 🔴 risky).
- Click a card to drill into its files. Hover a row for why it's cleanable and how it comes back.
- Select items (or Select all safe), pick Move to Trash or Delete permanently, then Clean. Risky items need an explicit confirmation.
Root-owned System Data is shown read-only with the exact terminal command instead of a delete button. Nothing is ever deleted automatically.
✨ What's in this release
- System Data insights — old iOS device updates, Homebrew cache, diagnostic reports, update payloads, Time Machine local snapshots, system caches, sized and explained.
- Full Disk Access onboarding — three-step guided grant with live detection.
- CleanMyMac-style UI — category card grid with safety-split bars, drill-down detail views, subtle animations (respects reduced motion).
- Native app — Swift WKWebView shell with a bundled Node runtime; FDA is attributed to Mac Cleaner itself. The same code still runs as a local web dashboard from source.
- Hardened deletes: root-owned items rejected both when queued and at delete validation.
100% local · zero dependencies · per-boot token · path validation · open source (MIT).
🔐 Verify your download
shasum -a 256 Mac.Cleaner.dmg
# 4180a7db1e67df4b83af168a5364e6e230f5886b5da3bf36b9df2ce3deb3e8d3
Full docs: README