Wire your PipeWire audio once, and keep it wired.
AutoRoute is a background service and GUI for Linux that remembers your PipeWire connections and re-applies them as apps come and go. It's a persistent Helvum: you patch things the way you want, and AutoRoute keeps them that way — even though PipeWire hands out fresh node IDs every time an app restarts.
By default WirePlumber routes every app to your default output. The moment you run separate virtual sinks — a GameSink feeding OBS, a MusicSink, a DiscordSink — you're back in Helvum re-patching by hand, because node IDs change on every launch and streams like Discord's per-call recording input reappear constantly. And those sinks themselves used to be a static config file you hand-edited and hoped survived the next PipeWire restart.
AutoRoute makes those patches stick, in both directions:
- A connection you make is re-created whenever the matching apps come back.
- A connection you remove stays removed — even when WirePlumber tries to re-add its default link.
- Rules that survive relaunches — connections are matched by application, not by throwaway node IDs.
- Virtual sinks you own — create and delete null sinks (
GameSink,MusicSink, …) right from the board. AutoRoute keeps them alive across PipeWire restarts and recreates them at boot even when it isn't running, via a generatedpipewire-pulsedrop-in. No more hand-edited config files. Existingvirtual-sinks.confsetups are detected and imported. - Fan-out — send one source to several sinks at once (your headset and a capture sink for streaming).
- Keep-connected and keep-disconnected — positive rules hold a link open; suppressions keep it closed on every cycle.
- Protected nodes — mark routing that another tool owns (e.g. an EasyEffects chain) as off-limits, and AutoRoute won't touch it. Precedence is absolute: protected > suppression > connect.
- A board, not a patchbay — a clean dark UI with one column per sink and a palette of draggable sources; drag to connect, drop into several columns to fan out, filter by name. Channels are paired for you, and each card's state — managed, unsaved, manual, or protected — is spelled out at a glance.
- Sample rate & bit depth at a glance — every source and sink wears a small badge showing what it's running at (e.g.
48 kHz · 24-bit,44.1 kHz · 32-bit float), read straight from the live graph. - Video too, same rules — PipeWire carries video, so the board has an Audio / Video switch in the toolbar. Route a Spout2PW sender (VTube Studio, an overlay, …) into OBS's PipeWire Video Capture exactly the way you route audio, and stop re-connecting it by hand every session.
- Runs in the background — a tray app (and an optional systemd service) reconcile the graph continuously; closing the window just hides it.
- Non-destructive — AutoRoute only ever removes links it created or ones you explicitly suppressed. Your other manual patches are never touched.
- Linux with PipeWire and WirePlumber (
pw-dump,pw-link,pw-monavailable on yourPATH) pactl(fromlibpulse) for the virtual-sink features- The .NET 10 SDK — only to build from source; the AppImage and the AUR package don't need it
AppImage (no .NET required): grab AutoRoute-*-x86_64.AppImage from the latest
release (or the AutoRoute-AppImage
artifact on any CI run), chmod +x it, run it. Built by scripts/build-appimage.sh.
Once running, an AppImage keeps itself current: the ⬇ toolbar button checks the Gitea releases for
a newer version and installs it in place (download → checksum + boot self-test → atomic swap →
restart). AutoRoute --check-update is the headless equivalent.
Arch (AUR): autoroute — build it with your
AUR helper of choice:
paru -S autorouteIt builds framework-dependent against dotnet-runtime-10.0, so .NET security updates arrive
through pacman rather than being frozen into the package. The PKGBUILD lives in
packaging/aur/. A pacman install gets /usr/bin/autoroute, a desktop
entry, and a systemd --user unit (see Autostart); the in-app updater stays inert
there, since pacman owns the files.
From source:
git clone https://git.bussy.cloud/jessie/AutoRoute.git
cd AutoRoute
dotnet builddotnet run --project src/AutoRoute.App # open the window
dotnet run --project src/AutoRoute.App -- --background # tray only, no windowFlags:
--background— start hidden (tray only)--poll— pollpw-dumpfor changes instead of watchingpw-mon
- Launch AutoRoute — the board mirrors your current audio graph. Links that already exist show up as unsaved.
- Hit + New Sink to spin up a virtual sink (name, description, stereo or mono). It appears as a column tagged VIRTUAL and is recreated on every boot; its delete button tears it down and cleans up any rules pointing at it.
- Drag a source (an app stream, a microphone, or a sink's monitor) from the palette into a target sink's column to connect it. Drop it into more than one column to fan out.
- Remove a card to disconnect. Removing an external link records a suppression so it stays gone.
- Mark a node protected to tell AutoRoute to leave it and everything touching it alone.
Every card wears its state on its sleeve — managed, unsaved, manual, or protected. The circled ? in the toolbar opens a legend explaining each:
Rules are written to ~/.config/autoroute/rules.json the moment you make a change — there is no Save button.
PipeWire carries video alongside audio, and the same "the IDs changed, re-patch it by hand" problem applies — which is why reconnecting VTube Studio to OBS becomes a ritual every time you start them. The Video switch in the toolbar shows the video half of the graph: video Sources on the left (a Spout2PW sender, a screen-capture stream), video Target Sinks as columns (OBS's PipeWire Video Capture source).
It works exactly like the audio board: drag a Source onto a column once, and the link is re-created
every time both apps come back. The rules live in the same rules.json and are re-applied by the
same background reconciler — there is nothing extra to configure or enable.
Both apps have to be running for their nodes to exist, so start VTube Studio (with Spout2PW) and OBS first, then make the connection once. Virtual-sink creation is audio-only, so + New Sink is hidden on this tab.
Easiest: open the ⚙ menu in the toolbar and flip Start AutoRoute when I log in. AutoRoute
detects its own path (the AppImage included — no symlink, no hand-edited unit) and adds a desktop
autostart entry (~/.config/autostart). Your desktop launches it as part of your session, so it
inherits the display and tray environment a GUI app needs. Flip it off to remove it.
By hand (headless / no desktop): a systemd user service is also included, for setups without a
graphical session to autostart it. It launches ~/.local/bin/AutoRoute --background,
so first make that name exist — publish the binary there or, if you use the AppImage, symlink it
(ln -sf ~/Apps/AutoRoute-x86_64.AppImage ~/.local/bin/AutoRoute). Full steps for both, plus an
XDG-autostart alternative, are in dist/README.md. Then:
systemctl --user enable --now autoroute.service
journalctl --user -u autoroute -f # follow its logsThe unit lives at dist/systemd/autoroute.service.
AutoRoute reads the graph from pw-dump, matches it against your rules, and makes only the pw-link changes needed to reach the state you asked for — an idempotent reconcile that runs whenever the graph or your rules change. Each cycle first ensures your declared virtual sinks exist (loading them with pactl and writing a pipewire-pulse conf.d drop-in for boot persistence), then reconciles the links. Every link it creates is tagged autoroute.managed, so it can always tell its own links from yours and never cleans up a connection it didn't make. pw-mon tells it when to re-check; it never routes by name, only by numeric port ID.
src/AutoRoute.PipeWire/ # PipeWire interop: graph model + pw-dump/pw-link/pw-mon drivers
src/AutoRoute.Engine/ # rule matching, the reconciler, and rules.json persistence
src/AutoRoute.App/ # Avalonia MVVM board UI, tray, and the always-on host
tests/AutoRoute.Tests/ # xUnit test suite
Built with C# / .NET 10 and Avalonia.
The reasoning behind the architecture is written down in the repo:
PLAN.md— the v1 build specPLAN.v2.md— the v2 build spec (app-managed virtual sinks)CONTEXT.md— the project's vocabularydocs/adr/— architecture decision records


