Releases: trsdn/OpenZombr
Release list
OpenZombr 0.4.2
Changed
- All targets build in the Swift 6 language mode. Two pre-Sendable Foundation types
that are documented thread-safe when used the way this codebase uses them — a
FileManagerinstance never mutated after creation, a date formatter configured once and
only read from — are markednonisolated(unsafe)at their one call site each. No
concurrency behaviour changes; the compiler now checks the claims the code already made
in comments, such asIdleTracker's internal locking.
Documentation
- Audit finding 5 is confirmed intentional, not a gap. A parent whose session children
are a rotating cast of short-lived helpers (sh -c …,curl, leak plumbing) rather than
one persistent process can never produce a readable CPU idle signal, because
IdleTrackerneeds to see the same pid twice. That keeps the parent's session signal
permanently unreadable, which AGENTS.md already requires the emergency override to
respect.TransientSessionChildTestspins this down; no behavior changed.
OpenZombr 0.4.1
Added
- The dropdown menu opens with the application's name and version, above everything else.
OpenZombr 0.4.0
Fixed
-
Sampling no longer runs on the main actor. Reading the process table,
proc_pidinfo
per session child and the tails of session logs happens on a background thread, so a
slow sample on a machine that is out of slots can no longer freeze the menu bar item or an
open menu. A poll that finds another one still sampling is skipped rather than queued, and
a manual cleanup waits for a sample in flight instead of racing it.IdleTrackeris now
internally synchronised, since it is no longer confined to the main actor. -
The denylist no longer fails open where it is blind. Executable paths were resolved
for the 20 largest offenders only, so a path-based deny entry could not match the rest,
which were compared on their 16-characterp_commalone. Every offender is now resolved,
and a parent whose path is still unknown is refused whenever a denylist is configured. -
A target that exits between the liveness check and the identity check is reported as
gone, not asidentity-changed. A successful SIGTERM in that window used to be logged
as a failure. A live process whose identity cannot be read is still never signalled. -
Session logs untouched for longer than the reader's horizon are no longer opened on
every poll. Nothing can be written after a file's modification time, so the file cannot
hold newer activity. This bounds the per-poll cost on a directory that accumulates logs;
mtimeis still never used to make a session look recent. -
"Jetzt aufräumen" no longer acts on a stored snapshot older than 60 s when the fresh
sample fails. Identity verification covers pid reuse but not a session that became
active since; the snapshot can be an hour old. The run is refused with a visible message. -
Time the Mac spent asleep no longer counts as CPU-idle time. A session worked in at
midnight read as eight hours idle at breakfast. The tracker now compares the wall clock
with the awake clock and subtracts the difference. Both idle signals still have to agree. -
Candidate ordering is a strict weak ordering. An unreadable log age tied with an age of
0 without falling through to the zombie count, so the order depended on table layout. -
Log directories are read a bounded number of times per poll. More than 32 files that
need reading make the directory unknown — which protects the session — rather than
reading an unbounded amount or guessing from a subset.
Changed
- A plain allowlist pattern names a program, not a directory.
agencymatched any
executable under a folder whose name contained it. Patterns without a/are now matched
against the process name and executable file name; patterns with a/still match the
whole path. The denylist is unchanged and still vetoes on name or path.
Tests
- The path from
poll()to the destructive path is now covered at model level: the
confirmation gate, the cooldown, disabled auto-cleanup and the halted state.
OpenZombr v0.3.0 — Signed, notarized, and self-updating
- Releases are now Developer ID-signed and notarized. No more clearing the quarantine attribute by hand.
- In-app updates: a daily background check (can be switched off), "Nach Updates suchen …", and install-and-restart from the menu. An install waits for a running cleanup to finish, and automatic checks wait while the process table is critical.
- The emergency override keeps reaping until the machine is projected to be healthy, instead of one parent per run.
v0.2.0 and earlier are ad-hoc signed and cannot update themselves: install this release manually once. See CHANGELOG.md for details.
v0.2.0 — the log signal reads content, not mtime
The cleanup could never touch the process this app was built for.
The liveness guard read the log signal as the newest write in the session's --log-dir. That file was refreshed every 30 s by the leak's own telem flush: spawned detached child pid=… heartbeat — and each of those lines is one of the zombies to be cleaned up. The leaking wrapper was protected by its own leaking.
Measured on 2026-08-29: wrapper 86183, 17 h old, 1022 zombies at 39 % of kern.maxprocperuid, CPU idle for 7.6 h, log age reported as 4 s. Never offered as a candidate. Since both signals must agree, the log signal alone neutralised the whole cleanup feature.
The age is now derived from the log content: the tail is read backwards from EOF and the scan stops at the first line that is not a known heartbeat. The 253 MB log of that session is judged in 60 ms against a 60 s poll interval.
The safety direction is unchanged. Classification is a denylist, so an unrecognised format counts as real work and protects; a file with no parseable timestamp makes the whole directory unknown. The hard guards — PID 1, foreign uid, ancestry, zombie threshold, allowlist — are untouched.
Also in this release: --log-probe for auditing a log directory by hand, GitHub Actions for build, test and release, and install instructions.
130 tests. See CHANGELOG.md for the full entry and the measurements behind each decision.
This build is ad-hoc signed, not notarised. Verify the checksum against checksum.txt, then:
xattr -dr com.apple.quarantine /Applications/OpenZombr.appGiven that this app sends SIGKILL on your behalf, building from source (make install) is the better option. Auto-cleanup is off by default — watch --idle-watch on your own machine first.