Skip to content

Releases: cacheout-app/cacheout

Cacheout v2.1.7

Choose a tag to compare

@acebytes acebytes released this 21 Jun 04:58

Security

  • DaemonMode PID file is no longer susceptible to a symlink-swap overwrite. The daemon created its PID file with open(O_WRONLY | O_CREAT | O_CLOEXEC) — without O_NOFOLLOW, an attacker with write access to the parent directory could plant a symlink at the PID path and redirect the open to truncate or overwrite an arbitrary file. Now opens with O_NOFOLLOW and an explicit 0o600 mode so the kernel refuses to follow a symlink at the final component. (#416)
  • StatusSocket POSIX path bridging hardened. The open(2) calls in StatusSocket.swift (state-directory hardening and config-validation read) now bridge their paths through URL(fileURLWithPath:).withUnsafeFileSystemRepresentation instead of passing Swift Strings directly, bringing both call sites in line with the project's path-bridging standard. (#417)

Changed

  • SysctlJournal stale-entry and rollback index maps now build via reduce(into:) over state.entries.indices instead of incremental for-loop mutation. Behavior-preserving cleanup of revertStaleEntries() and performRollback(). (#414)

UX / Accessibility

  • Check For Updates button now carries a .help() tooltip that explains why it is disabled when update checking is unavailable (e.g. no appcast), removing ambiguity around the greyed-out control. (#415)

Cacheout v2.1.6

Choose a tag to compare

@acebytes acebytes released this 19 Jun 10:10

Maintenance release. Lands a CRITICAL TOCTOU security fix on the daemon state-directory setup (distinct from v2.1.5's chmod fix on the config-reload path), plus an a11y polish and a perf cleanup on the daemon hot path.

Security

  • DaemonMode state-directory permissions are no longer settable via symlink swap. The daemon hardens its state directory (~/Library/Application Support/Cacheout or --state-dir) by chmod'ing it to 0o700 after createDirectory — but the previous FileManager.setAttributes(ofItemAtPath:) follows symlinks, so an attacker with write access to the parent could swap the directory for a symlink between the two calls and redirect the chmod. Now opens the directory with open(O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC) and applies fchmod(0o700) to the resulting descriptor — the kernel can't be tricked into chmod'ing somewhere else. Distinct from v2.1.5's #346 which fixed a different chmod site on the config-reload path. (#410)

Changed

  • RecommendationEngine recommendation loops now use for proc in scanResult.processes where condition instead of scanResult.processes.filter { ... }.forEach. Avoids two intermediate array allocations on every recommendation pass — runs on the daemon hot path, so the saving compounds at long-running-daemon scale. (#407)

UX / Accessibility

  • Disk-usage bar and MenuBar stat pills now read as single coherent items in VoiceOver. Added .accessibilityElement(children: .combine) at the outermost modifier position on DiskUsageBar's outer frame and on MenuBarView's statPill so the entire visible card announces together. (#409)

Install

Download Cacheout-2.1.6.dmg below, or via Homebrew:

brew upgrade --cask cacheout

Cacheout v2.1.5

Choose a tag to compare

@acebytes acebytes released this 17 Jun 05:35

Maintenance release. Lands the DaemonMode TOCTOU security fix that was deferred from v2.1.4 due to a merge conflict, plus two small polish PRs.

Security

  • DaemonMode config load no longer susceptible to chmod/read TOCTOU. The previous path-based chmod(path, 0o600) followed by FileManager.contents(atPath:) could be redirected by a symlink swap between the two calls — an attacker with write access to the directory could trick the daemon into chmod'ing or reading an arbitrary file. Now opens the config with open(O_RDONLY | O_NOFOLLOW | O_CLOEXEC) once, applies fchmod(0o600) to the resulting descriptor, and reads via FileHandle.readToEnd(). Both the permission change and the read happen on the same fd so the kernel can't be tricked into operating on a substituted path. (#346)

Changed

  • CacheoutViewModel.selectedSize uses scanResults.lazy.filter(\.isSelected) instead of materializing selectedResults to compute a sum — saves the intermediate array allocation on every recomputation.
  • CacheoutViewModel.hasSelection uses contains(where:) on both scanResults and nodeModulesItems instead of comparing selectedNodeModulesSize > 0, which previously forced a full reduce over nodeModulesItems just to check whether any item was selected. Now short-circuits on the first match. (#404)

UX / Accessibility

  • Memory dashboard stat cards are now read as a single coherent item by VoiceOver. Added .accessibilityElement(children: .combine) to the two stat-card views in MemoryView so the title and value announce together. (#401)

Install

Download Cacheout-2.1.5.dmg below, or via Homebrew:

brew upgrade --cask cacheout

Cacheout v2.1.4

Choose a tag to compare

@acebytes acebytes released this 16 Jun 08:24

Maintenance release consolidating the TOCTOU/symlink security hardening landed since v2.1.3, plus a handful of accessibility and performance improvements.

Security

  • SysctlJournal no longer leaves the journal temp file world-readable during the umask window. The previous Data.write(to:) + setAttributes(0o600) sequence created the file under the process's default umask before tightening permissions. Now uses POSIX open(O_CREAT | O_WRONLY | O_EXCL | O_CLOEXEC, 0o600) so the file is born with the right mode and never observable to other local users. (#396)
  • CacheCleaner cleanup log is no longer vulnerable to TOCTOU symlink attacks. FileHandle(forWritingTo:) and the String.write(to:atomically:) fallback both follow symlinks. Replaced with open(O_CREAT | O_WRONLY | O_APPEND | O_NOFOLLOW | O_CLOEXEC, 0o600); the log directory is also explicitly created 0o700. (#391)
  • StatusSocket hardened against directory symlink swaps and validate_config file swaps. Directory chmod now uses open(O_NOFOLLOW | O_DIRECTORY) + fchmod instead of path-based setAttributes. Post-bind verification lstats the socket path and refuses to start if it isn't S_IFSOCK. handleValidateConfig opens with O_NOFOLLOW | O_CLOEXEC once, fstats the fd for type + size, and reads from the fd with a hard byte cap — closes the lstat → Data(contentsOf:) window where a swap could bypass the size cap or S_IFREG check. (#397)

Changed

  • Lazy-filter .count in MenuBarView and CleanConfirmation to avoid allocating intermediate arrays just to take a count. Minor allocator pressure win in views that re-render frequently. (#393)

UX / Accessibility

  • Disabled-state tooltips on MenuBar buttons (Scan, Quick Clean, Docker Prune) — .help() strings explain why each is disabled (in-progress, nothing to clean, etc.) instead of leaving the user to guess. (#379)
  • Docker Prune button in Settings now shows inline "Pruning…" text alongside the spinner and gets a .help() tooltip explaining the action. (#263)
  • NodeModulesSection header announces expanded/collapsed state to VoiceOver via .accessibilityValue. (#394)

Install

Download Cacheout-2.1.4.dmg below, or via Homebrew once the cask bumps:

brew upgrade --cask cacheout

Cacheout v2.1.2

Choose a tag to compare

@acebytes acebytes released this 17 May 23:02

Menubar icon hotfix

v2.1.1 correctly bundled menubar icon resources into the .app, but the source PNGs in Sources/Cacheout/Resources/ were a faint single-line "C" outline that rendered effectively invisible at 18×18 in template mode. The actual master artwork had been dropped from the repo at some point.

Fixed

  • Restored Resources/menubar-icon-master.PNG (the 2048×2048 master) and regenerated all four bundled variants — MenuBarIcon.png, MenuBarIcon@2x.png, MenuBarIconTemplate.png, MenuBarIconTemplate@2x.png — at the correct 18×18 / 36×36 sizes from the master. The Cacheout "C" now actually appears in the menubar.

Everything else from v2.1.0 + v2.1.1 (Sparkle auto-update bundling, helper daemon, security/perf/UX fixes) is unchanged.


Install

brew upgrade --cask cacheout

Or download Cacheout-2.1.2.dmg below and replace your /Applications/Cacheout.app.

Full Changelog: v2.1.1...v2.1.2

Cacheout v2.1.1

Choose a tag to compare

@acebytes acebytes released this 17 May 21:32

Hotfix for v2.1.0

v2.1.0 shipped with two regressions: the menubar icon resources and the CacheoutHelper privileged daemon were silently dropped from the .app bundle. v2.1.1 restores both.

Fixed

  • Menubar icon now displays again. Bundle.main.image(forResource: "MenuBarIconTemplate") couldn't find the PNG resources because scripts/bundle.sh was only copying Cacheout.icns. Now copies all four icon variants (regular + @2x, template + non-template) into Contents/Resources/.
  • CacheoutHelper daemon is now bundled at Contents/Library/LaunchDaemons/. The SMAppService.daemon(plistName:) registration relies on the binary + plist being inside the app bundle. Without them, the onboarding install-helper flow and --daemon autopilot silently broke. The helper is now signed before the outer app so codesign --verify --deep --strict passes.

Everything else in v2.1.0 (Sparkle auto-update bundling, HTTPS webhook enforcement, process pipe + PID-lock-fd-leak fixes, dictionary/scanner perf work, dynamic button labels + tooltips, empty states, accessibility) is unchanged.


Install

brew install --cask cacheout-app/cacheout/cacheout

Or download Cacheout-2.1.1.dmg below and drag to /Applications.

Full Changelog: v2.1.0...v2.1.1

Cacheout v2.1.0

Choose a tag to compare

@acebytes acebytes released this 17 May 15:53

⚠️ Known issue — please install v2.1.1 instead

v2.1.0 is missing the custom menubar icon resources and the CacheoutHelper daemon binary from the .app bundle (a scripts/bundle.sh regression). The menubar item can appear blank, and the install-helper onboarding flow / --daemon autopilot silently break. Both issues are fixed in v2.1.1

https://github.com/cacheout-app/cacheout/releases/tag/v2.1.1


What's new in v2.1.0

🚀 Auto-updates land

Sparkle.framework is now bundled inside Cacheout.app/Contents/Frameworks/ with the correct @executable_path/../Frameworks rpath, signed inner→outer with hardened runtime. Starting from this build, future releases will be installable via in-app auto-update instead of manual reinstall.

🎨 UX polish (Palette)

  • Dynamic Scanning… / Cleaning… labels on the Scan/Clean buttons with .help() tooltips explaining disabled states
  • .help() tooltips on per-process ellipsis menus
  • Visual empty state for the node_modules section (centered icon + callout)
  • Accessibility labels on icon-only controls so VoiceOver stops reading "Button"
  • Empty state for the Processes view

🛡️ Security & reliability (Sentinel)

  • HTTPS-only webhooksAutopilotConfigValidator and WebhookConfig.parse both reject http://
  • PID lock fd no longer leaks to childrenopen(2) now uses O_CLOEXEC and withUnsafeFileSystemRepresentation
  • Process pipe deadlock fixCacheoutViewModel.runCleanCommand (docker prune path) reads stdout before waitUntilExit()
  • Defense-in-depth pipe-deadlock guards across other process execution paths
  • Command injection vulnerabilities closed in runCleanCommand and CacheCategory.toolExists
  • Docker pruning now resolves via /usr/bin/env docker so Apple Silicon /opt/homebrew/bin users actually run it

⚡ Performance (Bolt)

  • JetsamHWM priority map built with reduce(into:) — no COW thrash
  • ProcessMemoryScanner uses a sliding-window TaskGroup
  • Scanner TaskGroups marked nonisolated so subtasks parallelize off the actor executor
  • Shared ByteCountFormatter/ISO8601DateFormatter instances + .lazy.filter.reduce
  • Batched @Published array mutations — one SwiftUI re-render per scan instead of N
  • CacheScanner enumerator prefetches .isRegularFileKey
  • NodeModulesScanner is truly concurrent with reduced syscalls
  • Blocking I/O offloaded via Task.detached so async @MainActor methods no longer stall the UI

🐛 Bug fixes

  • Process names no longer display as "unknown" in the main process list
  • Guard against empty arguments in runCleanCommand

Install

brew install --cask cacheout-app/cacheout/cacheout

Or download Cacheout-2.1.0.dmg below and drag to /Applications.

Full Changelog: v2.0.0...v2.1.0

Cacheout v2.0.0

Choose a tag to compare

@acebytes acebytes released this 13 Mar 22:34

Cacheout v2.0.0

Developer cache cleaner & memory manager for macOS.

Features

  • SSD Cache Cleaning — scan and clean 12 cache categories (Xcode, npm, pip, Homebrew, etc.)
  • Memory Pressure Monitoring — real-time telemetry with compressor tracking
  • Predictive Intelligence — memory exhaustion warnings before you run out
  • Intervention Engine — tiered memory recovery (purge, jetsam, sysctl tuning)
  • Privileged Helper — optional helper for advanced memory management
  • Headless Daemon Mode — long-running monitoring with Unix socket API
  • Autopilot Rules — automated actions with webhook/Telegram alerts
  • Docker System Prune — one-click Docker cleanup
  • CLI Interface — full JSON output for scripting and automation
  • MCP Server — available separately via pip install cacheout-mcp

Install

Download: Cacheout-2.0.0.dmg (signed & notarized)

Homebrew:

brew install --cask cacheout-app/tap/cacheout

Requirements

  • macOS 14 (Sonoma) or later
  • Apple Silicon or Intel

License

Sustainable Use License v1.0