Omarchy Snapshot Manager
A native Omarchy Shell bar plugin for viewing, creating, and deleting system snapshots managed by Snapper.
Features
- Lists snapshots from every configured Snapper subvolume.
- Creates a numbered snapshot with an optional description.
- Deletes one snapshot at a time after an explicit confirmation.
- Uses Omarchy's current bar, popup, type, and color components.
- Supports shell IPC (
open,close,toggle, andrefresh).
Snapshot access is privileged on a default Omarchy installation. The plugin
therefore uses pkexec for its narrow helper commands and Omarchy's Polkit
agent displays the authorization prompt. Inputs are passed as an argument
array, configuration names and snapshot numbers are checked again by the
helper, and no shell command strings are evaluated.
The helper runs as root, so it is installed to a root-owned prefix rather than
executed from the plugin checkout, and the Polkit action is pinned to that
absolute path. This requires a one-time sudo ./install.sh — see
Install and Security model.
Requirements
- Omarchy 4.0 or newer
- Snapper with at least one configured subvolume
pkexecand a working Polkit agent- Python 3
sudo/root access once, to install the privileged helper
The stock Omarchy Snapper setup can be repaired or created with the command
shown by omarchy snapshot create when no configuration exists.
Install
omarchy plugin add https://github.com/sahzudin/omarchy-snapshot-manager.git --enable
sudo ~/.config/omarchy/plugins/io.github.sahzudin.omarchy-snapshot-manager/install.sh
The second step is required. It copies snapshotctl.py to
/usr/local/lib/omarchy-snapshot-manager/ as root:root 0755 and installs the
Polkit action pinned to that path. Until it runs, the panel reports that the
helper is not installed.
Re-run sudo ./install.sh after every plugin update, or the panel keeps
using the previously installed helper. Check with:
./install.sh status
For local development, link the checkout into the user plugin directory and enable it:
ln -s "$PWD" ~/.config/omarchy/plugins/io.github.sahzudin.omarchy-snapshot-manager
omarchy plugin enable io.github.sahzudin.omarchy-snapshot-manager --section right
omarchy-shell shell rescanPlugins
sudo ./install.sh
The bar and user plugin tree hot-reload, but the privileged helper does not —
editing snapshotctl.py requires sudo ./install.sh again. If needed, run
omarchy restart shell after installing.
Remove
sudo ~/.config/omarchy/plugins/io.github.sahzudin.omarchy-snapshot-manager/install.sh uninstall
omarchy plugin remove io.github.sahzudin.omarchy-snapshot-manager
The first command removes the privileged helper and its Polkit action; run it before removing the plugin, while the script is still on disk. The second disables the widget, removes its bar entry, and removes the plugin checkout. Neither alters or deletes any Snapper snapshots.
Use
- Left-click the snapshot icon to open the manager.
- Enter an optional description and choose Create.
- Choose Delete, then Confirm within eight seconds to remove a snapshot.
- Right-click the bar icon to refresh while the panel is open.
Deleting a snapshot is permanent. Snapper snapshots share the source disk and do not replace an off-device backup.
Permissions and dependencies
The plugin has no network access and does not install packages or modify
Omarchy configuration itself. It depends on the system-provided snapper,
pkexec, Python 3, and Omarchy Polkit agent.
Snapper snapshot access is privileged on a default Omarchy installation. The
panel therefore asks Polkit to run only the installed snapshotctl.py helper as
administrator. That helper exposes three operations—list, create, and
delete—and validates configuration names and snapshot numbers before invoking
Snapper. It does not install a passwordless sudo policy.
Security model
The plugin checkout under ~/.config/omarchy/plugins/ is owned and writable by
the unprivileged user, so nothing in it can be trusted with root. install.sh
therefore moves the only privileged component out of it:
| Component | Location | Owner |
|---|---|---|
Panel.qml, BarWidget.qml, Model.js |
plugin checkout | user |
snapshotctl.py (runs as root) |
/usr/local/lib/omarchy-snapshot-manager/ |
root:root 0755 |
| Polkit action | /usr/share/polkit-1/actions/ |
root:root 0644 |
Panel.qml hardcodes the absolute helper path, and the Polkit action pins the
same path via org.freedesktop.policykit.exec.path. Tampering with the QML can
therefore only change the arguments passed to the helper, never the code that
runs as root — which makes the helper's own argument validation the security
boundary. It accepts three subcommands, checks configuration names against the
configs Snapper actually reports, checks snapshot numbers against snapshots that
actually exist, rejects multi-line descriptions, pins PATH, and passes every
value to snapper as an argument vector with no shell involved.
The helper fails closed on oversized data. Each operation has one 16 MiB Snapper-output budget shared across all configurations, each subprocess has a 16 KiB stderr cap, and the final JSON response is capped at 16 MiB while it is encoded. Listings are additionally limited to 256 configurations and 20,000 snapshots so bounded CSV input cannot expand into an unbounded number of in-memory objects. Crossing a producer limit terminates Snapper immediately.
Two things worth stating plainly:
sudo ./install.shruns a script from the user-writable checkout. That is a deliberate, inspectable, one-time action, unlike a recurring runtime prompt. Read the diff before running it after an update.- The action uses
auth_admin_keep, so authorization is cached for a few minutes and back-to-back operations do not re-prompt. Within that window a compromised session can invoke the helper without a prompt, but only for the three validated snapshot operations above. Change it toauth_admininpolkit/io.github.sahzudin.omarchy-snapshot-manager.policyif you would rather authenticate every time.
Development
Validate the manifest and run the backend unit tests:
omarchy plugin validate .
python -m unittest discover -s tests -v
The helper can be inspected without authorization:
./snapshotctl.py --help