Skip to content

Repository files navigation

doomsday

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.

Safety model

  • 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, doomsday displays 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.

Requirements

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) and notify-send
  • optional Docker CLI access for read-only container names and limits

Build and test

go test ./...
go build -o doomsday ./cmd/doomsday

No third-party Go modules or cgo are used.

Try it safely

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.service

Without an installed service, run it from the repository:

go run ./cmd/doomsday --dry-run

Standalone 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 events

Avoid deliberately exhausting memory in an active desktop session. Normal workloads are enough to validate collection and baseline learning safely.

Configuration

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.service

Configuration 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.

Commands

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 exit

Running 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.service

Updates

The 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 ready

To disable or restore periodic checks:

systemctl --user disable --now doomsday-update.timer
systemctl --user enable --now doomsday-update.timer

Installation

Automatic

Quick install:

curl -fsSL https://enzola.dev/doomsday/install.sh | bash

The -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.sh

For a specific release:

DOOMSDAY_VERSION=0.5.0 bash /tmp/doomsday-install.sh

The 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.

From a release

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.timer

From source

Source 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.timer

The service is installed only after the implementation, tests, and dry run are validated.

Publishing

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.0

Releases are public and can be downloaded without a GitHub account.

License

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.

Uninstall

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-reload

Scope

Version 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.

About

Fail-closed Linux desktop pressure guard that attributes CPU and memory incidents before the graphical session freezes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages