A sleek and intuitive GUI for WiVRn — Linux VR streaming made easy.
yakuda-connect is a powerful configuration hub and dashboard for Linux VR. It eliminates the need for complex terminal commands, allowing you to manage, configure, and launch your WiVRn environment with a single click.
🎮 Controller remapping for OpenVR and OpenXR games: OpenVR games are edited via obah, OpenXR games (e.g. Unreal games under Proton) via xrBinder — both in the same controller view in the Controls tab.
| System | Status | Notes |
|---|---|---|
| Arch-based | ✅ Tested — primary development system | Full feature set: AUR installation, all components |
| Fedora-based | ✅ Tested | Components come from the Fedora repos; xrizer from the COPR @xr-sig/xrizer or the GitHub release. The WiVRn dashboard is deliberately not offered here — yakuda-connect already provides the controls |
| Debian / Ubuntu / Linux Mint | ✅ Tested (Mint 22.3) | WiVRn from the Linux VR Adventures PPA (ppa:lvra/wivrn) where it builds, otherwise the Flathub Flatpak; xrizer straight from its GitHub release. Note: the PPA has no build for Ubuntu 24.04 noble, the base of Mint 22.x |
Dashboard![]() |
Installation![]() |
Streaming Settings![]() |
Tools Hub![]() |
General Settings![]() |
Advanced Settings![]() |
Games Library![]() |
Game Settings![]() |
Controls — Controller View![]() |
Controls — Editing Bindings![]() |
Dashboard![]() |
All screenshots: assets/README.md
- Centralized Dashboard: Start and stop your WiVRn server instantly with a clean, easy-to-use interface.
- VR Games Library: The Games tab auto-detects every installed Steam VR game and shows it as a cover tile — with curated Proton profiles and tested launch options for games like VRChat, auto-recommendations for everything else, and one-click Use (set Proton version) and ▶ Play (launch via Steam) buttons.
- ProtonPlus Integration: Install the recommended Proton builds (Proton-GE, GE-RTSP, Proton-CachyOS) straight from a game's panel via the ProtonPlus CLI.
- Advanced Autostart Chain: Launch multiple VR companion tools (such as WayVR, VRCX, OpenComposite, SlimeVR, or OSC tools) automatically in a custom sequence.
- Controls Tab (OpenVR & OpenXR): Switch on stick control via XR HOTAS or binding editing via obah — if a tool is missing, the app asks how to install it. Pick game, controller and bindings to load from dropdowns (preset: VRChat · Oculus/Meta Touch · xrizer), then browse every action set as a tab — both controllers drawn side by side with a line from each binding to its button, SteamVR-style. Click a button to edit its bindings (everything obah can do: add/remove bindings, mode, actions, parameters), edit poses, haptics, skeleton and chords (button combinations) in their own sections, and save as xrizer, VapoR or OpenComposite binding. Drag cards into any order — the card below moves out of the way and the gap closes — move each controller drawing on its own, swap the controller images for your own (
assets/controls), collapse cards down to their names with Tidy view (or one at a time by right-clicking), and keep whole arrangements as named profiles. Unsaved changes are never lost silently: switching game or closing the app asks first. - OpenXR games (xrBinder): Games that use OpenXR directly (no SteamVR bindings, e.g. Unreal games under Proton) appear in the same controller editor as OpenVR games (“Controls via obah & xrBinder”). One switch on the xrBinder card builds and enables xrBinder by mittorn; start the game once, then click a button and assign functions — reset per button or all at once, applied live where possible.
- One view for OpenVR and OpenXR: obah (SteamVR bindings) and xrBinder (OpenXR) are separate CLI/TUI tools that don't know each other — Yakuda Connect merges them into one view; you never see which one is working.
- Old SteamVR games without an action file (e.g. Gal*Gun 2): nothing for obah to edit — Yakuda Connect detects the buttons via xrizer's fixed legacy layout and remaps them through xrBinder.
- “⇄ Tilt = press” with threshold: replaces the popular SteamVR “dpad in touch mode” community bindings, which don't exist on Linux.
- Deadzone against stick drift: click the stick → “◎ Deadzone” tab with sliders for left, right and both (0–50 %, ↺ reset each); small movements around the center count as “stick at rest”. Works for OpenVR games too when they run via xrizer or OpenComposite (xrizer itself ignores SteamVR's
deadzone_pct). - Graphical, SteamVR-style view: both controllers side by side, lines to each button, movable cards, your own controller images, profiles.
- Takes care of the plumbing: builds and patches xrBinder, registers the layer, runs the IPC service as a systemd service, reloads live only when safe, backs up foreign files.
- Launch tools from their card: every installed tool in the Tools tab has a ▶ Start button — command-line tools (obah, XR HOTAS, adb) open in a terminal, everything else starts straight away.
- Cargo Tools on any Distro: obah and XR HOTAS are built with
cargo installin a visible terminal; a missing C compiler, OpenXR library or Rust toolchain is installed along the way. - OSC Toolbox: One-click OSC Query fix for supported OSC tools (OSC Leash, OscGoesBrrr) when VRChat OSC acts up.
- One-Click Environment Setup: Automated installation of essential WiVRn dependencies and network/firewall configuration (Port 9757).
- Headset Client Installer: Easily install and sideload the companion Android client (.apk) directly onto your standalone VR headset (Pico / Quest) via USB.
- Stream Fine-Tuning: Configure encoders, pick the graphics card WiVRn runs on (handy when an integrated GPU keeps winning the Vulkan lottery), toggle OpenVR compatibility, and manage your OpenXR runtimes directly from the UI.
- Backup & Restore: Instantly save or recover your entire VR environment configuration.
- Customizable Interface: Eight built-in themes plus per-role colour pickers, an optional background image and adjustable card opacity — under Settings → Design.
- Light on resources: starts in well under a second, builds the Tools tab only when you open it, no app-wide event filter, no UI freeze while checking packages and next to no CPU at idle — stays out of the way while you're in VR.
- Terminal mode (no GUI, no Qt): saves RAM in VR. Commands:
YC-help,YC-status,YC-wivrn-toggle(server on/off),YC-openvr,YC-encoder,YC-GPU,YC-killapps(close autostart programs),YC-autostart-reset(reset start timer),YC-pairing(shows the PIN) — pick by number, or directly (YC-encoder vaapi,YC-GPU 1). Included with AUR and the curl installer; for AppImage/source use Settings → Advanced → Set up commands. Start in terminal opens the menu and closes the GUI (the server keeps running). Autostart programs work here too: they start once the headset connects. - Desktop Compatibility: Runs smoothly across various desktop environments including KDE Plasma, GNOME, and Hyprland.
Whether you are a Linux newcomer or a power user, there are several straightforward ways to get yakuda-connect up and running.
yakuda-connect is available in the AUR. Install it with your favourite AUR helper — all dependencies are pulled in automatically, and you get updates through your normal system update:
yay -S yakuda-connector
paru -S yakuda-connectThen launch it from your application menu or simply run:
yakuda-connectThe setup script detects the package manager itself (pacman, dnf, apt, zypper) and installs PySide6 from the matching distribution package; if there is none, it builds its own venv, touching neither the system Python nor PEP 668:
bash <(curl -s https://raw.githubusercontent.com/yakuda-stack/yakuda-connect/main/install.sh) && yakuda-connectThe VR components themselves are then installed from within the app (Installation tab). On Fedora they come from the official repos; xrizer is available from the COPR @xr-sig/xrizer or, as an alternative, straight from the GitHub release — selectable per component.
Choose one of the two options below to get started as quickly as possible:
Open your terminal and paste the following command. It will automatically download the setup script, install the tool, and launch it immediately:
bash <(curl -s https://raw.githubusercontent.com/yakuda-stack/yakuda-connect/main/install.sh) && yakuda-connect- Navigate to the Releases section on GitHub.
- Download
yakuda-connect-<version>-x86_64.AppImage. One file for every system: it works with FUSE 3 (Arch, CachyOS, Fedora 40+, Ubuntu 24.04+, Bazzite, SteamOS) and FUSE 2 (Ubuntu 22.04 and earlier, Debian 11/12).libfuse2does not need to be installed — libfuse3 is linked statically into the file. - Make the file executable:
- Via GUI: Right-click the file -> Properties -> Permissions -> Enable "Allow executing file as program".
- Via Terminal:
chmod +x yakuda-connect-*.AppImage
- Double-click the file to launch the dashboard!
If it won't start with an error mentioning
fusermountor/dev/fuse(containers, hardened kernels, FUSE disabled), run it unpacked instead — this works on every system, it is just slightly slower to start:./yakuda-connect-*.AppImage --appimage-extract-and-run
Something not working? The app writes a log to
~/.cache/yakuda-connect/app.log. Attaching it to a bug report or Discord message makes problems far easier to track down. For more detail, start withYAKUDA_LOG_LEVEL=DEBUG yakuda-connect.
If you prefer to clone the repository and run the application directly from the source code, execute these commands in your terminal sequence:
- Clone the repository[cite: 2]:
git clone https://github.com/yakuda-stack/yakuda-connect.git- Change to the project directory[cite: 2]:
cd yakuda-connect- Run the installation script[cite: 2]:
bash install.shEverything below is taken from the source code, and each claim names the file it can be checked against. If you find a difference between this section and the code, that is a bug — please report it.
| Question | Answer |
|---|---|
| Telemetry? | None. No usage data, no events, no crash reporting. |
| Analytics? | None. No analytics SDK is present anywhere in the codebase. |
| Usage data collected? | None. Nothing about your usage is recorded or transmitted. |
| Data sent to a server? | No server exists. There is no yakuda-connect backend. |
| User account / cloud? | Not possible. There is no login, no account and no cloud service. |
| Unique ID / fingerprint? | None. Requests carry only User-Agent: yakuda-connect — no ID, no hardware or system information. |
| Does it open a port? | No. yakuda-connect never listens on a port (no socket is created anywhere in the code). |
| Where does the log go? | ~/.cache/yakuda-connect/app.log — local only. It leaves your machine only if you copy or save it. |
These are all of them. Only the first two happen without you clicking anything.
| Host | When | Purpose | Source |
|---|---|---|---|
raw.githubusercontent.com |
~1.5 s after start, automatic | Reads one file and compares its version number with the installed one | core/install_worker.py |
raw.githubusercontent.com |
~1.5 s after start, automatic | Version of the game database (config/games.json) |
core/games.py |
api.github.com |
Only on click | Finds the latest WiVRn release (APK), AppImage tools and the newest xrizer release; latest XR HOTAS commit for the update check (only if git is not installed — otherwise git ls-remote against github.com) |
core/main.py, core/appimage_installer.py, core/xrizer_github.py, core/cargo_installer.py |
crates.io |
Only on click on "Check for Updates" | Latest published version of obah (JSON, one request) | core/cargo_installer.py |
ppa.launchpadcontent.net |
Only on click, apt systems only | Checks whether the WiVRn PPA has a build for this Ubuntu release before adding it (HEAD request, no download) | core/appimage_installer.py |
github.com / codeload.github.com |
Only on click | Downloads AppImages, the WayVR design, the reference backup, the xrizer release ZIP (into ~/.local/share/xrizer, no root) |
core/appimage_installer.py, core/backup_manager.py, core/xrizer_github.py |
shared.fastly.steamstatic.com |
When opening the Games tab | Cover images for detected Steam games, cached locally | core/games.py |
index.crates.io, static.crates.io, github.com |
Only when installing via Cargo, inside the visible terminal | cargo downloads the source of obah (crates.io) or XR HOTAS (GitHub) and their dependencies |
core/cargo_installer.py |
sh.rustup.rs, static.rust-lang.org |
Only when installing via Cargo and Rust is missing or too old (not on Arch/Fedora, which use their own packages) | Official rustup installer and Rust toolchain, installed for your user only | core/cargo_installer.py |
Nothing is uploaded to any of these. Every request is a plain download.
What is received: release metadata (JSON) from the GitHub API, .AppImage/.apk/.tar.gz files you asked for, the game database, and cover images. Nothing is executed automatically after download; AppImages are verified as ELF-64 binaries before being installed (core/vrcvideocacher_install.py, core/vr_environment.py). The one exception is the Cargo install method, and only after you pick it: it compiles the downloaded source and — if Rust is missing — runs the official rustup install script. All of that happens in a visible terminal, logged to install.log in the tool's folder.
No port is opened for yakuda-connect. The firewall button opens ports for wivrn-server — see the table below.
| Port | Protocol | Used by | Purpose |
|---|---|---|---|
| 9757 | TCP + UDP | wivrn-server |
The connection between the PC and the VR headset |
| 5353 | UDP (mDNS) | avahi / wivrn-server |
Lets the headset discover the PC — without it the server list in the headset stays empty even when 9757 is open |
Defined in core/firewall.py (PORT, MDNS_PORT). On ufw the rule is created as the named application profile /etc/ufw/applications.d/wivrn — byte for byte what WiVRn's own dashboard writes (dashboard/firewall.cpp). The name matters: WiVRn checks whether that file exists to decide if the firewall still needs setting up, so a plain ufw allow 9757 would open the port but leave WiVRn asking for setup forever. ports=9757 without a protocol means TCP and UDP in ufw. Only the firewall that is actually active is changed (firewalld or ufw). nftables and iptables are detected but never modified automatically — you get the commands to copy instead, because there is no reliably identical place to insert a rule and a misplaced one can take a machine off the network.
Only permissions the program actually uses are listed.
| Permission / access | Purpose | When | Source |
|---|---|---|---|
Firewall rule (pkexec) |
Open ports 9757 and 5353 so the headset can reach the WiVRn server | Only on click on "Fix Firewall" | core/firewall.py |
CAP_SYS_NICE (pkexec setcap) |
Lets wivrn-server run reprojection at high priority. A capability on one file — not a permanently elevated process |
Only on click on "Enable VR Priority" | ui/vr_runtime_widget.py |
pkexec for OpenXR config |
Write active_runtime.json when the file or its folder belongs to root. The folder is handed back to your user afterwards, so later fixes need no root |
Only as a fallback when writing without root fails | core/openxr_manager.py |
pkexec for restore |
Copy files back to /usr/share/openxr, /opt/xrizer, /opt/opencomposite. A timestamped backup is made first; nothing is deleted |
Only on click on "Restore" | core/backup_manager.py |
sudo in a terminal |
Package installation and updates (yay, paru, dnf). Runs in a visible terminal window so you see the package list and enter the password yourself — yakuda-connect never handles your password |
Only on click in the Installation tab | core/install_worker.py |
sudo in a terminal (Cargo method) |
Installs what the build needs if missing: a C compiler, the OpenXR library for XR HOTAS, and Rust itself on Arch/Fedora (pacman, dnf, apt-get, zypper). Same visible terminal, your own password prompt. The build itself runs without root |
Only on click, when installing via Cargo in the Tools or Controls tab | core/cargo_installer.py |
Read /sys/bus/usb/devices |
Detect a connected headset. Plain file reads, no root, no lsusb |
Background check | core/usb_headsets.py |
adb |
Install the WiVRn APK onto the headset and detect USB debugging status. Only called when a headset was found on the bus | Only on click / when a headset is present | core/main.py, core/usb_headsets.py |
yakuda-connect is never run as root itself. Each of the actions above elevates a single command through pkexec, which shows the system's own password dialog.
| Path | What happens | Source |
|---|---|---|
~/.config/yakuda-connect/ |
This program's own settings, backups and downloaded tools | core/paths.py |
~/.cache/yakuda-connect/ |
Log file, game covers, downloaded APK | core/paths.py |
~/.config/openxr/1/active_runtime.json |
Which OpenXR runtime is active. Previous file kept as .bak.<timestamp> |
core/openxr_manager.py |
~/.config/openvr/ |
OpenVR paths (read; written by WiVRn itself) | core/vr_environment.py |
~/.config/wivrn/config.json |
WiVRn server settings (encoder, bitrate, codec, OpenVR compatibility). Read, changed key by key, written back atomically — never rebuilt | core/config_manager.py |
~/.config/wivrn/wivrn-dashboard.conf |
Only the auto_connect_usb key |
core/wivrn_dashboard.py |
~/.config/wayvr/ |
WayVR design. Backed up before every change | core/overlay_manager.py |
~/.config/OSCLeash/Config.json, ~/.config/OscGoesBrrr/config.json |
The OSCQuery fix sets exactly one key. Files that do not exist are not created | core/queryfix.py |
Steam config.vdf / localconfig.vdf |
Proton version and launch options for a game, when you press "Use" | core/games.py |
~/.bashrc, ~/.zshrc |
When installing an AppImage tool: appends one marked block that puts ~/.local/bin on your PATH, only if it is not already there |
core/appimage_installer.py |
~/.local/bin/, ~/.local/share/applications/ |
Launcher scripts and .desktop entries for tools you install |
core/appimage_installer.py |
~/.config/yakuda-connect/tools/cargo/<tool>/ |
Cargo-built tools (obah, XR HOTAS): the binary, install.sh and install.log. "Remove" deletes the folder and its link in ~/.local/bin |
core/cargo_installer.py |
~/.cargo/, ~/.rustup/, ~/.profile, ~/.bashrc, ~/.zshenv |
Only if the Cargo method has to install Rust via rustup: the toolchain lands in ~/.cargo and ~/.rustup, and rustup adds one line to your shell profiles that puts ~/.cargo/bin on your PATH (rustup's standard behaviour). Removing a Cargo tool leaves Rust installed |
core/cargo_installer.py |
| Steam game folders | The obah section of the Controls tab looks for OpenVR action files and existing xrizer/, OpenComposite/ and vapor_binding.json bindings and reads them to display them. Written only when you press Save: <game>/xrizer/<controller>.json, <game>/OpenComposite/<controller>.json or <game>/vapor_binding.json — the same files obah writes, including the poses, skeleton, haptics and chords sections. An existing file is first copied to <name>.bak |
core/obah_bindings.py, core/obah_editor.py |
~/.config/yakuda-connect/config/controls_layout.json |
The order of the binding cards and where you moved the controller drawings in the Controls tab, per controller and hand | core/tabs/controls_mixin.py |
~/.config/yakuda-connect/config/controls_profiles.json |
Your named Controls profiles: under each name the arrangement, the complete binding, the controller type and the name of the game it came from. No paths from your library. Written only when you press "Save as …", removed again by "Delete" | core/tabs/controls_mixin.py |
/usr/share/openxr, /opt/xrizer, /opt/opencomposite |
Only when restoring a backup, via pkexec, with a timestamped backup first |
core/backup_manager.py |
wivrn-server— started and stopped from the Dashboard; output goes to~/.cache/yakuda-connect/wivrn-server.logwivrnctl pair— while pairing mode is active- Autostart programs — the ones you configured yourself in the Dashboard, plus your own kill commands from Settings → Advanced (these run as a shell command, so they do exactly what you wrote)
install.shof a Cargo tool — in a visible terminal, only when you install via Cargoobah,xr-hotas— in a visible terminal, only when you press ▶ Start in the Controls tab- Any installed tool from the Tools tab — only when you press ▶ Start on its card; command-line tools open in a terminal
adb,pactl,getcap,pgrep,systemctl— short queries;adbonly when a headset is connected
The log is written locally and rotates at 1 MB (3 old files kept). Settings → General & Updates → Diagnostics & Log lets you open it, copy the last part, or save a report containing the log plus version, distribution and the active OpenXR runtime. The VRChat video diagnostic deliberately redacts the resolved stream URL, because it contains your public IP and signatures (core/vrchat_check.py).
The switch at the bottom left of the sidebar turns on extra technical information. When enabled, actions that change something on your system show an expandable box with a short explanation, the affected files, the required permissions and the equivalent terminal command with a copy button. The command is never executed — there is deliberately no "run" button. No feature behaves differently in Advanced Mode; only the information appears. The existing manual OpenXR fix stays exactly where it was.
yakuda-connect is a free hobby project — built by VR enthusiasts, for VR enthusiasts.
💡 Tip: Both buttons are also built right into the app — Settings → Community & Updates, where you can also check for new versions with one click.
The full changelog lives in its own file — it is kept in both English and German:
➡️ CHANGELOG.md
🤖 Transparency note: This project and its documentation are proudly developed and optimized with the support of AI coding assistants (Claude by Anthropic & Gemini). Idea, architecture & UX/UI design: conceived, designed and architected entirely by me.










