Velin
0.2.1-beta.1is a cross-platform LAN audio routing beta for sending system audio and microphone audio between Windows and Linux machines on the same local network. It includes a desktop GUI, headless/session commands, encrypted pairing, receiver discovery, saved settings, and a working TCP/UDP transport path.
Current State | What Works Today | What Is Not Built Yet | Stack | Workspace Layout | Roadmap | Getting Started
Velin is now usable as a beta!
What exists now:
- A Slint desktop GUI with
Session,Metrics,Settings, andExtra Optionstabs SenderandReceiversession modes in the app- Manual IP connection for sender mode
- Automatic receiver discovery in the GUI
- Persisted local settings for target IP, control port, audio port, and theme
- Persisted local settings for bind IP and output device selection
- Sender capture mode selection for system audio, microphone, or mixed system-plus-microphone
- Advanced sender capture selection for specific Linux sources, microphones, and external virtual devices
- Receiver-side concurrent multi-stream mixing from multiple senders
- Headless sender and receiver commands that run from saved settings without the GUI
- Fingerprint-confirmed encrypted pairing with trusted-peer persistence
- Dark and light mode in the app
- TCP control handshake plus UDP frame streaming
- Stop, disconnect, mute, and graceful window-close shutdown
- CLI and headless commands for transport testing and unattended sessions
- A bind-IP setting for receiver mode
- Output device enumeration and selection for receiver playback
- Receiver playback of streamed audio
- Windows system audio capture for sender mode via WASAPI loopback
- Linux system and microphone capture for sender mode via Pulse/PipeWire-compatible
parec
What it does not do yet:
- Full per-stream routing, naming, and mixer controls on the receiver
- Production-grade background service integration for auto-start / OS service install
- Broader automated test coverage for transport, audio timing, and pairing flows
+----------------+ local network +----------------+
| Sender machine | -----------------------> | Receiver |
| system audio | TCP control + UDP | plays stream |
| capture | | audio |
+----------------+ +----------------+
| Area | Current behavior |
|---|---|
| GUI | Starts by default with a small desktop window |
| Roles | Sender and receiver actions are available in the session tab |
| Connection | Receiver discovery works, with manual IP connect as fallback |
| Settings | Target IP, bind IP, output device, capture defaults, control port, audio port, and theme are saved locally |
| Transport | TCP control plus UDP audio streaming works, with sender auto-reconnect |
| Sender behavior | Windows sender mode uses WASAPI loopback and input capture. Linux sender mode uses Pulse/PipeWire-compatible parec. Sender capture can run as system audio, microphone, or system-plus-microphone |
| Receiver behavior | Receiver advertises itself on LAN, accepts multiple concurrent senders, mixes them into one output, and reports stream activity and latency metrics |
| Security | Sessions use encrypted pairing with trusted fingerprint confirmation and stored trusted peers |
| Session controls | Start, stop, disconnect, and mute are available in the GUI |
| Headless mode | headless / service sender and receiver commands run without the GUI and reuse saved settings |
| CLI fallback | listen and connect <ip> still work |
These are still planned, not implemented:
- Per-stream routing and mixer controls on the receiver
- Real background service / daemon installation and management
- Wider automated test coverage and release-hardening work
| Area | Choice |
|---|---|
| Language | Rust |
| UI | Slint |
| Async runtime | Tokio |
| Control + security | JSON handshake messages, X25519 session key exchange, HKDF, ChaCha20-Poly1305 |
| Audio payload | UDP PCM frames with per-stream IDs |
| Transport | TCP for control, UDP for frame streaming |
| Config and storage | Serde + local JSON settings |
This is the current repo shape, not the final intended crate layout:
velin/
|-- assets/
| `-- logo.svg
|-- crates/
| |-- velin-app
| | `-- src/
| | |-- audio.rs
| | |-- app.rs
| | |-- capture.rs
| | |-- discovery.rs
| | |-- main.rs
| | |-- settings.rs
| | |-- transport.rs
| | `-- ui.rs
| `-- velin-proto
| `-- src/
| `-- lib.rs
|-- Cargo.toml
`-- README.md
- Workspace and crate structure
- Sender and receiver role flow
- GUI-first app shell
- Manual IP connection
- Persisted basic settings
- Dark and light mode
- TCP/UDP transport prototype
- Basic TCP handshake (
Hello/Accept) - Real receiver playback
- Audio device enumeration
- Automatic peer discovery
- Real system audio capture
- Basic connect/disconnect/mute session controls
- Capture full system audio on Windows
- Capture full system audio on Linux
- Improve stream health and latency reporting
- Remember preferred peers and devices
- Auto-reconnect
- Specific or per-application audio capture
- Microphone forwarding
- Virtual sinks and sources
- Multi-stream support
- Encrypted sessions and trusted pairing
- Headless or service mode
Velin can run with the desktop GUI or in headless/service mode.
git clone https://github.com/p-stanchev/velin.git
cd velin
cargo run -p velin-appGUI:
- Open the
Sessiontab - Use
Start Receiveron one machine - Use
Refreshin sender mode to look for receivers, or enter the receiver IP manually - Use
Start Senderon the other machine once a receiver is selected or entered
CLI and headless commands:
cargo run -p velin-app -- listen
cargo run -p velin-app -- connect 127.0.0.1
cargo run -p velin-app -- headless receiver
cargo run -p velin-app -- headless sender
cargo run -p velin-app -- service sender 192.168.0.62Headless and service commands:
headless receiverorservice receiverstarts the receiver without opening the GUIheadless sender [target-ip]orservice sender [target-ip]starts the sender without the GUIheadlessprompts in the terminal when an unknown peer fingerprint must be approvedserviceis non-interactive and only works with already-trusted peers- if sender mode omits
[target-ip], Velin uses the saved target IP from settings
| Platform | Requirements |
|---|---|
| Windows | Rust toolchain |
| Linux | Rust toolchain, parec, aplay, and PulseAudio or PipeWire pulse compatibility |
| Both | cargo, git, and two machines on the same local network if you want a real network test |
Windows MSI:
cargo install cargo-wix
.\scripts\dist-windows.ps1Linux Debian package:
cargo install cargo-deb
./scripts/dist-linux.shBoth scripts write installer output into dist/.
The Debian package also installs a desktop launcher and application icon for menu integration.
When the receiver runs on Windows, inbound firewall rules may be required before another machine can connect.
- TCP
49000for the control channel - UDP
49001for audio frames - UDP
49002for discovery
Example PowerShell commands:
New-NetFirewallRule -DisplayName "Velin TCP 49000" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 49000
New-NetFirewallRule -DisplayName "Velin UDP 49001" -Direction Inbound -Action Allow -Protocol UDP -LocalPort 49001
New-NetFirewallRule -DisplayName "Velin UDP 49002" -Direction Inbound -Action Allow -Protocol UDP -LocalPort 49002- Windows sender mode now captures real system audio from the default render endpoint.
- Linux sender mode captures system audio through a monitor source using
parec. It can useVELIN_LINUX_MONITORto override the detected monitor source when needed. - Linux microphone capture also uses Pulse/PipeWire-compatible
parecsources instead of relying on the less stable ALSA input path. - Sender mode can now run as
System,Mic, orSystem + Mic, and theExtra Optionstab stores those defaults locally. - External virtual devices can be used by selecting or entering the source/device name exposed by the host audio stack.
- Receiver mode now supports multiple concurrent senders and mixes them into one playback stream.
- The current receiver can play streamed audio on a selected output device.
- Linux is a supported beta target, but lower-powered Linux machines may still need slightly higher latency for smooth playback.
- Receiver mode can bind to
Automatic(0.0.0.0) or a specific local IPv4 address. - Receiver discovery uses local-network UDP discovery packets, and sender mode also has a manual refresh action.
- Receiver playback currently uses the selected device's default output config.
- Linux capture currently expects
parecplus PulseAudio/PipeWire pulse compatibility to be available.
- If Linux cannot connect to a Windows receiver and TCP
49000times out, check Windows Defender Firewall first. - If receiver discovery does not find a peer, use the
Refreshaction in sender mode or enter the receiver IP manually. - If headless mode asks for a fingerprint, compare it on both machines before typing
y. - If
servicemode refuses a new peer, trust that peer once in the GUI or inheadlessmode first. - If Linux sender capture produces no audio, verify
parecis installed and the selected monitor/source exists inpactl list short sources. - If Linux receiver playback fails, verify
aplayis installed and the selected output device is healthy. - If Linux playback is a little choppy on older hardware, increase the system load headroom first: close other apps, reduce stream count, and retest before changing app settings.
- If Windows reports
OS Error 10040, the network path is rejecting oversized UDP datagrams. Update to a build with smaller audio frames and retest on the same LAN.