Operations Overview
This section covers day-to-day operation of the Shiloh Broadcaster system.
Sections
- Systemd Units — user-mode units, linger, dependency ordering, socket activation
- Monitoring & Logs — log locations, verbose stats, health checks, alerting
- Security — firewall, WireGuard, allow-lists, control plane isolation
- Performance Tuning — JACK buffers, CPU priority, network socket tuning
Ports
| Port | Protocol | Service | Notes |
|---|---|---|---|
| UDP 5005 | UDP | shiloh-mixer | Relay fan-out + broadcaster ingest (same socket) |
| UDP 127.0.0.1:19997 | UDP | shiloh-mixer control plane | mixer_web → mixer; loopback only |
| UDP 0.0.0.0:19999 | UDP | shiloh-mixer MIDI listener | shiloh-midi-sender → mixer |
| UDP 127.0.0.1:3819 | UDP | Ardour OSC | mixer → Ardour |
| UDP 0.0.0.0:3820 | UDP | Ardour OSC feedback | Ardour → mixer |
| TCP 8889 | HTTP | mixer_web (Phoenix LiveView) | Public or LAN-facing web UI |
| TCP 127.0.0.1:8890 | HTTP | shiloh-web-relay (WHEP) | Proxied via :8889/whep/* by mixer_web |
| UDP ephemeral | WebRTC media | shiloh-web-relay | One per PeerConnection; high port range 32768–65535 |
| TCP 6600 | MPD | Mopidy | Optional; only if Mopidy is installed |
State files
All files live in ~/mixer-state/ by default (override in the TOML).
| File | Written by | Purpose | Rate |
|---|---|---|---|
meters.json |
shiloh-mixer | [id, peak_l, peak_r] per channel |
~10 Hz |
sessions.json |
shiloh-mixer | Active UDP relay + ingest sessions | ~5 Hz |
scenes.dets |
mixer_web | Named scene snapshots (DETS store) | On save |
relay-assignments.json |
shiloh-mixer | Client name → feed (main/monitor/cue) mapping | On change |
midi-state.json |
shiloh-mixer | Current MIDI modal state + recent keys | On key event |
These files are the only coupling between shiloh-mixer and mixer_web. There is no inter-process socket between them.
systemd units (user-mode)
All services run as the shiloh user with systemctl --user. No root or sudo is required for day-to-day operation.
Starting / stopping
# Start all services
systemctl --user start shiloh-mixer shiloh-web-relay mixer_web
# Stop all services
systemctl --user stop shiloh-mixer shiloh-web-relay mixer_web
# Restart a single service
systemctl --user restart shiloh-mixer
# Enable auto-start on login
systemctl --user enable shiloh-mixer shiloh-web-relay mixer_web
Logs
# Follow live
journalctl --user -u shiloh-mixer -f
journalctl --user -u shiloh-web-relay -f
journalctl --user -u mixer_web -f
# Last 50 lines
journalctl --user -u shiloh-mixer -n 50
# Since a specific time
journalctl --user -u shiloh-mixer --since "30 min ago"
Unit locations
~/.config/systemd/user/shiloh-mixer.service
~/.config/systemd/user/shiloh-web-relay.service
~/.config/systemd/user/mixer_web.service
After editing a unit file: systemctl --user daemon-reload.
Updating binaries
Mixer server update
# Binary (built locally or downloaded)
systemctl --user stop shiloh-mixer
cp target/release/shiloh-mixer ~/mixer/shiloh-mixer-linux-x86_64
systemctl --user start shiloh-mixer
# Web relay binary
systemctl --user stop shiloh-web-relay
cp target/release/shiloh-web-relay ~/mixer/shiloh-web-relay-linux-x86_64
systemctl --user start shiloh-web-relay
# mixer_web Phoenix release
# (deploy method depends on your CI; typically tar extract to ~/mixer_web/)
systemctl --user restart mixer_web
NOTE:
shiloh-mixersends BYE to all connected relay clients when it stops cleanly. They reconnect automatically within the backoff window (1–30 s). Broadcaster senders also reconnect automatically.
Relay client update
# On the relay machine
cp new/shiloh-relay ~/.local/bin/shiloh-relay
systemctl --user restart shiloh-relay # if running as a service
# or kill and restart manually
WirePlumber: keep hardware devices awake
PipeWire suspends ALSA devices after ~5 s of inactivity. When the device sleeps, the JACK graph has no hardware clock driver and all processing stalls — VU meters show 0.0 even though JACK connections look correct.
Drop this into ~/.config/wireplumber/wireplumber.conf.d/53-keep-devices-awake.conf (adjust node.name patterns to match your hardware — check names with pactl list short sinks):
monitor.alsa.rules = [
{
matches = [
{ node.name = "~alsa_(output|input).usb-Burr-Brown.*" }
{ node.name = "~alsa_(output|input).usb-Focusrite_Scarlett.*" }
]
actions = {
update-props = {
session.suspend-timeout-seconds = 0
}
}
}
]
Apply: systemctl --user restart wireplumber
Verify: timeout 2 pw-top -b -n 3 — at least one ALSA node should show status R with non-zero QUANT/RATE.
Desktop audio null sink (PipeWire)
To route Firefox, VLC, and other PulseAudio apps into the mixer, create a persistent PipeWire null sink. Create ~/.config/pipewire/pipewire.conf.d/desktop-audio-sink.conf:
context.objects = [
{ factory = adapter
args = {
factory.name = support.null-audio-sink
node.name = desktop-audio
node.description = "Desktop Audio"
media.class = Audio/Sink
audio.position = [ FL FR ]
monitor.channel-volumes = true
object.linger = true
}
}
]
NOTE: Do not add an
adapter.auto-port-configblock. WirePlumber negotiates the adapter’s port-config and a pre-applied auto-port-config deadlocksWpSiAudioAdapteractivation. Symptom: apps are stuck atCorked: yesin pavucontrol and every stream goes toSink: 4294967295.
Apply: systemctl --user restart pipewire pipewire-pulse wireplumber
MIDI control
shiloh-midi-sender reads a USB MIDI keyboard and forwards note-on events as raw UDP to the mixer’s MIDI listener on port 19999. The mixer runs a modal state machine to translate key presses into fader/mute/scene actions and Ardour OSC transport commands.
Running the MIDI sender
shiloh-midi-sender --device "USB MIDI" --server stg-srv001.bq.shilohbv.com:19999
Key map (default config)
Mode entry (C3 octave):
| Note | MIDI # | Effect |
|---|---|---|
| A3 | 57 | Reset to Idle from any mode |
| C3 | 48 | Enter ChannelSelect |
| G3 | 55 | Enter Transport (sticky) |
Channel navigation (in ChannelSelect / ChannelControl):
| Note | MIDI # | Action |
|---|---|---|
| C4 | 60 | Previous channel |
| D4 | 62 | Next channel |
Channel actions (C5 octave, in ChannelControl):
| Note | MIDI # | Action |
|---|---|---|
| C5 | 72 | Fader down (−0.05) |
| D5 | 74 | Fader up (+0.05) |
| E5 | 76 | Mute toggle |
Transport (C6 octave, in Transport mode):
| Note | MIDI # | Ardour OSC |
|---|---|---|
| C6 | 84 | Play |
| D6 | 86 | Stop |
| E6 | 88 | Record enable toggle |
| F6 | 89 | Goto start |
| G6 | 91 | Goto end |
| A6 | 93 | Previous marker |
| B6 | 95 | Next marker |
| C7 | 96 | Add marker |
The MIDI panel in the web UI shows the current mode, the last 8 keys pressed, and the action each key triggered. Keys that produced no action (not mapped in the current mode) show —.
Key mappings are configured in [midi.*] blocks in shiloh-mixer.toml. After changing them, redeploy the TOML and restart the mixer.
Ardour integration
shiloh-mixer can control and receive feedback from Ardour over OSC and Ardour’s WebSocket surface.
One-time Ardour setup
- Edit → Preferences → Control Surfaces → Open Sound Control → Show Protocol Settings
- Enable OSC, listen port:
3819 - Reply port:
3820, enable feedback on strips and master (fader + mute) - Click Apply
Install / update the Ardour session template
The template is generated live from the running mixer config:
mkdir -p ~/.config/ardour8/templates/shiloh-broadcast
curl http://localhost:8889/ardour/template \
-o ~/.config/ardour8/templates/shiloh-broadcast/shiloh-broadcast.template
Re-run after changing the channel layout.
Recovery after PipeWire upgrade
PipeWire upgrades can reset JACK port state. Restart the mixer and web UI:
systemctl --user restart shiloh-mixer mixer_web
If the graph doesn’t recover (all meters at 0.0):
systemctl --user restart pipewire pipewire-pulse wireplumber
# then:
systemctl --user restart shiloh-mixer