IPC protocol documentation
jackmoebiusd exposes a small JSON protocol over a local Unix-domain socket. It’s the same contract the jackmoebius CLI and the JackMate GUI use, so you can drive JackMoebius from any language that can open a socket.
Transport
- Socket:
/tmp/jackmoebius.sock, mode0600(owner only). - Request / response: send one JSON line, read one JSON line, the connection closes:
→ {"command":"status"}
← {"status":"ok","data":{ … }}Every reply is {"status":"ok","data":…} or {"status":"error","message":"…"}. An error may carry an optional code, a machine-readable marker (e.g. "licensing" when a routing command is refused because the trial has expired), so a client can react to a class of error without parsing message.
- Event stream: a
subscribeconnection stays open and streams invalidation events (see Event stream).
Health: status
→ {"command":"status"}
← {"status":"ok","data":{
"version":"1.0.0","device_acquired":true,"jack_connected":true,
"exposed_out":2,"exposed_in":1,"master_on":false,"uptime_s":1234,
"licensing":{"state":"trial","days_remaining":9}, … }}Always present: version, device_acquired, jack_connected, exposed_out, exposed_in, uptime_s, licensing. version is the installed JackMoebius version (handy for an “About” box, and it answers even before the audio device is acquired). licensing carries {state, days_remaining} (state ∈ trial | licensed | expired; days_remaining is present for trial/expired, absent for licensed). Reading or changing the license itself (status while stopped, activation) is a CLI concern. See the CLI reference; the socket only reports the live state here and notifies changes via licensing_changed.
App state: apps
Returns one entry per app the driver knows about, both directions in one call:
← {"status":"ok","data":[
{"name":"Music","key":"com.apple.Music","pid":606,
"out_exposed":true,"out_channels":[2,3],
"in_exposed":false,"in_channels":[],
"can_capture":false,"blacklisted":false}, … ]}key: bundle ID (stable persistence key) ·name: display name ·pid.out_exposed/in_exposed: whether the box is exposed (the stateadd/add --inset), per direction.out_channels/in_channels: the reserved bus channels (persistent), or[].can_capture: the app can be routed on the input side.blacklisted: hidden from routing selectors.
Expose & hide
| Command | Effect | Reply |
|---|---|---|
{"command":"jack_add","app":"<key>"} |
expose output | {name,key,channel_offset} |
{"command":"jack_remove","app":"<key>"} |
hide output (reservation kept) | null |
{"command":"jack_add_in","app":"<key>","n":2} |
expose input card (N ch) | {name,key,channels, …} |
{"command":"jack_remove_in","app":"<key>"} |
hide input card (reservation kept) | null |
Listings
{"command":"jack_list"}→ exposed output boxes, each without_status(+current_deviceon a mismatch).{"command":"jack_list_in"}→ exposed input cards, each within_status.{"command":"jack_assignments"}/jack_assignments_in→ the persistent reservations.
Volume, master, blacklist
{"command":"set_volume","app":"<name>","locked":false,"gain":0.5}→ per-app output volume (locked = follows master; unlocked = independent linear gain). Reply{app,key,locked,gain}.{"command":"jack_master","enabled":true}→ the system-mix monitor box →{master_on}.{"command":"blacklist_show|blacklist_add|blacklist_remove|blacklist_reset", …}.
Event stream
Send {"command":"subscribe"} on a dedicated connection. The daemon keeps it open and streams one event line per change (invalidations, never full state):
→ {"command":"subscribe"}
← {"event":"exposure_changed"} # a box was added/removed (out or in)
← {"event":"apps_changed"} # app launched/quit, capture-capability, blacklist
← {"event":"routing_changed"} # an app's device usage may have changed
← {"event":"master_changed"} # master box toggled
← {"event":"jack_changed"} # JACK came up / went down
← {"event":"licensing_changed"} # trial/license state changed (day count, activation, revoke)
← {"event":"stopping"} # clean shutdown, just before the socket closesOn each event, re-fetch the matching query (single source of truth). Ordering matters: subscribe then your first fetch, so nothing slips through the gap.
Liveness is free: while the connection is open, the daemon is alive. An EOF preceded by stopping = a clean stop; a bare EOF = a crash.
Routing status
jack_list / jack_list_in report, per box, whether the app actually uses the right JackMoebius device:
| value | meaning |
|---|---|
ok |
the app reads/writes the right JackMoebius device |
mismatch |
the app uses another device (also returns current_device + current_device_uid) |
idle |
the app uses no device of that direction yet |
absent |
the app is not running |
The colour is the front-end’s choice: a GUI shows a green / red / amber indicator, and raises an alert only for boxes it knows are wired.