| I1 |
iOS Safari requires HTTPS for getUserMedia (mic access) |
Medium |
Open |
Use TLS reverse proxy (nginx) or connect via HTTPS |
| I2 |
PyAudio device index not configurable via env var |
Low |
Resolved (V2.6) |
Implemented as MRRC_AUDIO_RX_DEVICE / MRRC_AUDIO_TX_DEVICE (index or name substring); Windows package pre-locks USB Audio |
| I3 |
No per-band TX power control (FT-710 uses hardware power setting) |
Low |
N/A |
FT-710 has hardware RF POWER knob; CAT PC; command sets power globally |
| I4 |
No ATR-1000 / external tuner support |
Low |
Future |
Could add via second serial port |
| I5 |
No digital mode support (CW decoder, FT8, RTTY) |
Low |
Future |
Specialized DSP/packet decode needed |
| I6 |
No multi-client control arbitration — concurrent browsers can issue conflicting PTT/frequency commands (last-writer-wins) |
Medium |
Open |
Define arbitration rules (e.g., single-controller lock or role-based gating) |
| I7 |
mem_channels.json POST has no schema validation or backup |
Low |
Open |
Server-side payload validation; keep .bak copy before overwrite |
| I8 |
serve_static path traversal — STATIC_DIR / path (server.py) builds the response path from the request URL without resolve() + containment check, so an authenticated non-browser client can read arbitrary server-readable files (e.g. GET /../server.py, cert keys). Browsers normalize .., so exposure is raw-HTTP clients. |
High |
Resolved 2026-08-26 (_resolve_static_path resolves the join and rejects unless contained inside STATIC_DIR; traversal and absolute request paths now 404 instead of falling through to the SPA fallback; regression tests in tests/test_server_security.py) |
Resolve the joined path and reject unless is_relative_to(STATIC_DIR) before FileResponse; add a regression test Done |
| I9 |
Login password compared with != (non-constant-time, timing side channel) and a weak default password only logs a warning — a fresh install that skips the warning is effectively open. |
Medium |
Partially resolved 2026-08-26 (hmac.compare_digest via _password_matches; _warn_if_default_password() fires a loud startup WARNING when the well-known default is active; login-time <12-char warning kept). Still open: first-login forced password change, shorter cookie TTL. |
Switch to hmac.compare_digest Done; forced first-login change remains future work |
| I10 |
Web client audio/spectrum subchannels (/WSaudioRX, /WSaudioTX, /WSspectrum) have no independent reconnect — only /WSradio auto-reconnects, so a transient drop yields “controls alive but audio dead” until the control channel also cycles. |
Medium |
Resolved 2026-08-26 (subchannelReconnect gives each subchannel its own exponential backoff 1s→30s with on-open reset, CONNECTING-state guards against socket stacking, auth-expiry 4001 stays with the control flow, and _webClientOff suppresses self-heal while the power button has the client OFF; contract test in tests/test_server_ws_protocol.py) |
Give each subchannel its own exponential-backoff reconnect Done |
| I11 |
iOS app PTT release race (P0 in docs/IOS_APP_ANALYSIS.md): DragGesture.onEnded sends ptt:false only when the server echo already shows tx_status > 0, and there is no client watchdog/scenePhase guard — fast taps over WAN can leave the radio keyed up. Approved PTTManager fix design not yet implemented (0/N tasks). |
High |
Partially resolved 2026-08-26 (release race fixed: PTT gesture now tracks local pttHeld like TUNE’s tuneHeld, releases unconditionally on gesture end with optimistic local txStatus update in RadioViewModel.setPTT; requires device verification). Still open: 500 ms×3 watchdog, scenePhase force-release. Server side gained an opt-in stuck-keyup layer: MRRC_PTT_MAX_TX_SECONDS watchdog forces RX after continuous TX beyond the limit, covering zombie-but-connected sockets that neither client watchdogs nor the dead-man switch see. |
Unconditional release on gesture end Done; watchdog + scenePhase remain |
| I12 |
Stuck-keyup gap for connected-but-hung clients: every existing TX-release layer assumes either a working client or a disconnect. A zombie socket mid-TX keys the radio forever. |
High |
Resolved 2026-08-26 (server-side _max_tx_watchdog, opt-in via MRRC_PTT_MAX_TX_SECONDS, default off so operator QSO patterns are never interrupted; fire-and-forget unkey + zeroed TX meters + error toast, no verify loop per ch15) |
Opt-in max-continuous-TX force-RX implemented as a new outermost safety layer |