GitHub - sderosiaux/launchdeck: Unified macOS service console for launchd and Homebrew

6 min read Original article ↗

CI Release macOS Rust 2024

The missing service dashboard for macOS.

Launchdeck gives launchd jobs and Homebrew services one fast, readable control surface: status, schedules, plist metadata, logs, and guarded actions without spelunking through launchctl, brew services, and scattered plist files.

It is built for real developer machines, where background work lives across ~/Library/LaunchAgents, /Library/LaunchDaemons, Apple-managed jobs, vendor helpers, and Homebrew formulas. Launchdeck keeps the noisy system universe available when you need it, but starts with the services you are most likely to care about.

Launchdeck TUI

Why Launchdeck?

macOS service management is powerful, but the day-to-day experience is fragmented:

  • launchctl knows runtime state, but its output is dense and domain-oriented.
  • Plist files explain schedules, logs, RunAtLoad, and KeepAlive, but they are spread across multiple directories.
  • brew services is convenient, but only sees the Homebrew side of the world.
  • A stopped process might actually be a scheduled job waiting for its next run.
  • Killing a process is not the same thing as unloading a launchd job.

Launchdeck brings those pieces together and adds the safety rails you want before changing anything. It shows the command before it runs, blocks risky system/vendor actions, preserves selection by service identity across refreshes, and makes logs and schedules visible where you make decisions.

Install

Install the latest macOS release binary:

brew install sderosiaux/tap/launchdeck

Without Homebrew:

curl -fsSL https://raw.githubusercontent.com/sderosiaux/launchdeck/main/scripts/install.sh | sh

The installer writes to ~/.local/bin by default. Override it with:

BIN_DIR=/usr/local/bin sh -c "$(curl -fsSL https://raw.githubusercontent.com/sderosiaux/launchdeck/main/scripts/install.sh)"

Install from source with Cargo:

Install from the GitHub repository:

cargo install --git https://github.com/sderosiaux/launchdeck

Or build locally:

git clone https://github.com/sderosiaux/launchdeck.git
cd launchdeck
cargo build --release
./target/release/launchdeck

Usage

Print inventory without opening the TUI:

launchdeck list uses the same default visibility as the TUI and hides Apple/system services. Print the full discovered inventory with:

Features

  • One service list for launchd jobs and Homebrew services.
  • Runtime state from launchctl, enriched with Homebrew metadata and parsed plist configuration.
  • A distinct scheduled state for loaded jobs that are not running now but will wake up later.
  • Compact schedule summaries such as 5min, 1h, 00:00, and Sun 09:00.
  • Type-to-search, source/status filters, warnings-only view, Apple/system toggle, and practical sorting.
  • Detail modal for status, scope, safety level, command, plist path, schedule, logs, and health warnings.
  • Scrollable stdout/stderr log view from configured StandardOutPath and StandardErrorPath.
  • Confirmed actions for start, stop, restart/load, enable/disable, RunAtLoad, edit plist, and delete plist.
  • User LaunchAgent creation for common plist fields without hand-writing XML.
  • Background refresh that keeps the selected service stable by identity, not row index.

Status Model

Launchdeck separates states that can otherwise look the same in launchctl output:

Status Meaning
running launchd has an active PID for the job.
scheduled job is loaded, has no active PID, and has a launchd schedule configured.
stopped job is loaded, has no active PID, and has no known schedule.
unloaded plist exists, but the job is not loaded into launchd.
disabled launchd marks the job disabled in its domain.
failed launchd or Homebrew reported a non-zero exit/error state.
unknown Launchdeck could not classify the current state.

For scheduled jobs, stop uses launchctl bootout so the job is actually unloaded and will not wake up on the next schedule.

Keybindings

Overview

Key Action
Type text Start quick search
Down / Up Move selection
PageDown / PageUp Move by a page
Enter Open service detail
? / F1 Open keyboard help
/ Search
C Clear search
Y Copy selected service name
P Cycle source filter
F Cycle status filter
O Cycle sort mode
A Toggle Apple/system services
W Toggle warnings-only view
F5 / Ctrl-r Refresh inventory
L Open logs
S Prepare start action
X Prepare stop action
R Prepare restart/load action
T Prepare enable/disable action
U Prepare RunAtLoad toggle action
E Prepare edit plist action
D Prepare delete plist action
N Create a user LaunchAgent
q Quit

Actions show the exact command before execution. Press y/Enter to confirm or n/Esc to cancel.

Detail

Key Action
j / Down Move down inside detail
k / Up Move up inside detail
PageDown / PageUp Move by a page
g / G First / last detail row
Enter Act on selected row: status, plist, RunAtLoad, stdout, or stderr
c Copy selected field value
l Open stdout logs
u Prepare RunAtLoad toggle action
E Prepare edit plist action
D Prepare delete plist action
Esc / Backspace / Left Back to overview

Logs

Key Action
j / Down Newer lines
k / Up Older lines
PageDown / PageUp Move by a page
g / G Top / bottom of loaded tail
Tab / Left / Right Switch stdout/stderr
c Copy current log path
Esc / Backspace Back to detail

The log view opens at the end of the selected stream and keeps a scrollback window from the latest 500 lines.

Safety

Launchdeck is conservative by default:

  • Homebrew services are managed through brew services.
  • User-owned launchd jobs are managed through launchctl.
  • Services under /System/Library are inspect-only.
  • Admin-required services are blocked until sudo handling is implemented.
  • Vendor/runtime services are blocked unless they can be classified safely.
  • Destructive actions, including delete, always require confirmation.

Create Form

The create form writes user LaunchAgents only, under ~/Library/LaunchAgents.

It supports common plist fields: label, program arguments, working directory, stdout/stderr paths, environment variables, RunAtLoad, KeepAlive, StartInterval, optional bootstrap, and optional start.

Arguments and environment values accept shell-style quotes, so values like --name "hello world" are preserved correctly.

Requirements

  • macOS
  • Homebrew optional, for brew services support
  • Rust toolchain only when installing from source

Test Fixture

The repository includes a fake user agent plist for local discovery testing:

cp fixtures/com.sderosiaux.launchdeck.fake.plist ~/Library/LaunchAgents/
cargo run -- --list | rg launchdeck.fake

The fixture is not loaded or started by that command. Remove it with:

rm ~/Library/LaunchAgents/com.sderosiaux.launchdeck.fake.plist

Development

scripts/lint.sh
cargo run
cargo run -- --list

Release

Releases are built from tags:

git tag v0.1.0
git push origin v0.1.0

The release workflow builds macOS archives for Apple Silicon and Intel, publishes checksums, and updates the GitHub release assets.

Project Status

Launchdeck is early software. It is already useful for inventory, inspection, filtering, navigable logs, guarded lifecycle actions, and creating user LaunchAgents. Sudo-backed admin actions are not implemented yet.