README
¶
tui-snapper
btrfs snapshots, managed by snapper, from the terminal. Part of the tui-tools family.
Your machine has been taking snapshots before every package upgrade for
months. snapper list prints them; answering "what did that upgrade actually
change, and can I put back just this one file" from there is a different
matter. That is what this is for.


make demo # the whole UI, on sample data, with no btrfs and no root
Install
Any distribution, static binary
curl -fsSL https://github.com/tui-tools/tui-snapper/releases/download/v{version}/tui-snapper_{version}_linux_amd64.tar.gz | tar -xz tui-snapper
sudo install -m0755 tui-snapper /usr/local/bin/tui-snapper
Static, no runtime dependencies beyond the snapper binary it drives.
From source
git clone https://github.com/tui-tools/tui-snapper
cd tui-snapper && make demo
make demo needs no btrfs filesystem and no root.
The distribution packages are not published yet. The commands below are here so
you know what they will be; a package repository at pkgs.tui.tools is what
turns them on.
Arch Linux — coming soon
Needs the tui-tools repository, which is a one-time setup.
sudo pacman -S tui-snapper
Needs the snapper package, which Arch already has.
Debian and Ubuntu — coming soon
Needs the tui-tools repository, which is a one-time setup.
sudo apt install tui-snapper
Needs the snapper package.
Fedora and RHEL — coming soon
Needs the tui-tools repository, which is a one-time setup.
sudo dnf install tui-snapper
Needs the snapper package.
openSUSE — coming soon
Needs the tui-tools repository, which is a one-time setup.
sudo zypper install tui-snapper
openSUSE ships snapper by default, and is the layout snapper rollback was written for.
Verify a download
Every release of tui-snapper ships a checksums.txt. Check an archive
against it before installing:
sha256sum -c checksums.txt --ignore-missing
What it does
The history. Every config's snapshots, newest first: the hourly timeline
ones, the pre/post pairs your package manager left behind — a post snapshot
shows the pre it belongs to, 47←46 — and the ones you took by hand. A
snapshot no cleanup algorithm will ever remove is colored as kept, because
that is the difference between a state you can rely on and one a timer will
quietly take away. The header carries the config, its subvolume, how far back
the history goes, and the space the snapshots hold when btrfs quotas make that
figure real.
What changed. d compares two snapshots. A post snapshot pairs with its
own pre without being asked, which is the comparison anyone opening a package
upgrade wants; mark two snapshots with space to compare any other pair.
enter on a path opens its unified diff, read-only and scrollable.

Putting things back. u in the diff view runs undochange for the
selected paths, or for every path you marked. One configuration file goes back
without the rest of the filesystem moving.
Managing the history. Create a snapshot with a description, a type and a
cleanup algorithm; delete one, or every snapshot you marked; change a
description; pin a snapshot by clearing its cleanup algorithm; run number,
timeline or empty-pre-post cleanup. Every one of them is shown as the
exact snapper command line first.

Rollback, honestly. This is the one thing that genuinely differs between machines, so the tool works out which mechanism is in front of you instead of assuming:
- where
snapper rollbackis the real mechanism — the config is the root filesystem and snapper carries therollbackflag — it is offered, with the warning it deserves; - on an Omarchy or limine-snapper-sync layout, the snapshots are in
the boot menu and booting one is the rollback. tui-snapper reads
/boot/limine.confand shows those entries read-only. It never edits that file, and it never swaps subvolumes itself.

The timers, read-only. T shows whether snapper-timeline.timer and
snapper-cleanup.timer are running and enabled, which is the answer to "why
did nothing get cleaned up". Turning them on and off is systemd's job, and
tui-systemd already does it.
It needs root, including to read
Most tools in this family read as an ordinary user and escalate only to change
something. snapper does not work that way: it opens its subvolumes directly,
so even snapper list fails for a non-root user with
IO error (open failed path:/ errno:1 (Operation not permitted)).
So either run the whole thing with sudo tui-snapper, or leave
sudo = "sudo -n" configured and keep a sudo timestamp warm — every snapper
call is then escalated individually. The tool says which of the two it is
doing in its header, and explains this failure rather than showing you an
empty list. --demo needs neither.
Keys
| Key | What it does |
|---|---|
↑ ↓ j k |
move the selection; g / G first and last, pgup / pgdn a page |
space |
mark a snapshot: two marks are a range, many are one delete |
d / enter |
compare two snapshots; a post snapshot pairs with its own pre |
enter |
in the diff: that file's changes, read-only |
u |
in the diff: put the selected or marked files back |
c |
create a snapshot: description, type, cleanup algorithm |
D |
delete the selected snapshot, or every marked one |
e / a |
change the description / the cleanup algorithm |
C |
run a cleanup: number, timeline or empty-pre-post |
R |
rollback, or how this machine actually rolls back |
s / T |
switch config / snapper's timers, read-only |
/ |
filter the list |
r |
re-read the current view |
? / q |
help / quit |

Configuration
/etc/tui-snapper/config.toml, then ~/.config/tui-snapper/config.toml, then
TUI_SNAPPER_* in the environment, then the flags. See
examples/config.toml.
config = "root" # the snapper config to open on
sudo = "sudo -n" # "" runs snapper directly
boot-config = "/boot/limine.conf" # where the rollback screen reads the boot menu
no-dbus = false # true where no snapperd is running
theme = "" # empty follows the active Omarchy theme
no-dbus is worth knowing about. snapper normally talks to snapperd over
D-Bus; in a container, a chroot or a rescue shell there is no snapperd, and
every command comes back as
Failure (org.freedesktop.DBus.Error.FileNotFound). --no-dbus makes snapper
do the work itself. The flag becomes part of the previewed command line, so
the dialog still shows exactly what will run.
The rules it keeps
These are the family's, and they are what the tests are about:
- Preview, then confirm. Nothing changes without the exact
snappercommand line being shown first. The dialog is the only path to a mutation, andinternal/snapper/command.gois the only place a command line is built. - Read-only by default. Starting the tool only reads.
- No daemon, no state of its own. The filesystem is the source of truth, and it is re-read after every change.
--demoalways works. It builds and previews every command for real and touches nothing.- Backend behind an interface. The UI never names a binary.
- Responsive. It works in a 40-column pane and on a full screen.
Development
make check # gofmt, go vet, tests
make demo # the UI on sample data
make screenshots # re-render the README frames from --demo
make manifest # validate tool.json against the family schema
The parsers are table-tested against output captured from a real snapper
0.13.1 on a real btrfs filesystem — internal/snapper/testdata holds those
captures verbatim. The limine fixtures in limine_test.go are reconstructions
rather than captures, and say so: there is no public dump of a generated
limine.conf to paste in, so they were rebuilt from the writer that produces
them and from limine's own CONFIG.md.
Two things worth knowing if you extend a parser:
snapper statusis plain text even under--jsonoutin 0.13.1, so it is parsed line by line whilelistandlist-configsare read as JSON;snapper --jsonout listkeys its array by the config's own name, not by a fixed field, so{"root": [...]}is what comes back.
License
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
tui-snapper
command
Command tui-snapper is a terminal UI for btrfs snapshots managed by snapper.
|
Command tui-snapper is a terminal UI for btrfs snapshots managed by snapper. |
|
internal
|
|
|
snapper
Package snapper holds the model tui-snapper renders, the actions it can perform, and the interface every backend satisfies.
|
Package snapper holds the model tui-snapper renders, the actions it can perform, and the interface every backend satisfies. |