MEHS / Knowledge base

This proposed specification retains historical reference-path limits and design requirements. Some values differ from current packaged defaults. For deployment settings, use the configuration reference and support matrix; do not treat proposed behavior as verified release behavior.

MEHS protocol v1 — proposed specification

Status: partial implementation baseline, not a stable or interoperable v1 profile. The frame codec, ACK/error payloads, in-memory reliability/migration state, and selected authenticated TCP/QUIC data paths are implemented and tested. The control/API negotiation, transport selection, live probes, and cross-implementation interoperability are not a complete wire protocol. Behavior described as a reference implementation is not a promise that independent peers interoperate. The data listener selects its profile with TLS ALPN mehs-data/1; failure to negotiate a configured profile fails before frame exchange.

Compatibility and evolution policy

The frame-header protocol version and the TLS ALPN transport profile are separate compatibility identifiers:

Release stateSupported client/server pairs
Current 0.9.x reference releasesUse the exact same MEHS release for client and server. 0.8.x is incompatible with 0.9.0; mixed 0.9.x patch versions are not certified, even when they report the same frame version.
Future 1.0.x, once declared stableAll 1.0.x client/server pairs that report the same frame version and use the same named profile and authentication path are intended to interoperate. A patch release must not break that wire contract. The release support matrix will list any exception.
Any intentional wire-incompatible changeDo not mix versions unless the release support matrix explicitly lists the pair. Increment the frame version for frame incompatibility and/or define a new ALPN/profile for incompatible transport or authentication semantics; coordinate deployment when no common-version negotiation exists.

The current reference data server and the separate certificate-bound transport path both use ALPN mehs-data/1 despite having different authentication exchanges. They are distinct implementation paths and are not interchangeable; the identifier/version check does not resolve this historical profile ambiguity. Deploy each only with its matching client path. Before declaring 1.0 stable, give incompatible authentication paths distinct profile identifiers.

Every intentional wire break must include a changelog migration note with: old and new frame versions/profile ALPNs; exact supported client/server release pairs; whether mixed versions fail at authorization, TLS/ALPN, or frame decode; required server-first/client-first upgrade sequence; rollback constraints; and any data/session impact. Never silently change a frame version, profile meaning, or compatibility promise.

1. Scope and terminology

MEHS is intended to be a shared proxy/tunnel core. The application provides configuration and secrets; current behavior includes selected authentication, transport, reliability/migration, and local-proxy paths, not the complete workflow described by this design. The server uses one main configuration and a configured client allowlist for the reference profile.

A carrier is the network substrate used by a connection:

Profiles: mehs-data/1 (ALPN mehs-data/1), and the HTTP profiles trusttunnel (CONNECT), xhttp (POST on http_path) and notmehs (POST + x-padding, decoy page for other requests), using ALPN h2 over TLS/TCP or h3 over QUIC. Authentication and framing are identical; only the byte-stream carrier differs. They are not wire-compatible with third-party TrustTunnel/XHTTP. There is no automatic fallback to another profile.

A logical session persists across carrier changes. A transport connection is one concrete TCP- or UDP-carried path. A flow is one proxied TCP stream or datagram flow.

1.1. MEHS data frame v1

MEHS uses this fixed-header framing. Multi-byte integers use network byte order. One frame is a 36-byte header followed by exactly the declared payload. On byte-stream transports, frames are concatenated and the receiver reads one header and its declared payload at a time; bytes after that payload belong to the next frame. The frame decoder accepts exactly one complete frame and rejects both truncation and trailing bytes. The transport reader validates the declared payload bound before allocating or reading the payload.

OffsetSizeFieldRule
04MagicASCII MEHS
42Version1
61Frame type1 stream data, 2 datagram, 3 ACK, 4 probe, 5 close, 6 error, 7 data authenticate, 8 data authenticated, 9 flow open, 10 flow-open result, 11 encrypted record, 12 E2E server public key, 13 E2E client ciphertext, 14 E2E ready
71FlagsMust be zero in v1; nonzero is rejected
88Session epochNonzero on every frame; must equal the epoch authenticated/bound on this connection
168Flow IDNonzero for StreamData, Datagram, Ack, FlowOpen, FlowOpenResult, and flow Close; zero for Probe, Error, DataAuthenticate, DataAuthenticated, and session Close
248SequencePositive for StreamData, Datagram, and Probe; zero for every other type
324Payload lengthUnsigned byte count, at most 65,536; StreamData must contain 1–65,536 bytes, Datagram may contain 0–65,536 bytes
36variablePayloadExactly payload length bytes; no trailing data

The codec rejects epoch zero, invalid type/flow/sequence combinations, reserved flag bits, unknown frame types, unsupported versions, oversized lengths, malformed type-specific payloads, truncation, and trailing bytes. It does not preserve arbitrary flags: v1 encoders emit zero flags and decoders reject nonzero flags. A bad frame or epoch on an established reference data connection is rejected and the connection is closed; it is not retried as another version/profile.

Stream and datagram sequence spaces are independent for each flow and direction and start at 1. A flow cannot switch between stream and datagram data. Retransmission keeps the original sequence number. A receiver deduplicates before delivery; stream data is released in sequence order per flow. Empty datagrams are valid. Datagram delivery is at most once per flow/sequence at the MEHS endpoint and does not promise delivery to the final UDP destination. The Datagram frame is an application-level UDP-flow unit; it does not make TCP/TLS or QUIC's bidirectional byte stream a datagram carrier.

An ACK payload consists of an 8-byte cumulative sequence followed by a 2-byte selective-range count and that many (start, end) pairs of 8-byte inclusive sequence numbers. The count must fit in the payload limit (at most 4,095 ranges); the payload length must equal 10 + 16 * count. Ranges must have positive endpoints, start <= end, and be strictly increasing, non-overlapping, and above the cumulative sequence. A cumulative sequence of zero and zero selective ranges are valid. The ACK header's flow ID identifies the acknowledged flow and its sequence field is zero. A sender rejects an ACK whose cumulative or selective endpoint exceeds the highest sequence it sent on that flow; ACKs for an unknown flow with no sequence beyond zero are ignored. The ACK applies only to data that the receiver accepted into bounded state.

Probe is session-scoped (flow zero), has a positive sequence, and may carry profile-specific probe bytes. Close has an empty payload and sequence zero; flow zero terminates the data session and a nonzero flow ID closes that flow. Error is session-scoped, has sequence zero, and carries exactly one registered 2-byte error code. FlowOpen has sequence zero and a nonzero flow ID; FlowOpenResult has the same flow ID, sequence zero, and a one-byte result (0 success, 1 failure). DataAuthenticate and DataAuthenticated are session-scoped with sequence zero; the successful response payload is empty. Frame validation checks syntax, not whether the connection is in a state where a syntactically valid frame is allowed.

Authenticated TCP data session and flow-open

The reference data listener uses TLS 1.3 with the configured server certificate and negotiates ALPN mehs-data/1. A client must require that ALPN, verify the certificate and hostname, and use a session finalized for the selected carrier. Immediately after TLS setup, the client sends one DataAuthenticate frame with the finalized nonzero session_epoch, flow ID and sequence zero, and a payload of client_id_length:u16 | client_id:utf8 | secret_length:u16 | secret. Lengths are byte counts; the client ID is limited to 256 bytes and the secret to 4,096 bytes. When E2E is enabled, the client appends E2 | crypto_version:u8 | suite_id:u8 (E2, version 1, suite 1 = ML-KEM-768 + AES-256-GCM). The server accepts this only when the credentials are configured and the epoch was finalized for that same client. With E2E off, it replies with the original empty DataAuthenticated frame. With E2E on, it sends that response and a CryptoServerKey frame containing the 1,184-byte ML-KEM-768 public key. The secret is protected in transit by TLS and must not be logged.

Optional E2E frame protection

Client e2e_crypto defaults to off. Server policy e2e_crypto defaults to off and may be optional or required; optional accepts the suite only when requested, while required rejects an unextended DataAuthenticate. A forbidden, unsupported, or version-mismatched selection returns an Error frame with code 9 (crypto_negotiation) before any flow is opened. There is no fallback to plaintext.

When selected, the client validates the server key, creates an ML-KEM-768 ciphertext (1,088 bytes), and sends it in CryptoClientCiphertext. Both sides derive independent client-to-server and server-to-client AES-256-GCM keys with HKDF-SHA-256 over the KEM shared secret and a transcript binding the suite, epoch, public key, and ciphertext. The server's first CryptoReady is itself an authenticated encrypted record, confirming that both sides derived the same key. All subsequent MEHS frames are encoded in full and encrypted as the payload of an Encrypted record. Its outer header contains only the session epoch and a strictly increasing per-direction record counter; the inner MEHS type, flow ID, sequence, payload, and header are confidential and authenticated as ciphertext. Nonces are unique counters under directional keys; out-of-order, replayed, altered, or plaintext records after negotiation are rejected.

The KEM exchange is authenticated by the existing TLS/QUIC channel, not by an independent server identity. This layer is defense in depth for MEHS frames; it does not replace certificate validation or pinning and does not protect against an active TLS-terminating intermediary able to alter the KEM exchange. TLS/QUIC remains enabled and required regardless of the E2E setting. The same frame protection is used over TCP, QUIC, HTTP/2, and HTTP/3 carriers.

After authentication, a client opens each flow with a FlowOpen frame: nonzero flow ID, sequence zero, and a destination payload of protocol:u8 | address_type:u8 | address | port:u16. Protocol 1 is TCP and 2 is UDP; address type 4 is followed by four IPv4 octets, and type 6 by sixteen IPv6 octets. Ports are network byte order and must be nonzero. The server connects to that address and answers with FlowOpenResult on the same flow; its one-byte payload is 0 for success and 1 for failure. The client sends no data until it receives success. SOCKS5 domain names are resolved by the local proxy before encoding the IP destination, unless the optional remote_dns proxy setting (default off) is enabled; then the client sends address type 3 (length:u8 | ASCII host, 1-253 bytes of letters, digits, -, ., _) and the server resolves it, answering failure on resolve errors. Servers that predate address type 3 reject such frames, so enable remote_dns only against a server that supports it. The local SOCKS5 proxy also supports UDP ASSOCIATE, mapping each destination to a UDP flow. TCP flows carry StreamData; UDP flows carry one datagram per Datagram frame. Both use normal per-flow sequence and ACK rules. A Close frame with nonzero flow ID closes that flow; flow ID zero remains available for session control.

The reference TLS/TCP and optional QUIC data listeners support TCP and UDP destination flows, with a limit of 128 opened flow IDs per session and 32 simultaneous connections per listener (up to 64 if both listeners run). The local SOCKS5 proxy supports TCP CONNECT and UDP ASSOCIATE; remote DNS is an opt-in proxy-library setting. The Linux TUN bridge supports packet forwarding/injection when wired to a datagram flow, but does not configure host networking or automatically create that flow. These are evolving reference behaviors, not a finalized interoperable wire profile. A server operator must restrict outbound reachability using host firewall/network policy appropriate to the deployment.

The DataAuthenticate flow above is the packaged server data listener's authentication exchange. Separately, certificate-bound transport exposes a certificate-bound listener that authenticates the TLS client certificate and then requires a Probe binding request/response. These are alternative library paths, not two steps of one handshake: the application listener does not accept the certificate-bound Probe exchange, and the standalone certificate-bound listener does not accept DataAuthenticate. Both currently use ALPN mehs-data/1, so ALPN alone does not distinguish the authentication profile. A client and server must use the same implementation path; a mismatch fails closed. This profile ambiguity is one reason the wire protocol is not declared interoperable or stable.

The v1 error code registry is: 1 unauthorized, 2 expired session, 3 replay, 4 unsupported version, 5 invalid frame, 6 policy rejected, 7 resource limit, 8 internal error, and 9 E2E crypto negotiation. An error-frame payload is exactly the 2-byte unsigned code in network byte order. Unknown codes or any other payload length are rejected. These numeric values are strictly validated. The API error response envelope remains unimplemented.

Test vector: a stream-data frame with epoch 1, flow 2, sequence 3, and payload hi encodes as:

4d454853 0001 01 00 0000000000000001 0000000000000002
0000000000000003 00000002 6869

ACK payload test vector: cumulative sequence 4 with one selective range [6, 8] encodes as:

0000000000000004 0001 0000000000000006 0000000000000008

Local version selection can choose the highest common value from two supplied lists; there is no implemented on-wire version negotiation. The frame header's version must be exactly 1. A peer that cannot use the selected TLS ALPN/profile fails transport setup. An unsupported frame version, unknown type, or malformed frame is rejected by the decoder and the reference data connection is closed; no fallback is attempted. Implementations may send an Error only when the peer/session is sufficiently identified and the frame can be safely written; malformed headers and unsupported versions do not guarantee a MEHS Error response. The reference DataAuthenticate listener closes on authentication failure, while flow-open connection/policy failures return FlowOpenResult(1).

Reference dispatch, close, and timeout behavior

The authenticated reference data dispatcher accepts DataAuthenticate only as the first frame. After successful authentication, it accepts FlowOpen, matching-flow StreamData/Datagram, Ack, and Close; receiving another authentication/control frame, data for an unopened flow, a mismatched flow type, an invalid ACK, or a frame from another epoch terminates the connection. A repeated flow ID or a flow-limit rejection returns FlowOpenResult(1); data must not be sent until FlowOpenResult(0). Malformed flow-open payloads terminate the connection. Flow IDs are not reusable within the session. Flow Close discards its pending sender/receiver state; session Close ends the connection. A flow Close for an unknown ID is ignored. A valid Error terminates the receiving proxy runtime with the decoded error code; the server data dispatcher closes on frame types it does not accept. Transport EOF/TLS/QUIC closure also ends the connection. The reference server does not promise a Close frame or Error frame on every failure.

The reference server's defaults are a 10-second TLS handshake timeout and a separate 10-second first-frame authentication timeout, a 10-second target-connect timeout, 32 simultaneous data connections per listener, and 128 opened flow IDs per data session. QUIC connection and stream setup also use 10-second timeouts. The established data read has no protocol-level idle timeout. The session sender schedules its first retransmission after 200 ms, doubles the delay up to 5 seconds, returns a frame at most 12 times, and then reports unrecoverable delivery; callers must poll and send due frames. Sequence values do not wrap: sender/receiver operations fail when advancing a flow sequence would overflow the unsigned 64-bit counter. Receiving a valid ACK removes pending retransmission state, while malformed or impossible ACKs are rejected. These are reference implementation behaviors, not negotiated limits.

2. Security baseline (requirements; not all are implemented)

2.1. Data-peer binding (TCP/TLS)

A data connection must be bound to a finalized session epoch before any application frame is sent or accepted.

This subsection describes the separate certificate-bound transport path described above, not the DataAuthenticate flow used by the packaged server.

  1. The client opens TCP and completes TLS 1.3, verifying the server certificate per its trust policy. The server requires a client certificate; the server maps the verified end-entity certificate to a client identity through its configured policy. A missing, unverifiable, or unmapped client certificate fails closed.
  2. Client sends a binding request: a Probe frame with the finalized session epoch, flow ID 0, sequence 1, and payload MEHS-BIND-1 (ASCII).
  3. The server checks that the epoch belongs to a finalized session, that the mapped identity equals the identity that finalized it, and that the session has a remaining binding (at most tcp_connections + udp_connections connections per epoch, minimum one). Each success consumes one binding, so repeated binds beyond the allowance are replays.
  4. On success the server replies with a Probe frame with the same epoch, flow 0, sequence 1, payload MEHS-BOUND-1. On failure it sends an Error frame (same epoch, flow 0, sequence 0) with code 1 unauthorized (identity/transport mismatch), 2 expired session (unknown epoch), or 3 replay (bindings exhausted), then closes the connection.
  5. The client must not send application frames until the acknowledgement is received. After binding, both sides reject frames whose epoch differs from the bound epoch.

The binding exchange uses no custom cryptography. The reference implementation also binds QUIC data connections to finalized sessions over QUIC's TLS 1.3 connection. The transport-specific handshake details and wire profile remain evolving and are not a stability or interoperability guarantee.

3. Initial authorization and configuration negotiation (proposed)

The following describes the intended control-plane workflow. It is not a complete, interoperable API wire specification; API request/response schemas and end-to-end negotiation remain incomplete.

  1. Client opens one short-lived HTTPS connection using TLS 1.3 and sends an API POST containing configured client authentication and requested configuration.
  2. Server validates credentials against its client list and validates the requested configuration against server policy.
  3. Server returns a UUIDv4 session_id, server policy/version information, and either an accepted configuration or explicit correction/rejection details.
  4. Client applies server policy. Any changed parameter is reported to the application through a structured status/configuration event.
  5. In Auto mode, client probes only server-permitted candidate transports. In explicit mode, it probes only the requested/allowed candidate set.
  6. Client reports selected effective policy through the authorized negotiation API. Both sides must finalize negotiation within 120 seconds of session ID issuance.
  7. After successful negotiation, the control connection is closed unless the chosen reference profile explicitly requires otherwise. The session ID and negotiation context must not be reused for another connection. The persistent logical session uses the nonzero epoch returned at finalization, bound by the embedding to the same authenticated peer on its data transport.

If negotiation expires, discard the temporary session ID and restart authorization; do not silently reuse it.

4. Connection counts and startup (policy defaults)

5. Probe and transport selection (policy defaults)

Each candidate connection is measured independently. Record:

Use at least 5 RTT samples and at least 3 throughput samples per candidate as initial implementation defaults. Discard failed/timeout samples from latency statistics but count them as failures for availability/loss. Use a bounded probe payload and a per-server rate limit.

Initial score policy:

  1. Exclude candidates that violate server policy, fail authentication, or exceed timeout/loss thresholds.
  2. Prefer the candidate with lower sustained loss and fewer failures.
  3. Then prefer lower median RTT and jitter.
  4. Use throughput as a tie-breaker or weighted secondary metric.
  5. Require a sustained improvement (initial default: at least 15% score improvement across two consecutive windows) before switching a healthy active path, except when the active path is failing.
  6. Apply a 10-second minimum migration cooldown after a successful switch unless the active path fails.

These weights are implementation defaults to benchmark and version; don't advertise them as universal optima.

The current selector implements a versioned integer score so different paths can be compared reproducibly: packet loss contributes 50% (loss in ppm, capped at 100%); median RTT contributes 30% (capped at 1 s); median absolute deviation from median RTT contributes 15% (capped at 500 ms); and median throughput contributes 5% (capped at 100 MB/s, with higher throughput reducing the score). Lowest score wins, with path ID as the deterministic tie-breaker. Switching away from a healthy path requires at least 15% improvement in two consecutive windows and observes a 10-second cooldown; a failed active path bypasses both. This selection logic is available in the client; automatic selection and live migration have been manually verified.

6. Reliability and delivery

MEHS reliable delivery applies to the logical MEHS data channel, not to the entire Internet path beyond the remote MEHS endpoint.

No protocol can guarantee delivery if the server permanently fails, the process exits, the device loses state, buffers are exhausted, or no path becomes available indefinitely. In those cases, fail explicitly rather than claiming lossless delivery. Session handling schedules retransmission deadlines (200 ms initial, exponential backoff capped at 5 s, max 12 attempts) and exposes due frames for the caller to send. The reference server implements an in-memory authenticated TCP data listener; it does not provide persistent session state or datagram/TUN forwarding.

7. Frame limits and resource protection

Initial resource limits:

These limits are implementation bounds/defaults, not negotiated values. The reference server currently uses at most 128 flow IDs and 128 buffered/unacknowledged frames per data session; the session primitives permit larger configured ceilings where specified above.

8. Errors and status events

Status events must be structured, stable, and safe to log. At minimum:

Session handling exposes these kinds as structured enums with fixed codes, rather than caller-supplied message strings. Never include secrets, raw credentials, session tokens, or private key material in status events.

9. Open protocol requirements

Before claiming v1 stability, complete a single unambiguous authenticated data profile, complete the control/API wire specification, connect live network probes over real authenticated sockets (including QUIC) to the selector, and add interoperability fixtures. TCP and QUIC authentication and flow-open behavior are reference implementations, not finalized interoperable profiles. Retransmission deadline scheduling (capped exponential backoff with a maximum attempt count) is implemented in session handling; callers must still poll and transmit due frames. This document specifies the implemented frame codec and records proposed policy behavior, but is not a complete control-plane wire specification.