Releases: emdzej/bleach
Releases · emdzej/bleach
Release list
bleach 0.1.1
Install
# picks the right slice for your Mac (arm64 or x86_64)
curl -fsSL https://github.com/emdzej/bleach/releases/download/0.1.1/bleach-macos-$(uname -m).tar.gz | tar xz
sudo mv bleach /usr/local/bin/
# unsigned build — clear the Gatekeeper attribute on first run:
xattr -d com.apple.quarantine /usr/local/bin/bleachmacOS 13+. Three builds, pick one:
| Asset | For |
|---|---|
bleach-macos-arm64.tar.gz |
Apple silicon |
bleach-macos-x86_64.tar.gz |
Intel |
bleach-macos-universal.tar.gz |
both, if you need one binary for a fleet |
Verify with shasum -a 256 -c bleach-macos-<arch>.tar.gz.sha256.
Docs: https://bleach.emdzej.pl
Packaging only. The binary is functionally identical to 0.1.0.
Changed
- Releases now ship three builds —
arm64,x86_64anduniversal— instead
of universal alone, so a typical download is a third of the previous size. - Release binaries are stripped of local and debug symbols, which roughly
halves each one. Exported symbols are retained, so crash backtraces still
symbolise. - Assets no longer carry a version in the filename. It was redundant:
releases/download/<tag>/<name>already pins a version, and
releases/latest/download/<name>needs the name to be stable. - Install instructions use
uname -mto pick the matching slice. - Tarballs now include
CHANGELOG.md.
Download sizes, compressed:
| Asset | 0.1.0 | 0.1.1 |
|---|---|---|
| arm64 | — | 0.78 MB |
| x86_64 | — | 0.83 MB |
| universal | 2.30 MB | 1.61 MB |
bleach 0.1.0
Install
curl -fsSL https://github.com/emdzej/bleach/releases/download/0.1.0/bleach-macos-universal.tar.gz | tar xz
sudo mv bleach /usr/local/bin/
# unsigned build — clear the Gatekeeper attribute on first run:
xattr -d com.apple.quarantine /usr/local/bin/bleachUniversal binary (arm64 + x86_64), macOS 13+.
Verify with shasum -a 256 -c bleach-macos-universal.tar.gz.sha256.
Docs: https://bleach.emdzej.pl
First release.
Added
Scanning
bleach scan— read-only measurement and tiering of~/Library, the XDG
directories (~/.cache,~/.local/share,~/.local/state) and dot-directories
in~. Output sorted by size, with--tier,--min-size,--limit,
--by-owner,--explain,--jsonand--quiet.- Owner attribution from six independent inventory sources, each weighted by how
strongly it implies the owner is currently installed: running processes (1.00),
filesystem walk and Spotlight (0.95), Homebrew Caskroom/Cellar (0.90), launchd
agents and daemons (0.70), the LaunchServices registry (0.60), and installer
receipts (0.25). - Resolution strategies tried in order: alias table, name-is-a-bundle-ID, App
Group team-ID prefix, canonical name match, and version-stripped stem match. - Corroborating signals independent of the name: newest internal mtime,
Preferencesplist, saved application state, launchd job liveness, the
cross-location sibling set, and detection of a registered.appbundle inside
a candidate. - Five tiers —
PROTECTED,CACHE-SAFE,ORPHAN?,REVIEW,UNKNOWN— assigned
most-protective-first, with score used only as a tiebreak within a tier. - Version retention: among candidates reducing to the same stem, the newest is
kept and the rest marked superseded. Only demotes soft protections, so
"its owner is installed" can be overridden but "a process is running here"
cannot. - Overlap detection: a candidate containing other candidates is excluded from
totals and from every actionable tier, so no byte is counted twice. - Delegated cleanups — when a tool ships its own cleanup with its own retention
policy, bleach reports the size and the command rather than reimplementing it. - System roots (
/Library/{Application Support,Caches,Logs}) are measured and
reported with full evidence but never actionable, since acting would need root.
Interactive
bleach tui— full-screen browser with the evidence trail for the selected
row, tier filtering, search, and per-row selection. Hand-rolled ANSI/termios,
no dependencies.- Apply directly from the TUI via a confirmation modal that states the disposal
mode and its consequence before accepting input. The irreversible mode requires
typingdeletein full. - Terminal state is restored on every exit path, including
SIGINT,SIGTERM
andSIGHUP.
Plans and disposal
bleach plan— writes a reviewable JSON plan.CACHE-SAFEandORPHAN?by
default; other tiers must be requested. Paths outside$HOMEare never
included.bleach apply— dry run by default. Re-derives every safety property from
scratch rather than trusting the plan: path shape and standardisation,
$HOMEcontainment, existence, symlinks, current protection rules, running
processes, nested app bundles, tier actionability, growth since planning, and
read completeness. Validation runs again immediately before each disposal.- Three disposal modes, all held to identical validation:
quarantine(default,
an O(1) APFS rename into~/.local/state/bleach/quarantine),trash
(Finder's Trash), anddelete(irreversible). bleach restore— restores a batch or individual paths. Never overwrites a
recreated destination.bleach quarantine— lists batches;--purgewith--older-thanor--all
commits them permanently.- Append-only journal at
~/.local/state/bleach/journal.jsonl, written for every
mode includingdelete.
Configuration and extension
bleach rules— prints the effective rules;--initwrites an overlay to
~/.config/bleach/rules.yaml. List entries in an overlay are appended to the
defaults, so an override can only ever add protections, never remove one.- Plugin protocol v1 — plain executables speaking JSON on stdio, with
manifest,enumerateandresolvemodes. Plugins propose candidates and
evidence; they cannot act, cannot escape their declaredownsscope, cannot
weaken a core protection, and have their evidence weights clamped to ±10. - Example plugin
claude-code-sessions— decodes~/.claude/projectsdirectory
names back into project paths and reports sessions whose project no longer
exists. - Example plugin
opencode-sessions— splits~/.local/share/opencodeby role,
deliberately never proposing the live database as a candidate.
Project
- Documentation site at bleach.emdzej.pl.
- 39 tests, with
SafetyTests,PluginSecurityTestsandRemovalModeTests
covering the invariants that must not regress. - CI builds and tests on macOS; tagged releases publish a universal
(arm64+x86_64) binary with checksums.
Known limitations
- No size cache, so a full scan re-walks everything and takes about a minute.
REVIEWis a large, under-subdivided bucket.- The alias table is small by design — only entries verified against a real
LaunchServices registry are shipped, since a wrong alias mis-attributes a
directory silently. - UUID-named sandbox containers can never be attributed, as their metadata is
entitlement-protected, so they are permanently protected. - Release binaries are unsigned and unnotarised; Gatekeeper blocks the first run
until the quarantine attribute is cleared. - Untested below macOS 13.