Skip to content

Releases: ArangoGutierrez/coffee-bar

v0.3.1: the copy button copies, and the black screen is explained

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 19 Aug 18:48
v0.3.1

A fix release for three things v0.3.0 got wrong on the surface a new user meets first. Nothing
here changes what coffee-bar decides. All three are the app either refusing an action it had just
offered, or failing to explain something it had just done.

Fixed

  • The button offering to copy the command could not be clicked. On a build where the app
    cannot register a privileged helper, the control is titled "Copy the command instead", and its
    disabled clause covered exactly that case, so it named an action and refused it. On a Homebrew
    install that button is the only route to the command, so the user who most needed it got a dead
    end. It copies now.
  • Arming lid-closed mode blanked the screen with no explanation. Arming forces the display to
    sleep, which is required rather than incidental: a machine held awake with its lid shut must not
    keep the panel lit. Nothing said so, and the most visible consequence of the click read as a
    crash. The armed line now says the display was put to sleep and that the lid can be closed.
  • The panel said it had never checked for updates, and then said when it last checked. The
    timestamp was saved and the result was not, so every relaunch showed a real timestamp beside
    "has not looked yet". The result is now saved with it. Upgrading from 0.3.0 you will see one
    line saying the previous result was not recorded, until the next check falls due.

Also in this release

The published 0.3.0 notes claimed four things that were not true of the build they described, and
those are corrected. The README still announced v0.1.1 three releases on. SECURITY.md now
records that helper removal and the unsigned-build fallback were both exercised, with the limits
of that measurement stated beside it.

Install

The disk image below is the signed artifact: Developer ID, notarised by Apple, and stapled, so it
opens on a machine that has never been online.

Homebrew builds from source. That copy is ad-hoc signed, Gatekeeper does not accept it, and it
cannot register the privileged helper, so lid-closed mode there is armed from the terminal. Both
routes ship 0.3.1.

Requirements

  • macOS 14.0 or later.
  • Apple silicon only. lipo -archs reports arm64. This is not a universal binary and an Intel
    Mac cannot run it.

Verifying the download

shasum -a 256 coffee-bar-0.3.1.dmg
# ca72a571f5595da27d377bbb69ed0fe3b20869bcc9823caeb490fbc2afc2badd

spctl --assess --type open --context context:primary-signature -vv coffee-bar-0.3.1.dmg
# accepted
# source=Notarized Developer ID

Size: 1059046 bytes.

v0.3.0: lid-closed mode is a button

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 19 Aug 13:41
v0.3.0
838a1a8

Lid-closed mode used to mean typing a sudo command. In 0.3.0 it is a control in Preferences,
backed by a privileged helper that macOS installs and that you can take back off from the same
window.

Lid-closed mode

coffee-bar can now register a small root helper through SMAppService. The XPC channel between
the app and that helper is pinned in both directions by Team ID and bundle ID, so each end refuses
to talk to anything else.

macOS shows no prompt for a daemon registration. The first time you arm it, approval is a switch
you turn on in System Settings, General, Login Items and Extensions, and then you arm again. There
is still no password, ever.

Removing the helper releases the sleep hold before it unregisters, in that order, so the machine
cannot be left held awake by a helper that is no longer there. macOS keeps the app's entry in Login
Items and Extensions after removal, because the helper's plist ships inside the bundle.

Arming puts the display to sleep immediately. That is deliberate: it is what makes the machine
ready for the lid to close.

An unsigned local build cannot register a helper, so it does not offer to. It names the sudo
command instead. A Homebrew install is ad-hoc signed and takes that same path.

Also in this release

  • A quick start the first time the window opens, covering agent tools, the battery floor and the
    display hold.
  • coffee-bar can open at login. Ticking it writes one file to your home folder and nothing else.
  • A check for updates. It asks this project's site which version is current, at most once a day
    when coffee-bar starts, and whenever you press Check now. It carries no identifier, downloads no
    update, and never replaces coffee-bar. The privacy page says exactly what the request contains.
  • An agent can read coffee-bar's own state over the same unix socket it already posts hook events
    to.
  • The lid-closed hold is a setting rather than a fixed 30 minutes. On battery it still ends at the
    battery floor.
  • Keeping the Mac awake now also keeps it answering, so an agent is not awake and unreachable.

Install

The disk image on this page is the signed artifact: Developer ID, notarised by Apple, and stapled,
so it opens on a machine that has never been online.

A Homebrew or source build is ad-hoc signed. Gatekeeper does not accept it, and it cannot use the
privileged helper. That is not a fallback path, it is a different one, and it is documented.

Requirements

  • macOS 14.0 or later.
  • Apple silicon only. lipo -archs reports arm64. This is not a universal binary and an Intel
    Mac cannot run it.

Verifying the download

shasum -a 256 coffee-bar-0.3.0.dmg
# 61009669234d891418bfd367289a95cb7fed85ab407711ba1038bbdde3fc441d

spctl --assess --type open --context context:primary-signature -vv coffee-bar-0.3.0.dmg
# accepted
# source=Notarized Developer ID

Size: 1054199 bytes.

v0.2.2 — a suite you can trust under load

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 11 Aug 14:53
v0.2.2
bcbf77d

This release is about trusting the suite that certifies this app. Nothing here
changes what coffee-bar does for you.

Seven tests could fail on a loaded machine while the code they covered was
correct. A suite that cries wolf is one you stop reading, and that is how a real
defect ships behind nine hundred green checks. Five root causes, each traced to
a specific line and each fix mutation-checked — delete the fix and the guard
must go red.

A test helper published its state non-atomically. It wrote with
fopen(path, "w"), which truncates a file to zero bytes before writing, on a
50 ms cycle — so a reader could catch it empty. Measured at 71 empty reads in
219,902 samples. Worse for the crash test, which reads after a SIGKILL: a kill
landing inside that window left the file empty permanently. The helper now
writes to a sibling path and renames over the target, so a reader sees the old
report or the new one and never neither. (#84)

A file-descriptor bound sat below its own noise floor. The leak guard
allowed a delta of 20 over 40 spawns, but that count is process-global and the
suite's parallel ramp alone measured 16 to 22 — the bound was under the noise
and tripped on innocent runs. Now 200 iterations against a bound of 100: clear
of the worst observed ramp by 4.5x, and still a factor of four under the leak it
exists to catch. (#57)

Two hook-shim tests asserted a guarantee the shim does not make. The shim
gives up after one second and exits silently, by design — a lost confirmation is
not worth a diagnostic on every tool call. Under load that budget expired and
the tests failed on behaviour that had not changed. The budget is now resolvable
and the tests raise it. (#90)

Three tests built disk images at the same time. Their file declares no
suite, so they ran concurrently, and concurrent hdiutil create fails with
Resource busy. All three started within 0.93 s of each other and ran about 45 s
concurrently. That cycle is now serialised: pairwise overlap went from 96 s to
zero. (#94)

A listener test pinned one exit code where two were correct. 52 and 55
both describe a socket accepting a post and then dropping it; which one arrives
depends on where the drop lands relative to the write. It now accepts exactly
those two, and still fails on delivery or refusal. (#94)

The one production change

COFFEE_BAR_SHIM_TIMEOUT_SECONDS sets the hook shim's total run budget, so
tests can raise it above what a loaded machine costs. Absent, unparseable, zero,
negative, non-finite, or above the five-second ceiling all fall back to the
shipped default of one second. Unless you set it deliberately, behaviour is
byte-for-byte what 0.2.1 did.

Install

Downloadcoffee-bar-0.2.2.dmg below is signed with a Developer ID,
notarised by Apple, and stapled. spctl accepts it with
source=Notarized Developer ID, and stapler validate passes on the disk image
and on the app inside it.

Homebrewbrew install coffee-bar builds from source, which is a
different artifact: ad-hoc signed, carrying no Developer ID, and Gatekeeper
does not accept it the way it accepts the download. The tap formula is bumped
separately from this release; until that lands, brew installs 0.2.1. Use the
disk image above if you want the signed, notarised build.

Fact Value
File coffee-bar-0.2.2.dmg
Size 860341 bytes
SHA-256 21839e1612b67a845943102b4737d4cd2f3984d5facee4e428a992774e08331b
Architecture Apple silicon (arm64) only
Minimum macOS 14.0
Signature Developer ID Application, team 85FN4Z37V8

This is not a universal binary. lipo -archs on the shipped binary reports
arm64 alone, so an Intel Mac cannot run it.

Verify the download before you open it:

shasum -a 256 coffee-bar-0.2.2.dmg
spctl -a -t open --context context:primary-signature -vv coffee-bar-0.2.2.dmg

What is not claimed: that a first launch with no network succeeds. The
ticket is embedded and stapler validate confirms it locally, which is strong
evidence, but the offline launch itself has never been executed. It is tracked
in #91 rather than asserted here.

v0.2.1

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 10 Aug 17:39
v0.2.1
2f3e2c7

This release is about trust in what the app tells you. Four things it reported, or
failed to report, were not true. Each is now correct, and each correction is held
in place by a check that fails if it regresses.

The app can now tell you when its root helper is stale. Installing a new build
left the old privileged helper in place, so privileged fixes never reached an
armed setup — and nothing said so. The panel and the Preferences window now raise
an advisory when the installed helper differs from the one in the build you are
running, carrying the exact command that repairs it. Paste it and the advisory
clears without relaunching.

A hook that cannot fire is no longer reported as wired. A tool event whose
matcher was missing, null, or not a string was counted as healthy, so
coffee-bar would tell you your hooks were fine while they could never run. Tool
events now require a matcher the tool can actually use, and lifecycle events must
carry none at all.

The app inside the disk image is stapled. Previously the image was notarised
but the app inside it was not stapled, so a first launch with no network had no
ticket to check. The app is now signed, notarised and stapled before the image
is built around it.

Two architectural justifications no longer rest on a dead premise. Comments
explaining why the privileged path avoids XPC peer pinning and SMAppService were
written when no signed bundle existed. One has shipped since v0.2.0. Those
decisions are now recorded as unimplemented rather than impossible, and the open
question is tracked in #71.

Install

Downloadcoffee-bar-0.2.1.dmg below is signed with a Developer ID,
notarised by Apple, and stapled. Gatekeeper accepts it, including on a first
launch with no network.

Homebrewbrew install coffee-bar builds from source. That is a different
artifact
: a source build is ad-hoc signed, carries no Developer ID, and
Gatekeeper does not accept it the way it accepts the download. Use the disk image
above if you want the signed, notarised build.

Requirements

  • macOS 14.0 or later.
  • Architectures: arm64. This is not a universal binary — Intel Macs are not
    supported by this build.

Verifying the download

shasum -a 256 coffee-bar-0.2.1.dmg
# 0c1cd40bbd2c8a1bd2e1cd54122ab49d7f5f40b5a716772d0713917178f11288

spctl --assess --type open --context context:primary-signature -vv coffee-bar-0.2.1.dmg
# accepted
# source=Notarized Developer ID

Both commands above were run against the published file, and the output shown is
what they printed.

Closes #81, #55, #82, #86.

v0.2.0 — lid-closed mode, the Preferences window, and a hardened privileged path

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 09 Aug 08:19
v0.2.0
d57ac5b

Lid-closed mode, a Preferences window, and a hardened privileged path. The disk image now carries coffee-bar-probe, so the feature it unlocks is reachable without building from source.

Added

  • Lid-closed mode. sudo coffee-bar-probe arm holds the Mac awake with the lid shut, governed by a launchd watchdog with a revert ladder and a hard eight-hour cap.
  • A Preferences window, split out of the panel, carrying the battery floor. It ships at 15%.
  • A process governor, wired into the app.
  • Codex and Cursor adapters, plus the coffeebar-hook shim.
  • An app icon, and an app palette aligned with the site.
  • coffee-bar-probe inside the bundle, and so inside the disk image. 0.1.1 shipped coffee-bar alone, which left lid-closed mode reachable only by building from source.
  • scripts/release-dmg.sh, which builds this disk image. 0.1.1's image left no trace in the repository of how it was made.

Fixed

  • The watchdog's uninstall booted the service out before removing its plist, leaving a root LaunchDaemon that came back at every boot.
  • The revert and refusal notifications sat after a self-terminating bootout, so neither ever fired on the daemon path.
  • The TTL rung measured elapsed time on the wall clock, so a backward step extended a privileged hold past its eight-hour cap. It now uses mach_continuous_time(), which keeps counting across sleep.

Upgrading

Installing this version ends an arm that is already running. A hold armed by an older build wrote a version 1 journal. The first watchdog rung reads it, answers .unknownSchema, and reverts. That is the fail-safe working as designed, but it is invisible unless you know the schema moved. Re-arm after installing.

Homebrew installs 0.2.0 as well: the tap now pins this tag. It builds on your machine, so that copy is signed only ad hoc and is not notarised. The disk image is the signed, notarised and stapled artifact.

The disk image

Fact Value
File coffee-bar-0.2.0.dmg
Size 844641 bytes
SHA-256 5c16bfd3636adfc568e14dbf26e8a3c62ecd9e2fb2606136a08e6342c965cd15
Architecture Apple silicon (arm64) only
Minimum macOS 14.0
Signature Developer ID Application, team 85FN4Z37V8
Notarisation spctl accepts it, source Notarized Developer ID
Staple xcrun stapler validate passes

Verify the download before you open it:

shasum -a 256 coffee-bar-0.2.0.dmg
spctl -a -t open --context context:primary-signature -vv coffee-bar-0.2.0.dmg

v0.1.1 — the first signed and notarised download

Choose a tag to compare

@ArangoGutierrez ArangoGutierrez released this 04 Aug 14:47
v0.1.1
d7cba62

coffee-bar holds your Mac awake while an AI coding agent is actually working, and
lets it sleep the moment every session is blocked on you. The hold is bound to
live agent session state, read over a 0600 unix socket — not a timer, not a
global override.

This release ships no code changes

v0.1.1 builds the same source as v0.1.0. The diff across Sources/,
Package.swift and Tests/ between the two tags is empty.

What is new is the artifact. This is the first build of coffee-bar that is signed
with a Developer ID, notarised by Apple, and stapled — the first one you can
install on a Mac that has never seen the source.

Install

Download (signed)

Open coffee-bar-0.1.1.dmg below and drag CoffeeBar to Applications.

Check it before you trust it:

shasum -a 256 coffee-bar-0.1.1.dmg
# afc1b15f9bde31aad09de80f23ae97b05f6053322b68b89bab36bcfbc641d2e6

spctl --assess --type open --context context:primary-signature -vv coffee-bar-0.1.1.dmg
# accepted
# source=Notarized Developer ID

The notarisation ticket is stapled to the app inside the DMG, so that check works
with no network connection.

Homebrew (builds from source)

brew tap ArangoGutierrez/coffee-bar
brew install coffee-bar

The tap builds from the source tarball. A source build is ad-hoc signed
(Signature=adhoc, TeamIdentifier=not set), so Gatekeeper does not accept it
the way it accepts the DMG. The code is the same either way — the DMG is the
signed path.

Requirements

  • macOS 14 or later.
  • Apple silicon only. This build is arm64; it does not run on Intel Macs.
    Intel users can still build from source via the tap.

Signing

Authority=Developer ID Application: Carlos Eduardo Arango Gutierrez (85FN4Z37V8)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
TeamIdentifier=85FN4Z37V8

What is in v0.1.0's code

Everything that needs no elevated privilege:

  • the menu-bar panel, with Off/Auto/On and live session counts
  • ingest over a unix socket, with the session state machine behind it
  • a crashed agent's session is retired, so a dead process cannot pin the machine
    awake for ever
  • the battery floor releases the hold rather than draining the machine
  • the running build version is shown in the panel
  • an opt-in Display control: the screen sleeps by default, and stays on when you
    ask it to

By default coffee-bar holds no display assertion.