Appliance Recipe: IRLP Ham Radio Node
A kldload appliance for ham radio operators — IRLP/EchoLink repeater linking over WireGuard, digital modes (FT8, JS8Call, APRS), SDR integration, and audio logging on ZFS. Voice goes in one radio, comes out another, encrypted over the internet. Digital mode contacts get logged, decoded, and archived on ZFS datasets with automatic snapshots.
The problem: Ham radio repeaters have limited range. Linking them over the internet traditionally requires exposed public ports, unencrypted VoIP, and complex NAT configuration. Digital mode software runs on Windows with no backup strategy.
The solution: Two kldload nodes connected via WireGuard. Voice goes in one radio, comes out the other — encrypted, NAT-friendly, single UDP port. Digital modes run headless with ZFS-backed logging.
Architecture
Site A (radio + antenna) Site B (radio + antenna)
┌──────────────────────┐ ┌──────────────────────┐
│ FM Transceiver │ │ FM Transceiver │
│ │ │ │ │ │
│ ┌────▼────┐ │ │ ┌────▼────┐ │
│ │USB Audio│ CM108 │ │ │USB Audio│ CM108 │
│ │ Card │ + PTT │ │ │ Card │ + PTT │
│ └────┬────┘ │ │ └────┬────┘ │
│ │ │ │ │ │
│ ┌────▼────────────┐ │ │ ┌────▼────────────┐ │
│ │ kldload │ │ │ │ kldload │ │
│ │ SvxLink │ │ │ │ SvxLink │ │
│ │ wg0: 10.99.0.1 ├─┼── UDP ──────┼──┤ wg0: 10.99.0.2│ │
│ └─────────────────┘ │ 51820 │ └─────────────────┘ │
└──────────────────────┘ └──────────────────────┘
Hardware
| Component | Details | Cost |
|---|---|---|
| Mini PC | Any x86_64 (Raspberry Pi works too with ARM build) | $100-200 |
| USB sound card | CM108/CM119 chipset (has GPIO for PTT) | $5-15 |
| FM transceiver | Handheld or mobile radio (e.g., Baofeng UV-5R) | $25-50 |
| Audio cables | 3.5mm to radio mic/speaker jacks | $10 |
| USB stick | kldload installer | $5 |
| Total | ~$150-280 |
CM108 PTT wiring
The CM108 USB audio chipset has a GPIO3 pin (pin 13) that can be used for Push-To-Talk control without a separate interface board:
CM108 GPIO3 (pin 13) → 2N2222 transistor base (via 4.7K resistor)
Transistor collector → Radio PTT line
Transistor emitter → Ground
Hamlib supports this natively as PTT type CM108.
Step 1: Install kldload
cat > /tmp/answers.env << 'EOF'
KLDLOAD_DISTRO=debian
KLDLOAD_DISK=/dev/sda
KLDLOAD_HOSTNAME=irlp-node-a
KLDLOAD_USERNAME=admin
KLDLOAD_PASSWORD=changeme
KLDLOAD_PROFILE=server
KLDLOAD_NET_METHOD=dhcp
EOF
kldload-install-target --config /tmp/answers.env
Step 2: Install SvxLink
apt install -y svxlink-server svxlink-calibration-tools
apt install -y alsa-utils sox hamlib-utils
Step 3: Configure USB audio
# Find the USB sound card
arecord -l
# card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]
# Test recording (speak into the radio, key up)
arecord -D plughw:1,0 -f S16_LE -r 8000 -c 1 -d 5 /tmp/test.wav
aplay /tmp/test.wav
# Set levels
alsamixer -c 1
# Set capture to ~80%, playback to ~70%
# Save ALSA state
alsactl store 1
Step 4: Set up CM108 PTT
# udev rule for CM108 GPIO access
cat > /etc/udev/rules.d/50-cm108-ptt.rules << 'EOF'
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d8c", MODE="0666"
EOF
udevadm control --reload-rules
udevadm trigger
# Test PTT with hamlib
rigctl -m 1 -p /dev/hidraw0 -P CM108 T 1 # key up
rigctl -m 1 -p /dev/hidraw0 -P CM108 T 0 # key down
Step 5: Configure SvxLink
# /etc/svxlink/svxlink.conf
[GLOBAL]
LOGICS=SimplexLogic
CFG_DIR=/etc/svxlink/svxlink.d
TIMESTAMP_FORMAT="%c"
CARD_SAMPLE_RATE=48000
[SimplexLogic]
TYPE=Simplex
RX=Rx1
TX=Tx1
MODULES=ModuleEchoLink
CALLSIGN=YOURCALL-L
SHORT_IDENT_INTERVAL=10
LONG_IDENT_INTERVAL=60
ROGER_SOUND=0
[Rx1]
TYPE=Local
AUDIO_DEV=alsa:plughw:1
AUDIO_CHANNEL=0
SQL_DET=VOX
VOX_FILTER_DEPTH=20
VOX_THRESH=500
DEEMPHASIS=0
PEAK_METER=1
DTMF_DEC_TYPE=INTERNAL
[Tx1]
TYPE=Local
AUDIO_DEV=alsa:plughw:1
AUDIO_CHANNEL=0
PTT_TYPE=Hidraw
HID_DEVICE=/dev/hidraw0
HID_PTT_PIN=GPIO3
PREEMPHASIS=0
DTMF_TONE_LENGTH=100
DTMF_TONE_SPACING=50
Step 6: Configure EchoLink module (optional)
# /etc/svxlink/svxlink.d/ModuleEchoLink.conf
[ModuleEchoLink]
NAME=EchoLink
ID=2
TIMEOUT=60
SERVERS=servers.echolink.org
CALLSIGN=YOURCALL-L
PASSWORD=your-echolink-password
SYSOPNAME=Your Name
LOCATION=[Svx] Your City, ST
MAX_QSOS=4
MAX_CONNECTIONS=4
LINK_IDLE_TIMEOUT=300
Step 7: Set up WireGuard between nodes
Node A:
umask 077
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = <node-a-private-key>
[Peer]
PublicKey = <node-b-public-key>
Endpoint = node-b.example.com:51820
AllowedIPs = 10.99.0.2/32
PersistentKeepalive = 25
EOF
systemctl enable --now wg-quick@wg0
Node B: Same config, swap IPs and keys.
Step 8: Link nodes over WireGuard with remotetrx
Instead of EchoLink (which requires public servers), use SvxLink’s
remotetrx to link two nodes directly over the WireGuard
tunnel.
Node A — runs svxlink (the repeater
controller):
# In svxlink.conf, change Rx1 to use a network receiver
[Rx1]
TYPE=Net
HOST=10.99.0.2
TCP_PORT=5210
CODEC=S16
Node B — runs remotetrx (remote
transceiver):
# /etc/svxlink/remotetrx.conf
[GLOBAL]
LOGICS=Trx1
CFG_DIR=/etc/svxlink/svxlink.d
TIMESTAMP_FORMAT="%c"
[Trx1]
TYPE=Net
RX=Rx1
TX=Tx1
LISTEN_PORT=5210
AUTH_KEY=shared-secret-key
[Rx1]
TYPE=Local
AUDIO_DEV=alsa:plughw:1
AUDIO_CHANNEL=0
SQL_DET=VOX
VOX_FILTER_DEPTH=20
VOX_THRESH=500
[Tx1]
TYPE=Local
AUDIO_DEV=alsa:plughw:1
AUDIO_CHANNEL=0
PTT_TYPE=Hidraw
HID_DEVICE=/dev/hidraw0
HID_PTT_PIN=GPIO3
# Node B
systemctl enable --now remotetrx
# Node A
systemctl enable --now svxlink
Now voice on Node A’s radio comes out Node B’s radio and vice versa — over an encrypted WireGuard tunnel. No public ports exposed except UDP 51820.
Step 9: Firewall
cat > /etc/nftables.d/irlp.nft << 'EOF'
table inet filter {
chain input {
# SSH
tcp dport 22 accept
# WireGuard
udp dport 51820 accept
# SvxLink remotetrx (only on WireGuard interface)
iifname "wg0" tcp dport 5210 accept
# EchoLink (if used — only needed on one node)
udp dport { 5198, 5199 } accept
tcp dport 5200 accept
}
}
EOF
systemctl reload nftables
Step 10: ZFS snapshots for audio logs
# Dataset for recorded audio
zfs create -o mountpoint=/srv/radio-logs -o compression=zstd rpool/srv/radio-logs
# SvxLink can log audio — configure in svxlink.conf:
# REC_DIR=/srv/radio-logs
# Daily snapshots
echo '0 0 * * * root zfs snapshot rpool/srv/radio-logs@daily-$(date +\%Y\%m\%d)' >> /etc/crontab
# Replicate logs to backup node
echo '0 2 * * * root syncoid rpool/srv/radio-logs 10.99.0.2:rpool/srv/radio-logs-backup' >> /etc/crontab
Test
# Check audio
arecord -D plughw:1,0 -f S16_LE -r 8000 -c 1 -d 3 /tmp/test.wav && aplay /tmp/test.wav
# Check PTT
rigctl -m 1 -p /dev/hidraw0 -P CM108 T 1 && sleep 2 && rigctl -m 1 -p /dev/hidraw0 -P CM108 T 0
# Check WireGuard
wg show wg0
# Check SvxLink
systemctl status svxlink
journalctl -u svxlink -f
# Key up on the local radio, you should hear it on the remote radio
Digital Modes — FT8, JS8Call, APRS
FT8 / WSJT-X (headless)
# Install WSJT-X
apt install -y wsjtx
# ZFS dataset for FT8 logs and audio
zfs create -o mountpoint=/srv/ft8 -o compression=zstd rpool/srv/ft8
# For headless operation, use jtdx or wsjtx with a virtual display
apt install -y xvfb
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
# Or use the command-line decoder directly
jt9 -8 -d 3 -f 1500 /srv/ft8/audio/capture.wav
JS8Call (keyboard messaging over HF)
# Install JS8Call
apt install -y js8call
# JS8Call can relay messages between stations — a mesh network over HF
# Combined with WireGuard, you can bridge JS8Call stations over the internet
# for a hybrid RF/internet messaging network
APRS with Direwolf (packet radio + position tracking)
# Install Direwolf — software TNC (modem) for APRS
apt install -y direwolf
# ZFS dataset for APRS logs
zfs create -o mountpoint=/srv/aprs -o compression=zstd-9 rpool/srv/aprs
cat > /etc/direwolf.conf << 'DW'
ADEVICE plughw:1,0
CHANNEL 0
MYCALL YOURCALL-10
MODEM 1200
# iGate — relay APRS packets to the internet
IGSERVER noam.aprs2.net
IGLOGIN YOURCALL-10 12345
# Log all received packets
LOGDIR /srv/aprs/logs
# Beacon your own position every 30 minutes
PBEACON delay=1 every=30 via=WIDE1-1,WIDE2-1 \
lat=YOUR_LAT long=YOUR_LON symbol="/-" \
comment="kldload APRS iGate"
DW
# systemd service
cat > /etc/systemd/system/direwolf.service << 'EOF'
[Unit]
Description=Direwolf APRS TNC
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/direwolf -c /etc/direwolf.conf -t 0
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now direwolf
Fldigi (multi-mode digital)
# Fldigi supports PSK31, RTTY, Olivia, MFSK, and dozens more
apt install -y fldigi
# For headless contest logging, use fllog + flrig for rig control
apt install -y fllog flrig
Winlink (email over radio)
# Pat — open-source Winlink client for Linux
# Sends and receives email over HF/VHF radio — critical for EMCOMM
go install github.com/la5nta/pat@latest
# Configure Pat with your callsign and radio
pat configure
# Connect to a Winlink RMS gateway
pat connect telnet # internet (for testing)
pat connect ardop # HF radio (ARDOP modem)
Emergency Communications (EMCOMM)
# EMCOMM kit checklist:
# - kldload USB stick (pre-built ISO with all packages)
# - Laptop or mini PC with USB ports
# - CM108 USB sound card + audio cables
# - Handheld radio (Baofeng UV-5R or similar)
# - Antenna (roll-up J-pole for VHF, wire dipole for HF)
# - 12V battery + inverter (or USB power bank for the laptop)
# Boot the USB. Install in 5 minutes. You have:
# - SvxLink for voice repeater linking
# - Direwolf for APRS packet radio
# - Pat for Winlink email
# - WSJT-X for FT8 long-distance contacts
# - ZFS-backed logging for everything
# - WireGuard if any internet is available (satellite, cellular)
FCC Note
FCC Part 97 prohibits obscuring the meaning of amateur radio transmissions over the air. The WireGuard encryption applies to the internet backhaul, not the RF transmission. The RF side remains unencrypted as required. This is the same approach used by IRLP, EchoLink, AllStarLink, and 44Net Connect — all of which encrypt internet traffic while keeping RF transmissions in the clear.