midi-harbor/specs/001-service-and-clients/spec.md
James Coleman f059eaef65 docs(specs): drop the spec names used before the renumbering
- The specs index no longer carries a table mapping the three original spec names to the renumbered ones, and spec headers no longer record a feature branch or "first written as" name.
- A task that pointed at research under the old 001 directory now points at its current path, so every reference resolves to a spec that exists.
- The constitution's 1.4.1 amendment still records that the specs were split and renumbered, without listing the retired names.
2026-09-29 14:35:33 -05:00

182 lines
13 KiB
Markdown

# Feature Specification: Midi Harbor — Service and Clients
**Created**: 2026-09-20
**Status**: Implemented
**Split 2026-09-27**: This was the whole of Midi Harbor's first specification. Each capability now
has a spec of its own, from 002-configuration to 010-graphical-interface, holding its user story,
requirements, success criteria, research and tasks under their original numbers; [the
index](../README.md) says which spec holds each. This one keeps the service, the command line, the
daemon contract, the architecture and the original input.
**Input**: User description: "Midi Harbor: a cross-platform (macOS + Linux) MIDI connectivity manager. It manages three kinds of MIDI endpoint from one place: (1) virtual MIDI ports — the IAC-driver equivalent — that let apps on the same computer talk to each other over MIDI; (2) RTP-MIDI network sessions that let computers exchange MIDI over a LAN, discovered via mDNS/Bonjour and interoperable with Apple Network MIDI, rtpMIDI on Windows, and rtpmidid on Linux; (3) Bluetooth LE MIDI links, both connecting out to BLE MIDI peripherals (keyboards, controllers) and advertising this computer as a BLE MIDI peripheral. The defining value is connection resilience: unlike the built-in macOS support, connections must self-heal — automatically reconnecting after network changes, sleep/wake, device unplug, peer restart, and Wi-Fi roaming — and must recover MIDI state so no notes are left stuck. Users configure named virtual ports that persist across reboots, define routes between any two endpoints, see live connection health and traffic, and get a clear reason when something genuinely cannot connect. A headless daemon runs at login and owns all connections; a libcosmic GUI and a CLI are clients of it."
**Amended 2026-09-20**: Ship as a single executable rather than separate daemon and GUI binaries.
The background service is a subcommand of that executable. The graphical interface is optional and
can be excluded at build time, leaving a fully functional headless command-line build. The
executable sets up its own launchd (macOS) or systemd (Linux) per-user service.
**Amended 2026-09-20 (2)**: Physical MIDI hardware attached to the computer (USB and DIN
interfaces, controllers, keyboards) is a first-class endpoint kind alongside virtual ports, network
sessions, and Bluetooth devices. This enables the repeater use case: take a physical device and
carry it over the network or over Bluetooth to another machine.
## User Scenarios & Testing *(mandatory)*
### User Story 2 - One binary: headless service, CLI, and optional GUI (Priority: P1)
A user installs a single Midi Harbor executable. Running it with no arguments opens the graphical
interface; running `midi-harbor service install` registers it as a user service that starts at
login and keeps running. On a headless machine — a rack computer, a stage box, a Raspberry Pi with
no desktop — the same executable is built without the graphical interface and driven entirely from
the command line, with the same configuration and the same self-healing behaviour.
**Why this priority**: This is the delivery vehicle for every other story. Virtual ports cannot
survive a reboot (User Story 1) without the service, and the graphical interface cannot be optional
unless the command-line surface is complete on its own.
**Independent Test**: On a machine with no graphical environment, install the service from the
command line, create a virtual port and a route, reboot, and verify from the command line that
everything came back — without the graphical interface ever being built or run.
**Acceptance Scenarios**:
1. **Given** a freshly installed executable, **When** the user runs the service install command,
**Then** a per-user service is registered with the operating system's service manager, started
immediately, and set to start at every login.
2. **Given** the service is installed, **When** the user runs the service status command, **Then**
they are told whether it is installed, whether it is running, its version, and its uptime.
3. **Given** the service is installed, **When** the user runs the service uninstall command,
**Then** the service is stopped and deregistered, and the user's configuration is left intact.
4. **Given** a build produced without the graphical interface, **When** the user runs any
command-line command, **Then** it behaves identically to the same command on a full build.
5. **Given** a build produced without the graphical interface, **When** the user runs the
executable with no arguments, **Then** they are shown command-line help explaining that the
graphical interface is not included in this build.
6. **Given** the service is running, **When** the user performs any configuration action available
in the graphical interface, **Then** an equivalent command-line command exists that performs
the same action against the same running service.
7. **Given** the user wants to script against Midi Harbor, **When** they request machine-readable
output from any command-line command, **Then** they receive structured output suitable for
automated parsing.
8. **Given** the service is not installed, **When** the user opens the graphical interface,
**Then** they are offered installation of the service in one action, and told what that will
do.
9. **Given** the executable is run as the service directly in the foreground, **When** the user
does so, **Then** it runs the service without registering anything, so it can be supervised by
other means or debugged interactively.
---
### Edge Cases
- **Daemon not running**: When the user opens the interface and the background service is not
running, the interface offers to start and install it rather than showing an empty or broken
view.
- **Daemon crash**: If the background service terminates unexpectedly, it is restarted
automatically by the operating system's service supervisor, and it restores all configured
endpoints and routes on startup.
- **Version mismatch**: When the interface and the background service are different incompatible
versions, the user is told clearly which component to update instead of experiencing undefined
behaviour.
- **Concurrent clients**: When more than one interface or command-line client is connected at
once, all of them see consistent state and updates.
- **No service manager**: When the platform's user service manager is unavailable or unsupported
(for example a container, or a Linux system without the expected service manager), service
installation reports this clearly and the user is told how to run the service in the foreground
instead.
- **Service already installed**: When the user runs service installation and a registration
already exists, it is updated in place rather than duplicated, and the user is told it was
updated.
- **Stale service registration**: When a service registration points at an executable that has been
moved or deleted, the status command reports the registration as stale and names the missing
path.
- **Graphical interface requested on a headless build**: When a user asks for the graphical
interface on a build that excludes it, they are told which build to install rather than seeing a
crash or silent exit.
## Requirements *(mandatory)*
### Functional Requirements
#### Service architecture and clients
- **FR-036**: System MUST run a background service that owns all endpoints and connections and
starts automatically at user login.
- **FR-037**: System MUST keep all connections operating when no graphical interface is running,
and MUST NOT disturb any connection when an interface is opened or closed.
- **FR-038**: System MUST be restarted automatically by the operating system if it terminates
unexpectedly, and MUST restore all configured endpoints and routes on startup.
- **FR-039**: System MUST ship as a single executable that selects its role from its command-line
arguments — running the background service, running the graphical interface, performing a
command-line action, or managing its own service registration. There MUST NOT be a separate
daemon executable.
- **FR-039a**: System MUST run the background service as a subcommand of that executable, in the
foreground when invoked directly, so it can be supervised externally or debugged interactively.
- **FR-039b**: System MUST make the graphical interface an optional component that can be excluded
at build time, producing a fully functional headless build with no graphical dependencies.
- **FR-039c**: System MUST expose, through command-line commands, every configuration and control
action available in the graphical interface, so the graphical interface is never required to
operate any feature.
- **FR-039d**: System MUST offer machine-readable output for command-line commands, so the product
can be scripted and automated.
- **FR-039e**: System MUST show command-line help, explaining that the graphical interface is not
included, when a headless build is invoked with no arguments.
- **FR-039f**: System MUST provide commands to install, uninstall, start, stop, and report the
status of its own per-user service registration, using the platform's native user service
manager, and MUST NOT require the user to author service configuration files by hand.
- **FR-039g**: System MUST install its service as a per-user service that starts at login and is
restarted by the operating system if it terminates unexpectedly.
- **FR-039h**: System MUST leave the user's configuration intact when its service is uninstalled.
- **FR-040**: System MUST support multiple clients connected at once, each seeing consistent state
and receiving updates as state changes.
- **FR-041**: System MUST detect incompatible client and service versions and report clearly which
component needs updating.
- **FR-042**: System MUST offer to install and start the background service in one action when a
client finds it is not running, and MUST state what installing it will do.
- **FR-043**: System MUST operate without requiring administrator or root privileges.
### Key Entities
- **Endpoint**: Anything MIDI can flow to or from. Has a stable identity, a user-visible name, a
kind (virtual port, network session, Bluetooth device), an enabled setting, a current state, and
traffic counters.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-010**: The background service uses under 1% of one CPU core when idle with 10 configured
endpoints, and under 150 MB of memory under normal use.
- **SC-014**: The background service continues running and carrying MIDI with zero interruption
across 50 consecutive open-and-close cycles of the graphical interface.
- **SC-014a**: A build produced without the graphical interface has no graphical or display-server
dependencies and passes the full command-line acceptance suite on a machine with no desktop
environment installed.
- **SC-014b**: Every configuration and control action available in the graphical interface has a
documented command-line equivalent, verified as 100% coverage.
- **SC-014c**: A user goes from a freshly downloaded executable to a service running at login with
a working virtual port using a single install command plus one create command.
- **SC-016**: All features present on one supported operating system are present and behave
equivalently on the other, except those explicitly documented as platform-limited.
## Assumptions
- Users are musicians, engineers, and hobbyists comfortable with MIDI concepts such as ports,
channels, and controllers, but not necessarily with networking.
- Both supported operating systems are used on a single-user desktop where the person configuring
Midi Harbor is the person logged in; multi-user and remote administration are out of scope.
- MIDI 1.0 message semantics are the baseline for routing, recovery, and monitoring. MIDI 2.0 is
out of scope for this feature but the design should not preclude it.
- The graphical interface is excluded or included at build time rather than being downloaded or
enabled at runtime, so distributions can offer a headless package without graphical dependencies.
- The platform's per-user service manager is assumed available: launchd on macOS, and systemd user
units on Linux. Linux systems without systemd are supported only by running the service in the
foreground under whatever supervisor the user prefers.
- Service installation is per-user and needs no elevated privileges, so it registers only for the
user who runs it and does not start before that user logs in.
- Windows is not a target for this feature.
- Where the operating system already provides its own inter-application or network MIDI facility,
Midi Harbor runs alongside it rather than replacing or reconfiguring it, and both may be in use
at once.