Beta — tncd 2.0 line. The pure-Go rewrite. Newer and
not yet as thoroughly tested as the stable 1.3.x Python line,
which remains the recommended version for production stations and is what the
public
apt.tncd.dev / rpm.tncd.dev repositories serve.
New Features
- KISS-over-TCP passthrough — a Direwolf-8001-style KISS
listener lets KISS-native applications (woad, mobile clients,
kissutil) share one TNC alongside AGWPE clients. Connected clients hear every frame received from the air and can transmit, sharing the same channel-timing discipline as the AX.25 engine. New[kisstcp]section, disabled by default (127.0.0.1:8001when enabled). Client timing / SetHardware KISS commands are forwarded to the TNC; the exit-KISS command is never forwarded, so one client cannot take the shared TNC out of KISS mode.
Internal
- Generalized frontend subscriber bus in the bridge; the AGWPE monitor is now a consumer of that bus (monitor output unchanged).
Beta — tncd 2.0 line. This is the pure-Go rewrite of tncd.
It is newer and not yet as thoroughly tested as the stable 1.3.x Python
line, which remains the recommended version for production stations
(and is what the public
apt.tncd.dev / rpm.tncd.dev
repositories continue to serve). Try the 2.0 beta if you want the single static
binary with no Python runtime — but keep 1.3.x if you need proven stability.
Bug Fixes
- Clearer error when a serial port is busy — opening a port that is
already in use (or exclusively locked by another program) now reports a plain
"port is busy" message instead of a cryptic
device or resource busyerrno. - KISS-entry probe hardening — when an
init_stringis configured, tncd now retries the command-mode probe a few times (spaced byinit_delay) to give a TNC time to finish rebooting afterRESET. On no response it sends the init string anyway and logs an honest warning, instead of silently assuming the TNC was already in KISS mode and skipping initialization.
Internal
- Defensive AX.25 SSID masking in the address encoder; additional codec test coverage (mod-128 short-frame handling, multi-SREJ XID round-trip) and comment cleanup.
Beta — first tncd 2.0 release. The initial pure-Go rewrite.
See the note on v1.98-Beta above: the 1.3.x Python line remains the stable,
recommended version.
New
- Complete rewrite of tncd in Go — the daemon is now a single, fully
static binary (
CGO_ENABLED=0) with nopython3runtime dependency. - AX.25 v2.2 support: SABME / modulo-128 with automatic fallback to v2.0 modulo-8, XID parameter negotiation, and selective reject (SREJ) recovery — validated over the air against a Direwolf reference in both directions.
- Native Bluetooth SPP support via BlueZ D-Bus (no
rfcommsetup). - Packaging moves from fpm/Python to nfpm/Go cross-compilation; prebuilt
.deb,.rpm, and static binaries for amd64, arm64, armhf, i386, and riscv64 on each GitHub release.
Bug Fixes
- Shared-channel fix (recommended upgrade): tncd no longer transmits responses to frames overheard from other stations' sessions. Previously, an I-frame poll, DISC, SABM, or SABME addressed to a different station on the same frequency could trigger a DM or UA transmission from tncd — potentially tearing down or hijacking other stations' connections. All four paths now respond only when the frame is addressed to a callsign registered by a connected client application.
Notes
- The stale-session DM (clearing remote sessions after a tncd restart) still works — it now requires the client application (e.g. PAT) to have reconnected and registered its callsign first.
New Features
- KISS exit on shutdown — tncd now sends the standard KISS exit sequence
(
C0 FF C0) to serial TNCs on graceful shutdown so they return to command mode. Enabled by default; disable withsend_kiss_exit = false. - New
host_exit_stringoption (sent afterexit_delay, default 1.0s) for TNCs that persist KISS mode in NVRAM — e.g. sethost_exit_string = KISS OFF\rfor the AEA PK-232MBX.
Tested
- AEA PK-232 — OTA-verified at 1200 baud; programmatic KISS init via
KISS ON\r+RESTART\r. Note: ships from the factory at 1200 7E1 — normalize to 8N1 before enabling KISS. - QtTermTCP — OTA-verified
- AEA DSP-2232 — KISS init verified (same sequence as the PK-232); OTA untested due to station audio limitations
Bug Fixes
- Stop retransmitting I-frames on receipt of an RR(P=1) poll — respond with RR(F=1) only, letting the remote's N(R) drive retransmission
Bug Fixes
- Eliminate double retransmit on REJ P=1
- Promote T1 retransmission logging to INFO for easier field debugging
Bug Fixes
- Parallel Bluetooth startup — multiple BT TNCs now connect concurrently instead of serially blocking each other
- Eliminate SIGABRT on shutdown by replacing the blocking D-Bus thread with an async call
- Handle D-Bus UnknownObject errors during Bluetooth reconnect
First stable release. tncd is no longer beta — the AGWPE bridge, AX.25 v2.0 connected-mode state machine, UI-frame passthrough, multi-port operation, Bluetooth SPP, and security hardening from the 0.11.x series are all considered feature-complete and field-tested.
Tested
- Kantronics KPC+ family (KPC-3+ / KPC-9612+) — OTA-verified at 1200 baud
serial; programmatic KISS init via
INTFACE KISS\r\nRESET\rwithinit_delay = 2.0
Security Hardening
- Default
listen_hostchanged from0.0.0.0to127.0.0.1— prevents unauthenticated remote access to the AGWPE server. Set to0.0.0.0in config if remote access is needed. - Maximum AGWPE payload size limit (64 KB) — disconnects clients sending oversized frames to prevent memory exhaustion
- Maximum concurrent AX.25 connections limit (64) — prevents unbounded connection state accumulation
- Outbound queue cap (512 entries per connection) — prevents memory exhaustion
from rapid
Dframe sends
Bug Fixes
- Fix incoming SABM handling at connection limit: send DM (reject) instead of UA followed by DM, per AX.25 §2.3.4
New Features
- Native Bluetooth SPP via BlueZ D-Bus Profile API — connects directly to
Bluetooth TNCs without
rfcommortncd-rfcomm - New
type = bluetoothconnection type in[client]config - Auto-reconnect with exponential backoff for Bluetooth connections (configurable via
reconnect,reconnect_delay,reconnect_max_delay) - RFCOMM channel auto-detection via SDP — no manual channel configuration needed
- SABM retransmission via T1 timer during connection setup (AX.25 6.3.1)
Bug Fixes
- Disconnect existing BLE auto-connection before SPP connect to prevent
br-connection-page-timeouton dual-mode devices (e.g. Mobilinkd TNC3) - Run
ConnectProfilein a background thread to avoid blocking asyncio on the expected D-Bus NoReply - Fix
blocking_start()serial port guard: only accesstransport.serialfortype = serialconnections
Packaging
- Nix:
bluetoothSupportoption on package, auto-enabled by NixOS module whenbluetooth.enable = true - Arch PKGBUILD updated with
python-dbusandpython-gobjectdependencies - Deprecated
tncd-rfcommhelper (still included, will be removed in a future release)
Tested
- Full Winlink CMS round-trip with 10KB binary attachment over Mobilinkd TNC3 Bluetooth SPP via KU0HN-10 gateway at 1200 baud
Bug Fixes
- Fix
init_stringserial probe: pause asyncio reader during KISS mode entry to avoid consuming the TNC's response - Fix
init_stringto use the same serial connection (no re-open) and probe for command mode before sending - Fix HUPCL termios flag: prevent DTR reset on serial port close, which was kicking TNCs out of KISS mode
Tested
- Kenwood TS-2000 built-in TNC: full OTA Winlink session at 1200 baud via
init_string - Kenwood TH-D7A: OTA PASS with
init_stringprobe
New Features
- AX.25 spec compliance: RNR (Receive Not Ready) flow control, FRMR (Frame Reject) handling, N2 retry limit with automatic disconnect
- Raw KISS serial spy for debugging TNC communication
- Implicit connect on lost UA: if remote sends I-frames during CONNECTING state, promote to CONNECTED (the UA was lost OTA)
Bug Fixes
- T1 poll-then-retransmit: first expiry sends RR poll only, second consecutive expiry also retransmits unacked I-frames (avoids flooding slow TNC buffers)
Tested
- Mobilinkd TNC4: full OTA Winlink session via USB at 1200 baud
Bug Fixes
- Fix download stall in AX.25 connected mode caused by duplicate RR responses flooding the half-duplex TX buffer
_send_rr_guarded()suppresses duplicate RR F=1 with same N(R) within 3s- Defer poll response via
call_soonto let queued I-frames advancerecv_seqnofirst - I-frame coalescing in
_drain_outboundmerges adjacent small payloads up to 256 bytes - Y-frame reports
unacked + queued(matches Direwolf, fixes PAT flow control) - Guard against backwards N(R) from retransmitted frames
- Send DM for I-frames to unknown connections (clears stale sessions after restart)
Bug Fixes
- Retransmit unacked I-frames when remote polls with RR P=1
- Fix
tncd-rfcommservice PATH to include bluez binaries
Packaging
- NixOS module imported from nix-ham-packages
- Switched to nix-ham-packages overlay (removed local
nix/overlay.nix) - Removed stale OBS packaging (releases now use Cloudflare Pages)
New Features
- Dynamic T1/T2 timers calculated from over-the-air baud rate
(
ota_baudrateconfig option) - Piggybacked N(R) acknowledgement fix for connected mode
- Direwolf compatibility verified (KISS-over-TCP and PTY modes)
Tested
- First successful OTA Winlink session at 1200 baud
Bug Fixes
- Fix incoming connected mode: SABME rejection (mod-128 not supported), owner assignment from registered callsigns, correct AGWPE notification format
Other
- Added user-focused documentation website
- Tabbed install section with NixOS, macOS, and Windows instructions
New Features
- AX.25 connected mode: window flow control, immediate RR, T1 retransmit timer
- End-to-end test suite with PipeWire audio cross-linking, Direwolf pairs, PAT P2P messaging, and KISS PTY mode
Other
- Cloudflare Pages deployment for package repository and website
- Cross-compiled .deb and .rpm packages for amd64, i386, armhf, arm64, riscv64
Initial Release
- AGWPE-to-KISS bridge with serial and TCP KISS support
- AX.25 UI (unproto) frame forwarding
- Multiple simultaneous AGWPE client support
- Bluetooth RFCOMM helper (
tncd-rfcomm)