Skip to content

v3.14.0 — per-(client, radiod) multicast destinations

Choose a tag to compare

@mijahauan mijahauan released this 13 May 11:03
· 112 commits to main since this release

Closes the CONTRACT v0.3 §7 gap: RadiodControl now derives a deterministic per-(client, radiod) multicast destination when constructed with client_id=. Peer clients on the same host land on distinct multicast groups automatically — no per-client derivation code required.

New

  • RadiodControl(status_address, client_id="<name>") — when set, ensure_channel(destination=None) auto-derives generate_multicast_ip(client_id, radiod_host=self.status_address).

Destination precedence:

  1. Explicit destination= to ensure_channel (operator override, unchanged).
  2. Derived from (client_id, status_address) when client_id was set on the RadiodControl.
  3. None — radiod uses its config-file default (pre-3.14 behavior, preserved for rollback).

The (client_id, radiod_host) pairing gives the right behavior for every multi-instance shape:

  • Same client on two radiods → two distinct destinations (radiod's status_address feeds the hash).
  • Two clients on one radiod → two distinct destinations (different client_id feeds the hash).
  • Same client + same radiod → repeatable across restarts (deterministic hash).

MultiStream._attempt_restore inherits the behavior because it reuses the same RadiodControl. A radiod restart that drops a channel restores it on the same per-client multicast group it was on before.

Because allocate_ssrc already hashes destination into the SSRC, per-client destinations produce per-client SSRCs — radiod's channel table cleanly separates concurrent clients that ask for the same (freq, preset, sample_rate, encoding) tuple instead of silently collapsing them into one shared channel.

Why now

CONTRACT v0.3 §7 (sigmond's client contract, 2026-04-12) said "ka9q-python derives the multicast destination deterministically." It didn't — for ~13 months ensure_channel(destination=None) was effectively a no-op that left the field unspecified, so radiod fell back to its config-file default group and every client on a given radiod shared one multicast address. v3.14.0 makes the spec true.

Backwards compatibility

  • RadiodControl(status) without client_id preserves pre-3.14 behavior: destination=None flows through, radiod uses its config-file default. Clients opt in by passing client_id="<name>".
  • Bumping the floor for §7 conformance: clients should pin ka9q-python>=3.14.0 in their pyproject.toml when they adopt client_id=.

Companion client adoptions

All five radiod-consuming sigmond clients were updated in the same release wave (2026-05-13) and deployed live on bee1:

Client Repo + commit Live multicast group
psk-recorder mijahauan/psk-recorder@47cf92c 239.95.99.52
wspr-recorder mijahauan/wspr-recorder@0620bf6 239.169.255.247
hfdl-recorder mijahauan/hfdl-recorder@af84271 239.150.155.78
codar-sounder mijahauan/codar-sounder@b0ddcb0 239.178.79.119
hf-timestd mijahauan/hf-timestd@5a8767c 239.78.93.2

Contract prose updated at mijahauan/sigmond@39b97db.

Tests

9 new unit tests in tests/test_client_id_destination.py cover the precedence rules, the uniqueness invariants, and SSRC divergence. 134 offline unit tests green.

Install

pip install ka9q-python>=3.14.0