MEHS transport migration and recovery
Status: The client has live TCP/QUIC path selection, migration wiring, and reconnect/recovery. The migration state controller and bounded retransmission/reordering behavior have been tested with deterministic fault injection. Live migration was manually verified on loopback (TCP to QUIC under injected TCP latency, with downloads continuing); that end-to-end scenario has not received broader interoperability validation. This document describes evolving behavior, not a stable cross-implementation protocol guarantee.
1. Goals
Switch between TCP/TLS and QUIC data paths without resetting the logical MEHS session. Both carry the same MEHS frames under the selected profile (mehs-data/1, xhttp, trusttunnel, or notmehs; HTTP/2 over TCP, HTTP/3 over QUIC). Preserve ordered reliable stream data and deduplicate retransmissions. A transport change should not require the application to reconnect, provided at least one path and the server-side logical session remain alive.
This does not promise zero latency or uninterrupted delivery through permanent server failure or a total network outage.
2. State machine
- ACTIVE(A): path A carries new data.
- PREPARING(B): establish and authenticate path B while A remains active.
- READY(B): B passed peer authentication, session binding, and path-health checks.
- MIGRATING(A→B): new frames are assigned to B; A can still deliver in-flight frames and ACKs.
- DRAINING(A): resend unacknowledged logical frames over B using their original sequence IDs; accept late frames and ACKs from A; deduplicate at the session layer.
- ACTIVE(B): all outstanding data assigned to A has been acknowledged or safely retransmitted and accepted through B; close A.
- ROLLBACK(A): if B fails while A is healthy, cancel A's close timer and return all new traffic to A.
- FAILED: no eligible path is usable or the server-unreachable threshold is reached; notify the application and stop attempts if terminal.
Only one path is the authoritative sender for each newly assigned frame at a time. Retransmissions may travel on the replacement path, but the same sequence ID must never be delivered twice to the logical flow.
2.1 Transitions and invariants
A path-failure event is applied by path ID; events naming a path outside the current state are stale and ignored.
| State | Event | Next state |
|---|---|---|
| Active(A) | A fails | Failed |
| Preparing(A→B) | B fails | Active(A) |
| Preparing(A→B) | A fails | Failed |
| Draining(old A, new B) | B fails | Active(A) (rollback) |
| Draining(old A, new B) | A fails | Active(B) |
| Failed | any | error (new controller needed) |
Sender/receiver sequence, ACK, and deduplication state is path-independent and survives every transition. Unacknowledged frames stay queued until acknowledged, so after any transition retransmitting all outstanding frames on the surviving path redelivers them with their original sequence IDs. Stale-epoch frames are rejected; duplicate ACKs/data are no-ops; buffers are bounded and full buffers apply backpressure (SendBufferFull/ReceiveBufferFull) rather than dropping acknowledged data.
3. Timers and thresholds
- Candidate connection setup timeout: 15 seconds.
- Server-unreachable threshold: 15 consecutive seconds during which no eligible path can reach/authenticate with the server.
- Health-check trigger: 30 seconds without a healthy response; run
generate_204plus an authenticated MEHS path-health probe. - Maximum old-path drain: 60 seconds.
- Migration cooldown: 10 seconds after a successful migration, unless the active path fails.
- Candidate quality improvement: initial default of at least 15% over two consecutive measurement windows to migrate away from a healthy path.
- Startup probe defaults: at least 5 RTT samples and 3 bounded throughput samples per candidate.
These are initial defaults for benchmarking, not universal network-optimal values. Server policy may tighten limits.
A candidate timeout alone does not trigger terminal failure while another path remains healthy. The 15-second terminal threshold applies only when all eligible paths are unreachable. If an active path remains healthy, keep it while testing candidates.
4. Packet handling during migration
- Assign each logical frame a stable sequence ID before sending.
- Do not assign the same new frame to both paths.
- Do not mark a frame delivered until its MEHS-level ACK is received.
- On migration, retain unacknowledged frames and retransmit them through B with the same sequence IDs.
- The receiver deduplicates before forwarding/injecting data to the target flow.
- Preserve ordering for byte streams. For datagrams, deduplicate per flow and make at most one forwarding/injection attempt per logical datagram; a MEHS-level ACK does not guarantee final UDP delivery.
- Bound buffers and apply backpressure. If a reliable stream cannot be retained safely, surface an error instead of silently dropping bytes.
5. Drain deadline
At the 60-second deadline, stop using A for new traffic. Before closing A, resend all still-unacknowledged logical frames over B and keep the receiver's deduplication state long enough to cover delayed packets. The deadline is not permission to discard reliable stream data.
If B is not healthy at the deadline but A is healthy, cancel retirement and roll back to A. If neither path is healthy, enter the failure policy. Never close the only healthy path merely because the timer expired.
6. Rollback
If B fails before A has been retired:
- Cancel A's pending close timer.
- Restore A as the authoritative sender.
- Keep sequence/ACK/deduplication state across the rollback.
- Continue retransmission and ordering.
- Notify the application only if the effective transport/configuration changed or the connection state changed.
The initial user requirement explicitly calls out rollback to TCP. The same safe rule should generalize to any still-healthy old path, unless server policy pins a transport and forbids rollback.
7. Server reachability and terminal failure
When no eligible path can reach/authenticate with the server for 15 consecutive seconds:
- Stop automatic connection and recovery attempts.
- Close local resources cleanly.
- Emit a fatal structured status to the application.
- Require the application/user to initiate a new connection attempt.
Do not infer a server restart from one failed socket alone. Use the threshold across all allowed paths and authenticated health checks. If a protocol-level terminal server error is received, it can trigger immediate terminal failure.
8. Device sleep/wake
The OS may suspend timers and sockets during sleep; continuous connectivity cannot be guaranteed by core behavior alone. On wake, check active-path health, preserve the logical session if the path remains valid, and otherwise run the normal candidate evaluation and migration flow. If the server-unreachable threshold is reached, follow terminal failure rules.
9. Acceptance tests
- Loss and reordering during TCP→QUIC and QUIC→TCP migration.
- Lost ACK followed by retransmission and duplicate suppression.
- Late data/ACK arriving from the retired path.
- Candidate fails while active path remains healthy.
- Active path fails while candidate is preparing.
- Rollback just before the 60-second drain deadline.
- All paths unreachable for 15 seconds, then server recovers: the client must not auto-reconnect after terminal failure.
- One path fails while another is healthy: no false terminal failure.
- Queue limits, backpressure, cancellation, sleep/wake, and simultaneous timeout races.
- Verify byte-stream order and that a datagram is injected no more than once by the remote MEHS endpoint.