Files
cortandClaude Opus 5 6f6dadd6ac Make a first-time install work without prior DMR or Linux experience
HBlink4 draws operators who have never run a network server before, and
several things in the install path assumed otherwise.

Sample configuration split in two. config_sample.json was a feature
showcase, and copying it as documented produced a broken server: its
example repeater pattern claimed IDs 310000-312999 with a passphrase of
its own, so an operator whose repeater fell in that range was rejected
with no way to diagnose it (patterns match first-wins and never fall
through to "default"). It also shipped an outbound link and an OpenBridge
trunk enabled against hosts that do not exist, and bound both address
families to one port, which cannot work. It is now a minimal working
config needing one edit -- the passphrase. The full reference moves to
config_advanced_sample.json with its link examples disabled and the ID
overlap called out.

Ship IPv4-only. Dual-stack is fully supported and now documented as four
concrete layouts, each verified to bind; it is simply not the default,
because most repeaters are IPv4 and the same-port bind conflict is not
something a new operator should have to learn first. The built-in default
when the key is absent stays false, so existing installs are unaffected.
"IPv6 is globally disabled" drops from WARNING to INFO -- it is now the
recommended setting and should not read as a fault.

install_services.sh: refuse paths containing a space, $ or % (systemd
mis-parses them in ExecStart=, and a space previously produced a unit
that passed verification and then failed at start); escape & in the path
substitution; render and verify units in a temp dir so a failed
substitution cannot leave a broken unit in /etc/systemd/system. Check the
things that otherwise surface only in the journal: complete checkout,
Python 3.9+, server and dashboard dependencies, both config files, and
agreement between the two halves of the dashboard event link.

readme: seven numbered steps with no diversions, adding the prerequisites
(python3-venv, which Debian and Ubuntu users hit constantly) and the
firewall ports, neither of which were mentioned.

Also: the dashboard logged a hardcoded localhost:8080 regardless of the
configured port; run_all.sh now fails clearly on missing config instead of
a traceback; pyproject's comment claimed systemd should invoke the console
scripts, contradicting how the units actually work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 08:37:11 -05:00

6.8 KiB

HBlink4 Systemd Service Installation

This directory contains systemd service files for running HBlink4 and its dashboard as system services.

Service Files

  • hblink4.service - Main HBlink4 DMR server
  • hblink4-dash.service - Web dashboard (depends on hblink4.service)

The units run the programs straight from the checkout — there is no install step, so after a git pull you only need to restart the services. If you are upgrading from a version whose units pointed at run.py, re-copy the unit files below before restarting.

The shipped unit files contain the author's user and paths (User=cort, /home/cort/hblink4), so they will not work as-is on your system. This script asks for your service user and group, fills in your paths, installs both units and reloads systemd:

sudo ./scripts/install_services.sh

It offers sensible defaults (the user who invoked sudo, and that user's primary group), backs up any units you already have into the project root as <unit>.<timestamp>.bak, and optionally enables and starts the services.

Before writing anything it checks the things that otherwise fail later, in the journal, where they are hard to read:

  • the checkout is complete, and its path contains no character systemd will mis-parse in ExecStart= (a space, $ or %)
  • venv/ exists and its Python is 3.9 or newer
  • the server's dependencies are installed, and the dashboard's — a missing fastapi is the usual reason hblink4-dash restarts in a loop
  • config/config.json and dashboard/config.json exist, offering to create them from the samples if not
  • the two halves of the dashboard event link agree; if they do not, the dashboard silently shows HBlink4 as disconnected
  • the service user can write to the installation directory

To see what it would produce without touching the live system:

sudo DESTDIR=/tmp/preview ./scripts/install_services.sh

The manual steps below do the same thing by hand.

Manual installation

⚠️ The shipped unit files contain the author's user and paths — User=cort, Group=cort, and /home/cort/hblink4 throughout. They will not work as-is on your system. You must change the user, the group, and every path to match your installation.

Edit the installed copies in /etc/systemd/system/, not the ones in the repository — editing the repo copies makes them show up as local changes and conflict on the next git pull.

1. Install the service files

Copy the service files to systemd's system directory:

sudo cp hblink4.service /etc/systemd/system/
sudo cp hblink4-dash.service /etc/systemd/system/

2. Set your user and paths

Four settings in each file need to match your system: User=, Group=, WorkingDirectory=, and ExecStart=. Edit them with your editor of choice:

sudo nano /etc/systemd/system/hblink4.service
sudo nano /etc/systemd/system/hblink4-dash.service

Or, run this from your HBlink4 directory to substitute them automatically:

sudo sed -i "s|/home/cort/hblink4|$PWD|g; s|^User=cort$|User=$USER|; s|^Group=cort$|Group=$(id -gn)|" \
    /etc/systemd/system/hblink4.service /etc/systemd/system/hblink4-dash.service

Check the result before continuing — no cort or /home/cort should remain:

grep -nE "^(User|Group|WorkingDirectory|ExecStart)=" /etc/systemd/system/hblink4*.service

3. Reload systemd

Tell systemd to reload its configuration. This is required after any edit to a unit file — systemd will keep using the old version until you do:

sudo systemctl daemon-reload

4. Enable services (optional)

To start the services automatically at boot:

sudo systemctl enable hblink4
sudo systemctl enable hblink4-dash

5. Start the services

sudo systemctl start hblink4
sudo systemctl start hblink4-dash

Service Management

Check service status

sudo systemctl status hblink4
sudo systemctl status hblink4-dash

View logs

# View logs for HBlink4
sudo journalctl -u hblink4 -f

# View logs for dashboard
sudo journalctl -u hblink4-dash -f

# View last 100 lines
sudo journalctl -u hblink4 -n 100

Stop services

sudo systemctl stop hblink4
sudo systemctl stop hblink4-dash

Restart services

sudo systemctl restart hblink4
sudo systemctl restart hblink4-dash

Disable autostart

sudo systemctl disable hblink4
sudo systemctl disable hblink4-dash

Configuration

As shipped, the service files:

  • Run as user cort in group cort — change this to your own user and group
  • Use the Python virtual environment at /home/cort/hblink4/venv — change to your path
  • Automatically restart on failure (after 10 seconds)
  • Log to systemd journal (view with journalctl)
  • Start after network is available
  • Dashboard starts after and depends on HBlink4

Changing a unit file later

Always edit the copy in /etc/systemd/system/, then reload and restart:

sudo nano /etc/systemd/system/hblink4.service
sudo systemctl daemon-reload      # required, or systemd keeps the old version
sudo systemctl restart hblink4

Editing the copy in the repository has no effect on a running service, and leaves you with local changes that conflict on the next git pull.

Security Features

Both services include security hardening:

  • NoNewPrivileges=true - Prevents privilege escalation

Do not add PrivateTmp=true. It gives each service its own private /tmp namespace, so with the unix event transport HBlink4 and the dashboard would each create a separate /tmp/hblink4.sock and never see each other. The dashboard would simply report HBlink4 as disconnected, with no error on either side. If you want PrivateTmp, move unix_socket out of /tmp on both sides first (or use the tcp transport).

Troubleshooting

Service won't start

Check the status and logs:

sudo systemctl status hblink4
sudo journalctl -u hblink4 -n 50

Common issues:

  • Virtual environment not found: Check path in ExecStart=
  • Permission errors: Ensure user/group are correct
  • Config file errors: Check HBlink4 configuration files
  • Port conflicts: Another service using the same ports

Dashboard can't connect to HBlink4

  1. Ensure HBlink4 is running: sudo systemctl status hblink4
  2. Check dashboard config points to correct socket/host
  3. If using the unix transport, confirm neither unit sets PrivateTmp=true (see Security Features above) and that both sides name the same socket path
  4. Check logs: sudo journalctl -u hblink4-dash -n 50

Notes

  • The dashboard service has Wants=hblink4.service, so it will start after HBlink4
  • Both services have Restart=always for automatic recovery
  • Logs are sent to systemd journal, not file-based logging
  • Services run with the same privileges as the cort user