EARLY ACCESS Muster is being validated by pilot departments. Schedule a walk-through →

Self-hosted relay — deployment guide

The self-hosted relay is a standalone binary that extends Muster's mesh across networks without the traffic transiting Safe Signals' cloud relay. Deploy it when your department:

  • Has data-residency requirements that keep mesh traffic on-premises.
  • Wants federation resilience against internet outages at the firehouse.
  • Runs a multi-station deployment and wants each station's local mesh to survive a WAN failure.
  • Has strict cybersecurity policies that disallow cloud data flow.

Tier requirement: Tier 2 and above. Emergency and Tier 1 don't include self-hosted relay licensing.

Full technical reference: contact support@safesignals.io to request the self-hosted relay architecture and deployment specification.


Table of contents

  1. What the relay does
  2. Hardware and platform choices
  3. Install on Linux
  4. Install on Windows
  5. Install via Docker
  6. Provision the relay in the admin console
  7. Point devices at the relay
  8. Operations
  9. Troubleshooting

What the relay does

The relay is a Dart daemon that:

  • Accepts device registrations on port 8443 via WebSocket.
  • Verifies each device's License Key against the shared Muster issuer public key.
  • Pairs devices into tunnels so they can exchange end-to-end-encrypted mesh events even when they can't discover each other via mDNS on the local network.
  • Federates upstream to the Safe Signals cloud relay so devices behind the self-hosted relay can still participate in mutual-aid scenarios with departments that don't have their own relay.

The relay is not a general-purpose mesh transport. It handles WebSocket + JSON envelopes; the payloads are ChaCha20-Poly1305 AEAD-encrypted bytes the mobile app sends and receives. The relay never sees plaintext incident data.

Hardware and platform choices

Recommended:

  • Raspberry Pi 4 (2 GB RAM or higher) — cheapest option that handles 25-50 devices with headroom. ~$50 for the hardware.
  • Existing Linux server if your department already runs one (systemd unit ships with the relay).
  • Docker container if you have a container platform.
  • Windows Server or Windows 10/11 machine with the Inno Setup installer if your department is Windows-shop.

Not recommended:

  • Consumer routers with custom firmware — the resource profile is too tight for the WebSocket registrations.
  • Anything without a stable IP address on the department LAN.
  • Public cloud VMs (defeats the "self-hosted" purpose; Safe Signals' Stage 1 cloud relay is the correct choice if you want cloud hosting).

Install on Linux

  1. Download the tarball from https://releases.safesignals.io/relay/linux/muster-relay-<version>.tar.gz.

  2. Extract:

    sudo tar -xzf muster-relay-<version>.tar.gz -C /opt/
                  
  3. Run the install script:

    sudo /opt/muster-relay/install.sh
                  

    The script:

    • Creates a muster-relay system user.
    • Copies the binary to /usr/local/bin/muster_relay.
    • Installs the systemd unit to /etc/systemd/system/muster-relay.service.
    • Generates the Ed25519 identity key at /etc/muster-relay/identity.ed25519.
    • Drops a sample config at /etc/muster-relay/config.yaml that you'll edit next.
  4. Edit /etc/muster-relay/config.yaml. The YAML shape:

    identity:
                    private_key_file: /etc/muster-relay/identity.ed25519
                  
                  license:
                    scope_token_file: /etc/muster-relay/scope.token
                  
                  network:
                    listen_address: 0.0.0.0  # bind all interfaces
                    listen_port: 8443
                    # tls:
                    #   cert_file: /etc/muster-relay/tls.crt
                    #   key_file: /etc/muster-relay/tls.key
                  
                  federation:
                    upstream:
                      endpoint: wss://relay.safesignals.io
                    auth_token_file: /etc/muster-relay/federation.token
                  
                  ops:
                    status_listen_addr: 127.0.0.1:9090  # loopback-only
                    log_level: info
                  

    The scope.token and federation.token files are the department-admin-signed tokens you'll download from the admin console in the next section. See config.example.yaml in the repo for the full annotated example.

  5. Start the daemon:

    sudo systemctl enable --now muster-relay
                  sudo systemctl status muster-relay
                  
  6. Verify the daemon is up:

    curl http://127.0.0.1:9090/status
                  

    The response is a JSON document with the daemon's version, uptime, registered device count, and federation link state.

Install on Windows

  1. Download the installer from https://releases.safesignals.io/relay/windows/Muster-Relay-Setup-<version>.exe.

  2. Run the installer. It:

    • Places the binary at C:\Program Files\Muster Relay\muster_relay.exe.
    • Registers a Windows service via sc.exe create.
    • Generates the Ed25519 identity key at C:\ProgramData\Muster Relay\identity.ed25519.
    • Drops a sample config at C:\ProgramData\Muster Relay\config.yaml.
  3. Edit the config (same schema as the Linux example above).

  4. Start the service:

    sc.exe start "Muster Relay"
                  
  5. Verify at http://127.0.0.1:9090/status.

Firewall: the installer adds a Windows Defender Firewall rule for TCP 8443 inbound. If your department uses Group Policy to manage firewall rules, mirror the rule there.

Install via Docker

  1. Pull the image:

    docker pull gcr.io/muster-services/muster-relay:<version>
                  
  2. Prepare a host directory for the persistent config and identity:

    mkdir -p /srv/muster-relay
                  docker run --rm -v /srv/muster-relay:/etc/muster-relay \
                    gcr.io/muster-services/muster-relay:<version> genkey
                  
  3. Drop your config at /srv/muster-relay/config.yaml (same schema as Linux above).

  4. Run:

    docker run -d --name muster-relay \
                    -p 8443:8443 \
                    -p 9090:9090 \
                    -v /srv/muster-relay:/etc/muster-relay \
                    --restart unless-stopped \
                    gcr.io/muster-services/muster-relay:<version> start
                  

    Or use the docker-compose.yml sample bundled at apps/relay_daemon/packaging/docker/docker-compose.yml in the project repository.

  5. Verify: curl http://<host>:9090/status.

Provision the relay in the admin console

Before the relay can federate upstream, the department's chief must provision it. In the admin console:

  1. Go to Devices.

  2. Click → Provision a self-hosted relay (chief / admin only). This lands on the Provision Relay page.

  3. On the relay host, read the relay's Ed25519 fingerprint from the /status endpoint (which the daemon binds to 127.0.0.1:9090 by default per section §"Operations"):

    curl -s http://127.0.0.1:9090/status | jq -r .identity_fingerprint
                  

    (Docker: docker exec muster-relay curl -s http://127.0.0.1:9090/status | jq -r .identity_fingerprint.)

  4. Paste the fingerprint into the admin console form.

  5. Click Generate provisioning tokens. The console downloads two files:

    • scope.token — authorizes the relay's device scope (department id + mode).
    • federation.token — authorizes federation to the upstream cloud relay.
  6. Copy both files to the relay host at /etc/muster-relay/ (Linux) or C:\ProgramData\Muster Relay\ (Windows) or /srv/muster-relay/ (Docker host mount).

  7. Restart the daemon so it picks up the new tokens.

Point devices at the relay

Two options for pointing Muster devices at the self-hosted relay:

Per-device override (small deployments): each device's operator opens Settings → Peer Mesh → Cloud relay → Custom endpoint and enters the relay's wss:// URL (e.g., wss://relay.station-1.fd-example.local:8443).

Department-wide default (recommended for departments with an MDM): the admin console's Devices → Relays → Push relay URL to all devices action stamps every activated device's License Key with the department's relay URL. New devices activated after that inherit the URL automatically.

Operations

Status endpoint (/status) returns:

{
                "version": "0.1.0",
                "uptime_seconds": 12345,
                "identity_fingerprint": "…",
                "registered_devices": 12,
                "federation": {
                  "upstream_url": "wss://relay.safesignals.io/…",
                  "state": "connected",
                  "last_reconnect_at": null
                }
              }
              

Health endpoint (/health) returns 200 OK if the daemon is running and the identity key is loadable. Use this as a container HEALTHCHECK or an external monitor probe.

Logs:

  • Linux: journalctl -u muster-relay -f.
  • Windows: Event Viewer → Applications and Services Logs → Muster Relay.
  • Docker: docker logs -f muster-relay.

Upgrading:

  1. Download the new package version.
  2. Stop the daemon.
  3. Replace the binary (Linux/Docker) or run the installer (Windows).
  4. Start the daemon.
  5. Verify at /status that the version reported matches the new release.

Configs, identity keys, and provisioning tokens are preserved across upgrades.

Troubleshooting

Daemon won't start

  • muster_relay validate-config /etc/muster-relay/config.yaml reports any YAML parse or field-validation errors without starting the daemon. Run it first.
  • Systemd exit codes surface in journalctl -u muster-relay.

Devices can't connect

  • Verify TCP 8443 is reachable from the device's network (test with curl -v https://<relay-host>:8443/health).
  • Verify the device's Muster tier is Tier 2 or above.
  • Check the daemon logs for LicenseAuthException — the device's License Key may be expired, revoked, or issued against a different issuer key.

Federation upstream shows disconnected

  • Verify outbound TCP 443 to relay.safesignals.io from the relay host.
  • Verify the federation.token file matches the fingerprint the admin console generated for. If you regenerated the identity key, the token is invalid and you must re-provision.

Identity key lost

  • Re-run muster_relay genkey to create a new one.
  • Re-provision in the admin console (Devices → Relays → Re-provision existing relay). The old provisioning tokens become invalid; every device that had the old relay's URL cached needs its next mesh session to re-negotiate.

For deeper diagnostics, capture a status snapshot + recent logs and email support@safesignals.io with your department id.