A minimalist, hardened status bar for Wayland.
tkbar is small and opinionated on purpose: every optional piece is feature-gated so it can be compiled out, and what doesn't exist is hardcoded by design. A single TOML file tunes the layout and behaviour, a CSS file can restyle everything, and the rest is baked in. Want to go deeper? Fork it, or open a PR with proper feature gating (See CONTRIBUTING.md) .
- 🖥️ Workspace compositors — niri (default), Hyprland, and sway, selected at build time via Cargo features.
- 🧩 Components — clickable workspaces, battery, Wi-Fi (SSID & signal), brightness, volume, clock, spacer and a logo glyph.
- 📐 Any edge — vertical (
left/right) or horizontal (top/bottom) bar, with adjustable thickness. - 🎨 Themes — ten compiled-in color themes, plus an optional user CSS file that overrides the base stylesheet.
- ⚙️ Optional configuration — TOML and CSS live behind the
configfeature, build with--no-default-featuresand no parser is linked at all. - 🔒 Hardened — no
unsafe, no network code, no dynamic loading, no panics on external data, and a minimal, auditable dependency tree (see Security). - 💻 CLI — optional
clapfeature gate provides--help,--version, and--config <path>flags for overriding the configuration file path at runtime. - 🚧 Coming soon — optional
logging(thelogcrate) feature gate, for people who want more batteries included.
The bar aims for a minimal dependency tree: every crate and library is code that runs with your full user privileges, so only what is strictly needed is pulled in. There is currently no logging framework (just eprintln!), and config parsing is feature-gated so it can be dropped entirely.
| Dependency | Notes |
|---|---|
| Rust (edition 2024) | stable toolchain for builds; nightly only in the devshell for rustfmt/clippy |
| GTK4 >= 4.12 | C library, resolved through pkg-config |
| gtk4-layer-shell >= 1.0 | C library implementing the layer-shell protocol |
| glib | comes with GTK4 |
With the default config feature, three direct Rust crates are added: directories, serde, toml. Of these, only serde is already present transitively via the default niri backend (pulled in by niri-ipc), so it adds no new transitive code. directories and toml are genuinely new dependencies, and toml in turn pulls in its own small tree. If you build with the hyprland backend instead of niri, serde is also a new dependency. The sway backend pulls in serde, serde_json, and thiserror (via swayipc/swayipc-types). Build with --no-default-features to drop the config crates entirely: no TOML parser is then linked at all.
| Dependency | How it is used | Trust level |
|---|---|---|
| GTK4 / Pango / Cairo / FreeType | linked, renders everything | large audited C codebase, keep it updated |
| gtk4-layer-shell | linked, positions the window | small C library |
| a Wayland compositor (niri, Hyprland, or sway) | Wayland protocol | fully trusted, see SECURITY.md |
| the compositor's IPC (niri IPC / Hyprland IPC / sway IPC) | Unix socket, workspace list/buttons | trusted (it is the compositor) |
wpctl (WirePlumber) |
spawned to get/set volume and mute | local daemon client, output parsed defensively |
nl80211 (neli-wifi) |
kernel netlink socket, reads Wi-Fi SSID/signal | linked, carries untrusted data (SSID) |
sysfs (/sys/class/backlight, /sys/class/power_supply) |
read directly; backlight also written directly (needs group write access, see the NixOS module) | kernel-provided |
All spawned tools are looked up in PATH. The Nix package wraps the binary so that PATH resolves them from pinned, absolute /nix/store paths.
The flake ships a NixOS module (nixosModules.default) that installs the bar and can grant backlight write access without any setuid binary or external tool:
{
imports = [ tkbar.nixosModules.default ];
programs.tkbar = {
enable = true;
backlight = {
enable = true; # udev rule for /sys/class/backlight/*/brightness
users = [ "alice" ]; # members of the backlight group
group = "video"; # or reuse the conventional video group
};
};
}With backlight.enable, a udev rule gives the tkbar-backlight group (default) write access to /sys/class/backlight/*/brightness, and the bar writes sysfs directly. On an already-booted system, apply it after nixos-rebuild switch with sudo udevadm trigger --subsystem-match=backlight --action=add. See docs/CONFIGURATION.md for all module options.
Optional and strictly validated: unknown keys are rejected, and a missing file
falls back to the hardcoded default. See
docs/CONFIGURATION.md for the full TOML format, the
optional CSS overloading, and how to select a theme or drop the config
feature from Nix.
When built with the cli feature (enabled by default):
tkbar [OPTIONS]
| Flag | Description |
|---|---|
--help |
Print help information |
--version |
Print version |
--config <PATH> |
Path to configuration file (requires the config feature) |
Example:
tkbar --config ~/.config/tkbar/alt.tomlThe --config flag overrides the default $XDG_CONFIG_HOME/tkbar/config.toml
path for that invocation only.
A status bar runs unsandboxed with your full user privileges, so attack surface
is taken seriously: minimal dependencies, no unsafe, no panics on external
data, no network code, no dynamic loading, and a pinned, reproducible build.
See docs/SECURITY.md for the full threat model, the enforced security properties, and how to report a vulnerability.
I was unsatisfied with the state of Wayland bars and shells (bloat, huge attack surface, not enough config options, protocols left unhandled), so I wrote a minimal thing that worked for me. It has since grown into a bar meant to work for anyone: multiple compositor backends, themes, Nix packaging, and a documented security model, without giving up on minimalism.







