Skip to content
Launch Pilotv0.3.1
For macOS developers who inherited a plist

Your daemonshave been runningin the dark.

Launch Pilot is a local-first control console for macOS launchd. Every LaunchAgent, every restart loop, every log line — streaming to a browser tab over SSE, running on your laptop, touching no cloud, asking no permissions you haven't already given launchctl.

runtime
Local · Go + SSE
scope
per-user agents · system daemons
install time
< 4s via Homebrew
/launchctl/status
live · streaming over /api/events
  • com.docker.vmnetd
    pid 8493d 12h 04m
  • homebrew.mxcl.postgresql@16
    pid 36111d 02h
  • com.tailscale.tailscaled
    pid 120414h 03m
  • com.user.nightly-backup
    pid 9321restarted ×3
  • com.zsa.wally.daemon
    crashed · exit 139
  • com.vercel.cli.updater
    next run · 03:00
  • homebrew.autoupdate
    loaded, not run
event: heartbeat · 1 Hz
scroll to inspect

Why

launchctl is a power tool with no dashboard.

Three hundred services run on a fresh macOS install. You inherited thirty more when you said yes to Docker, Homebrew, Rectangle, and that Python installer from 2021.

When one fails, you read plist XML, grep Console.app, and pray /var/log/system.log still has the window. Launch Pilot replaces that ritual with a real-time console — one tab, every agent, full log tail, zero kernel extensions.

Without Launch Pilot

  • launchctl list | grep | head, every time
  • plist XML open in BBEdit, fingers crossed
  • Console.app with ten filters you rebuilt last week
  • No history when a daemon crashes at 3am

With Launch Pilot

  • One browser tab, every service, sorted by health
  • Plist rendered as a form, not a tag soup
  • Per-service log tail, filtered server-side, streamed live
  • A circular restart buffer that remembers the 3am crash

How it works

Three commands between you and a live daemon console.

  1. 01

    Install from Homebrew.

    A single Go binary, no Electron, no background services it didn't ship with. The formula is public and auditable — read the bottle, read the source.

    $ brew install launch-pilot/tap/launch-pilot
  2. 02

    Run launch-pilot open.

    The CLI boots a local web server on 127.0.0.1, opens a browser tab, and subscribes to launchctl events through the native bootstrap APIs. Nothing leaves your machine.

    $ launch-pilot open --port 7331
  3. 03

    Watch, diagnose, fix.

    Every service is a row. Green means healthy, amber means thrashing, red means bring-it-back-up. Click a row for tail logs, exit codes, the plist that defined it, and a one-click restart button.

Live stream

Logs arrive before Console.app can catch up.

A single SSE channel multiplexes stdout, stderr, and launchd status emissions. Server-side filtering keeps the payload small; the browser renders deltas, not full frames.

Server-Sent EventsPer-service tailSearch & filterRing buffer (500 lines)
/api/events?service=*streaming
  • 14:02:11.402infocom.tailscale.tailscaledaccepted inbound conn from 100.64.0.1
  • 14:02:11.889streamhomebrew.mxcl.postgresql@16autovacuum: completed on idea_business.public.events (2.1s)
  • 14:02:12.014warncom.user.nightly-backuprsync exited 24 (vanished source files) — scheduling retry
  • 14:02:12.311infocom.docker.vmnetdproxy: bound 127.0.0.1:6443 → gvproxy
  • 14:02:12.744errorcom.zsa.wally.daemonexit status 139 (segmentation fault) after 842ms — KeepAlive triggers respawn
  • 14:02:13.001warncom.zsa.wally.daemonrespawned too quickly: 4 times in 12s, throttling to 10s
  • 14:02:13.210infohomebrew.mxcl.postgresql@16checkpoint complete, wal=47MB kept
  • 14:02:13.558streamcom.tailscale.tailscaledmagicsock: derp-2 active · rtt 18ms
  • 14:02:13.902infocom.user.nightly-backupretry 1/3 — rsync resumed from offset 0x2fa00
  • 14:02:11.402infocom.tailscale.tailscaledaccepted inbound conn from 100.64.0.1
  • 14:02:11.889streamhomebrew.mxcl.postgresql@16autovacuum: completed on idea_business.public.events (2.1s)
  • 14:02:12.014warncom.user.nightly-backuprsync exited 24 (vanished source files) — scheduling retry
  • 14:02:12.311infocom.docker.vmnetdproxy: bound 127.0.0.1:6443 → gvproxy
  • 14:02:12.744errorcom.zsa.wally.daemonexit status 139 (segmentation fault) after 842ms — KeepAlive triggers respawn
  • 14:02:13.001warncom.zsa.wally.daemonrespawned too quickly: 4 times in 12s, throttling to 10s
  • 14:02:13.210infohomebrew.mxcl.postgresql@16checkpoint complete, wal=47MB kept
  • 14:02:13.558streamcom.tailscale.tailscaledmagicsock: derp-2 active · rtt 18ms
  • 14:02:13.902infocom.user.nightly-backupretry 1/3 — rsync resumed from offset 0x2fa00
⌘K · filterj/k · next · prev/ · searchEsc · unpin

Features

Built the way a macOS utility should be.

01

Real-time SSE, not polling.

A launchctl subscription fans out over Server-Sent Events. Status changes land in the browser in under 50ms — faster than you can alt-tab to a terminal.

02

Crash-loop detective.

Exit codes, signal names, and a rolling buffer of the last 500 log lines per service. When something dies at 3am, the evidence is waiting when you wake up.

03

Plist, rendered sanely.

LaunchAgent plists are XML pretending to be dictionaries. Launch Pilot parses them into typed forms — ProgramArguments, KeepAlive, StartInterval — with inline validation and diff-on-save.

04

Log tail that survives restart.

Standard out, standard error, and launchd's own emission — merged, timestamp-aligned, searchable. Filter by level without losing context.

05

Zero cloud, zero telemetry.

Everything runs on 127.0.0.1. No analytics, no crash reporter, no call-home update check. You'd hear about a new release from Homebrew, like every other local tool.

06

Keyboard-first, everywhere.

⌘K opens command palette. j/k moves between services. r restarts. l opens logs. Esc closes. Designed for someone who types faster than they click.

Install · 4 seconds

From zero to a live daemon console.

One tap, one binary, one browser tab. No daemon-for-the-daemons, no elevated privileges beyond what launchctl already has.

RunningDegradedFailedLoadedIdle
~ launch-pilot
$ brew install launch-pilot/tap/launch-pilot==> Fetching launch-pilot
==> Pouring launch-pilot-0.3.1.arm64_sonoma.bottle.tar.gz
  🚀  /opt/homebrew/Cellar/launch-pilot/0.3.1: 11 files, 8.9MB
~ launch-pilot
$ launch-pilot open▶ launch-pilot v0.3.1
✓ launchctl subscription (313 services indexed)
✓ SSE bus on 127.0.0.1:7331
✓ opening http://127.0.0.1:7331/ in your browser$

/stat 01

313

services indexed on a fresh macOS 15 install

on this M2 MacBook Air, right now

/stat 02

< 50ms

SSE round-trip from launchctl event to DOM update

95th percentile, local loopback

/stat 03

0

System Preference panes opened to diagnose a crash

this has been a System Preference-free quarter

/stat 04

8.9MB

single static Go binary, arm64 + x86_64

smaller than the average launch-screen video

FAQ

Questions a careful developer would ask.

Install

Take the hood off launchd.

Four seconds of Homebrew and one browser tab. That's the whole setup. The next daemon that dies will announce itself.

$brew install launch-pilot/tap/launch-pilot

then: launch-pilot open