doomsday is a fail-closed Linux desktop pressure guard. It watches CPU and
memory pressure, identifies the application or Docker container driving an
incident, and takes the smallest safe action before the graphical session
freezes.
The daemon runs as the current user. It never requires root, never stops Docker containers, and cannot signal processes owned by other users.
- Memory kills require sustained global PSI pressure, low available RAM and swap, and a causally dominant user application.
- CPU-only incidents lower an application's scheduling priority; they never kill it.
- Session infrastructure is never eligible.
- Terminal workloads get an extra 15-second grace period.
- Docker participates in attribution but is observation-only. If Docker
dominates,
doomsdaydisplays the exact unrelated application it will sacrifice; cancel moves to the next candidate and 30 seconds of silence approves the displayed action. - Missing or inconsistent metrics fail closed: no destructive action.
- At most one application is handled at a time and at most three destructive actions are allowed per hour.
See docs/design.md for the complete state machine, thresholds, attribution rules, and failure behavior.
This first version targets the machine it was designed on:
- Linux amd64
- systemd user manager and cgroup v2
/proc/pressure(PSI)- Hyprland/UWSM graphical session
- a Freedesktop notification server (
mako) andnotify-send - optional Docker CLI access for read-only container names and limits
go test ./...
go build -o doomsday ./cmd/doomsdayNo third-party Go modules or cgo are used.
Dry-run mode observes the real machine and sends notifications, but never changes process priorities or sends signals. Run only one instance because the service and standalone command share learned state.
If the user service is already installed:
systemctl --user stop doomsday.service
~/.local/bin/doomsday --dry-run
# Press Ctrl-C after observing it.
systemctl --user start doomsday.serviceWithout an installed service, run it from the repository:
go run ./cmd/doomsday --dry-runStandalone logs appear in the current terminal. When running as a service, show formatted decisions and pressure incidents with:
doomsday logs
doomsday logs -f # follow new events
doomsday logs --since "2 hours ago" # choose a journal time range
doomsday logs -n 20 # limit recent eventsAvoid deliberately exhausting memory in an active desktop session. Normal workloads are enough to validate collection and baseline learning safely.
Defaults are built in. To override them:
mkdir -p ~/.config/doomsday
test -e ~/.config/doomsday/config.json || \
cp config.example.json ~/.config/doomsday/config.json
$EDITOR ~/.config/doomsday/config.json
systemctl --user restart doomsday.serviceConfiguration is loaded only at startup and rejected when invalid. See
config.example.json for every supported override.
hard_unit_patterns adds protected units, while eligible_unit_patterns can
remove the extra delay from soft application units such as herdr.service.
Built-in session and desktop protections cannot be demoted.
doomsday # start the service if needed and open the live dashboard
doomsday status # show current health and operational state
doomsday live # continuously refresh the status dashboard
doomsday logs # show formatted pressure events and actions
doomsday stop # stop the user service
doomsday version # print the installed release
doomsday update # install the latest verified release
doomsday --dry-run # observe and notify without changing processes
doomsday --reset-baseline # remove learned thresholds and exitRunning doomsday starts the user service if needed and opens the live panel;
leaving the panel with Ctrl-C does not stop the service.
doomsday status renders service and timer state, persisted learning state, next
update check, live PSI, available memory and swap, and effective safety
thresholds as a compact terminal dashboard. doomsday live opens a full-screen htop-style dashboard refreshed
every two seconds, with health, threshold bars, and ranked applications instead
of raw service logs. Ctrl-C closes only the panel. doomsday logs renders recent
journald events as a compact timeline, including pressure metrics, the leading
application, and whether TERM, KILL, priority adjustment, or no intervention
occurred; -f follows new events. Semantic colors are enabled only on a terminal;
pipes, TERM=dumb, and NO_COLOR stay plain. These
commands do not perform network requests.
Restart the service after resetting so it begins retraining. The service manager remains available for raw diagnostics:
systemctl --user status doomsday.service
journalctl --user -u doomsday.serviceThe installer enables a weekly systemd timer. It only checks the latest public release and sends a desktop notification when the installed version differs; it never installs an update automatically.
systemctl --user list-timers doomsday-update.timer
systemctl --user start doomsday-update.service # check now
doomsday update # update when readyTo disable or restore periodic checks:
systemctl --user disable --now doomsday-update.timer
systemctl --user enable --now doomsday-update.timerQuick install:
curl -fsSL https://enzola.dev/doomsday/install.sh | bashThe -L is required because the domain redirects to the latest GitHub release.
To inspect the installer before running it:
installer_url="https://enzola.dev/doomsday/install.sh"
curl --proto '=https' --proto-redir '=https' -fsSLo \
/tmp/doomsday-install.sh "$installer_url"
less /tmp/doomsday-install.sh
bash /tmp/doomsday-install.shFor a specific release:
DOOMSDAY_VERSION=0.5.0 bash /tmp/doomsday-install.shThe installer verifies the release checksum, preserves customized configuration,
installs only in the current user's home, and rolls back failed migrations. The
v0.5 installer upgrades only the byte-for-byte unmodified v0.4 safety profile;
customized files remain untouched. Do not run it with sudo.
Piping the installer directly to bash does not provide an opportunity to
inspect it first.
Download the public archive and checksum, verify it, and install the packaged files:
version=0.5.0
base="https://github.com/enzilla/doomsday/releases/download/v${version}"
curl --proto '=https' --proto-redir '=https' -fLO \
"${base}/doomsday_${version}_linux_amd64.tar.gz"
curl --proto '=https' --proto-redir '=https' -fLO \
"${base}/doomsday_${version}_linux_amd64.sha256"
sha256sum -c "doomsday_${version}_linux_amd64.sha256"
tar -xzf "doomsday_${version}_linux_amd64.tar.gz"
cd "doomsday_${version}_linux_amd64"
systemctl --user stop doomsday.service 2>/dev/null || true
timeout --preserve-status --signal=TERM 10s ./doomsday --dry-run
install -Dm755 doomsday ~/.local/bin/doomsday
install -Dm755 install.sh ~/.local/lib/doomsday/install.sh
install -d ~/.config/doomsday
test -e ~/.config/doomsday/config.json || \
install -m600 config.example.json ~/.config/doomsday/config.json
install -Dm644 doomsday.service ~/.config/systemd/user/doomsday.service
install -Dm644 doomsday-update.service \
~/.config/systemd/user/doomsday-update.service
install -Dm644 doomsday-update.timer \
~/.config/systemd/user/doomsday-update.timer
systemctl --user daemon-reload
systemctl --user enable --now doomsday.service doomsday-update.timerSource installation is intentionally a few direct commands rather than another script:
go test ./...
go build -o doomsday ./cmd/doomsday
systemctl --user stop doomsday.service 2>/dev/null || true
timeout --preserve-status --signal=TERM 10s ./doomsday --dry-run
install -Dm755 doomsday ~/.local/bin/doomsday
install -Dm755 install.sh ~/.local/lib/doomsday/install.sh
install -d ~/.config/doomsday
test -e ~/.config/doomsday/config.json || \
install -m600 config.example.json ~/.config/doomsday/config.json
install -Dm644 systemd/doomsday.service ~/.config/systemd/user/doomsday.service
install -Dm644 systemd/doomsday-update.service \
~/.config/systemd/user/doomsday-update.service
install -Dm644 systemd/doomsday-update.timer \
~/.config/systemd/user/doomsday-update.timer
systemctl --user daemon-reload
systemctl --user enable --now doomsday.service doomsday-update.timerThe service is installed only after the implementation, tests, and dry run are validated.
Pushing a v* tag runs the release workflow.
It tests the repository and installer, builds a static Linux amd64 archive,
writes its SHA-256 checksum, and creates a GitHub Release with the standalone
installer and generated notes.
git tag -a v0.5.0 -m "v0.5.0"
git push origin v0.5.0Releases are public and can be downloaded without a GitHub account.
Licensed under the Apache License 2.0. It permits commercial use, modification, and redistribution, including proprietary derivatives, while requiring preservation of the license and relevant notices. It also includes an explicit patent grant from contributors.
systemctl --user disable --now doomsday.service doomsday-update.timer
rm -f ~/.config/systemd/user/doomsday.service \
~/.config/systemd/user/doomsday-update.service \
~/.config/systemd/user/doomsday-update.timer \
~/.local/bin/doomsday
rm -rf ~/.config/doomsday ~/.local/lib/doomsday ~/.local/state/doomsday
systemctl --user daemon-reloadVersion 1 intentionally does not manage I/O pressure, GPU hangs, thermal pressure, disk exhaustion, system services, other users, or Docker lifecycle. It currently publishes only Linux amd64 archives and has no package-manager installer.