midi-harbor/docs/installation.md
2026-09-28 13:59:10 -05:00

9.4 KiB

Installation

One binary, midi-harbor, contains the daemon, the command line, and optionally the graphical interface. It comes as a package, or can be built from source.

Installing a package

macOS: open the disk image and drag Midi Harbor to Applications. Opening the app shows the graphical interface, which offers to start the daemon at login. From a terminal, the same binary is at /Applications/Midi Harbor.app/Contents/MacOS/midi-harbor.

The app is signed with a Developer ID and notarized by Apple, so it opens like any other downloaded app. A copy built from source is signed ad hoc instead: the first time it is opened macOS refuses, saying Apple could not verify it is free of malware. Open System Settings → Privacy & Security, scroll to the message that Midi Harbor was blocked, and choose Open Anyway; macOS asks once more, and opens it normally from then on.

Mac App Store: the App Store build, which needs macOS 13 or newer, runs differently. The app starts its own daemon and lives in the menu bar: closing the window, or ⌘Q, leaves everything running with Midi Harbor's item in the menu bar, and Quit Midi Harbor there, or ⌥⌘Q, stops it. Instead of a launchd service, Start at login in Settings starts it at login, opening the window only if it was open when Midi Harbor last quit. Its setup is kept inside the app's container, so midi-harbor config path, run from the app's own binary, prints a path under ~/Library/Containers/com.mrgeckosmedia.MidiHarbor. Of the service commands, only service stop works there.

Debian and Ubuntu: sudo apt install ./midi-harbor_<version>_<arch>.deb, or the midi-harbor-headless package for a machine without a display.

Fedora, RHEL and other RPM distributions: sudo dnf install ./midi-harbor-<version>-1.<arch>.rpm, or midi-harbor-headless.

The Linux packages need glibc 2.35 or newer: Debian 12, Ubuntu 22.04, Fedora 36, RHEL 10 and anything newer. On RHEL 9 and its rebuilds, build from source. The packages recommend Avahi and BlueZ. Installing a package does not start anything. Each user who wants the daemon at login runs midi-harbor service install --start.

Windows: unpack midi-harbor-<version>.windows-amd64.zip somewhere it will stay, such as %LOCALAPPDATA%\Programs\Midi Harbor; the service remembers where it is. There is no installer. The executable is not signed, so the first time it runs Windows may show "Windows protected your PC"; choose More info, then Run anyway.

Without installing: each release also has a .tar.gz of the binary for Linux, and a midi-harbor-headless one for Linux and for each kind of Mac. On the Mac the graphical interface comes only in the app. The headless binary is not signed, so macOS refuses to run one downloaded through a browser until the download's quarantine is removed with xattr -d com.apple.quarantine midi-harbor.

packaging/README.md describes how the packages are built.

Building from source

Requirements

Rust 1.96 or newer.

macOS: the Xcode command line tools (xcode-select --install). CoreMIDI, CoreBluetooth and Bonjour ship with the system.

Linux: a C toolchain and the development files for ALSA, D-Bus and Avahi, plus libclang, which the Avahi bindings are generated with. 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, where RHEL and its rebuilds need the CodeReady Builder repository enabled first:

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

A build without the graphical interface, --no-default-features, needs neither of the second lines.

At run time, Linux needs:

  • the ALSA sequencer (snd-seq), which every desktop distribution loads;
  • avahi-daemon running, for other machines to find this one's network ports;
  • BlueZ 5.50 or newer, for Bluetooth;
  • systemd, to run the daemon at login. Without it, run the daemon in the foreground.

Without Avahi, network ports still work. Other machines just have to connect to them by address.

Windows: Windows builds are cross-compiled from macOS or Linux with mingw-w64:

rustup target add x86_64-pc-windows-gnu
brew install mingw-w64                       # or apt install gcc-mingw-w64-x86-64
cargo build --release --target x86_64-pc-windows-gnu

The linker is set in .cargo/config.toml. The binary is target/x86_64-pc-windows-gnu/release/midi-harbor.exe.

At run time, Windows 10 or 11 (tested on Windows 11) needs Windows MIDI Services for virtual ports. Windows 11 includes it from its late-2026 update; before that, install Microsoft's Windows MIDI Services SDK Runtime and Tools. Before the update, closing a virtual port stops the Windows MIDI Service answering until it is restarted; Platforms says how. Everything else Midi Harbor needs is part of Windows.

Building

cargo build --release                        # with the graphical interface
cargo build --release --no-default-features  # without it

The build without the graphical interface leaves out the whole GUI toolkit, not just the window. Use it on servers and machines with no display. Building the graphical interface on Linux needs libcosmic's own build dependencies as well.

To install the binary into ~/.cargo/bin:

cargo install --path .                       # or add --no-default-features

Testing

make test                # unit tests: wire formats, external formats, algorithms
make test-integration    # the daemon, CLI and protocols as a whole, over real files and sockets
make test-live           # tests needing CoreMIDI, ALSA, WinMM, a radio or a session peer

The first two need nothing but the build dependencies above. make test-live runs the tests that need something real on this machine, one at a time; each names what it needs, and fails without it.

Running the daemon

Nothing connects until the daemon is running. There are two ways to run it.

At login, as a service. This is the normal way:

midi-harbor service install --start
midi-harbor service status

service install registers the binary it was run from:

Registration Controlled with Log
macOS a launchd agent, ~/Library/LaunchAgents/com.mrgeckosmedia.MidiHarbor.daemon.plist launchctl ~/Library/Logs/midi-harbor/daemon.log
Linux a systemd user unit, ~/.config/systemd/user/midi-harbor.service systemctl --user journalctl --user -u midi-harbor
Windows a Task Scheduler task, "Midi Harbor", started at sign-in Task Scheduler %LOCALAPPDATA%\midi-harbor\logs\daemon.log

On macOS and Windows the daemon writes the log file itself. Past 10 MB it moves the file to daemon.log.1, replacing the one before, and starts again, so the two never take more than 20 MB. On macOS, Console.app shows it under Log Reports. A registration made before this log existed has none: run service install again to add it.

Running install again updates the registration in place. It does not add a second one. If the binary has since moved, service status reports the registration as stale, and install repairs it. service stop and service start stop and start the daemon without changing the registration.

In the foreground, for trying things out or for a machine without a service manager:

midi-harbor daemon

It logs to the terminal and stops on Ctrl-C, releasing any notes still sounding as it goes. Add -v or -vv for more detail, or --log-file <PATH> to log to a file that rolls over the same way as the service's.

Only one daemon runs per user. The command line finds it through a socket in the user's runtime directory ($XDG_RUNTIME_DIR/midi-harbor/daemon.sock on Linux, the per-user temporary directory on macOS). --socket points a command, or a second daemon, somewhere else. The socket is readable only by its owner, and the daemon is never reachable over the network.

On Windows the daemon listens on a named pipe with a random name instead, and writes that name to daemon.sock in the user's temporary directory, where the command line reads it. The pipe refuses connections from other machines. On Windows the task runs the daemon under a supervisor that starts it again after a crash, and service stop asks the daemon to stop rather than ending it, so held notes are released first.

Bluetooth on macOS

macOS asks for permission before a program may use Bluetooth, and grants it to an app. Installed from the disk image, the daemon runs from inside Midi Harbor.app and should be asked about as Midi Harbor. If Bluetooth is refused to it, run midi-harbor daemon from a terminal, which uses the terminal application's permission instead. A bare binary registered with service install has neither, so on macOS install the service from the app. midi-harbor capabilities reports which Bluetooth roles are usable, and why not when they are not.

Removing it

midi-harbor service uninstall

This stops the daemon and removes its registration. The configuration file is left where it is (see Configuration file), so installing again brings the same setup back. Delete it by hand to start from nothing.