Security
What these tools can, and cannot, do to your machine
These are front ends for programs that change a running system. That is worth being precise about rather than reassuring about. Here is every promise the family makes, what each one is worth, what the release gate proves, and what is still not done.
The principles
Preview, then confirm
No tool changes anything without first showing the exact command line. A tool never assembles a shell string: it builds a command value, the confirm dialog displays that value, and the runner receives the same one. The preview cannot drift from what executes, because they are the same object. That is the whole trust boundary, and it is one small package in tui-kit rather than a rule each tool re-implements.
Read-only by default
Starting a tool only reads. Nothing is written, nothing is scheduled, and there is no first-run setup that changes your machine. If a read itself needs root, as ufw's status does, the tool says so instead of silently escalating.
Runs as you
Reads work unprivileged wherever the underlying tool allows it. Only an action escalates, through sudo -n, which never prompts: run sudo -v in another terminal first, or start the tool with sudo. The prefix is configurable, so doas works, and an empty prefix runs the command directly when you are already root.
No daemon, no state of its own
One static binary. Nothing keeps running after you quit, nothing is installed to run later, and no state is kept beside the system's own. The system is the source of truth; a tool re-reads it after every change, so what you see is what the machine reports rather than what the tool assumed.
No network
The tools open no network connection of their own. No telemetry, no update check, no crash reporting, no phone home. Upgrading means downloading a newer binary, on purpose.
--demo always works
Every tool runs against in-memory sample data with --demo. Every key works and every command is built and previewed for real; the fake runner records what it was asked to run and answers from a table, so nothing reaches your system. It exists so a reviewer can read the previews before ever pointing a tool at a real machine.
What the gate checks
every tag, every packageBefore a tag becomes a release
The security job runs govulncheck over the code the build actually reaches and gitleaks over both the working tree and the whole git history, because a secret committed once stays reachable long after the commit that removed it. gosec runs earlier, inside the lint bar. The release job needs the security job to be green; a red one is a tag without a release.
What ships beside the binaries
A CycloneDX SBOM per archive, a keyless cosign signature over checksums.txt as a Sigstore bundle, and SLSA build provenance attested for every archive, every package and the checksum file. None of it needs a key to be distributed: both signatures are tied to the workflow identity GitHub issued at build time.
Before a package enters the repository
pkgs.tui.toolsaccepts a package only if its sha256 matches the release's checksums.txt, that file carries a valid cosign signature from the tool's own release workflow, gh attestation verify passes on the file itself, and trivy finds no HIGH or CRITICAL vulnerability inside it. Any one failing rejects the package and fails the build; there is no warn-only mode. These are in addition to the repository's GPG signatures, not instead of them: provenance and the scan are about what may go in, the GPG signatures are what your machine checks on the way out.
Best Practices
OpenSSF, passingThe family holds the OpenSSF Best Practicespassing badge, a self-assessment of 67 criteria (process, reporting, quality, security) registered once for every tool, with tui-kit as the repository of record. Every answer names the file or page that backs it.
Scorecard
OpenSSF, weeklyThe score is the aggregate of the 18 checks OpenSSF Scorecardruns weekly against each repository, and it is the same number the badge in that repository's README shows. The chips are every check that did not score 10, so what is still open is on this page rather than one click away.
| Repository | Badge | Score | Measured | Below 10 |
|---|---|---|---|---|
| tui-cert | 7.6 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9 | |
| tui-containers | 7.3 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9Packaging n/a | |
| tui-cron | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-dc | 8.0 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Contributors 3SAST 9Branch-Protection n/a | |
| tui-disk | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-firewall | 7.6 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9 | |
| tui-kit | pending | — | the first weekly run has not been indexed yet | |
| tui-logs | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-network | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9Signed-Releases 9Packaging n/a | |
| tui-router | 8.0 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Contributors 3SAST 9Branch-Protection n/a | |
| tui-samba | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-secure | 7.5 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9Signed-Releases 9 | |
| tui-snapper | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-ssh | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-systemd | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9Signed-Releases 9Packaging n/a | |
| tui-tailscale | 7.4 | 2026-10-12 | Branch-Protection 0CII-Best-Practices 0Code-Review 0Maintained 0Contributors 3SAST 8 | |
| tui-template | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9Signed-Releases n/a | |
| tui-tools | 7.6 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3SAST 9 | |
| tui-traffic | 7.8 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Contributors 3SAST 8Branch-Protection n/aPackaging n/a | |
| tui-update | 7.5 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Packaging n/a | |
| tui-users | 7.4 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Branch-Protection 3Contributors 3Signed-Releases 7SAST 9 | |
| tui-wireguard | 8.0 | 2026-10-12 | CII-Best-Practices 0Code-Review 0Maintained 0Contributors 3SAST 9Branch-Protection n/a |
Verify a release yourself
tui-firewall, as the example$ gh attestation verify tui-firewall_0.2.2_amd64.deb -R tui-tools/tui-firewall$ cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp \
'^https://github.com/tui-tools/tui-firewall/\.github/workflows/ci\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt$ sha256sum -c --ignore-missing checksums.txtWhy the regexp is the point
The identity regexp says the signature has to come from that repository's ci.yml, running on a tag. Without it, cosign would accept anything signed by anyone. Replace tui-firewall with the tool you are verifying; everything else is identical across the family.
Every asset of every release, with its SHA-256 as parsed from checksums.txt at build time, is also in catalog.json, so you can compare before downloading anything.
Every tool, answered
from each tool.json| Tool | Escalates | No daemon | No network | Static | Signed release | SBOM |
|---|---|---|---|---|---|---|
| tui-cert | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-containers | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-cron | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-dc | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-disk | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-firewall | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-logs | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-network | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-router | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-samba | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-secure | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-snapper | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-ssh | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-systemd | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-tailscale | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-tools | actions only | ✓ | ✕ | ✓ | ✓ | ✓ |
| tui-traffic | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-update | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-users | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-wireguard | actions only | ✓ | ✓ | ✓ | ✓ | ✓ |
| tui-template | actions only | ✓ | ✓ | ✓ | — | — |
The first four columns come from each tool's tool.json. The last two do not, because those manifests still declare signed_releases: false — they were written before the gate existed and are being corrected tool by tool — so the columns read the latest release itself: a checksums.txt.sigstore.json is the cosign bundle, a .cyclonedx.json per archive is the SBOM. Where the manifest and the release disagree, the release is the fact. What the gate checks is how they got there and verify a release yourself is how you confirm it without taking this page's word for it.
What is not done yet
openEach of these is one of the checks scoring below 10 in the table above. They are listed here so the score has an explanation on the same page as the number.
Branch rulesets on main
Pull request required, the two CI checks required, no force-push: written and ready, waiting on an organization admin token. Until they are applied, Scorecard's Branch-Protection and Code-Review checks score 0 for every repository, and they are right to.
Fuzzing
No fuzz targets yet. The parsers that read command output are where they would go first, and they are the same parsers gosec's findings were about.
CodeQL
gosec runs in the lint bar and govulncheck in the security job, but no CodeQL analysis runs, which is what Scorecard's SAST check is looking for.
Pinned dependencies
The workflows call actions by tag rather than by commit SHA, and the container images the lab builds are not digest-pinned. Scorecard scores that 0 and the score stays where it is until it changes.
How sudo is used
A tool resolves the binary it drives — ufw, systemctl — to an absolute path, and prefixes it with the escalation prefix from its configuration. The default is sudo -n: the -n means non-interactive, so it never prompts. If your sudo timestamp has expired, the command fails and the tool tells you, rather than a password box appearing inside a TUI where you cannot tell what is reading it.
The whole prefix is one configuration value. Set it to doas, or to the empty string when you are already root:
$ sudo -v$ sudo tui-firewall --sudo ''Verifying a download
$ curl -fsSLO https://github.com/tui-tools/tui-firewall/releases/latest/download/checksums.txt
sha256sum -c checksums.txt --ignore-missingEach tool's page lists the SHA-256 of every asset, parsed from that same file at build time, so you can compare without downloading anything first. --ignore-missing checks only what you have.
What a checksum proves
That the file arrived intact from the same place the checksum did. Both come from the GitHub release, so this catches corruption and a broken mirror, not a compromised release.
Signatures close that gap, and they are in place: verify a release yourself is the check that answers who built the file, not just whether it arrived intact.
Reporting a vulnerability
Report privately, through GitHub's private vulnerability reporting on the affected repository's Security tab. Do not open a public issue. Expect a first reply within a week; this is a small project maintained in spare time, and there is no bounty.
In scope is anything that breaks the trust boundary: a command that executes without having been previewed and confirmed; a preview that does not match what is executed; an argument taken from a config file, a theme file or parsed command output that reaches a shell or changes the meaning of a command line; escalation beyond the configured prefix.
Not in scope is anything you can already do with the underlying tool at the same privilege level. These are terminal front ends, not a privilege boundary of their own. Needing root to change the system is the design, and locking yourself out of a machine with a rule that does so is something the tools warn about but do not second-guess.