1.0.0 was the first official, tested release; see Releases for the current version and CHANGELOG.md for what changed. The web interface (
fettle web) is still experimental.New in 1.20.0 and marked experimental: the startup-persistence checks added to
compromise-check(socket, path and drop-in units, generators, init scripts,profile.d, MOTD and autostart files, file-content signals, and the startup inventory with its baseline diff) and theauditingaxis inhardening-audit. Every threshold in them was measured across six hosts and every one has tests, but they have not yet been through a full hand-run QA pass on real machines the way the rest of the tool has. Treat their findings as worth reading and not yet as settled.
in fine fettle — in good working order.
fettle is a cross-distribution Linux system-maintenance and supply-chain tool. One command surface keeps your machine updated and clean, audits where your software came from and whether it has been tampered with, checks how the system is hardened, and scans the firmware / boot chain for security posture — on Arch/Manjaro, Debian/Ubuntu and the RHEL family alike.
It is the Python successor to the Arch/Manjaro update.sh, aur-precheck.sh, and
supply_chain_check.sh scripts (from
linux_hacks), rebuilt around a
pluggable per-distro backend so a new distribution is a single new class, and with
real unit-test coverage the bash originals never had.
- Pure Python standard library — zero third-party runtime dependencies.
- Python 3.11+ (uses
tomllib). - Nothing to
pip install: install a package, or run the repo in place.
📖 The full manual is in the wiki. This page is what fettle is, whether it runs on your machine, and how to install it.
- What it does
- Supported distributions
- Requirements
- Installation
- Quick start
- Documentation
- fettle vs. topgrade
- Changelog
- License
fettle has six feature families.
- Maintenance — update packages, clean caches, prune orphans, check for rebuilds/service-restarts, review config-file drift, report whether automatic updates are enabled, apply firmware updates, and manage kernels.
- Package Supply Chain — where software came from and whether it's tampered:
third-party repos/PPAs, publishers, staleness, sandbox permissions, and (for
the AUR) live malware-IOC feeds. Exposed as
pkg-audit, pluspkg-integrity(do the installed files still match the package?), the Arch-specificaur-audit, and the install-time yay hook (aur-precheck). - System Supply Chain — the machine's firmware/boot/hardware posture:
Secure Boot, BIOS/UEFI, TPM, Intel ME, CPU microcode, hardware and storage
firmware. Exposed as
sys-audit, runnable locally or over SSH. - System Hardening — is this machine configured safely? Ten independent
axes: were the installed binaries built with the distro's hardening flags, can a
local user tamper with shared directories, how much of the system can each
running service reach, are the kernel's runtime protections switched on, what is
sshd actually configured to do, is a firewall both active and filtering, are
any TLS certificates expired, is AppArmor confining anything or merely
switched on, what mode SELinux is in, and will a change to the startup
locations leave any record at all (
auditing, experimental). Exposed ashardening-audit(-H). - Security advisories — is what you have installed known-vulnerable?
Per-package CVEs from your distro's own tracker, including the ones you're
vulnerable to with no fix released yet, plus the Python/Node/Rust packages
your distro doesn't manage, via OSV. Exposed as
advisory-check. - Compromise indicators — is something already here? What starts at boot that
no package installed (units, timers, sockets, path units, drop-in overrides,
generators, init scripts,
profile.d, MOTD and autostart scripts, cron,at), what those files say (LD_PRELOAD, a download piped into a shell, a bash network redirection), the loader and kernel (/etc/ld.so.preload, unsigned modules, kernel taint nothing explains, the eBPF surface, processes hidden from/proc), what is running (executed from memory, deleted-but-running, listening sockets nothing vouches for) and the boot chain. It also records a startup inventory and reports what changed since the previous run, which is the only check here that can see a package-owned file edited in place. Exposed ascompromise-check(-M). It reports anomalies to investigate and never a fix — if a finding is real, running the fix destroys the evidence.
The last two are the pair most easily conflated, and they are separate actions because they have different answers: hardening asks whether the machine is configured safely, compromise asks whether something is already here. One ends in a command to run; the other ends in something to look at before you touch anything.
Three of the names are easy to confuse, so they are kept deliberately distinct in
code, docs, and CLI:
"where did this software come from / is it tampered?" → Package
(pkg-audit); "is the machine's firmware/boot sound?" → System (sys-audit);
"is the machine configured safely?" → Hardening (hardening-audit).
All six run three ways. Locally; over SSH against one host or a named group,
with nothing installed on the far side — fettle ships itself (fettle remote); and
into a report — every run saves one under ~/.fettle/ with a JSON sibling, and
fettle report builds a multi-host HTML dashboard with a per-host verdict.
upgrade-check (-U, experimental) adds an AI second opinion on a pending upgrade
set, local or remote.
One invariant runs through all of it: a check that cannot look never renders like a clean result. Every audit distinguishes "found nothing wrong" from "could not tell", and says which it means.
| Family | Backend | Package tooling | Detected ID / ID_LIKE |
|---|---|---|---|
| Arch / Manjaro | arch |
pacman + yay/pamac + AUR | arch, manjaro, endeavouros, … |
| Debian / Ubuntu | debian |
apt/nala + flatpak + snap | debian, ubuntu, linuxmint, pop, … |
| RHEL family | rhel |
dnf + rpm + podman | rhel, centos, rocky, almalinux, ol |
RHEL support is complete, at parity with Debian: every action except the three that
are Arch-only by nature (aur-audit, aur-precheck, and python-rebuild-check, which dnf
handles itself). Fedora is deliberately not claimed as a distro: it shares dnf, but its
advisories come from Bodhi as FEDORA-* rather than Red Hat's RHSA-* (--distro rhel
still works there, and is how the dnf5 code path is tested).
● supported · — not applicable to this family · ✔︎ runs by default when you type plain
fettle. Per-distro behaviour is in Maintenance actions; this is
the at-a-glance "will it run on my box" view.
| Flag | Arch | Debian | RHEL | Default | |
|---|---|---|---|---|---|
| Update everything | -u |
● | ● | ● | ✔︎ |
| Refresh metadata + report upgradable | -O |
● | ● | ● | |
| Clean package caches | -c |
● | ● | ● | ✔︎ |
| Orphaned / unused packages | -o |
● | ● | ● | ✔︎ |
| Pending reboot, rebuilds & restarts | -r |
● | ● | ● | ✔︎ |
| Pending config-file merges | -d |
● | ● | ● | ✔︎ |
| Automatic-update posture | -x |
● | ● | ● | ✔︎ |
| Firmware updates | -f |
● | ● | ● | ✔︎ |
| Kernel management | -k |
● | ● | ●¹ | |
| Supply-chain audit | -P |
● | ● | ● | ✔︎ |
| Binary hardening audit | -H |
● | ● | ●² | |
| Compromise indicators | -M |
● | ● | ● | ³ |
| Container image updates | -C |
● | ● | ● | |
| Python rebuild check | -y |
● | — | — | ✔︎ |
| AUR health census | -A |
● | — | — | |
| AUR compromise (IoC) scan | -P |
● | — | — | ✔︎ |
| 16/16 | 13/16 | 13/16 |
The three gaps are the same on Debian and RHEL and are Arch-only by nature — there is no AUR elsewhere, and both apt and dnf handle Python interpreter transitions themselves. So Debian and RHEL are complete, not partial.
¹ Reported, never removed: dnf enforces installonly_limit and prunes old kernels itself.
Arch and Debian do offer removal, because pacman and apt do not.
³ Not in the default set — it needs root, and a compromise finding is not something
to meet in a routine maintenance run. It is swept by --everything, where it runs
last: an update removes a vulnerable package and does not remove an implant.
² Needs checksec, which is not packaged for RHEL 10 — EPEL included — so in practice
this cannot run there yet. The code and tests are in place for when it is. Both checksec
generations are handled: 3.x (Arch) and 2.x (Fedora, Debian, Ubuntu), which share no
command line.
Distro-independent features work the same everywhere: the sys-audit firmware/boot
scan (-S — every one of its checks is distro-neutral), fettle remote over ssh, the
AI upgrade checker (-U), the HTML report and the web UI. advisory-check has native CVE
feeds for Arch, Debian, Ubuntu and RHEL, plus language dependencies via OSV. And
pkg-audit covers the same seven ecosystems on every distro — the native one
(AUR / apt / dnf) plus flatpak, snap, containers, GNOME extensions, VS Code extensions and
GitHub CLI extensions.
Ubuntu-specific: on a host not attached to Ubuntu Pro, apt cannot see the
esm-infra / esm-apps pockets, so the number of available security updates it reports is
smaller than reality. fettle reads pro security-status and says so — both in the upgrade
preview when updates are being withheld, and in -x, which names how many installed
packages (typically Universe/Multiverse) receive no security updates at all without a
subscription.
Three RHEL-specific things worth knowing:
-u --dry-runshows upgrades only. dnf has no rootless equivalent ofapt-get -s—dnf upgrade --assumenoresolves the complete transaction but refuses to run without root. The preview says so rather than passing a partial answer off as a complete one; add--full-previewto elevate and see new dependencies and removals too.- An upgrade from a repository with
gpgcheck=0asks one extra time. Those packages are installed without verifying their signature.--yesproceeds, loudly. -onever offers a kernel for removal. dnf's ownautoremovehas been known to propose removing kernels when thednf markreason data is incomplete, and removing a running one leaves an unbootable machine. Installonly packages are held back and named, and if the query that identifies them fails, nothing is offered.
On an image-based host (rpm-ostree, Fedora Silverblue, RHEL Image Mode / bootc)
fettle refuses to dnf-upgrade at all and points at bootc upgrade / rpm-ostree upgrade, because a dnf transaction there does not survive a reboot.
Detection reads /etc/os-release and falls through the ID_LIKE chain, so
derivatives resolve to their parent family with no extra code. Override with
--distro <name> (handy for dry-runs of another backend).
Only Python 3.11+ and git are mandatory. Everything else is optional: fettle never installs tools — it detects what's present and skips what's missing with a note, so you install only what the commands you actually use need.
| Arch / Manjaro | Debian / Ubuntu | |
|---|---|---|
| AUR / extras | yay or pamac |
— |
| rebuilds | rebuild-detector (checkrebuild) |
needrestart |
| config drift | pacman-contrib (pacdiff) |
(built-in dpkg) |
| orphans | (built-in) | apt-show-versions; deborphan if present, else dpkg reverse-deps (built-in) |
| firmware | fwupd |
fwupd |
| kernels | mhwd-kernel (Manjaro) |
(built-in dpkg) |
| flatpak / snap | — | flatpak, snapd |
hardening audit (-H) |
checksec |
checksec |
compromise indicators (-M) |
bpf — not bpftool |
bpftool |
(RHEL family: checksec and bpftool, both dnf install.)
Every tool above is optional and its check is skipped with a note when absent. Two are worth installing before you rely on the audits:
sudo pacman -S checksec bpf # Arch / Manjaro
sudo apt install checksec bpftool # Debian / Ubuntu
sudo dnf install checksec bpftool # RHEL family (checksec needs EPEL)On Arch and Manjaro bpftool ships in the bpf package — there is nothing called
bpftool in the repos or the AUR. Debian, Ubuntu and the RHEL family each name the
package after the binary.
Installing it is half the job: bpftool also needs root to list anything. Without
it, -M examines only pinned BPF objects, and a program can be loaded and attached
without ever being pinned. fettle -M elevates itself, so a plain run gets both — it is
only fettle -M --dry-run that stays unprivileged and reports the gap.
Nothing extra is required — the AUR audit uses only pacman + the network, and
the APT/Flatpak/Snap providers read config you already have.
| Arch / Manjaro | Debian / Ubuntu | |
|---|---|---|
| standard | (none — uses pacman) |
debsums (file integrity); flatpak, snapd if you use them |
| manual | (none) | (none) |
Standard packages (install what you want covered; missing ones are skipped):
# Arch / Manjaro
sudo pacman -S --needed mokutil efitools dmidecode inxi lshw pciutils \
tpm2-tools smartmontools cpuid fwupd pacutils
# Debian / Ubuntu
sudo apt install mokutil efitools dmidecode inxi lshw pciutils \
tpm2-tools smartmontools cpuid fwupd debsumsWhich check uses what: secureboot → mokutil/efitools (+ systemd's bootctl);
bios·hardware → dmidecode,inxi,lshw,pciutils,cpuid; fwupd → fwupd;
intel-me → pciutils; tpm → tpm2-tools,dmidecode; storage →
smartmontools; packages → pacutils (paccheck) on Arch / debsums on Ubuntu.
Manual tools (not in standard repos — the checks degrade to advice without
them). fettle looks for each under /opt/<name>/, /usr/share/<name>/, and
~/<name>/:
| Check | Tool | Get it |
|---|---|---|
firmware |
chipsec (chipsec/chipsec_main.py) |
Arch: AUR chipsec; else git clone https://github.com/chipsec/chipsec |
intel-me |
Intel CSME Version Detection Tool (intel_csme/intel_csme_version_detection_tool) |
download from Intel |
tpm |
tpm-vuln-checker (tpm-vuln-checker/tpm-vuln-checker) |
git clone https://github.com/google/tpm-vuln-checker |
Example: git clone https://github.com/google/tpm-vuln-checker ~/tpm-vuln-checker
puts the tool where the tpm check will find it.
Packages for each release are on the releases page. Every one is built and then installed and run in a clean container of its own distro before it is published.
sudo apt install ./fettle_*_all.deb # Debian, Ubuntu
sudo dnf install ./fettle-*.noarch.rpm # RHEL, Rocky, AlmaLinux, Fedora
sudo pacman -U fettle-*-any.pkg.tar.zst # Arch, ManjaroThey depend on nothing but a python 3.11+ interpreter, and pull one in on the
distributions whose default python3 is older (RHEL 9, Ubuntu 22.04).
Check what you downloaded against the release's SHA256SUMS:
sha256sum -c SHA256SUMS --ignore-missingNo package for your system? The zipapp runs anywhere there is a python 3.11+, and the prebuilt binary needs no python at all — both are on the same page, and both are described below.
fettle is pure standard library, so there is nothing to build or pip install —
the launcher puts the repo on PYTHONPATH and runs python3 -m fettle.
git clone https://github.com/pasadoorian/fettle.git ~/src/fettle
ln -s ~/src/fettle/bin/fettle ~/.local/bin/fettle # ensure ~/.local/bin is on PATH
fettle --helpUpdate with a plain git pull. To drop it in for the old updater:
ln -sf ~/src/fettle/bin/fettle ~/update.shOne file that runs under any python 3.11+, with nothing to install — the fallback for
a system with no package and a glibc too old for the binary. Attached to each release
as fettle-<version>-zipapp.tar.gz / .zip.
tar -xzf fettle-*-zipapp.tar.gz && cd fettle-*/
./fettle --version # runs in place
sudo install -m 755 fettle.pyz fettle /usr/local/bin/ # or put it on PATHfettle is a small launcher that resolves fettle.pyz beside itself and picks a
suitable interpreter, so install the pair into the same directory. It exists because
python3 is not reliably 3.11+ — it is 3.9 on RHEL 9 and 3.10 on Ubuntu 22.04, and
running fettle under either fails somewhere further in rather than at once.
A single self-contained executable — no python needed, nothing to install. Attached to
each release as fettle-<version>-linux-x86_64.tar.gz / .zip.
tar -xzf fettle-*-linux-x86_64.tar.gz && cd fettle-*/
sudo install -m 755 fettle /usr/local/bin/fettleIt needs glibc 2.38 or newer, so it runs on Ubuntu 24.04, Debian 13, Fedora 40+ and Arch, but not on Ubuntu 22.04, Debian 12 or RHEL/Rocky/AlmaLinux 9. On those, use the distro package or the zipapp — both are on the same release page and both work everywhere. The limit comes from the python runtime compiled into the binary, not from fettle.
fettle --version prints (binary) for this build, so a bug report says which artifact
it came from.
source ~/src/fettle/contrib/fettle.bash # or, system-wide:
sudo ln -s ~/src/fettle/contrib/fettle.bash /usr/share/bash-completion/completions/fettleCompletes every flag and action at the top level, and each subcommand's own options
inside it — so fettle report <TAB> offers --open and --backfill-json, and does
not offer --dry-run, because fettle report --dry-run is not a thing.
fettle sys-audit <TAB> also offers the nine check categories and drops the ones you
have already typed. fettle -S <TAB> completes as sys-audit, since that is what it
runs.
The script is about six lines and knows nothing about fettle's options — it asks
fettle itself, so it cannot fall out of step with the CLI. Each tab press costs
roughly 70 ms.
Two things it deliberately does not do. It completes names, not values: no paths for
--config, hosts for remote, or package names for aur-precheck (sys-audit's
categories are the exception, being a fixed set). And it binds to the fettle command,
so python -m fettle gets nothing — bash completes on the command name, and there the
command is python.
An advisory, warn-only AUR pre-flight that fires at install time — flagging orphaned / out-of-date / stale packages, known-compromised names, and malicious maintainers — on top of yay's built-in build-file review. It never blocks an install.
cp ~/src/fettle/contrib/yay-init.lua ~/.config/yay/init.luaThe hook calls fettle aur-precheck <pkg> under the covers; you can run that
directly too.
fettle # run the default maintenance set (auto-elevates)
fettle -a --dry-run # preview the whole default set; change nothing
fettle -c -u # clean, then upgrade packages (short flags)
fettle clean update # identical — every action also works as a bare word
fettle upgrade # `upgrade` is a synonym for `update`
fettle -O # refresh metadata + report upgradable (no upgrade; safe)
fettle -A # AUR health audit -> ~/.fettle/reports/
fettle -P # package supply-chain audit -> ~/.fettle/reports/
fettle -H # system hardening audit -> ~/.fettle/reports/
fettle -M # compromise indicators: is something already here?
fettle -S # full security scan (sys-audit --all; self-elevates)
fettle -U # AI: is this upgrade safe? [experimental] (needs API key)
fettle report # multi-host HTML dashboard from the saved reports
fettle remote host -u # any action on another box over ssh (nothing to install there)Everything else — every action, every flag, every config key, and the reasoning behind the defaults — is in the wiki.
| Page | What's in it |
|---|---|
| Maintenance actions | Reading fettle's output, the full action table per distro family, what each action actually runs, --everything, and previewing an upgrade |
| Package supply-chain | pkg-audit, pkg-integrity, aur-audit, aur-precheck — provenance, IoC feeds, and the pre-upgrade gate |
| System hardening audit | -H and its ten axes, what each one can and can't see, and how to tune or disable them |
| System supply-chain | sys-audit — Secure Boot, TPM, microcode, SPI/BIOS, storage firmware; local and remote |
| Security advisories | advisory-check — distro CVE feeds, OSV for language dependencies, and the warn-gate |
| Remote maintenance | fettle remote, host groups, and how fettle gets itself onto a host that doesn't have it |
| Configuration & reporting | The full config.toml, reports and run logs, fettle report, and the experimental web UI |
| AI upgrade check | upgrade-check — what it sends, what it costs, and why it's experimental |
| Reference | Common options, exit codes, how elevation works, architecture, and development |
topgrade is the closest widely-used tool, and fettle's design was informed by it — so here's an honest comparison. They aim at different problems. topgrade is a broad, cross-platform upgrade orchestrator: it detects the tools you use and runs all of them. fettle is a focused Linux maintenance and supply-chain-security tool with a small, curated command set.
| topgrade | fettle | |
|---|---|---|
| Platforms | Linux, macOS, Windows, BSD | Arch/Manjaro, Debian/Ubuntu and RHEL families only |
| Integrations | ~60+ across many ecosystems | curated: pacman/apt/dnf (+ yay/pamac/nala), flatpak, snap, containers, fwupd, kernels, AUR |
| Language toolchains (pip/npm/cargo/gem…), editors, dotfiles, git repos | ✅ updates them | ❌ deliberately out of scope |
| Tool selection | auto-detects installed tools | explicit per-distro allowlist — config tunes behaviour, never discovers commands |
| Self-update | ✅ | ❌ by design (your package manager owns fettle) |
| Skip / run-only specific steps | ✅ | ✅ (--skip / --only) |
| Dry-run | ✅ | ✅ — plus a full resolved transaction preview (upgrades + new deps + removals) |
| Asks before upgrading | mostly unattended | ✅ by default (--yes for unattended) |
| Remote over SSH | ✅ run topgrade on remote hosts | ✅ any action over ssh, plus remote security scan and remote AI upgrade-check |
| Config | TOML | one flat TOML + a safety gate (refuses world-writable / wrong-owner) |
| Firmware updates (fwupd) | ✅ | ✅ |
| Auto-update posture report (is the system set to auto-update itself?) | ❌ (runs upgrades; doesn't report update config) | ✅ auto-updates (-x) |
| End-of-run summary | ✅ | ✅ (+ next steps) |
| Runtime | single Rust binary | pure Python standard library (any python3; no pip) |
| Maturity / ecosystem | established, widely packaged, large community | released and stable, three distro families (Arch, Debian, RHEL) |
| Package provenance / tamper audit (AUR/APT/Flatpak/Snap) | ❌ | ✅ pkg-audit |
| System hardening audit (build flags, filesystem, services, kernel, sshd, firewall, TLS certs) | ❌ | ✅ hardening-audit (-H) |
| Firmware / boot security scan (Secure Boot, TPM, microcode, chipsec…) | ❌ | ✅ sys-audit |
| AUR IoC scan + install-time pre-flight | ❌ | ✅ pkg-audit, aur-precheck |
| Package-file integrity verification | ❌ | ✅ via pkg-integrity (paccheck / debsums / rpm -Va) |
| Security advisories / CVE tracking (incl. vulnerable, no fix released yet) | ❌ | ✅ advisory-check (distro feeds + OSV for Python/Node/Rust) |
| AI pre-upgrade advisor | ❌ | ✅ upgrade-check (local and remote) |
Which should you use?
- topgrade if you want one command to update everything, everywhere — system packages, language toolchains, editors, containers, dotfiles — with a huge, battle-tested integration set across every major OS.
- fettle if you're on Arch, Debian or RHEL and want maintenance with a security lens: know where your packages came from and whether they've been tampered with, scan your firmware/boot posture, and get an AI second opinion before a big upgrade — through a small, curated, auditable command set with no runtime deps.
They're complementary — it's reasonable to run topgrade for breadth and fettle for the Linux provenance / security / firmware angle. Several of fettle's deliberate non-goals (no self-update, no auto-discovery of commands, no cascading config) are lessons taken from topgrade's rough edges — not a knock on a tool that does far more, on far more platforms, than fettle aims to.
topgrade details summarized from its README and config.example.toml, July 2026.
See CHANGELOG.md for the full, versioned history.
MIT.
