Switch the lab reflector to jayReflector and harden local station use.
Build DJ1JAY’s fork with an optional Satellite/Trunk stub, allow -c/--config, open capture and playback as separate devices, and add a lab start helper. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,15 +4,16 @@ TZ=Europe/Berlin
|
||||
|
||||
LISTEN_PORT=5300
|
||||
|
||||
START_SVXLINK=0
|
||||
START_REMOTETRX=0
|
||||
START_SVXREFLECTOR=1
|
||||
# 0 = local lab only (default). 1 = append config/svxreflector.link.conf
|
||||
# (Satellite/Trunk from DJ1JAY). Satellite dials out — no public DNS needed.
|
||||
ENABLE_FM_LINK=0
|
||||
|
||||
SVXREFLECTOR_CONF=/etc/svxlink/svxreflector.conf
|
||||
SVXREFLECTOR_LINK_CONF=/etc/svxlink/svxreflector.link.conf
|
||||
SVXREFLECTOR_ARGS=--logfile=/var/log/svxlink/svxreflector.log
|
||||
|
||||
FUNKNETZ_HOST=127.0.0.1
|
||||
FUNKNETZ_PORT=5300
|
||||
FUNKNETZ_CALLSIGN=DL0FN-1
|
||||
FUNKNETZ_AUTH_KEY=local-lab-secret-change-me
|
||||
FUNKNETZ_TALK_GROUP=9
|
||||
FUNKNETZ_TALK_GROUP=20
|
||||
|
||||
@@ -4,3 +4,7 @@ data/*
|
||||
!data/.gitkeep
|
||||
.env
|
||||
*.log
|
||||
.funknetz.generated.json
|
||||
# Optional: keep a private link file with real DJ1JAY secrets untracked.
|
||||
# The committed config/svxreflector.link.conf is a commented stub only.
|
||||
config/svxreflector.link.local.conf
|
||||
|
||||
+40
-45
@@ -1,77 +1,72 @@
|
||||
# Build and run SvxReflector for the Funknetz local lab.
|
||||
# Provenance: patterned on f4hlv/svxlink-docker (community); builds from upstream sm0svx/svxlink.
|
||||
# Build jayReflector (DJ1JAY / FM-Funknetz) for the Funknetz local lab.
|
||||
# Binary name remains svxreflector; Trunk/Satellite are opt-in via config.
|
||||
# Upstream pattern: dj1jay/jayReflector Dockerfile (clone + cmake, not stock sm0svx).
|
||||
|
||||
# =========================================================
|
||||
# STAGE 1 — BUILD
|
||||
# =========================================================
|
||||
FROM debian:trixie-slim AS builder
|
||||
FROM debian:bookworm AS builder
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get -y install --no-install-recommends \
|
||||
git ca-certificates \
|
||||
cmake g++ make dpkg-dev \
|
||||
libsigc++-2.0-dev libgsm1-dev libpopt-dev tcl8.6-dev \
|
||||
libgcrypt20-dev libspeex-dev libasound2-dev \
|
||||
libopus-dev librtlsdr-dev libjsoncpp-dev \
|
||||
libcurl4-openssl-dev libgpiod-dev libogg-dev \
|
||||
ladspa-sdk libssl-dev \
|
||||
build-essential cmake \
|
||||
libsigc++-2.0-dev libssl-dev libjsoncpp-dev libpopt-dev \
|
||||
libopus-dev libgsm1-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG GIT_URL=https://github.com/sm0svx/svxlink.git
|
||||
ARG GIT_BRANCH=master
|
||||
ARG GIT_URL=https://github.com/dj1jay/jayReflector.git
|
||||
ARG GIT_BRANCH=main
|
||||
ARG GIT_REF=
|
||||
ARG NUM_CORES=4
|
||||
|
||||
WORKDIR /build
|
||||
RUN git clone --recursive --depth 1 --branch "${GIT_BRANCH}" "${GIT_URL}" svxlink
|
||||
WORKDIR /build/svxlink
|
||||
|
||||
RUN set -eux; \
|
||||
SVX_VER="$(awk -F= '$1=="SVXSERVER"{gsub(/[[:space:]]/,"",$2); print $2}' src/versions)"; \
|
||||
echo "SVXSERVER version from src/versions: ${SVX_VER}"; \
|
||||
rm -rf build && mkdir -p build && cd build; \
|
||||
cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INSTALL_SYSCONFDIR=/etc \
|
||||
-DCMAKE_INSTALL_LOCALSTATEDIR=/var \
|
||||
if [ -n "${GIT_REF}" ]; then \
|
||||
git clone --recursive "${GIT_URL}" jayReflector; \
|
||||
cd jayReflector && git checkout "${GIT_REF}"; \
|
||||
else \
|
||||
git clone --recursive --depth 1 --branch "${GIT_BRANCH}" "${GIT_URL}" jayReflector; \
|
||||
fi
|
||||
|
||||
WORKDIR /build/jayReflector
|
||||
RUN set -eux; \
|
||||
cmake -S src -B build \
|
||||
-DLOCAL_STATE_DIR=/var \
|
||||
-DUSE_QT=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCPACK_GENERATOR=DEB \
|
||||
-DCPACK_PACKAGE_VERSION="${SVX_VER}" \
|
||||
-DCPACK_DEBIAN_PACKAGE_VERSION="${SVX_VER}" \
|
||||
../src; \
|
||||
make -j"${NUM_CORES}"; \
|
||||
make package; \
|
||||
ls -lh *.deb
|
||||
-DUSE_ALSA=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release; \
|
||||
cmake --build build --target svxreflector -j"${NUM_CORES}"
|
||||
|
||||
# =========================================================
|
||||
# STAGE 2 — RUNTIME
|
||||
# =========================================================
|
||||
FROM debian:trixie-slim
|
||||
FROM debian:bookworm-slim
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get -y install --no-install-recommends \
|
||||
adduser init-system-helpers ca-certificates openssl \
|
||||
libasyncaudio1.6t64 libasynccore1.6t64 libasynccpp1.6t64 libcurl4t64 \
|
||||
libgcrypt20 libgsm1 libjsoncpp26 libpopt0 librtlsdr0 \
|
||||
libsigc++-2.0-0v5 libtcl8.6 libgcc-s1 libstdc++6 \
|
||||
libasound2 libgpiod3 libogg0 libopus0 libspeex1 \
|
||||
ca-certificates openssl \
|
||||
libsigc++-2.0-0v5 libssl3 libjsoncpp25 libpopt0 \
|
||||
libopus0 libgsm1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /build/svxlink/build/*.deb /tmp/svxlink.deb
|
||||
COPY --from=builder /build/jayReflector/build/bin/svxreflector /usr/bin/svxreflector
|
||||
COPY --from=builder /build/jayReflector/build/lib/lib*.so* /usr/lib/
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install --no-install-recommends /tmp/svxlink.deb && \
|
||||
rm -f /tmp/svxlink.deb && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /etc/svxlink /var/log/svxlink /var/lib/svxlink && \
|
||||
RUN mkdir -p /etc/svxlink /etc/svxlink/pki /var/log/svxlink /var/lib/svxlink && \
|
||||
ldconfig && \
|
||||
useradd -r -u 1000 -m -d /home/svx svx || true
|
||||
|
||||
ENV SVXLINK_CONF=/etc/svxlink/svxlink.conf \
|
||||
REMOTETRX_CONF=/etc/svxlink/remotetrx.conf \
|
||||
SVXREFLECTOR_CONF=/etc/svxlink/svxreflector.conf
|
||||
ENV SVXREFLECTOR_CONF=/etc/svxlink/svxreflector.conf \
|
||||
SVXREFLECTOR_LINK_CONF=/etc/svxlink/svxreflector.link.conf \
|
||||
ENABLE_FM_LINK=0
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# 5300: local Funknetz / SvxLink clients (TCP+UDP)
|
||||
# Satellite mode dials out to parent :5303 — no inbound peer port required.
|
||||
# Trunk mode would need 5302/tcp inbound; not published by default in compose.
|
||||
EXPOSE 5300/tcp 5300/udp
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
###################################################################
|
||||
# Local lab SvxReflector for Funknetz (protocol 2.0 / ReflectorV2)
|
||||
# Local lab jayReflector for Funknetz (protocol 2.0 / ReflectorV2)
|
||||
# Secrets here are placeholders — do not reuse production keys.
|
||||
#
|
||||
# Standalone by default: local clients only. FM-Funknetz Satellite/Trunk
|
||||
# keys live in svxreflector.link.conf and are appended when
|
||||
# ENABLE_FM_LINK=1 (see .env and docker-compose).
|
||||
###################################################################
|
||||
|
||||
[GLOBAL]
|
||||
@@ -8,10 +12,13 @@ TIMESTAMP_FORMAT="%c"
|
||||
LISTEN_PORT=5300
|
||||
TG_FOR_V1_CLIENTS=999
|
||||
# CODECS=OPUS
|
||||
COMMAND_PTY=/dev/shm/reflector_ctrl
|
||||
COMMAND_PTY=/dev/shm/svxreflector_ctrl
|
||||
CERT_PKI_DIR=/etc/svxlink/pki/
|
||||
# Optional status JSON (publish 8080 in compose if you enable this):
|
||||
# HTTP_SRV_PORT=8080
|
||||
|
||||
# Newer SvxReflector bootstraps a local PKI even for protocol 2.0 AUTH_KEY clients.
|
||||
# COMMON_NAME is required so the server certificate can be generated on first start.
|
||||
# jayReflector / modern SvxReflector bootstraps a local PKI even for
|
||||
# protocol 2.0 AUTH_KEY clients. COMMON_NAME is required on first start.
|
||||
[SERVER_CERT]
|
||||
COMMON_NAME=funknetz-lab.local
|
||||
SUBJECT_ALT_NAME=DNS:funknetz-lab.local,DNS:localhost,IP:127.0.0.1
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
###################################################################
|
||||
# Optional FM-Funknetz link stub (jayReflector)
|
||||
#
|
||||
# Default: leave ENABLE_FM_LINK=0 in .env — this file is ignored.
|
||||
# When DJ1JAY provides values:
|
||||
# 1. Fill ONE of the blocks below (prefer Satellite for home/NAT labs).
|
||||
# 2. Set ENABLE_FM_LINK=1 in .env
|
||||
# 3. docker compose up -d
|
||||
#
|
||||
# Satellite (recommended): this reflector dials OUT to the parent on
|
||||
# TCP 5303. No public DNS, no inbound peer port, no port-forward required
|
||||
# for the link. Local Funknetz clients still use host :5300 only.
|
||||
#
|
||||
# Trunk: equal mesh peer; needs mutual reachability on TCP 5302 and a
|
||||
# matching [TRUNK_x] section name + SECRET on both sides.
|
||||
#
|
||||
# Do not commit real secrets. Prefer a local override or keep secrets
|
||||
# only in your private .env workflow if you copy this file.
|
||||
# Docs: docs/research/2026-10-07-jayreflector-fm-funknetz.md
|
||||
# README: https://github.com/dj1jay/jayReflector
|
||||
###################################################################
|
||||
|
||||
# --- Satellite (fill from DJ1JAY; uncomment to use) ---
|
||||
# SATELLITE_OF=parent.example.org:5303
|
||||
# SATELLITE_ID=lab-yourcall
|
||||
# SATELLITE_SECRET="replace-with-secret-from-dj1jay"
|
||||
# Only these TGs cross to/from the parent (bidirectional). Omit for all TGs.
|
||||
# SATELLITE_FILTER=2621,2629,20
|
||||
|
||||
# --- Trunk peer alternative (fill from DJ1JAY; usually leave commented) ---
|
||||
# LOCAL_PREFIX=9
|
||||
# TRUNK_LISTEN_PORT=5302
|
||||
#
|
||||
# [TRUNK_LAB_FM]
|
||||
# HOST=fm-parent.example.org
|
||||
# PORT=5302
|
||||
# SECRET="replace-with-agreed-trunk-secret"
|
||||
# REMOTE_PREFIX=262
|
||||
# ALLOW_TGS=2621,2629,20
|
||||
# # TG_MAP=1:2624100
|
||||
# MQTT_NAME=Lab-YourCall
|
||||
@@ -1,6 +1,9 @@
|
||||
# Local SvxReflector lab for Funknetz.
|
||||
# Build once, then: docker compose up -d
|
||||
# Local jayReflector lab for Funknetz.
|
||||
# Build once, then: docker compose up -d --build
|
||||
# Connect the app with host/port/callsign/authKey from .env (see FUNKNETZ_*).
|
||||
#
|
||||
# FM-Funknetz link (optional): fill config/svxreflector.link.conf from DJ1JAY,
|
||||
# set ENABLE_FM_LINK=1, recreate. Satellite mode is outbound-only (no DNS/inbound).
|
||||
|
||||
services:
|
||||
svxreflector:
|
||||
@@ -8,9 +11,11 @@ services:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
GIT_URL: https://github.com/sm0svx/svxlink.git
|
||||
GIT_BRANCH: master
|
||||
image: funknetz-svxreflector:local
|
||||
GIT_URL: https://github.com/dj1jay/jayReflector.git
|
||||
GIT_BRANCH: main
|
||||
# Optional pin (overrides branch clone when set), e.g.:
|
||||
# GIT_REF: 50997ed21a37c7c05c132eca5ebb07bab4b80fc7
|
||||
image: funknetz-jayreflector:local
|
||||
container_name: funknetz-svxreflector
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
@@ -18,16 +23,21 @@ services:
|
||||
ports:
|
||||
# Host LISTEN_PORT → container 5300 (must match config/svxreflector.conf).
|
||||
# Control is TCP; audio and UDP keepalives use the same number.
|
||||
# Satellite uplink uses outbound TCP 5303 — not published here.
|
||||
- "${LISTEN_PORT:-5300}:5300/tcp"
|
||||
- "${LISTEN_PORT:-5300}:5300/udp"
|
||||
# Optional HTTP /status when HTTP_SRV_PORT=8080 is set in conf:
|
||||
# - "8080:8080/tcp"
|
||||
# Trunk peer only (not needed for Satellite):
|
||||
# - "5302:5302/tcp"
|
||||
environment:
|
||||
TZ: ${TZ:-Europe/Berlin}
|
||||
START_SVXLINK: ${START_SVXLINK:-0}
|
||||
START_REMOTETRX: ${START_REMOTETRX:-0}
|
||||
START_SVXREFLECTOR: ${START_SVXREFLECTOR:-1}
|
||||
ENABLE_FM_LINK: ${ENABLE_FM_LINK:-0}
|
||||
SVXREFLECTOR_CONF: ${SVXREFLECTOR_CONF:-/etc/svxlink/svxreflector.conf}
|
||||
SVXREFLECTOR_LINK_CONF: ${SVXREFLECTOR_LINK_CONF:-/etc/svxlink/svxreflector.link.conf}
|
||||
SVXREFLECTOR_ARGS: ${SVXREFLECTOR_ARGS:-}
|
||||
volumes:
|
||||
- ./config/svxreflector.conf:/etc/svxlink/svxreflector.conf:ro
|
||||
- ./config/svxreflector.link.conf:/etc/svxlink/svxreflector.link.conf:ro
|
||||
- ./logs:/var/log/svxlink
|
||||
- ./data:/var/lib/svxlink
|
||||
- ./data/pki:/etc/svxlink/pki
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
SVXLINK_CONF="${SVXLINK_CONF:-/etc/svxlink/svxlink.conf}"
|
||||
REMOTETRX_CONF="${REMOTETRX_CONF:-/etc/svxlink/remotetrx.conf}"
|
||||
SVXREFLECTOR_CONF="${SVXREFLECTOR_CONF:-/etc/svxlink/svxreflector.conf}"
|
||||
|
||||
START_SVXLINK="${START_SVXLINK:-0}"
|
||||
START_REMOTETRX="${START_REMOTETRX:-0}"
|
||||
START_SVXREFLECTOR="${START_SVXREFLECTOR:-1}"
|
||||
|
||||
SVXLINK_ARGS="${SVXLINK_ARGS:-}"
|
||||
REMOTETRX_ARGS="${REMOTETRX_ARGS:-}"
|
||||
SVXREFLECTOR_LINK_CONF="${SVXREFLECTOR_LINK_CONF:-/etc/svxlink/svxreflector.link.conf}"
|
||||
SVXREFLECTOR_ARGS="${SVXREFLECTOR_ARGS:-}"
|
||||
ENABLE_FM_LINK="${ENABLE_FM_LINK:-0}"
|
||||
RUNTIME_CONF="/tmp/svxreflector.runtime.conf"
|
||||
|
||||
pids=""
|
||||
|
||||
@@ -24,15 +18,6 @@ die() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
need_file_if_enabled() {
|
||||
enabled="$1"
|
||||
file="$2"
|
||||
name="$3"
|
||||
if [ "$enabled" = "1" ] && [ ! -f "$file" ]; then
|
||||
die "Config for $name not found: $file (expected volume mount)"
|
||||
fi
|
||||
}
|
||||
|
||||
start_bg() {
|
||||
name="$1"
|
||||
shift
|
||||
@@ -70,37 +55,34 @@ stop_all() {
|
||||
|
||||
trap 'stop_all; exit 0' INT TERM
|
||||
|
||||
if [ "$START_SVXLINK" = "1" ] && ! command -v svxlink >/dev/null 2>&1; then
|
||||
die "svxlink binary not found"
|
||||
fi
|
||||
if [ "$START_REMOTETRX" = "1" ] && ! command -v remotetrx >/dev/null 2>&1; then
|
||||
die "remotetrx binary not found"
|
||||
fi
|
||||
if [ "$START_SVXREFLECTOR" = "1" ] && ! command -v svxreflector >/dev/null 2>&1; then
|
||||
die "svxreflector binary not found"
|
||||
command -v svxreflector >/dev/null 2>&1 || die "svxreflector binary not found (jayReflector build)"
|
||||
[ -f "$SVXREFLECTOR_CONF" ] || die "Config not found: $SVXREFLECTOR_CONF (expected volume mount)"
|
||||
|
||||
# Local-only by default. When ENABLE_FM_LINK=1, append the link stub
|
||||
# (Satellite or Trunk keys from DJ1JAY) after the base lab conf.
|
||||
if [ "$ENABLE_FM_LINK" = "1" ]; then
|
||||
[ -f "$SVXREFLECTOR_LINK_CONF" ] || die "ENABLE_FM_LINK=1 but link config missing: $SVXREFLECTOR_LINK_CONF"
|
||||
{
|
||||
cat "$SVXREFLECTOR_CONF"
|
||||
echo
|
||||
echo "###################################################################"
|
||||
echo "# Appended FM-Funknetz link stub (ENABLE_FM_LINK=1)"
|
||||
echo "###################################################################"
|
||||
cat "$SVXREFLECTOR_LINK_CONF"
|
||||
} >"$RUNTIME_CONF"
|
||||
conf_path="$RUNTIME_CONF"
|
||||
log "FM link enabled: using $SVXREFLECTOR_CONF + $SVXREFLECTOR_LINK_CONF"
|
||||
else
|
||||
conf_path="$SVXREFLECTOR_CONF"
|
||||
log "FM link disabled (ENABLE_FM_LINK=0): local lab reflector only"
|
||||
fi
|
||||
|
||||
need_file_if_enabled "$START_SVXLINK" "$SVXLINK_CONF" "svxlink"
|
||||
need_file_if_enabled "$START_REMOTETRX" "$REMOTETRX_CONF" "remotetrx"
|
||||
need_file_if_enabled "$START_SVXREFLECTOR" "$SVXREFLECTOR_CONF" "svxreflector"
|
||||
# Ensure PKI dir exists for first-start cert bootstrap.
|
||||
mkdir -p /etc/svxlink/pki /var/lib/svxlink/pki
|
||||
|
||||
if [ "$START_SVXREFLECTOR" = "1" ]; then
|
||||
start_bg "svxreflector" svxreflector --config "$SVXREFLECTOR_CONF" $SVXREFLECTOR_ARGS
|
||||
fi
|
||||
start_bg "svxreflector" svxreflector --config "$conf_path" $SVXREFLECTOR_ARGS
|
||||
|
||||
if [ "$START_REMOTETRX" = "1" ]; then
|
||||
start_bg "remotetrx" remotetrx --config "$REMOTETRX_CONF" $REMOTETRX_ARGS
|
||||
fi
|
||||
|
||||
if [ "$START_SVXLINK" = "1" ]; then
|
||||
start_bg "svxlink" svxlink --config "$SVXLINK_CONF" $SVXLINK_ARGS
|
||||
fi
|
||||
|
||||
if ! echo "$pids" | grep -q '[0-9]'; then
|
||||
die "No service enabled. Set START_SVXREFLECTOR=1 (lab default) and/or START_SVXLINK / START_REMOTETRX"
|
||||
fi
|
||||
|
||||
log "Requested services are running. Waiting…"
|
||||
log "jayReflector (svxreflector) is running. Waiting…"
|
||||
|
||||
exit_code=0
|
||||
for pid in $pids; do
|
||||
|
||||
Reference in New Issue
Block a user