Skip to content

0.3.0

Choose a tag to compare

@emdzej emdzej released this 14 Jul 08:15

[0.3.0] — 2026-07-14

Adds a macOS menubar companion app, daemon-side plumbing to push live
drive updates over Server-Sent Events, and a VitePress product site
at airlock.emdzej.pl. No changes to the
existing 0.2.0 REST API — the companion app is a strict additive
layer.

Added — Mac companion app (companion/mac/)

  • New menubar-only AirlockCompanion.app (Swift Package +
    LSUIElement). Discovers airlock instances via the new
    _airlock._tcp Bonjour service; lists their drives; per-drive
    actions Mount / Mount and Open in Finder / Unmount /
    Reveal in Finder / Copy SMB URL / Eject drive from
    airlock
    ; per-host Eject all drives and Open web UI.
  • Silent mount via NetFSMountURLSync from NetFS.framework
    same DiskArbitration path Finder uses, but no window pops on
    mount. NSWorkspace.open(smb://…) was the first draft; it worked
    but always opened a window, making "Mount" and "Mount and Open"
    indistinguishable.
  • Auto-unmount reconciliation: when a drive is ejected on the
    daemon side (web UI, GPIO button, format), the Mac's local SMB
    mount is dropped automatically. Skipped when the host is
    currently offline so a Pi reboot doesn't yank live mounts.
  • Preferences window: Auto-mount all discovered drives,
    Open in Finder after mounting, Start at login (via
    SMAppService.mainApp on macOS 13+), plus a footer with the app
    version + a GitHub link.
  • Persistent host store: airlocks the app has seen appear in the
    menu even when offline, with offline · 2h ago. Prune after 30
    days of not-seen. Restores drive list from cache on launch so the
    menu is populated before the first SSE reconnect finishes.
  • Coloured host status dot: green live · grey offline · red
    error, with the last error inline.
  • Live updates via SSE with exponential-backoff reconnect and
    URLSessionDataDelegate-based frame parsing (bypasses the
    URLSession.bytes buffering issue on macOS 13/14).
  • XcodeGen project.yml for developers who want Xcode.
    .xcodeproj stays gitignored.
  • Distributable DMG via package.sh. The .app inside is
    ad-hoc signed (codesign --force --deep --sign -) so macOS 15
    Sequoia's stricter Gatekeeper — which no longer honours the old
    right-click Open bypass on unsigned apps — treats it as a normal
    first-run prompt instead of "damaged and can't be opened." Not
    notarized (no Apple Developer Account); users still need one
    xattr -dr com.apple.quarantine /Applications/AirlockCompanion.app
    after copying, documented in the companion guide.

Added — daemon

  • GET /api/events — Server-Sent Events stream broadcasting the
    current drive list on every mount-manager change. Initial snapshot
    on connect, heartbeat comment lines every 30 s, event types are
    wrapped in an envelope ({"type":"drives","drives":[...]}) so we
    can add more event kinds without a breaking wire change.
  • mount.Manager.AddListener so multiple downstream consumers can
    subscribe to drive-change events. The Samba writer and the SSE
    broadcaster both hook in via this now instead of wrapping the
    primary listener.
  • Additional _airlock._tcp Bonjour advertisement (port 80, TXT
    api=1 path=/). Companion apps browse for exactly that instead of
    filtering _smb._tcp / _http._tcp.

Added — release workflow

  • New companion-mac job on macos-14. Stamps Info.plist with
    the release tag, builds the .app via swift build, packages a
    DMG via hdiutil, attaches AirlockCompanion-<tag>.dmg +
    sha256 to the same GitHub release the daemon binary and pi-gen
    image are already on.

Added — product site (docs/)

  • airlock.emdzej.pl — VitePress site
    built from the existing docs/ folder. New landing hero page, a
    product overview, and a dedicated macOS companion guide sit next
    to the existing user guide and install guide (same source, no
    duplication).
  • .github/workflows/site.yml builds and publishes to GitHub Pages
    on every push to main that touches docs/**, using
    actions/deploy-pages@v4. Custom domain via docs/public/CNAME;
    DNS + Pages source (GitHub Actions) configured out-of-band on
    the repo.

Version surfaces

  • Web UI footer already links Version 0.3.0 → the repo.
  • Companion app's Preferences window shows Airlock Companion 0.3.0
    at the bottom with a matching GitHub link.
  • Daemon main.version bumped to 0.3.0; release workflow
    overrides via -ldflags -X main.version=<tag> for tagged builds.

Full Changelog: 0.2.0...0.3.0