# Robovoice DHCP Tunnel Protocol ## Overview Robovoice communicates with a remote STT server by tunneling through the DHCP UDP ports (68→67). This exploits a common killswitch exception: VPN software (e.g. WireGuard) blocks all traffic except DHCP, which is allowed for network connectivity maintenance. ``` [Robovoice client] --broadcast UDP :68→:67--> [STT server] [Robovoice client] <--unicast UDP :67→:68-- [STT server] ``` The client broadcasts NOP heartbeats while PTT is held. The server starts recording on the first NOP and stops when it receives OFF or when 150ms pass with no NOPs. ## Transport - **Protocol:** UDP (connectionless, unreliable) - **Client → Server:** broadcast, source port 68, dest port 67 - **Server → Client:** unicast, source port 67, dest port 68 - **Client binds:** to a specific LAN interface IP on port 68 (with `SO_REUSEADDR` to coexist with the Windows DHCP service) - **No connection state** — purely fire-and-forget datagrams ## Wire format All messages are plain text, newline-terminated (`\n`). Every message starts with the 6-byte magic `HKMSTR` to distinguish our traffic from real DHCP. ### Client → Server **NOP (heartbeat while PTT held):** ``` HKMSTR \n ``` Sent every 50ms while PTT is held. The nonce is an incrementing unsigned integer that makes each datagram unique. The server discards it — it's purely for packet uniqueness, not for any protocol logic. **OFF (PTT released):** ``` HKMSTR:OFF \n ``` Sent once when PTT is released. This is the fast-stop signal. If lost, the 150ms timeout acts as a backstop. ### Server → Client **Partial transcript:** ``` HKMSTR:P \n ``` Intermediate recognition result. Fire-and-forget. Client logs it but does not act on it. **Final transcript:** ``` HKMSTR:F \n ``` Complete utterance. Client feeds this to the TTS engine. ## Server state machine ``` ┌──────────────────────────────────────────┐ │ │ ▼ │ ┌──────────┐ first NOP ┌──────────────┐ │ │ IDLE │ ──────────► │ RECORDING │ │ └──────────┘ └──────────────┘ │ │ │ │ OFF │ │ 150ms │ recv'd │ │ silence │ ▼ ▼ │ ┌─────────────┐ │ │ PROCESSING │ │ └─────────────┘ │ │ │ STT │ │ done │ │ ▼ │ send HKMSTR:F ─────────┘ ``` - **IDLE → RECORDING:** first NOP received, start mic capture - **RECORDING → PROCESSING:** OFF received, OR 150ms since last NOP - **PROCESSING → IDLE:** STT done, send `HKMSTR:F ` ## Timing | Parameter | Value | Purpose | |-----------|-------|---------| | NOP interval | 50ms | Heartbeat frequency while PTT held | | Silence timeout | 150ms | Stop recording if no NOPs (3 missed = lost OFF) | | NOP bandwidth | ~20 msg/s × ~20 bytes | ~400 bytes/s — negligible | ## Why this works 1. **Outbound broadcast `:68→:67` to `255.255.255.255`** passes the WireGuard WFP killswitch (DHCP exception matches this exact pattern) 2. **Inbound `:67→:68`** has no address restriction in the WFP rule, so unicast replies pass through 3. **Binding to a specific interface IP** (not `0.0.0.0`) wins unicast delivery over the Windows DHCP client service 4. **NOP spam** ensures the ON message gets through even at 5% packet loss (3 consecutive NOPs = ~0.01% drop probability) 5. **150ms timeout** is the backstop for lost OFF — at 50ms intervals, 3 consecutive NOPs must all be lost to false-stop