swhkd: a hotkey daemon that does not care which display server you run
Sxhkd clone for Wayland (works on TTY and X11 too)
At a glance
- What is it?
- swhkd is a Rust hotkey daemon for Wayland, X11 and even a TTY, with an sxhkd-compatible config format and a split design that keeps the privileged daemon away from your environment variables.
- Who is it for?
- swhkd fits Linux users on Wayland who already have a working sxhkd config, want the same key bindings to keep working, and are willing to run a setuid daemon or a paired server process. It does not fit someone who needs a single process with no privilege split, since the environment handling depends on swhks running alongside swhkd, and the TTY claim covers a session rather than a display server.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 102 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A hotkey daemon that ignores the display protocol
swhkd, which expands to Simple Wayland HotKey Daemon, is a daemon that watches for key combinations and runs shell commands when you press them. The project describes it as a display protocol-independent hotkey daemon written in Rust, and that independence is the whole point of it. The same configuration file works on an Xorg session, on Wayland, and in a TTY, which means a key binding for a terminal multiplexer does not have to be rewritten when you change desktops. The configuration system is inspired by sxhkd, and the project goes further, stating that it attempts to be a drop-in replacement, so an sxhkd config file is also compatible with swhkd. The audience is narrow and clear: Linux users on Wayland who want the sxhkd habit without the X11 dependency, and who are comfortable running a privileged background process.
Two binaries, one privileged and one not
The most interesting design decision here is a split between two processes. The daemon, swhkd, is the privileged process: it listens to key events and runs the shell commands bound to them. The server, swhks, is not privileged: it keeps track of environment variables and sends them to the daemon, which then uses them when running a command. The stated purpose is isolation. The daemon only runs shell commands that have been parsed from the config file, and the documentation says there is no way to run arbitrary shell commands through the server, which only forwards environment data and nothing else. The environment is not hard-coded into the daemon. It is sourced by the SWHKS binary running in the background and then supplemented to the command that will run, which emulates the environment of the default shell. Commands execute through SHELL -c 'command', so the environment comes from whatever the default shell provides.
The config path is ~/.config/swhkd/swhkdrc
Configuration is where most of the day-to-day friction lives, and swhkd tries hard not to add any. The default configuration file is in ~/.config/swhkd/swhkdrc, with a fallback to etc/swhkd/swhkdrc. The syntax closely follows sxhkd, so most existing sxhkd configs should already be functional, and the sxhkd man pages on the Arch wiki serve as the reference for that syntax. Names of every supported key and modifier are listed in man 5 swhkd-keys, which is the file to check when a binding refuses to fire. Vim users can get syntax highlighting through the swhkd-vim plugin, installed with vim-plug using Plug 'waycrate/swhkd-vim'. The project also states that it contains no AI generated content and that AI generated contributor patches will always be rejected, which is unusual enough to be worth knowing before you open a pull request.
Starting swhks and swhkd together
There is no package in the README, only a build and run path, and the run step needs two commands. The README gives the invocation as a single line that starts the server and then the daemon with higher rights:
./swhks && doas ./swhkdIf you would rather not use doas or sudo on every start, the documented alternative is to make the daemon binary setuid, which the README says can be done with these two commands:
sudo chown root:root swhkd
sudo chmod u+s swhkdAfter that, the startup pair is simply:
swhks &
swhkdYou should see the server running in the background and the daemon in the foreground, and a binding from your config file firing on the key combination you expect. The setuid route is a permanent change to a file on the system, and the README does not discuss how to undo it, so the doas form is the one to try first.
Pausing, resuming and reloading with signals
Once the daemon is running you do not have to restart it to change behaviour, because it can be driven with signals. The README lists three of them, each with a matching pkill invocation:
sudo pkill -USR1 swhkd
sudo pkill -USR2 swhkd
sudo pkill -HUP swhkdUSR1 pauses key checking, USR2 resumes it, and HUP reloads the config file. The reload case is the one that pays off during a session, since it lets you fix a binding without tearing down the daemon or restarting your session. The pause signal has a clear use too: it stops a hotkey from firing while you type, which is the classic problem with a keybinding that overlaps with ordinary typing. Note that the signals need privilege to deliver, which is why the documented commands carry sudo.
A Cargo workspace with two members and a setuid install target
The build is a Cargo workspace with two members, the daemon and the server, and the release profile is tuned hard:
[workspace]
members = [
"swhkd",
"swhks"
]
resolver = "2"
[profile.release]
lto = true # Enable Link Time Optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
panic = 'abort' # Abort on panic
strip = true # Strip symbols from binary*The Makefile installs the man pages into the man1 and man5 directories, copies both binaries into /usr/bin, and then changes the ownership of the daemon binary to root and sets the setuid bit, which is the same privilege model the README describes. It also seeds a default config file in the etc directory only if one is not already there. A check target runs cargo fmt, cargo check and cargo clippy, and a setup target installs the stable Rust toolchain through rustup. The repository also carries flake.nix and flake.lock for Nix users, a rust-toolchain.toml, and service files under contrib/init for systemd and OpenRC, with Runit mentioned as a possible addition. For autostart, the README offers either wiring the run commands into your window manager configuration or enabling the service file for your init system.
BSD-2-Clause, a 2022 release and a 2026 push
The licence is BSD-2-Clause, which is a permissive grant that allows redistribution and modification, so a local rebuild or a packaged copy is straightforward, subject to keeping the licence text. Maintenance is the part worth thinking about. The repository is not archived, and the last push was on 2026-07-18, so the source is being touched. The released versions tell a different story: the newest tag is 1.2.1 from 2022-07-23, before it 1.2.0 from 2022-05-11, which the release name marks as a CVE fix edition, and 1.1.7 from 2022-03-13. In other words the commits continue without corresponding tagged releases, so anyone packaging this for distribution is building from a commit that has no release note attached to it. A CHANGELOG.md exists at the root, and INSTALL.md holds the build and installation instructions that the README links to rather than duplicating.
Editorial conclusion
swhkd fits Linux users on Wayland who already have a working sxhkd config, want the same key bindings to keep working, and are willing to run a setuid daemon or a paired server process. It does not fit someone who needs a single process with no privilege split, since the environment handling depends on swhks running alongside swhkd, and the TTY claim covers a session rather than a display server. Before adopting it, read man 5 swhkd-keys against your existing key names, verify the config path in ~/.config/swhkd/swhkdrc, and remember that the newest tagged release is 1.2.1 from 2022-07-23 even though the last push was on 2026-07-18.
Frequently asked questions
What is the difference between swhkd and swhks?
swhkd is the privileged daemon that listens to key events and runs shell commands, while swhks is the non-privileged server that keeps track of environment variables and sends them to the daemon. Commands themselves are parsed from the config file only.
Can I reuse an existing sxhkd config with swhkd?
The project states that swhkd closely follows sxhkd syntax and attempts to be a drop-in replacement, so most existing sxhkd configs should be functional. The default config path is ~/.config/swhkd/swhkdrc with a fallback to etc/swhkd/swhkdrc.
How do I pause, resume and reload swhkd?
Send a signal to the running daemon: USR1 pauses key checking, USR2 resumes it, and HUP reloads the config file. The README documents them as sudo pkill -USR1 swhkd, sudo pkill -USR2 swhkd and sudo pkill -HUP swhkd.
Does swhkd only work on Wayland?
No, it is described as display protocol-independent. The same configuration works on Xorg and Wayland desktops, and the project also states you can use swhkd in a TTY.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/waycrate-swhkd)Was this useful?