- A network port kept a machine it had connected to after the connection was removed, listed it as on the network whenever its host advertised any session, and could not connect to it again: Connect resolved identifiers only among advertised sessions and failed with "no peer named". A machine remembered only for a connection is now dropped once no network port uses it, an untrusted machine is marked present only by a session at its exact address, and Connect resolves remembered machines by identifier or name. - A network port invited the one address it had connected to until it answered, so a session that came back on another port was never reached. The session name advertised at a machine's address is now stored as advertised_as in the configuration, and while the link is down the port connects where that session is advertised and stores the new address. A machine carrying MIDI is never moved. - Each daemon now holds an Ed25519 key in identity.key beside the configuration and publishes mhkey and mhport in every session's TXT record. Two packets on the control port, a challenge and a signed proof, let a network port prove which port of which daemon it is. A machine stored with a proved key and port_id is followed under any name and to another host, with its trust, and a port deleted and made again is not followed. The challenge is sent only to a session that advertises a key, so no other RTP-MIDI implementation receives it. - A machine with no key is followed by name to a new port on its host, and to another host only when it is not trusted, since an advertisement proves nothing and trust is held by host. - An invitation over an IPv6 link-local address was never answered, because the sender's address was kept without its scope. Apple's Network MIDI invites that way and reported that the port did not respond. The address is now kept whole for the control and data ports. - Adds ed25519-dalek and hex. The packet fuzz target reads the new packets. |
||
|---|---|---|
| .cargo | ||
| .claude/skills | ||
| .specify | ||
| crates | ||
| docs | ||
| fuzz | ||
| packaging | ||
| proto/midiharbor/v1 | ||
| scripts | ||
| specs | ||
| src | ||
| tests | ||
| .gitignore | ||
| .goreleaser.yaml | ||
| AGENTS.md | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| clippy.toml | ||
| Icon.svg | ||
| LICENSE.txt | ||
| Makefile | ||
| README.md | ||
| rust-toolchain.toml | ||
| rustfmt.toml | ||
| VERSION | ||
midi-harbor
Midi Harbor manages MIDI connections on macOS, Linux and Windows: virtual ports other applications can use, attached MIDI hardware, RTP-MIDI network sessions to other computers, and Bluetooth LE MIDI devices. A daemon owns every connection and repairs it on its own. A network session that drops is reconnected, hardware that is unplugged and plugged back in picks up where it left off, and notes sounding when a link went away are released rather than left ringing.
The command line and the graphical interface are both clients of the daemon, so closing either changes nothing.
Install
Download a package or archive from the releases, or build this project. midi-harbor-headless
leaves out the graphical interface, for machines without a display.
sudo apt install ./midi-harbor_<version>_<arch>.deb # Debian and Ubuntu
sudo dnf install ./midi-harbor-<version>-1.<arch>.rpm # Fedora and RHEL
chmod +x Midi-Harbor-<version>-x86_64.AppImage # any other distribution
The release builds need glibc 2.35 or newer, so RHEL 9 and its rebuilds must build from source.
The Windows executable is not signed, so it warns the first time it runs; Installation says how to let it run.
On Windows, virtual ports need Windows MIDI Services. Windows 11 includes it from its late-2026 update; before that, install Microsoft's Windows MIDI Services SDK Runtime and Tools.
Building
Building needs Rust 1.96 or newer. On Linux it also needs the ALSA, D-Bus and Avahi development files and libclang, and for the graphical interface the xkbcommon and Wayland development files.
On Debian and Ubuntu:
sudo apt install build-essential pkg-config libasound2-dev libdbus-1-dev \
libavahi-client-dev libclang-dev
sudo apt install libxkbcommon-dev libwayland-dev # for the graphical interface
On Fedora and RHEL (enable CodeReady Builder first on RHEL and its rebuilds):
sudo dnf install gcc pkgconf-pkg-config alsa-lib-devel dbus-devel avahi-devel clang-devel
sudo dnf install libxkbcommon-devel wayland-devel # for the graphical interface
Then:
cargo build --release # with the graphical interface
cargo build --release --no-default-features # without it
Windows builds are cross-compiled with mingw-w64:
rustup target add x86_64-pc-windows-gnu
cargo build --release --target x86_64-pc-windows-gnu
Release packages for every platform are built with GoReleaser in Docker, through make snapshot
and make release.
Running as a service
The daemon registers itself with launchd, a systemd user unit, or Task Scheduler, and runs at every login:
midi-harbor service install --start
midi-harbor service status
Running from the command line
To try things out, or to see more of what the daemon is doing, run it in the foreground:
midi-harbor daemon -vv
Config
The setup lives in one YAML file per user, which the daemon writes as things change.
midi-harbor config path prints where it is. It can be written by hand; only names and kinds
are required:
endpoints:
- name: Sequencer Bus
kind: virtual_port
- name: Studio
kind: network_port
routes:
- from: Sequencer Bus
to: Studio
Run midi-harbor config reload after editing it while the daemon runs.
Usage
Every command has help:
midi-harbor --help
Run with no command, midi-harbor opens the graphical interface.
A basic setup that sends a local virtual port to another computer:
midi-harbor port create "Sequencer Bus"
midi-harbor network create "Studio"
midi-harbor network discover
midi-harbor network connect "Studio" "Stage Mac"
midi-harbor route create "Sequencer Bus" "Studio"
MIDI played into "Sequencer Bus" now reaches "Stage Mac", and keeps reaching it across sleep, network changes, and either machine restarting.
Testing
make test # unit tests
make test-integration # the daemon, CLI and protocols over real files and sockets
make test-live # tests needing real MIDI, a Bluetooth radio or a session peer