libratbag provides ratbagd, a DBus daemon to configure input devices, mainly gaming mice. The daemon provides a generic way to access the various features exposed by these mice and abstracts away hardware-specific and kernel-specific quirks.
As of version 2.0, the ratbagd daemon has been rewritten in Rust and migrated to an unprivileged session daemon. The daemon and the companion ratbagctl CLI tool are consolidated in a unified codebase. The daemon speaks the same org.freedesktop.ratbag1 DBus API (version 2) and uses the same device database and .device files.
The old C daemon has been removed and the CLI replaced with a new Rust ratbagctl tool built on the same DBus API.
⚠️ Breaking change: ratbagd now runs on the session bus, not the system bus. This intentionally breaks compatibility with current Piper releases, which connect to ratbagd on the system bus. See Session Bus Migration below for what changed and why.
ratbagd no longer runs as a root system daemon on the system bus.
It now runs as an unprivileged session daemon on the user's session
bus (zbus::Connection::session()), spawned and managed by
systemd --user. Device access is granted to the physically seated user via
udev + uaccess instead of by running as root.
Existing Piper releases connect to
ratbagd over the system bus (Gio.BusType.SYSTEM). Because the daemon no
longer claims org.freedesktop.ratbag1 on the system bus, stock Piper can
no longer find or talk to ratbagd and will report that the daemon is not
running. Piper would need to be patched to connect to the session bus to work
with this daemon. Twister, the GUI included in this
repository, already connects on the session bus.
The new data/60-ratbagd.rules udev rule (which tags raw HID interfaces with
uaccess) does not itself affect Piper — it only governs /dev/hidraw*
node permissions. The compatibility break comes from the bus migration, not
the udev rule.
The legacy architecture ran ratbagd as root solely to bypass file
permissions on /dev/hidraw*. This violates the principle of least
privilege: configuring DPI, RGB, or button bindings does not warrant
administrative access. USB hardware is untrusted, and a malicious or
compromised device can send malformed HID reports crafted to exploit parsing
flaws — and any such exploit in a root daemon becomes a full system
compromise (privilege escalation).
Running as an unprivileged session daemon contains the blast radius: an
exploit triggered by a malicious mouse is confined to the unprivileged user's
session, leaving the host OS intact. It also models device settings correctly
as per-user preferences and behaves sensibly on multi-user systems, where the
kernel's seat management (systemd-logind) grants hardware access only to the
physically seated user.
libratbag supports devices from Asus, Etekcity, GSkill, Logitech (HID++ 1.0 and 2.0, G300, G600), MarsGaming, OpenInput, Roccat (including Kone Pure / Kone EMP variants), Sinowealth (including Nubwo), and Steelseries.
See the device files for a complete list of supported devices.
Users interact through a GUI like
Twister (a modern Tauri + Svelte desktop app included in this
repository), or the ratbagctl command-line tool (see below).
Piper is not currently compatible
because the daemon moved to the session bus — see
Session Bus Migration.
The core ratbagd daemon has been rewritten from C to Rust. Key changes:
- Async, actor-based architecture — each connected device gets its own
Tokio task (actor) that owns the HID file descriptor and serializes all
hardware I/O through an
mpscchannel. DBus interface objects share device state viaArc<RwLock<DeviceInfo>>. - Structured driver framework — all drivers implement a common
DeviceDrivertrait (probe,load_profiles,commit). Hardware I/O is abstracted behindDeviceIo(async hidraw read/write, feature report ioctls, request/response matching with timeouts and retries). - Full driver parity — all 15 drivers from the C codebase have been
ported:
asus,etekcity,gskill,hidpp10,hidpp20,logitech_g300,logitech_g600,marsgaming,openinput,roccat(with Kone Pure / Kone EMP),sinowealth,sinowealth_nubwo, andsteelseries. - Dev-hooks feature — compile with
--features dev-hooksto enableLoadTestDevice/ResetTestDeviceDBus methods on the Manager interface, allowing integration tests to inject synthetic devices without real hardware. - Session daemon, not a root system daemon — the daemon now runs
unprivileged on the user's session bus, managed by
systemd --user, with device access delegated viaudev+uaccess. The legacy system-bus DBus policy andUser=rootactivation are no longer used. See Session Bus Migration. - License change — the Rust daemon is licensed under GPLv3. Supporting assets (service templates, device data, docs) remain under MIT/Expat (see the License section below).
- The
org.freedesktop.ratbag1DBus API (version 2) — all interfaces (Manager,Device,Profile,Resolution,Button,LED) are wire-compatible with the C daemon (but now served on the session bus; see Session Bus Migration). - The
.devicefile database indata/devices/.
libratbag is not yet packaged for distributions. See the Compiling section below to build from source.
- Rust toolchain — a stable Rust compiler (Rust 1.85+; edition 2024) and Cargo. Install via rustup or your distribution's package manager.
- Meson (>= 0.59) and Ninja.
- System libraries:
libudev(required for runtime udev monitoring) andsystemd(only for installing the unit file; optional if you package the service files yourself). - pkg-config — used by Meson to locate
libudevandsystemd.
The Rust daemon itself depends on tokio, zbus, nix, udev, serde,
tracing, and other crates — Cargo resolves these automatically. The CLI
binary target (ratbagctl) depends on clap, zbus, tokio, and anyhow.
Cargo.lock files are committed for reproducible builds
(cargo build --locked).
libratbag uses the meson build system which in turn uses Ninja to invoke the compilers. Meson drives the Rust build automatically via Cargo. Run the following commands to clone libratbag and build everything:
git clone https://github.com/niltonperimneto/libratbag-rs.git
cd libratbag-rs
meson setup builddir --prefix=/usr
meson compile -C builddir
sudo meson install -C builddir
To build or re-build after code changes:
meson compile -C builddir
sudo meson install -C builddir
To remove/uninstall:
sudo ninja -C builddir uninstall
Note: builddir is the build output directory and can be changed to any
other directory name.
To list all options:
meson configure builddir
Notable options:
| Option | Default | Description |
|---|---|---|
-Dsystemd=true |
true |
Install the systemd unit file |
-Dsystemd-unit-dir=PATH |
auto | Override the systemd unit directory |
-Ddbus-root-dir=PATH |
auto | Override the DBus configuration directory |
-Ddbus-group=GROUP |
(everyone) | Restrict DBus access to a UNIX group |
To enable the synthetic test device DBus methods, edit the Cargo build
flags in meson.build or build the Rust crate directly:
cargo build --release --bin ratbagd --features dev-hooks
Never enable dev-hooks in production builds.
ratbagd is intended to run as a DBus-activated systemd service. At install time, the following files are placed on the system:
| File | Purpose |
|---|---|
/usr/share/dbus-1/system.d/org.freedesktop.ratbag1.conf |
DBus policy (who can own/talk to the bus name) |
/usr/share/dbus-1/system-services/org.freedesktop.ratbag1.service |
DBus activation (tells the bus how to start the daemon) |
$unitdir/ratbagd.service |
systemd unit (Type=dbus, BusName=org.freedesktop.ratbag1) |
Both the DBus activation file and the systemd unit point Exec/ExecStart
at $sbindir/ratbagd — the installed Rust binary.
See also the configure-time options -Dsystemd-unit-dir and
-Ddbus-root-dir. Developers are encouraged to symlink to the files in the
git repository.
After installing, reload the service manager:
sudo systemctl daemon-reload
sudo systemctl reload dbus.service
Enable the service (for automatic DBus activation):
sudo systemctl enable ratbagd.service
From now on, any DBus access to org.freedesktop.ratbag1 (for example via
busctl introspect org.freedesktop.ratbag1 /org/freedesktop/ratbag1) will
automatically start the Rust daemon through DBus activation.
systemctl status ratbagd
journalctl -u ratbagd -n 20 # should show "Starting ratbagd version ..."
You can also start it directly for debugging:
sudo ratbagd # production
sudo RUST_LOG=debug ratbagd # verbose logging via tracing
ratbagctl is the command-line interface for configuring devices. It talks
to the running ratbagd daemon over DBus.
ratbagctl list # list connected devices
ratbagctl info 0 # show device details
ratbagctl commit 0 # commit pending changes to hardware
ratbagctl profile list 0 # list profiles for device 0
ratbagctl profile info 0 0 # show profile 0 details
ratbagctl profile active 0 1 # switch to profile 1
ratbagctl profile name 0 0 "Gaming" # set profile name
ratbagctl profile enable 0 1 # enable profile 1
ratbagctl profile angle-snapping 0 0 on # enable angle snapping
ratbagctl profile debounce 0 0 10 # set debounce to 10 ms
ratbagctl resolution dpi 0 0 0 800 # set resolution 0 to 800 DPI
ratbagctl resolution active 0 0 2 # activate resolution 2
ratbagctl resolution default 0 0 1 # set default resolution to 1
ratbagctl button list 0 0 # list button mappings
ratbagctl button set-button 0 0 1 3 # set button 1 to logical button 3
ratbagctl button set-key 0 0 1 30 # set button 1 to keycode 30 (KEY_A)
ratbagctl button set-macro 0 0 1 30:1 30:0 # set button 1 to a key macro
ratbagctl led mode 0 0 0 breathing # set LED 0 to breathing mode
ratbagctl led color 0 0 0 ff0000 # set LED color to red
ratbagctl led secondary-color 0 0 0 00ff00 # set secondary LED color
ratbagctl led brightness 0 0 0 200 # set brightness to 200
ratbagctl led duration 0 0 0 1000 # set effect duration to 1000 ms
| Command | Description |
|---|---|
| General | |
list |
List all connected devices (shows API version) |
info <device> |
Show detailed info for a device |
commit <device> |
Commit all pending changes to hardware |
| Profile | |
profile list <device> |
List profiles (name, rate, dirty state) |
profile info <device> <profile> |
Show full profile details |
profile active <device> <profile> |
Set the active profile |
profile name <device> <profile> [name] |
Get or set profile name |
profile enable <device> <profile> |
Enable a profile |
profile disable <device> <profile> |
Disable a profile |
profile rate <device> <profile> <hz> |
Set profile report rate |
profile angle-snapping <device> <profile> [on|off] |
Get or set angle snapping |
profile debounce <device> <profile> [ms] |
Get or set debounce time |
| Resolution | |
resolution list <device> <profile> |
List resolutions (DPI list, capabilities) |
resolution dpi <device> <profile> <res> [dpi] |
Get or set DPI |
resolution active <device> <profile> <res> |
Set active resolution |
resolution default <device> <profile> <res> |
Set default resolution |
resolution enable <device> <profile> <res> |
Enable a resolution slot |
resolution disable <device> <profile> <res> |
Disable a resolution slot |
| Button | |
button list <device> <profile> |
List buttons |
button get <device> <profile> <button> |
Get button mapping details |
button set-button <device> <profile> <btn> <value> |
Map to logical button (action type 1) |
button set-special <device> <profile> <btn> <value> |
Map to special action (action type 2) |
button set-key <device> <profile> <btn> <keycode> |
Map to key (action type 3) |
button set-macro <device> <profile> <btn> <events...> |
Map to macro (action type 4); events are keycode:direction pairs |
button disable <device> <profile> <button> |
Disable a button |
| LED | |
led list <device> <profile> |
List LEDs |
led get <device> <profile> <led> |
Get LED info (mode, colors, brightness, duration, color depth) |
led mode <device> <profile> <led> <mode> |
Set mode (off, solid, cycle, wave, starlight, breathing, tricolor) |
led color <device> <profile> <led> <hex> |
Set primary color (e.g. ff0000) |
led secondary-color <device> <profile> <led> <hex> |
Set secondary color |
led tertiary-color <device> <profile> <led> <hex> |
Set tertiary color |
led brightness <device> <profile> <led> <0-255> |
Set brightness |
led duration <device> <profile> <led> <ms> |
Set effect duration in milliseconds |
| Test / Dev | |
test load-device <json_file> |
Load a test device from a JSON file |
test reset |
Remove all test devices |
<device> can be a zero-based index from ratbagctl list or a sysname
substring. All write commands automatically commit changes to hardware.
Twister is a modern, desktop-agnostic graphical frontend for configuring
gaming mice. It is built with Tauri 2 and Svelte 5 and is included in this
repository under twister/.
Twister communicates with ratbagd over the same org.freedesktop.ratbag1
DBus interface, so it works as a drop-in replacement for Piper on any Linux
desktop environment.
Status: Early alpha — core features (DPI, buttons, LEDs, profiles) work.
See twister/README.md for build instructions, screenshots, and detailed documentation.
The test/ directory contains a Python integration test suite that exercises
the full org.freedesktop.ratbag1 DBus API against the Rust daemon built
with the dev-hooks feature. Tests use pytest and cover the Manager,
Device, Profile, Resolution, Button, and LED interfaces.
See test/README.md for prerequisites and usage.
Full documentation of the DBus interface to interact with devices is available here: ratbagd DBus Interface description.
The daemon exposes the following interfaces on the session bus under
org.freedesktop.ratbag1:
| Interface | Object Path | Description |
|---|---|---|
Manager |
/org/freedesktop/ratbag1 |
Entry point; lists connected devices |
Device |
/org/freedesktop/ratbag1/device/<sysname> |
Per-device (name, model, profiles list) |
Profile |
.../p<N> |
Per-profile (active profile, DPI list) |
Resolution |
.../p<N>/r<N> |
Per-resolution (DPI x/y, report rate) |
Button |
.../p<N>/b<N> |
Per-button (action type, mapping) |
LED |
.../p<N>/l<N> |
Per-LED (mode, color, brightness, effect rate) |
+---------+
| Twister |--+
+---------+ | +------+ +-----------------+
+-> | DBus | -> | ratbagd (Rust) | -> /dev/hidraw*
+---------+ | +------+ +-----------------+
| Piper |--+ |
+---------+ +------+------+
| Device Actor | (one per mouse, owns DeviceIo)
+------+------+
|
+------+------+
| Driver | (HID++, Roccat, Steelseries, …)
+-------------+
src/main.rs— entry point; initializes tracing, loads the device database, spawns the udev monitor, and starts the DBus server.src/ipc/— zbus interface implementations forManager,Device,Profile,Resolution,Button, andLED.src/engine/actor.rs— per-device actor task that serializes hardware I/O. DBus handlers sendActorCommandmessages; the actor executes them against theDeviceDriver+DeviceIo.src/hal/— theDeviceDrivertrait and all protocol implementations.DeviceIowraps async hidraw I/O with feature report ioctl support.src/engine/device.rs—DeviceInfoand its children (ProfileInfo,ResolutionInfo,ButtonInfo,LedInfo) — the canonical device state shared between DBus objects and the actor viaArc<RwLock<…>>.src/engine/device_database.rs— parser for.devicefiles (INI-like config).src/udev_monitor.rs— monitors hidraw device add/remove events and sendsDeviceActionmessages to the main event loop.
libratbag relies on a device database to match a device with its driver.
See the data/devices/
directory for the set of known devices. These files are usually installed
into $prefix/$datadir (e.g. /usr/share/libratbag/).
Adding a new device can be as simple as adding a new .device file. This is
the case for many devices with a shared protocol (e.g. Logitech's HID++).
See the
data/devices/device.example
file for guidance on what information must be set. Look for existing devices
from the same vendor as guidance too.
If the device has a different protocol and doesn't work after adding the device file, you'll have to start reverse-engineering the device-specific protocol. Good luck :)
git clone https://github.com/niltonperimneto/libratbag-rs.git
Bugs can be reported in our issue tracker
For questions, feature requests, or general discussion, please open an issue on GitHub.
A number of device-specific notes and observations can be found in the upstream project wiki: https://github.com/libratbag/libratbag/wiki/Devices
This project uses a dual-license structure:
- ratbagd (the Rust daemon in
src/) is licensed under the GNU General Public License v3.0 (GPLv3). - ratbagctl (the CLI tool in
src/bin/ratbagctl/) is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). - Twister (the desktop GUI in
twister/) is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). - Supporting assets (service templates, device data, documentation, and other non-daemon content) remain licensed under the MIT/Expat license.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: [...]
See the LICENSE file for the MIT license and Cargo.toml for the GPLv3 declaration.