https://github.com/joleuger/vuinputd
container-safe mediation daemon for /dev/uinput
https://github.com/joleuger/vuinputd
Last synced: 3 months ago
JSON representation
container-safe mediation daemon for /dev/uinput
- Host: GitHub
- URL: https://github.com/joleuger/vuinputd
- Owner: joleuger
- License: mit
- Created: 2025-10-23T22:20:54.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-02-24T20:26:03.000Z (3 months ago)
- Last Synced: 2026-02-25T00:39:20.614Z (3 months ago)
- Language: Rust
- Size: 480 KB
- Stars: 13
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: docs/SECURITY.md
Awesome Lists containing this project
- awesome-sunshine - vuinputd - Enabling container-friendly input handling for Sunshine on Linux. (🛠️ Tools / ✴️ Third-Party Moonlight Clients)
README
# vuinputd
**Run Sunshine and other uinput-based apps inside containers — with full input isolation and zero kernel patches.**
A minimal **CUSE-based proxy for `/dev/uinput`** that lets unmodified applications (like [Sunshine](https://github.com/LizardByte/Sunshine)) run inside containers while creating virtual input devices safely on the host.
---
## Overview
Containerizing input-producing software (e.g. Sunshine, Moonlight host replacements, remote desktop servers) improves separation and simplifies deployment.
However, exposing the host’s `/dev/uinput` directly into a container breaks isolation:
* Containers can create devices visible system-wide or to other containers.
* Keyboards and mice may attach to host seats or inject input into active host sessions.
`vuinputd` exposes a virtual `/dev/uinput` device inside containers (via CUSE).
Input devices created by containerized apps are forwarded to the host kernel’s uinput subsystem, where they appear as normal `/dev/input/event*` devices visible to all host applications. Those devices are then injected into the containers with udev announcements.
---
## Architecture
`vuinputd` solves this by introducing a **mediated input stack**:
* A **fake `/dev/uinput`** inside each container.
* A **host proxy daemon** that safely creates the actual devices via `/dev/uinput`.
* The proxy **forwards add/remove udev events** into the container so that wayland compositors that use libinput and other applications see devices natively.
* **udev rules** tag and isolate devices per container, preventing the host from consuming them.
Applications use the `/dev/uinput` interface unmodified, and the mediation adds **negligible overhead**.
In principle, this design works with any container runtime — **systemd-nspawn, Docker, LXC, Podman**, and others.
```mermaid
sequenceDiagram
box transparent Host
participant Kernel as uinput (kernel)
participant Daemon as vuinputd
end
box transparent Container
participant App as Container App
participant VirtUinput as /dev/uinput (virt)
Participant Game as Game
end
Daemon->>VirtUinput: 1. provides virtual /dev/uinput via CUSE
App->>VirtUinput: 2. create virtual input device
VirtUinput-->Daemon: 3. data from virtual /dev/uinput via CUSE
Daemon->>Kernel: 4. create virtual input device
Kernel->>Daemon: 5. notify applications on host about new eventX device
Daemon->>App: 6. notify application in container about new eventX device
App->>VirtUinput: 7. send input data
VirtUinput-->Daemon: 8. data from virtual /dev/uinput via CUSE
Daemon->>Kernel: 9. send input data
Kernel->>Game: 10. send input data via eventX device
```
> **Performance note:**
> While `vuinputd` adds an extra userspace round trip via CUSE, the measured overhead is in the range of **tens of microseconds per event** in a simple integration test. This is several orders of magnitude smaller than typical sources of input latency such as frame rendering, compositor delays, scheduling jitter, or network latency. In practice, the additional cost is negligible for interactive and latency-sensitive applications, including gaming. More detailed benchmarks can be found in `TESTS.md`.
---
## Benefits
* 🎮 **SDL2 & Wayland compatibility:** `vuinputd` ensures compositors and games recognize input devices correctly.
* 🔒 **Strong isolation:** Containers see only their own devices; the host sees them but ignores them completely.
* ♻️ **Safe lifecycle:** Devices are removed cleanly when the containerized app stops.
* 🛠️ **Simple integration:** No kernel patches required — only userspace tools and udev rules.
---
## Documentation
See [docs/BUILD.md](https://github.com/joleuger/vuinputd/blob/main/docs/BUILD.md) for a short build and installation guide.
See [docs/DESIGN.md](https://github.com/joleuger/vuinputd/blob/main/docs/DESIGN.md) for a detailed overview of the architecture, design trade-offs, and security considerations.
See [docs/USAGE.md](https://github.com/joleuger/vuinputd/blob/main/docs/USAGE.md) for a short usage guide.
See [docs/DEBUG.md](https://github.com/joleuger/vuinputd/blob/main/docs/DEBUG.md) for a guide how to debug problems with containers.
---
## 🧩 Production Readiness
**Current Status:** 🚧 *Prototype / Alpha — functional, not yet production-grade.*
`vuinputd` is currently in a functional prototype stage.
It reliably demonstrates the core concept — exposing `/dev/uinput` devices inside containers via CUSE — but several aspects require hardening before production use.
### ✅ Goals for Production Readiness
* [ ] **Steam input support:**
Steam input is not supported, yet. For some strange reasons, steam creates 16 virtual devices. Maybe a race.
* [ ] **Error handling and recovery:**
Ensure the daemon gracefully handles container shutdowns, device races, and failed mounts without leaks or undefined states.
* [ ] **Security model:**
Review privilege requirements (root access, netlink permissions, CUSE capabilities) and ideally reduce the attack surface via namespace isolation, seccomp, or capability dropping.
* [ ] **Robust startup and shutdown:**
Add reliable cleanup of virtual devices and clear error feedback when reloading or restarting.
* [ ] **Container runtime integration:**
Validate compatibility with major runtimes (`systemd-nspawn`, `Docker`, `LXC`, `Podman`, etc.) and document integration steps.
* [ ] **Comprehensive testing:**
* Unit tests for the Rust core logic
* Integration tests with multiple containers
* Fuzz or stress testing of the CUSE layer
* [ ] **Code audit:**
Review `unsafe` sections (from FUSE bindings) and ensure memory safety and proper lifetime handling.
* [ ] **Distribution and packaging:**
Provide a deb/rpm package for simple deployment.
* [ ] **Check for compatibility with steam runtime:**
* [ ] **Forward known controller pids automatically:**
The main reason that vuinputd overrides pids is to ensure that those are not used by the host by accident, especially for keyboards that otherwise might get a seat assigned. This is irrelevant for gamepads. So the pids of known gamepads can just be forwarded. This is relevant for the 360 input devices that are created by steam.
* [ ] **Hidraw in Proton**
https://github.com/selkies-project/selkies/pull/173
https://github.com/GloriousEggroll/proton-ge-custom/blob/master/docs/CONTROLLERS.md
---
## License
MIT