tui-tools

tui-update

Pending package updates, what they restart, and a snapshot first

A terminal UI (TUI) for pacman, apt and dnf on Linux: the updates waiting on your machine.

v0.3.1betapackagesMIT
The pending list: current to new, repository, size and the security flag, kernel and firmware first
The pending list: current to new, repository, size and the security flag, kernel and firmware first

A terminal UI for the updates waiting on your machine. It reads them from whichever package manager the machine runs — pacman, apt or dnf — and shows what a list of versions never tells you: what applying them will cost.

Every pending update is on one screen, with its current and new version, its repository, its size and its security flag where the manager publishes one, and the kernel and firmware sorted to the top.

enter gives you the plan: the manager's own dry run, the restart classification — nothing, the services holding replaced code open, or a reboot with the reason — whether a snapper snapshot can be taken first, and the whole sequence in order.

U applies it behind a confirm dialog carrying that sequence, streaming the output as it goes. It never reboots by itself: when the upgrade needs one it offers R, confirmed too.

On the plan, m cycles the upgrades the manager really has, security-only on dnf included, and s turns the snapshot pair off and on. On the list, H holds a package at its version, or lifts the hold.

Run tui-update --demo to drive a sample machine: every key works, every command is previewed for real, and nothing touches your system.

Keys

KeyAction
enter / pPlan: what applying the updates would do
UApply the plan, after confirming the whole sequence
mOn the plan: cycle upgrade, dist-upgrade (apt) and security-only (dnf)
sOn the plan: take the pre/post snapper snapshot, or do not
hThe package manager's own transaction history
HHold the selected package at its version, or lift the hold
tThe unattended-update timers, with enable and disable
e / dOn the timers screen: enable or disable the unit
RRe-read; on a finished upgrade, offer the reboot
/Filter the packages across every column
?Help
qQuit

Press ? inside the tool for the full help screen, which is generated from the same action table as the key map.

Compatibility

This tool is a face on the program below, so its version matters. Tested is not a claim: a version lands there only after the tool's own suite passed against it on a real machine in the lab, and the run is kept as evidence in the repository. At startup the tool probes the backend once and says what it found in its header — a version nobody has run against is shown as (untested) in the warning colour, one older than the minimum as (below minimum) in the error colour. Neither stops it: the backend still refuses what it cannot do, in its own words.

pacman

pacman ≥ 6.0
Binary
pacman
Minimum
6.0
Probed with
pacman --version
Tested
7.1.0

What changes on older versions

  • pacman >=6.0

    pacman publishes no security metadata, so no update is ever marked as a security fix here; the column reads n/a rather than no

  • pacman >=6.0

    there is no dry run that does not first synchronise the databases, which needs root, so the plan quotes the pending list instead of a simulated transaction

  • pacman >=6.0

    checkupdates needs pacman-contrib *and* fakeroot, since it builds its private copy of the sync database under it; without either the pending list falls back to pacman -Qu, which is whatever the last pacman -Sy left on disk, and the screen says so

  • pacman >=6.0

    on Omarchy Server the upgrade runs through omarchy-server-update run --no-reboot, and omarchy-server-update-restart --dry-run is what classifies the restarts; on plain Arch there is no classifier and the package names decide

apt

apt ≥ 2.0
Binary
apt
Minimum
2.0
Probed with
apt --version
Tested
2.8.3

Needs a version this new

  • solver3since 2.9.3

What changes on older versions

  • apt >=2.0

    a security update is one whose pocket ends in -security; apt publishes no advisory id, so that pocket name is the whole reference

  • apt >=2.0

    neither apt list --upgradable nor apt-get -s upgrade reports a per-package size, so the size column is not shown; the plan carries apt's own download and disk totals instead

  • apt >=2.0

    the services to restart come from needrestart -b, which reads other processes' memory maps and therefore needs root; without it the package names decide

  • apt >=2.0

    there is no upgrade that applies only the security updates; unattended-upgrade honours Unattended-Upgrade::Allowed-Origins, which on Debian includes the plain archive too and can reboot on its own, so the security-only mode is not offered here

  • apt >=2.0

    a package is held with apt-mark hold, the dpkg selection every apt front end honours; apt-mark showhold is the unprivileged read the pending list marks its rows from

dnf

dnf ≥ 4.0
Binary
dnf
Minimum
4.0
Probed with
dnf --version
Tested
5.2.185.4.1

Needs a version this new

  • dnf5since 5.0

What changes on older versions

  • dnf >=5.0

    dnf --version prints dnf5 version 5.2.18.0, where dnf4 prints a bare 4.24.0 on its first line; both are read by the same pattern, which keeps three components because a four-part version is not one the family schema records

  • dnf >=5.0

    dnf needs-restarting refreshes the repository metadata before answering, which a read path must not do, so the standalone needs-restarting binary from dnf-plugins-core is used instead and its absence falls back to the package names

  • dnf >=4.0

    dnf check-update exits 100 when there are updates, so its exit code is interpreted rather than treated as a failure; it also prints only the new version, and the installed one is read from rpm

  • dnf >=4.0

    dnf upgrade --security narrows the same transaction to the advisories, so the security-only mode is the plain upgrade with one flag rather than a second code path; dependencies with no advisory of their own still come along

  • dnf >=4.0

    holding a package needs the versionlock plugin, which a minimal install does not carry; it is detected before the key is offered, and the refusal names python3-dnf-plugin-versionlock, or dnf-plugins-extras-versionlock on RHEL and its rebuilds

Maintain pacman, apt or dnf? You are welcome to help shape this tool. About → maintainers

Guides

Written after the work, from a machine the commands were actually run on. Every guide that mentions tui-update is listed here.

  • Transactional updates on a small server

    The upgrade runs inside a btrfs snapshot of the root; at reboot two renames swap it in, and rollback is the same two renames backwards. Built, measured and put through a 34-item acceptance list on a Secure Boot VM.

Install

Once per machine, then every tool in the family is one pacman away.

Add the repository
$ curl -fsSL https://pkgs.tui.tools/install.sh | sh
Or run the same thing yourself, one command at a time

A family whose whole promise is preview before you run is not going to insist you pipe a script into a shell. These are the commands that script runs.

The one-time setup, by hand
$ curl -fsSL -o /tmp/tui-tools.asc https://pkgs.tui.tools/pubkey.asc
  sudo pacman-key --add /tmp/tui-tools.asc
  sudo pacman-key --lsign-key \
    "$(gpg --show-keys --with-colons /tmp/tui-tools.asc | awk -F: '/^fpr:/{print $10; exit}')"
  printf '[tui-tools]\nServer = https://pkgs.tui.tools/arch/$arch\n' \
    | sudo tee -a /etc/pacman.conf
  sudo pacman -Sy
Arch Linux · tui-update
$ sudo pacman -S tui-update

Upgrades then arrive with the rest of your system updates.

What it can do to your machine

  • Preview, then confirm

    Every change is shown as an exact command line and confirmed before it runs. The dialog and the runner receive the same value, so the preview cannot drift from what executes.

  • Escalates for actions

    Reads are unprivileged and never refresh the manager's metadata: checkupdates, apt list --upgradable, dnf check-update and rpm -q all answer to any user from what is on disk. Escalation is sudo -n, which never prompts, and covers the restart classifiers plus every change.

  • No daemon, no state of its own

    Nothing keeps running after you quit, and nothing is installed to run later. The system is the source of truth; the tool re-reads it after every change.

  • No network access

    The tool opens no network connection of its own. No telemetry, no update check, no phone home.

  • Static binary

    Released statically linked, with no runtime dependencies to install.

  • Signed release, with provenance

    checksums.txt carries a keyless cosign signature from this repository's release workflow, every archive has a CycloneDX SBOM, and every archive, package and the checksum file carry SLSA build provenance. The security page shows the three commands that check it.

Reporting

Report a security issue privately, through GitHub's private vulnerability reporting on the repository's Security tab. The family-wide policy, and what counts as in scope, is on the security page.

Downloads

v0.3.1 · 2026-09-26
FileSizeSHA-256
checksums.txt1 KB—
checksums.txt.sigstore.json10 KB—
provenance.intoto.jsonl12 KB—
tui-update-0.3.1-1-aarch64.pkg.tar.zst1.7 MBedd4b119880184e5719226a35a7414308d648f9342ba5597229c67819f4e9a24
tui-update-0.3.1-1-x86_64.pkg.tar.zst1.9 MB728541603650afbdfadad05f8cc26919064836051578aa51db6e8718dde9ef9a
tui-update-0.3.1-1.aarch64.rpm1.7 MB718bfb84de0af1f57eb3b31bc758d434c0611912065c8dd68ca6727061947204
tui-update-0.3.1-1.x86_64.rpm1.9 MB7fe289ceb652c59c1ca465528558a8fca0374b5b099edabff4890bba72ae2be5
tui-update_0.3.1_amd64.deb1.9 MB8a31d831b24483fd266e007d50deea64c454ea2e84ba9c583a2f1a9a36981676
tui-update_0.3.1_arm64.deb1.7 MBf4ec112d6b82bb1aa810704ae73f954fde96e3044f6002303f4bc8900d29eb7f
tui-update_0.3.1_linux_amd64.tar.gz1.9 MB3b1fe84353fa635718b2cb138f54ddb293d908bcc98c3be0b7336dd989e90683
tui-update_0.3.1_linux_amd64.tar.gz.cyclonedx.json23 KB0d559d877e42bbef3ef8c42c0fe7ec0df967a0adee293464ba38145337c0d553
tui-update_0.3.1_linux_arm64.tar.gz1.7 MBba296ebf81c5f938472d67a0a9538445a71a95bb7ba18d5ada5fd08d421fa84d
tui-update_0.3.1_linux_arm64.tar.gz.cyclonedx.json23 KB32f947f5b8ff57b3d949ef5de1be0c49de2f1e209c1fb6ea7e921afe98274037

Every release ships a checksums.txt. Download it next to the archive and run sha256sum -c checksums.txt --ignore-missing. See verifying a download.

Releases

v0.3.1

2026-09-26
## tui-update v0.3.1

tui-update 0.3.1

- The pre/post snapshot pair around an upgrade now completes: the post snapshot is created with the number the pre snapshot printed, and the preview shows where it goes. If the pre snapshot fails, the upgrade does not run; if a later step fails, the pre snapshot's number and the command that removes it are shown.
- The snapshot commands now select the snapper configuration correctly (`snapper -c root create ...`). Before, `root` was taken as the snapshot's cleanup algorithm.
- apt and dpkg progress lines that redraw in place show only their final state in the apply pane instead of every state glued together.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* c27859348a1efe08c5036adbb7f359edd5405ede 0.3.1: pair the snapper post snapshot by number; clean apt progress in the apply pane (#25)

v0.3.0

2026-09-26
## tui-update v0.3.0

tui-update 0.3.0

tui-kit v0.4.4 runs every command in a session with no terminal, so a program that asks a question fails at once instead of hanging behind the screen. The two upgrades that could ask are now handled.
- **apt upgrades ask nothing.** The upgrade runs as `sudo -n env DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold upgrade` (or `dist-upgrade`). debconf takes its defaults, needrestart restarts the services left on replaced libraries, and a config file changed on the machine keeps the local version (the package's version is left beside it as `.dpkg-dist`). The confirm dialog shows the full command line and explains all three parts.
- **Omarchy Server's updater gets the terminal.** `omarchy-server-update run --no-reboot` is interactive by design, so it no longer runs as a background step. After the same preview and confirm, tui-update hands the terminal over to it (`tea.Exec`). When it exits, the screen comes back, the sequence continues and the machine is read again. It is previewed and run with `sudo` rather than `sudo -n`, because it has a terminal where sudo can ask for a password.
- **`--demo-machine ubuntu|omarchy`** runs both paths on a sample machine (the default is `fedora`). On the Omarchy sample machine, the hand-off shows on the real terminal what would run there and waits for Enter.
- README: new section "Nothing asks behind the screen", two new screenshots (`confirm-apt`, `confirm-omarchy`), and the install snippet now points at 0.3.0.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 9e2a37ec30b4eecc8f96444b6c29af9643e555e9 Non-interactive apt upgrades; Omarchy's updater as a hand-off (#21) (#22)

v0.2.2

2026-09-25
## tui-update v0.2.2

tui-update 0.2.2: tui-kit v0.4.4

tui-update 0.2.2 moves to tui-kit v0.4.4: every package manager step now runs in its own session without a controlling terminal, so a step that would prompt fails at once instead of hanging behind the UI, and a failed step reports its meaningful line, cleaned of escape codes. Command previews are POSIX shell-quoted (the dnf and rpm query formats now read quoted), `--check` keeps emitting no null now that kit commands carry an Env list, the README screenshots are re-rendered with continuous box borders, and the README banner now carries the kit's stability markers.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 614c88d83365a3521ffb75afcb889789bdeef220 0.2.2: tui-kit v0.4.4 (#20)
* e9ffe64b4c134bb633323be77e10ecd6464c3ad8 Bump anchore/sbom-action/download-syft in the github-actions group (#18)
* 33a9dda56d8e18f83c25cc73e0a30e46f65600b1 Bump the github-actions group with 4 updates (#19)
* a2d8ca13eebccd647c7eac80d4ac9d37a90a352e README: link the tool's page on tui.tools (#17)
* eac0a70d16983a6e91c0f62606858f42c53aebb7 README: point the install snippet at the current release (#16)

v0.2.1

2026-09-02
## tui-update v0.2.1

tui-kit v0.3.0: the dialogs wrap and scroll instead of clipping the
command preview, and the picker filters as you type.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* a449f959cfb784dd3aa0290f0bab1c2a273e1cef Bump tui-kit to v0.3.0 (#15)

v0.2.0

2026-09-02
## tui-update v0.2.0

Manage, not view (wave 3): create what does not exist, edit what does; lab-validated.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 1c308b158e3d10bc1e4dfcb821f91130df704870 A code of conduct that fits on one screen (#11)
* dd66fddfa54f0222baf514944ecd759af3701c68 Add --report, and a bug form that asks for it (#12)
* 4b4f68d46a38946fb4c5fe0c95b597f65666c865 Carry the OpenSSF Best Practices badge (#9)
* a3c836b10a8dd8acb40a9ed98234a490811efd39 Fixtures from nobody's machine (#3)
* a7d445d803d6ae94f4e278527f706c5816b79e67 Let the kit detect the manager, and keep only the update-specific half (#2)
* 470268612261b2a989e17d20f8587d0a7dc1f2f7 Pin what CI runs, fuzz the parsers, and say the releases are signed (#4)
* e0b3c069cc2deb9aabc5b80a41750c1f9507c1e3 Point the screenshot manifest at the frame that exists (#13)
* b6676160318032cd8b5aaec6713fb6b18dfeb170 Print the whole tag message, not only its body (#8)
* 3571ba88332b9258f0a7b91c33f98b4e04f7c79f Say beta once, and fetch the tag annotation before releasing (#7)
* 1b57822d674deb74764e2fa170f0340eba93a8ab Security-only upgrades, snapshot toggle, package holds (#14)
* e2d1e8b5b3e023b617ef9a39b7f252c0d7b761ad Take the govulncheck note back out of ci.yml (#6)
* d618c77fb260215b1c9f390c8f4b76596939d5b0 Take tui-kit v0.2.8: hashed pip install, provenance bundle on the release (#10)

v0.1.2

2026-08-30
## Changelog
* f0b4702892daafa88d6d04d97bbee1aadb610123 Drop cross-repo dispatch; the package repo and the site poll releases
* 37bfa305e336457c3ac889a0512071c264dec23f Gate the release on a security job, and sign what it publishes (#1)
* 636a0a24903b2519b205a8549fcd6214bfc68d65 Stop telling readers the packages are unpublished

v0.1.1

2026-08-30
## Changelog
* 6e872cc9b7abad19ddb1c868899e70d58f3b81df Packages are available from pkgs.tui.tools
* 250cde4e769a6120d4907b5aa6ef944267140bf5 Re-pin the kit at v0.2.3
* d9c3ed1e2a9654203aa7fe29a022bf9f4782066d Read the pending list on a machine checkupdates cannot run on
* 043fce7b7933b4c59931515c9c0fc8becc780d07 Read the version banners from the captured fixtures
* 43d2b76c3bc5d3e0d8a1fe004e9763af1eeebee4 Ship the tool as a .deb, an .rpm and a pacman package
* 4f49f0f222f37e2be5cd8f81e29e35f60449ea96 Show the apply screen the README was missing

v0.1.0

2026-08-30
## Changelog
* 7429a1b25376a82aae1d0d75ffa9410d7c0e3682 Initial commit
* 30753be833bd60f2b7d1085c988d38d5c089b865 Make tui-update the machine's update screen