Files changed: engine/.embcache.json engine/changelog.md engine/lint-report.md vault/.trash/2026-06-19.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/central.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/navi.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/fleet-patch-audit.md vault/projects/fleet-platform-baseline.md vault/projects/matrix-synapse-deployment.md vault/projects/meshai-config-hot-apply.md vault/projects/meshai-region-routing-plan.md vault/projects/meshai.md vault/projects/meshcore-transport.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/projects/nominatim-v5-reimport.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/central-deploy-cutover.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/fleet-magicdns-resolved-migration.md vault/runbooks/headless-browser-page-verification.md vault/runbooks/headscale-oidc-boot-order.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshai-prod-compose-override.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/pymc-repeater-kiss-tnc-reenumeration.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/runbooks/toc-cortex-pve9.2-update.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
5.4 KiB
| title | type | tags | aliases | related | updated | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| pymc-repeater KISS TNC Re-enumeration Fix | runbook |
|
|
2026-07-13 |
pymc-repeater KISS TNC Re-enumeration Fix
pymc-repeater.service on aida-nebra (Tailscale 100.64.0.9) runs the Meshcore LoRa repeater node EchoBase on 910.525 MHz SF7. It talks to a RAK4631 USB KISS TNC (VID 239a / PID 8029) via the /dev/pymc-radio udev symlink. The Nebra SX1262 hat on the same machine runs a separate meshtasticd instance on its own radio — see meshtastic-sidecar-node for that hardware's setup. Do not touch meshtasticd or the SX1262 hat during any pymc-repeater work.
Background — Why This Breaks
KissModemWrapper holds the serial file descriptor open but has no reopen logic. When the RAK4631 re-enumerates (power cycle, USB glitch, kernel rescan), the kernel assigns a new node: observed ttyACM0 → ttyACM1 around 2026-06-23. The service keeps a stale fd pointing at /dev/ttyACMx (deleted). Systemd still reports active (running) — the service is silently deaf and mute.
Symptom
Every ~30 seconds in the journal:
KissModemWrapper - ERROR - Serial write error: ... [Errno 5] Input/output error
TX frame write failed, dropping frame
Zero RF traffic in or out despite a green systemctl status.
Diagnose
1. Check the journal for the I/O error spam
ssh zvx@100.64.0.9
journalctl -u pymc-repeater.service --since '5 min ago' --no-pager
Look for [Errno 5] Input/output error or TX frame write failed — either confirms stale fd.
2. Inspect the process's open file descriptors
sudo ls -l /proc/$(systemctl show -p MainPID --value pymc-repeater.service)/fd/ | grep ttyACM
- Stale (broken): symlink target contains
(deleted)— smoking gun. - Healthy: target points at a real
/dev/ttyACMx.
3. Confirm where the device landed after re-enumeration
ls -l /dev/pymc-radio
Shows which ttyACM* the live RAK4631 is on now.
Manual Recovery
A restart clears the stale fd and re-opens the device via the udev symlink:
sudo systemctl restart pymc-repeater.service
Verify recovery
# fd should now point at a real ttyACMx (not deleted)
sudo ls -l /proc/$(systemctl show -p MainPID --value pymc-repeater.service)/fd/ | grep ttyACM
# Journal should show reconnect and normal TX
journalctl -u pymc-repeater.service -n 20 --no-pager
Expected healthy journal lines:
KISS modem connected to /dev/pymc-radio at 115200 baud
Retransmitted packet ...
The [Errno 5] spam must be absent.
Permanent Fix (applied 2026-07-01)
Two changes make pymc-repeater self-healing across any future re-enumeration. Original files were backed up as .bak alongside each.
1. udev rule — /etc/udev/rules.d/99-pymc-radio.rules
Added TAG+="systemd" and ENV{SYSTEMD_WANTS}="pymc-repeater.service" to the existing VID 239a / PID 8029 rule:
SUBSYSTEM=="tty", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="8029", \
SYMLINK+="pymc-radio", \
TAG+="systemd", \
ENV{SYSTEMD_WANTS}="pymc-repeater.service"
On any future re-enumeration, udev creates the dev-pymc\x2dradio.device unit for the new node and systemd activates pymc-repeater.service against it automatically.
2. systemd unit — /etc/systemd/system/pymc-repeater.service
[Unit] additions:
BindsTo=dev-pymc\x2dradio.device
After=dev-pymc\x2dradio.device
StartLimitIntervalSec=300
StartLimitBurst=10
[Service] additions:
Restart=always
RestartSec=5
BindsTo stops the service cleanly when the TNC drops (the device unit disappears) and restarts it when the device returns. Restart=always with RestartSec=5 ensures fast recovery from any modem crash without the device unit cycling.
Verify the Resilience Wiring Is Intact
Run these on aida-nebra after any future maintenance to confirm the permanent fix is still in place:
# BindsTo and After wired to the device unit
systemctl show pymc-repeater.service -p BindsTo -p After | grep -i pymc
# udev tagging correct
udevadm info -q property -n /dev/pymc-radio | grep -i SYSTEMD
# device unit tracked by systemd
systemctl list-units --type=device --all | grep -i pymc
Expected output:
BindsTo=dev-pymc\x2dradio.device
After=... dev-pymc\x2dradio.device ...
SYSTEMD_WANTS=pymc-repeater.service
dev-pymc\x2dradio.device loaded active plugged ...
Known Secondary Issue (non-blocking)
RRD graphing fails with rrdtool not available. Metrics/graphing only — no impact on RF operation.
Quick Reference
| Item | Value |
|---|---|
| Host | aida-nebra (100.64.0.9) |
| Service | pymc-repeater.service |
| Node / freq | EchoBase, 910.525 MHz SF7 |
| TNC hardware | RAK4631 USB KISS TNC (VID 239a / PID 8029) |
| udev symlink | /dev/pymc-radio |
| udev rule | /etc/udev/rules.d/99-pymc-radio.rules |
| Unit file | /etc/systemd/system/pymc-repeater.service |
| Unrelated radio | Nebra SX1262 hat → meshtasticd (do not touch) |
# Restart (manual recovery)
sudo systemctl restart pymc-repeater.service
# Tail journal
journalctl -u pymc-repeater.service -f
# Check fd state
sudo ls -l /proc/$(systemctl show -p MainPID --value pymc-repeater.service)/fd/ | grep ttyACM