MEHS / Knowledge base

Platform support and TUN mode

TUN mode

client_mode: "tun" (CLI --mode tun) is the supported way to tunnel a whole device. The client creates a virtual interface and runs a userspace TCP/IP stack. Every TCP connection and UDP flow the operating system sends into the interface is terminated locally and carried as a MEHS flow to the server, which opens the real connection. The server therefore needs no raw-IP gateway and no privileges. DNS (UDP/53) is just UDP; ICMP echo is answered locally.

PlatformMechanismNeedsStatus
Linux/dev/net/tun, routes via iproot or CAP_NET_ADMIN, iproute2Verified end to end (TCP and QUIC carriers: HTTPS, HTTP, DNS, ICMP, 10 MB download, route cleanup)
WindowsWintun, routes via PowerShell New-NetRouteAdministrator, wintun.dll of the same architecture next to the executableManually verified by maintainers; runtime coverage is limited.
macOSutun, routes via routerootImplemented; not runtime-tested.
AndroidHost-integrated VpnService descriptor passed with --tun-fdHost owns consent, foreground service, addresses, routes, DNS, UID exclusion, and revoke handlingImplemented; host integration not runtime-tested on device.
iOSNEPacketTunnelProvider packet-flow callbacksHost configures tunnel addresses, routes, and DNSImplemented; host integration not runtime-tested on device.

See Flutter integration for embedding and the CLI guide for TUN invocation examples. Do not run TUN mode while another VPN or proxy tool that manages a TUN device and default route (for example Happ/xray) is active.

Legacy packet bridge

The legacy packet bridge predates MEHS TUN mode. It is intended only for hosts that already own a packet device and operate a compatible remote raw-IP UDP gateway; MEHS does not provide that gateway. New integrations should use TUN mode.

The bridge contract is to expose an MTU, asynchronously read and write complete IP packets, and release device resources on shutdown. It forwards each complete IPv4 or IPv6 packet as one MEHS datagram payload on an already-opened datagram flow, and injects valid response packets received on that same flow. It rejects invalid MTUs, malformed or oversized packets, applies flow/device backpressure, and shuts the adapter down when the run exits.

The remote flow target must be a UDP endpoint implementing the raw-IP gateway contract: accept an IP packet as a datagram and return an IP packet as a datagram. The normal MEHS SOCKS/UDP proxy endpoint forwards application UDP payloads and is not such a gateway. A compatible remote gateway is required for end-to-end traffic through this legacy bridge.

Host responsibilities

The embedding host, not the packet adapter or bridge, configures interface addresses, MTU, DNS, routes, and policy routing. The host must:

  1. Create/configure the platform tunnel interface and select split-tunnel or full-tunnel routes.
  2. Keep the MEHS carrier's route on the physical network. For split tunneling, do not include the carrier endpoint in tunneled prefixes. For full tunneling, install a host route or policy-routing exclusion for every carrier address before diverting the default route. Otherwise the carrier can route into its own tunnel and stall.
  3. For the legacy packet bridge, open a MEHS datagram flow to the remote IP gateway, then run the bridge with the configured adapter and flow.
  4. On teardown, stop/drop the bridge and restore host routes and DNS. Follow the lifecycle sequence below for sleep, wake, and network changes.

The bridge is bounded by its packet buffer and the session flow's bounded queues. It reads one outbound packet at a time and continues servicing inbound packets while a send is waiting for flow capacity. Oversized or malformed packets are discarded; adapter I/O and flow failures end the bridge and trigger adapter shutdown. Aborting/dropping the bridge still drops the platform adapter, whose host implementation must release native handles; hosts needing an orderly asynchronous close should let it finish or call adapter shutdown before dropping it.

Sleep, wake, and network-change lifecycle

The host owns OS event observation and must serialize lifecycle notifications for each client handle. OS notifications can be repeated, coalesced, or missed while the app is suspended, so treat them as prompts to re-check the current system state rather than as a complete event log.

  1. Before sleep: call mehs_on_sleep (or PlatformLifecycle::on_sleep) and pause probes, packet forwarding, and creation of new links. Keep the logical MEHS session state; do not tear it down just because the device is sleeping.
  2. After wake: call mehs_on_wake (or PlatformLifecycle::on_wake), then check the authenticated carrier path and the host's carrier-exclusion routes. Keep probes available for recovery, but keep new traffic paused until the path is healthy. Call mehs_on_wake_path_health(client, 1) or complete_wake_health_check(true) only after that check succeeds. If it fails, keep traffic paused, update network identity if it changed, and recover or reselect the carrier before checking again. A failed health check does not complete the pending wake.
  3. On interface/path change: obtain the current interface index and a deterministic, non-secret fingerprint of the relevant network identity (for example, active interface and assigned addresses). Call mehs_on_network_change or PlatformLifecycle::on_network_change. When the identity changes, invalidate cached path-selection results, reselect or reconnect the carrier, and update host routing. The FFI API exposes the change through mehs_client_network_generation; it does not invalidate an embedding's separate cache for it.
  4. After recovery: resume forwarding and normal probe cadence only after the carrier is healthy and routes still keep the carrier outside the tunnel. Recreate the packet adapter/bridge only when the OS replaced the underlying device or its path cannot be repaired in place.

The mehs_on_* FFI lifecycle functions operate on a MehsClient handle. The separate mehs_runtime_* API used to run the client supervisor exposes start, status, and stop, but does not expose these lifecycle notifications. The standalone client has no sleep/wake command or OS-event subscription; it reconnects in response to transport failures. Hosts that need proactive pause and health-check behavior must integrate the lifecycle callbacks with an API that exposes them.

OS event sources

These are event sources for the embedding, not OS behavior implemented by MEHS. Re-query active interfaces, addresses, and routes after wake or a path event; the notification itself does not establish that the MEHS carrier is healthy.

OSSleep/wake signalsNetwork-change signals
macOSObserve NSWorkspace.willSleepNotification and didWakeNotification on NSWorkspace.shared.notificationCenter.Use NWPathMonitor for path updates, then re-read the selected interface and addresses. See NSWorkspace and NWPathMonitor.
WindowsHandle WM_POWERBROADCAST suspend/resume events, such as PBT_APMSUSPEND and PBT_APMRESUMEAUTOMATIC; resume may be reported before user activity.Use IP Helper notifications such as NotifyIpInterfaceChange and re-query interface/address state. See power broadcasts and NotifyIpInterfaceChange.
LinuxOn systemd systems, subscribe to logind's PrepareForSleep signal (true before sleep, false on resume). There is no single desktop-independent sleep notification API.Subscribe to rtnetlink link/address/route updates (RTM_NEWLINK, RTM_NEWADDR, and route notifications), or use the active network manager's API. See PrepareForSleep and rtnetlink(7).

Mobile hosts should also re-check state when their service or tunnel extension resumes. Android hosts can observe ConnectivityManager.NetworkCallback; iOS Network Extension hosts should use the provider's lifecycle and path-monitoring facilities. Neither platform guarantees that an app receives a desktop-style sleep callback while suspended. See Android network callbacks and iOS integration.

Platform adapters

Generic bridge behavior has host-level unit coverage; each release's checks cover only adapters available for its host target. This does not claim tested end-to-end VPN lifecycle behavior on each OS. Platform release support requires native builds and host-app tests for sleep/wake, network changes, adapter failure, and teardown.