LaunchDeck is intentionally conservative. It reads broadly, but writes narrowly.
The inventory reads plist files from:
~/Library/LaunchAgents
/Library/LaunchAgents
/Library/LaunchDaemons
/System/Library/LaunchAgents
/System/Library/LaunchDaemons
Read errors are kept on the job summary as parse errors. A bad plist should not crash the app or stop the rest of the inventory from rendering.
Core app-owned writes are limited to:
~/Library/LaunchAgents/io.github.launchdeck.*.plist
~/Library/Application Support/LaunchDeck/
~/Library/Logs/LaunchDeck/
The core model rejects app-owned writes unless the label starts with:
io.github.launchdeck.
The SwiftUI editor is also restricted. It only enables write controls for personal user LaunchAgents that are not Apple system jobs and have no parse error.
LaunchDeck does not edit or delete:
/System/Library/*
/Library/LaunchDaemons/*
/Library/LaunchAgents/*
These paths can be inspected so a user can understand what is installed, but operations that would require privileged system management are outside the app.
LaunchDeck operates in the current user GUI domain:
gui/<uid>
The command wrapper builds service targets as:
gui/<uid>/<label>
Labels containing / or a null byte are rejected before command execution.
When the SwiftUI editor saves a plist:
- It renders the updated plist to a temporary file beside the original.
- It runs
plutil -linton the temporary file. - It atomically writes the original plist only if lint passes.
- It removes the temporary file.
This keeps malformed edits from replacing the active plist.
The app displays log paths immediately, but it does not read log contents until the user clicks a log button. Log preview reads only the tail of the file.
This avoids selection freezes when a launchd job has written a large stdout or stderr file.
The proof script's live launchd check is opt-in:
RUN_LIVE_LAUNCHD=1 ./scripts/proof.shIt creates a temporary label matching:
io.github.launchdeck.proof.*
It writes temporary files under LaunchDeck-owned user locations, bootstraps the temporary LaunchAgent, kickstarts it, checks status, boots it out, removes the generated plist/script/task/marker files, and retains the generated proof log as evidence.
- No privileged helper installation.
- No root daemon management.
- No edits under
/System/Library. - No edits under
/Library/LaunchDaemons. - No signed installer or notarized release pipeline.
- No parsing of
launchctl printas a stable automation API.