A Discord Rich Presence IPC proxy that multiplexes RPC messages across multiple Discord instances.
presence-switch sits between Discord RPC client applications (games, media players, etc.) and running Discord instances. It binds the first available discord-ipc-{0..9} socket (preferring discord-ipc-0) and relays all incoming RPC messages to every other existing Discord IPC socket.
This means a single RPC client can broadcast its presence to multiple Discord clients simultaneously.
graph LR
A["RPC Client<br/>(e.g. game)"] --> B["presence-switch<br/>(discord-ipc-0)"]
B --> C["Discord #1"]
B --> D["Discord #2"]
B --> E["Discord #N"]
- The switch claims an available
discord-ipc-*socket name - RPC clients connect to the switch thinking it's Discord
- The switch relays messages to all real Discord instances on other sockets
The IPC binary protocol uses a simple format: 4-byte LE opcode + 4-byte LE length + UTF-8 JSON payload. The switch processes handshake, ping, and close opcodes directly, and forwards all other opcodes (frame, pong) to Discord.
- Rust (edition 2024)
- One or more running Discord instances
cargo build --releaseTagged releases publish .rpm and .msi builds to the Releases page. For unreleased changes, the Package workflow also produces dev artifacts on every push to main and every PR β download them from the workflow run's Artifacts section.
To build packages locally:
scripts/package.sh rpm # β target/generate-rpm/presence-switch-*.rpm
scripts/package.sh msi # β target/wix/presence-switch-*.msi (cross-compiled from Linux)
scripts/package.sh allSee scripts/package.sh --help for the toolchain requirements.
sudo dnf install ./presence-switch-*.rpm # Fedora, RHEL, CentOS, Rocky, Alma
sudo zypper install ./presence-switch-*.rpm # openSUSE
systemctl --user daemon-reload
systemctl --user enable --now presence-switchThe package installs a per-user systemd unit at /usr/lib/systemd/user/presence-switch.service. View logs with journalctl --user -u presence-switch. CI builds the RPM against Ubuntu 24.04's glibc (2.39+), so the target distro needs glibc β₯ 2.39 β that covers Fedora 41+, RHEL 10+, recent openSUSE Tumbleweed, and similar.
Double-click the .msi to install per-user (no admin prompt). The installer adds an entry under HKCU\Software\Microsoft\Windows\CurrentVersion\Run so presence-switch launches at every logon β inspect or disable it via Task Manager β Startup apps. Uninstall via Settings β Apps & features.
- Close Discord or ensure
discord-ipc-0is not taken - Run presence-switch:
cargo run --release
- Start your Discord instances β they will claim
discord-ipc-1,discord-ipc-2, etc. - Launch your RPC-enabled application β it connects to presence-switch on
discord-ipc-0, which relays to all Discord instances
For best results, start presence-switch before any Discord instances so it can claim discord-ipc-0, which is what most RPC clients connect to by default.
Press Ctrl+C to shut down gracefully.
| Platform | IPC mechanism |
|---|---|
| Linux | Unix domain sockets |
| macOS | Unix domain sockets |
| Windows | Named pipes |
Platform-specific implementations are selected at compile time via #[cfg].
src/
βββ main.rs
βββ switch/ # IPC server β accepts RPC client connections
β βββ ipc/
β βββ mod.rs # Server and Client logic
β βββ unix.rs # Unix domain socket listener
β βββ windows.rs # Named pipe listener
βββ discord/ # IPC client β connects to real Discord instances
βββ api.rs # Discord REST API for app metadata (cached)
βββ ipc/
βββ mod.rs # Client, protocol types, socket discovery
βββ unix.rs # Unix domain socket connection
βββ windows.rs # Named pipe connection
To cut a new release:
- Bump
versioninCargo.toml(and runcargo update -wsoCargo.lockmatches). - Commit the bump and merge to
main. - Tag the commit on
mainmatching the new version, e.g.:git tag v0.2.0 git push origin v0.2.0
- The
Packageworkflow runs on the tag, validates that the tag matchesCargo.toml, builds the.rpmand.msi, and publishes a GitHub Release with both attached and auto-generated notes from the commits since the previous release.
If the tag version doesn't match Cargo.toml's version field, both build jobs fail loudly before doing any work.
MIT Β© Kramer Campbell