• v0.9.2 235ed09bc0

    ET Phone Home v0.9.2
    All checks were successful
    Desktop FreeBSD / macOS (Apple Silicon + Intel) and Windows e2e, on the GitHub mirror (push) Successful in 3s
    Claims ledger / Every README claim has a test (push) Successful in 4s
    Rust CI / Format Check (push) Successful in 13s
    Rust CI / Clippy (push) Successful in 51s
    Android CI / Lint (push) Successful in 2m5s
    Rust CI / Security Audit (push) Successful in 2m44s
    Android CI / Build Debug APK (push) Successful in 2m32s
    Android CI / Unit Tests (push) Successful in 2m50s
    Rust CI / Test (push) Successful in 2m7s
    Desktop FreeBSD / FreeBSD packages (amd64 + aarch64) (push) Successful in 3m21s
    Rust CI / Build Linux (push) Successful in 3m18s
    OpenWrt router packages / Build router packages (5 CPU families, ipk + apk) (push) Successful in 7m1s
    Rust CI / End-to-end (real client + TUN + phone core, real internet) (push) Successful in 6m11s
    Rust CI / Build Windows (push) Successful in 1m48s
    Rust CI / Build Desktop GUI (push) Successful in 5m43s
    Desktop FreeBSD / Real FreeBSD 14 VM against the phone core (push) Successful in 12m46s
    Desktop FreeBSD / Real OPNsense + pfSense CE VMs (plugin/package, phone as gateway, WAN failover) (push) Successful in 16m35s
    OpenWrt router packages / Real OpenWrt VMs (24.10 opkg + 25.12 apk) against the phone core (push) Successful in 6m46s
    Release / Build Linux Release (push) Successful in 2m34s
    Release / Build Windows Release (push) Successful in 3m2s
    Release / Build FreeBSD Release (pkg, amd64 + aarch64) (push) Successful in 3m0s
    Release / Build Desktop GUI (Linux) (push) Successful in 4m17s
    Release / Build Android Release (push) Successful in 2m29s
    Release / Build OpenWrt router packages (push) Successful in 7m7s
    Release / Publish Forgejo release (push) Successful in 1m15s
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 4s
    Release / Attach the macOS package (built + tested on the GitHub mirror) (push) Successful in 3s
    Release / SHA256SUMS over every release file (push) Successful in 29s
    Release / Verify the published release (push) Successful in 5s
    Stable

    Ghost released this 2026-10-21 21:13:57 -07:00 | 43 commits to main since this release

    Fixed

    • Installers, packages and docs no longer mention or contact the project's private git host: the OpenWrt/GL.iNet installer downloads from https://errant.solutions/download/etphonehome/<tag>/<file> and finds the latest release through the public GitHub API; winget manifests, the README and the docs link errant.solutions or the public GitHub repository. CI now fails if the private host's name appears in a shipped file or in any release package.

    Changed

    • etphonehome list-profiles prints WARNING: connect will refuse this profile: ... under a profile whose enabled filter can't be sent to the phone (a rule with an address/port typo, more than 1024 rules, or rules too large for one message). This is the same check connect runs. Before, the listing looked normal and the problem only showed up when a connect attempt failed. The desktop app's profile list marks such a profile with "Filter problem: won't connect".
    Downloads
  • v0.9.1 4106577a53

    ET Phone Home v0.9.1
    All checks were successful
    Claims ledger / Every README claim has a test (push) Successful in 4s
    Desktop FreeBSD / macOS (Apple Silicon + Intel) and Windows e2e, on the GitHub mirror (push) Successful in 6s
    Rust CI / Format Check (push) Successful in 12s
    Rust CI / Clippy (push) Successful in 50s
    Android CI / Lint (push) Successful in 1m37s
    Rust CI / Security Audit (push) Successful in 2m53s
    Android CI / Unit Tests (push) Successful in 2m38s
    Android CI / Build Debug APK (push) Successful in 2m55s
    Desktop FreeBSD / FreeBSD packages (amd64 + aarch64) (push) Successful in 2m47s
    Rust CI / End-to-end (real client + TUN + phone core, real internet) (push) Successful in 5m16s
    OpenWrt router packages / Build router packages (5 CPU families, ipk + apk) (push) Successful in 5m36s
    Desktop FreeBSD / Real FreeBSD 14 VM against the phone core (push) Successful in 13m8s
    Rust CI / Test (push) Successful in 1m59s
    Rust CI / Build Desktop GUI (push) Successful in 2m25s
    Rust CI / Build Linux (push) Successful in 3m6s
    Rust CI / Build Windows (push) Successful in 1m17s
    Desktop FreeBSD / Real OPNsense + pfSense CE VMs (plugin/package, phone as gateway, WAN failover) (push) Successful in 16m58s
    OpenWrt router packages / Real OpenWrt VMs (24.10 opkg + 25.12 apk) against the phone core (push) Successful in 6m55s
    Release / Build FreeBSD Release (pkg, amd64 + aarch64) (push) Successful in 2m48s
    Release / Build Linux Release (push) Successful in 3m18s
    Release / Build Windows Release (push) Successful in 3m23s
    Release / Build Desktop GUI (Linux) (push) Successful in 3m51s
    Release / Build OpenWrt router packages (push) Successful in 7m22s
    Release / Build Android Release (push) Successful in 3m3s
    Release / Publish Forgejo release (push) Successful in 51s
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 3s
    Release / Attach the macOS package (built + tested on the GitHub mirror) (push) Successful in 9s
    Release / SHA256SUMS over every release file (push) Successful in 19s
    Release / Verify the published release (push) Successful in 1s
    Publish to Google Play / diagnose (push) Has been skipped
    Publish to Google Play / build-release-aab (push) Successful in 3m23s
    Publish to Google Play / sign-and-publish (push) Successful in 48s
    Stable

    Ghost released this 2026-10-21 01:07:14 -07:00 | 52 commits to main since this release

    Added

    • macOS client: etph-macos-<version>.pkg (Apple Silicon + Intel, macOS 11+): menu-bar app, CLI and a launchd service that owns the tunnel (utun, no kernel extension), DNS for the tunnel through scutil, etph-uninstall. Installed and tested in CI on an Apple Silicon and an Intel Mac (HTTPS, DNS and a 5 MB download through the phone core, the service and ctl, clean uninstall). Not notarized yet: the first open needs Control-click > Open (docs/DESKTOP.md).
    • OPNsense plugin os-etph: Interfaces > Phone uplink (ETPH) (settings, phone status, ETPH Pro, traffic), a configd-driven service, and etph0 offered under Interfaces > Assignments even while the phone is away, so the phone can be a gateway in a gateway group (WAN failover). Tested on a real OPNsense 26.7 VM.
    • pfSense CE package pfSense-pkg-etph: Interfaces > Phone uplink (ETPH) with a Status tab, the ETPH service under Status > Services, etph0 assignable as an interface for gateways and gateway groups. Tested on a real pfSense CE 2.7.2 VM.
    • Both firewall tests run in CI (firewall-vms): a LAN client keeps HTTPS through the phone when the primary WAN goes down, the firewall's own default route follows, the uplink survives the WAN coming back and the phone going away and returning, and the packages uninstall cleanly.
    • FreeBSD packages for FreeBSD 14 and 15 (-freebsd14-, -freebsd15-): pkg(8) refuses a package built for another major version.

    Fixed

    • BSD firewalls: when the firewall reconfigures the assigned phone interface (pfSense "IPv4 Configuration Type: None" strips its addresses), the client puts the link address back within seconds and re-runs the up hook instead of leaving the uplink dead until the next reconnect.
    • OPNsense: after the phone reconnects, its gateway comes back online (the link returns with the same address, and OPNsense's address-change hook skipped it as "no IP change", leaving gateway monitoring on the old device).

    Known limits

    • Firewalls: the phone connects over Wi-Fi (tested); USB to a phone plugged into the firewall is offered but not yet tested there.
    • The macOS package is ad-hoc signed, not notarized (no Apple Developer ID yet).
    Downloads
  • v0.9.0 f7bd9e2b39

    ET Phone Home v0.9.0
    All checks were successful
    Desktop FreeBSD / macOS (Apple Silicon + Intel) and Windows e2e, on the GitHub mirror (push) Successful in 3s
    Claims ledger / Every README claim has a test (push) Successful in 55s
    Desktop FreeBSD / FreeBSD packages (amd64 + aarch64) (push) Successful in 4m31s
    Release / Build Linux Release (push) Successful in 3m1s
    Release / Build Windows Release (push) Successful in 3m2s
    Release / Build FreeBSD Release (pkg, amd64 + aarch64) (push) Successful in 3m8s
    Release / Build Desktop GUI (Linux) (push) Successful in 3m28s
    Release / Build Android Release (push) Successful in 3m22s
    Release / Build OpenWrt router packages (push) Successful in 8m43s
    Desktop FreeBSD / Real FreeBSD 14 VM against the phone core (push) Successful in 10m51s
    Release / Publish Forgejo release (push) Successful in 46s
    Release / Attach the macOS package (built + tested on the GitHub mirror) (push) Has been skipped
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 4s
    Release / Verify the published release (push) Successful in 0s
    Stable

    Ghost released this 2026-10-20 23:05:42 -07:00 | 62 commits to main since this release

    Added

    • Free and Pro. One app, two tiers. Free: sharing over USB with one computer, no speed cap, no time limit, started and stopped on the phone. ETPH Pro, a one-time in-app purchase in the Google Play app (US$15 or Play's local price; no subscription), adds Wi-Fi and Bluetooth, routers, several computers and routers, auto-start when the computer asks over USB plus waiting for the computer after a disconnect, and a data usage history. "Upgrade to Pro" screen with the feature list and the Play price, Restore purchase; a quiet status line instead of pop-ups. Google Play Billing 9.1: purchases are acknowledged, restored on a reinstall or new phone, pending payments wait for Play, refunds and revocations take Pro away at the next check (every app start, every Start); purchase signatures are checked on the phone; no server (docs/pro.md).
    • The phone enforces the split; computers and routers never need a licence. The handshake now carries who the client is (a random client id and its host name; routers say they are routers) and whether the phone has Pro. A refused client gets "Pro required on the phone: ..." and the desktop/router client prints it with "nothing needs to be bought or licensed on this computer or router", keeps retrying once a minute (so buying Pro on the phone just works), the GUI shows it, and LuCI shows whether the phone has Pro. Older clients still connect over USB as one computer.
    • Paired computers and routers on the phone, each with Forget (Free works with the computer used last; forget it to switch).
    • Auto-start on plug-in (Pro): when the computer finds the phone on USB with nothing listening, it asks the phone to start sharing (adb shell am start ... AutoStartActivity, also through the routers' built-in ADB host), once per plug-in, so a Stop on the phone is respected until the cable is replugged. Switch "Start when the computer asks" on the phone.
    • Data usage (Pro): per computer/router (today, this month, 30 days) and by kind of traffic (web, QUIC, DNS, email, SSH, calls, VPN, NTP, ping), kept on the phone only.
    • Desktop: --router (set by OpenWrt's proto etph), $ETPH_CLIENT_ID/$ETPH_CLIENT_NAME; the status file has phone_pro.
    • USB without Developer options (routers and Linux computers). The client talks to the phone as an Android Open Accessory: plug the phone in, tap OK on "Open ETPH ...?" once, and the link comes up; no USB debugging, no adb. The ADB route stays as a fallback (--usb-link auto|aoa|adb, LuCI "USB link"). The phone coalesces frames into 16 KiB accessory transfers.
    • USB works in every app mode: a phone in Wi-Fi or Bluetooth mode still accepts a computer or router on USB.
    • The phone names what it shares with ("Router: slate (GL.iNet GL-AR750S, OpenWrt)", or the computer's host name).
    • Windows installer: etph-windows-<version>.msi (CLI, tray app, the ETPH tethering service, bundled Wintun) and winget manifests (etph-winget-<version>.zip).
    • ETPH Pro purchases are checked on the phone against the app's Play licence key: an unsigned or wrongly signed purchase never unlocks Pro.
    • FreeBSD 14 packages (amd64, aarch64): pkg add, an rc.d service and etphonehome ctl, tun(4) + resolvconf(8); tested on a real FreeBSD 14.4 VM.

    Known limits

    • macOS, pfSense and OPNsense clients come in a 0.9.x update.
    • Windows USB still needs USB debugging (the accessory link is Linux/router only).

    Changed

    • Free: when the computer disconnects, sharing stops (tap Start again); Pro keeps waiting for it.

    Fixed

    • Desktop/router: a filter rule with an address or port typo (dest_ip = "10.0.0.0/24/8", "010.0.0.1", dest_port = "443-80" or "8o") silently did nothing: the computer never matched it and the phone dropped it, so a BLOCK rule blocked nothing. A profile with such an enabled rule is now refused before connecting, with the rule and field named.
    Downloads
  • v0.8.0 c217a170b3

    ET Phone Home v0.8.0
    All checks were successful
    Claims ledger / Every README claim has a test (push) Successful in 3s
    Android CI / Lint (push) Successful in 2m1s
    Android CI / Unit Tests (push) Successful in 2m41s
    Android CI / Build Debug APK (push) Successful in 2m59s
    Release / Build Windows Release (push) Successful in 2m4s
    Release / Build Linux Release (push) Successful in 2m23s
    Release / Build Android Release (push) Successful in 3m9s
    Release / Build Desktop GUI (Linux) (push) Successful in 3m47s
    Release / Build OpenWrt router packages (push) Successful in 8m40s
    Release / Publish Forgejo release (push) Successful in 40s
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 3s
    Release / Verify the published release (push) Successful in 0s
    Stable

    Ghost released this 2026-10-20 16:28:41 -07:00 | 87 commits to main since this release

    Added

    • Routers: the phone as a router's internet (OpenWrt, GL.iNet). New packages put the ETPH client on an OpenWrt router (official OpenWrt and GL.iNet firmware), so every device behind the router gets the phone's connection. Over a USB cable or Wi-Fi (TLS, the token the app shows), or auto (USB when a phone is plugged in, else Wi-Fi). For USB the client has its own ADB host (usbfs, no adb package or server, so it fits small routers); the phone's "Allow USB debugging?" prompt is the pairing, and LuCI says "Unlock your phone and tap Allow" while it waits. The uplink is a normal network interface, option proto 'etph': netifd adds the default route with the interface's metric and gives dnsmasq the phone's DNS, so firewall zones and mwan3 failover/load balancing work as with any WAN. It reconnects by itself when the phone goes away and comes back, and right away when a phone is plugged into the router's USB port. LuCI page Network > Phone uplink: status (phone model, connection, speed, data), one-step setup into the wan zone, pairing/unpairing, USB phones and the router's adb key fingerprint. Packages: .ipk for OpenWrt 24.10 and older and GL.iNet 4.x, .apk for OpenWrt 25.x, for aarch64, ARMv7 (with FPU), MIPS 24Kc, MIPSel and x86_64; an installer script picks the right one. Guide: docs/OPENWRT.md.
    • Desktop client: usb --native (built-in ADB host on Linux: no adb tool needed), usb --adb-tcp HOST:PORT, auto, usb-list, adb-keygen, --no-routes, --up-script/--down-script (pppd-style hooks), --status-file (JSON status), --tun-address/--tun-peer, --adb-serial, discover-wifi --json, adb-fingerprint; the WiFi/Bluetooth token can come from $ETPH_TOKEN (keeps it out of ps); SIGUSR1 skips the reconnect wait; SIGTERM disconnects cleanly like Ctrl-C.
    • The phone tells the computer its name (e.g. "samsung SM-N986U") in the handshake; older desktops ignore it.

    Known limits

    • Routers over USB are tested in CI against the real app's adbd on Android emulators and against a fake adbd, and over Wi-Fi on a real GL.iNet GL-AR750S with a real phone; USB with a phone plugged into real router hardware is not verified yet.
    • ARMv7 routers without an FPU (e.g. Broadcom bcm53xx) and MIPS64 are not supported.

    Fixed

    • Desktop: a large traffic filter put the connection in a reconnect loop. A profile whose filter rules came to more than 65535 bytes (the most one message to the phone can carry) brought the tunnel up and dropped it straight away, then retried every second forever, with the default route coming and going each time. Such a profile, or an enabled filter with more than 1024 rules (the most the phone takes; it silently dropped the rest), is now refused before connecting, with an error that says why.
    • Android: a router using the phone over the router's own Wi-Fi made traffic loop. When the phone is joined to the Wi-Fi of a router whose internet is the phone, Android eventually treats that Wi-Fi as the phone's internet (its check passes through the tunnel), and the relay's connections went phone -> router -> phone. When the computer is the router of the Wi-Fi the phone is on, the relay now pins its sockets to another network (mobile data first), keeps mobile data up for it, and refuses connections rather than loop when there is no other network. A computer that is just another device on the phone's Wi-Fi is not affected.
    • The router build has no Bluetooth/D-Bus and uses only the ring TLS backend (no aws-lc), which also makes the desktop binary smaller.
    Downloads
  • v0.7.1 474811ebf7

    ET Phone Home v0.7.1
    All checks were successful
    Claims ledger / Every README claim has a test (push) Successful in 2s
    Release / Build Linux Release (push) Successful in 2m9s
    Release / Build Windows Release (push) Successful in 2m29s
    Release / Build Android Release (push) Successful in 3m20s
    Release / Build Desktop GUI (Linux) (push) Successful in 5m28s
    Release / Publish Forgejo release (push) Successful in 22s
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 5s
    Release / Verify the published release (push) Successful in 11s
    Stable

    Ghost released this 2026-10-18 20:21:03 -07:00 | 112 commits to main since this release

    Changed

    • Android: targets Android 16 (API 36), which Google Play requires for new apps. The app now draws edge to edge on every Android version, with its content kept clear of the status bar, navigation bar, camera cutout and keyboard (Android 15+ forces edge-to-edge for apps targeting it). Back navigation uses the system's predictive back. Still runs on Android 8.0 and newer.
    Downloads
  • v0.7.0 6ff1060b2f

    ET Phone Home v0.7.0
    All checks were successful
    Claims ledger / Every README claim has a test (push) Successful in 9s
    Release / Build Linux Release (push) Successful in 2m6s
    Release / Build Windows Release (push) Successful in 1m44s
    Release / Build Android Release (push) Successful in 2m5s
    Release / Build Desktop GUI (Linux) (push) Successful in 4m2s
    Release / Publish Forgejo release (push) Successful in 14s
    Release / Sign the APK with the ETPH release key and attach it (push) Successful in 3s
    Release / Verify the published release (push) Successful in 0s
    Stable

    Ghost released this 2026-10-18 18:46:14 -07:00 | 115 commits to main since this release

    Changed

    • New app id and name: "Errant Solutions ETPH" (solutions.errant.etph). This is the name and id the app will have on Google Play (ETPH = ET Phone Home); the launcher icon is labelled "ETPH". Android treats a new app id as a different app, so 0.7.0 installs next to an older ET Phone Home (com.etphonehome) instead of updating it. While the old app is still installed, the new one says so on its main screen, with a button that opens the old app's App info page (Uninstall is there). Uninstall the old app afterwards, and re-pair WiFi/Bluetooth desktops with the token the new app shows (the tokens belong to the app, so they did not carry over). USB needs nothing new. Settings such as the connection mode start fresh too.
    • The APK on the releases page is signed with the ETPH release key (the key registered for solutions.errant.etph with Google's Android developer verification, SHA-256 01:11:AF:00:57:D7:E9:28:90:FB:CE:02:E9:92:7A:85:E5:EA:B2:AD:AC:2C:00:76:55:E1:7C:B0:CF:7F:68:40). Releases up to 0.6.1 carried a public debug key.
    • Google Play release pipeline: the Play bundle is built unsigned on a normal runner and signed with the upload key on a dedicated signer, then uploaded to the internal testing track (release-play-* tags; a manual run is a dry run that changes nothing in Play). Store listing text and the Data safety answers live in store/.

    Fixed

    • A connection that stalled mid-handshake locked every desktop out of the phone. The phone serves one desktop at a time, and once a connection had sent its first byte nothing timed out until the handshake finished. So a connection that sent one byte, or a TLS ClientHello, and then went quiet held the session slot indefinitely, before any token check. Anyone on the same WiFi, or any app on the phone for the USB port, could do this. The whole handshake now has to finish within 15 s, otherwise the connection is dropped.
    • A phone-side filter rule with an invalid IPv4 address triggered a DNS lookup. Rule addresses like 256.1.1.1 were handed to the system resolver as hostnames, which blocked the session while the lookup ran, and on networks whose DNS answers every name the rule ended up applying to an unrelated address. Rule addresses are now parsed as numeric IPv4/IPv6 literals only, and anything else drops the rule.
    • A phone-side filter rule naming one IPv6 host covered its whole /32. A rule address with no /prefix defaulted to /32 for IPv6 as well as IPv4, so blocking 2001:db8::1 blocked all of 2001:db8::/32, and in whitelist mode allowing it allowed the whole /32. A bare IPv6 address is now /128.
    • IPv6 packets could slip past the phone's protocol and port rules. The packet parser stopped walking IPv6 extension headers after 8, and didn't know the Authentication Header, so TCP behind 9+ extension headers or an AH header came out as "any protocol, no ports" and a rule like block tcp 443 didn't match it. The parser now walks the whole chain (always bounded by the packet length) and understands AH.
    Downloads
  • v0.6.1 4aa2539d2d

    ET Phone Home v0.6.1
    All checks were successful
    Claims ledger / Every README claim has a test (push) Successful in 10s
    Release / Build Windows Release (push) Successful in 1m42s
    Release / Build Linux Release (push) Successful in 4m27s
    Release / Build Android Release (push) Successful in 1m52s
    Release / Build Desktop GUI (Linux) (push) Successful in 3m10s
    Release / Publish Forgejo release (push) Successful in 10s
    Stable

    Ghost released this 2026-10-17 04:42:50 -07:00 | 143 commits to main since this release

    Fixed

    • USB tethering on Android 8-10. The app's USB server listened on ::1; adbd before Android 11 forwards adb forward connections to 127.0.0.1 only, so every USB connection was refused on those versions (found by the new API 26 emulator test). It now listens on 127.0.0.1.
    • Linux release binary ran only on the newest distros. It needed glibc 2.39 (the CI image's), so it did not even start on Debian 12 or Ubuntu 22.04. Releases are now linked against glibc 2.31 with libdbus built in (no system libdbus needed); the nightly distro matrix runs a real tunnel on Debian 11-13, Ubuntu 20.04-24.04, Fedora, Arch and openSUSE.
    • A dropped WiFi link left a dead tunnel behind. The desktop kept the TUN device and default route for 90 s, and the phone held the dead session until TCP gave up (~15 min), refusing the desktop when it came back. The desktop now sends keepalives and gives up after 30 s; the phone ends a session whose (keepalive-sending) desktop has been silent for 30 s.
    • WiFi crashed the app on Android 8 and 9. The first encrypted WiFi connection crashed the app with NoSuchMethodError (the TLS layer called a Java 8 SSLSocketFactory method these Android versions lack); found by the API 26 emulator test. TLS is now layered through Bouncy Castle's own implementation of that method.
    • Bluetooth could never connect to a real phone. The desktop always dialed RFCOMM channel 1, but Android assigns channels dynamically (the app was on channel 6 on a Galaxy Note20), and the app told users to connect to 02:00:00:00:00:00 (Android hides the real Bluetooth address from apps). The app now listens on its own service UUID and shows its name; the desktop asks BlueZ to connect that service (BlueZ looks the channel up over SDP and hands over the socket, which also works from a container) and --address accepts the phone's name as paired. Verified on a Samsung Galaxy Note20 (Android 13): public HTTPS, DNS and a byte-identical download over Bluetooth, from a desktop whose only way out was the tunnel.
    • The desktop client no longer sets net.ipv4.ip_forward=1 on the computer (it never needed it and never undid it).
    • --reconnect no longer retries a protocol-version mismatch or a rejected token forever; it stops with the reason. The retry budget and backoff count failures since the last working session, not over the client's lifetime.

    Added

    • Nightly CI: integrity at scale (256 MiB both ways with digests, 100 parallel TCP + 50 UDP flows), failure and recovery (USB unplug, WiFi drop, app killed, Ctrl-C, crash, lossy link), throughput/latency baseline, upgrade compatibility with the previous release, a Linux distro matrix, arm64, the Windows client under wine, and the real APK on Android 8 and 14 emulators. Weekly mutation testing of the desktop and phone cores.

    Fixed

    • USB tethering on Android 8-10. The app's USB server listened on ::1; adbd before Android 11 forwards adb forward connections to 127.0.0.1 only, so every USB connection was refused on those versions (found by the new API 26 emulator test). It now listens on 127.0.0.1.
    • Linux release binary ran only on the newest distros. It needed glibc 2.39 (the CI image's), so it did not even start on Debian 12 or Ubuntu 22.04. Releases are now linked against glibc 2.31 with libdbus built in (no system libdbus needed); the nightly distro matrix runs a real tunnel on Debian 11-13, Ubuntu 20.04-24.04, Fedora, Arch and openSUSE.
    • A dropped WiFi link left a dead tunnel behind. The desktop kept the TUN device and default route for 90 s, and the phone held the dead session until TCP gave up (~15 min), refusing the desktop when it came back. The desktop now sends keepalives and gives up after 30 s; the phone ends a session whose (keepalive-sending) desktop has been silent for 30 s.
    • The desktop client no longer sets net.ipv4.ip_forward=1 on the computer (it never needed it and never undid it).
    • --reconnect no longer retries a protocol-version mismatch or a rejected token forever; it stops with the reason. The retry budget and backoff count failures since the last working session, not over the client's lifetime.

    Added

    • Nightly CI: integrity at scale (256 MiB both ways with digests, 100 parallel TCP + 50 UDP flows), failure and recovery (USB unplug, WiFi drop, app killed, Ctrl-C, crash, lossy link), throughput/latency baseline, upgrade compatibility with the previous release, a Linux distro matrix, arm64, the Windows client under wine, and the real APK on Android 8 and 14 emulators. Weekly mutation testing of the desktop and phone cores.

    Planned for v0.5.0

    • TLS encryption for WiFi/Bluetooth modes
    • Desktop GUI application
    • Traffic statistics and graphs
    • Root mode with iptables routing for better performance
    • Advanced routing and traffic filtering rules

    Planned for v1.0.0+

    • Multiple device support
    • Data usage limits and alerts
    • Traffic compression for bandwidth optimization
    • Custom DNS server configuration
    • Mobile data usage tracking

    Release Notes

    v0.4.0 Highlights

    Version 0.4.0 represents a major milestone, elevating ET Phone Home from Alpha to Beta status. All three tethering modes (USB, WiFi, Bluetooth) are now fully functional with enterprise-grade features:

    • Complete Bluetooth Support: Both Android and Desktop implementations finished
    • Security: Token-based authentication protects WiFi and Bluetooth connections
    • Reliability: Auto-reconnect ensures connections stay active
    • Convenience: Connection profiles and mDNS discovery simplify usage
    • Production Ready: Comprehensive testing and documentation

    Migration Guide: v0.3.0 → v0.4.0

    Breaking Changes

    None. Version 0.4.0 is fully backward compatible with v0.3.0.

    New Optional Features

    Token Authentication:

    • WiFi and Bluetooth connections now support (but don't require) authentication tokens
    • Old command: sudo etphonehome wifi --host 192.168.1.100
    • New command: sudo etphonehome wifi --host 192.168.1.100 --token AbC123XyZ
    • USB mode unchanged (no tokens needed)

    Connection Profiles:

    # Old way - manual command every time
    sudo etphonehome wifi --host 192.168.1.100
    
    # New way - save once, use forever
    etphonehome save-profile home --type wifi --host 192.168.1.100
    sudo etphonehome connect home
    

    Auto-Reconnect:

    # Add --reconnect to any connection command
    sudo etphonehome wifi --host 192.168.1.100 --reconnect
    

    mDNS Discovery:

    # New discover-wifi command replaces manual IP finding
    sudo etphonehome discover-wifi
    

    Android App Changes

    • WiFi and Bluetooth modes now display authentication tokens
    • Copy the displayed token for desktop connection
    • Tokens are automatically generated on first use
    • Same tokens persist across app restarts

    Known Issues

    • Windows Bluetooth: Some configurations may require manual COM port setup
    • Linux Bluetooth: Requires libdbus-1-dev package for compilation
    • mDNS Discovery: May not work across different network subnets (by design)

    Future Roadmap

    v0.5.0 (Planned):

    • TLS encryption for WiFi/Bluetooth modes
    • Desktop GUI application
    • Traffic statistics and graphs
    • Root mode with iptables routing

    v1.0.0 (Planned):

    • Multiple device support
    • Data usage limits and alerts
    • Traffic compression
    • Custom DNS configuration

    How to Read This Changelog

    • Added - New features
    • Changed - Changes in existing functionality
    • Deprecated - Soon-to-be removed features
    • Removed - Removed features
    • Fixed - Bug fixes
    • Security - Security improvements
    • Documentation - Documentation updates


    Contributing

    See CONTRIBUTING.md for how to contribute to this project.

    License

    This project is licensed under the MIT License. See LICENSE for details.


    For more details on any release, see the corresponding version tag in the repository.

    Downloads
  • v0.6.0 63320cf7aa

    ET Phone Home v0.6.0
    All checks were successful
    Android CI / Unit Tests (push) Successful in 1m33s
    Android CI / Lint (push) Successful in 2m35s
    Android CI / Build Debug APK (push) Successful in 4m8s
    Release / Build Windows Release (push) Successful in 1m49s
    Release / Build Linux Release (push) Successful in 2m11s
    Release / Build Desktop GUI (Linux) (push) Successful in 2m49s
    Release / Build Android Release (push) Successful in 3m18s
    Release / Publish Forgejo release (push) Successful in 13s
    Stable

    Ghost released this 2026-10-16 21:00:03 -07:00 | 159 commits to main since this release

    Added

    • The computer really gets the phone's internet, without root. The Android app now has a userspace relay (android/core/.../relay/UserspaceRelay.kt): it terminates the desktop's TCP connections and UDP flows itself and re-opens them as ordinary phone sockets (with sequence/ACK handling, flow control, retransmission, FIN/RST), answers ping for the phone and relays ping to other hosts. Before this the app only copied raw packets into a phone VPN, which Android never forwards, so no traffic ever reached the internet.
    • The phone keeps its own internet. The phone VPN is gone: nothing on the phone is captured, and the app no longer asks for VPN permission.
    • Encrypted WiFi by default. The app serves cert-pinned TLS 1.3 (same token-derived Ed25519 pinning as the desktop, via Bouncy Castle) and auto-detects TLS vs. plaintext. etphonehome wifi and WiFi profiles use TLS by default (--no-tls to opt out); the app refuses plaintext WiFi unless "Require encryption" is switched off.
    • Live status and statistics on the phone: "Waiting for desktop" until a desktop has actually completed the handshake, then the desktop's address, encryption, data each way, open/total connections, uptime and filter state; the notification shows the same and has a Stop button.
    • Traffic filter on the phone: right after the handshake the desktop sends the profile's filter (mode and every rule) to the phone, which enforces it on the connections it opens and resets connections a new rule blocks.
    • DNS on the desktop: the client points DNS for the tunnel at the phone's DNS server (resolvectl on Linux, netsh on Windows).
    • Real-internet end-to-end CI: the e2e job now runs the app's actual networking core (android/core, as the JVM standin) and checks, from a desktop container whose only way out is the tunnel: public HTTPS and HTTP, DNS, a 5 MiB download, TCP echo both ways, UDP, ICMP, 20 parallel connections, USB, filter rules, wrong tokens, plaintext refusal, reconnects.
    • Releases are published on Forgejo (the workflow used a GitHub-only action and never created one) and checked to contain all four packages.

    Removed

    • Root mode. It was claimed as implemented, but nothing could start it, and its iptables rules redirected all of the phone's own traffic to the desktop. Removed until a correct kernel-NAT design exists.
    • Dead Android TLS/connection classes that were never wired in.

    Changed

    • Android 8.0+ is now required (was 5.0).

    Fixed

    • USB tethering never connected: the desktop client (and setup-adb, quick-start-linux.sh, and every doc) used adb reverse, which maps a phone port to the computer. The app's USB server listens on the phone's loopback, so the client needs adb forward (computer port -> phone port); with the app running, adb reverse failed with "cannot bind listener: Address already in use". Confirmed against a real phone. The e2e adb stand-in now emulates both directions faithfully, so the wrong one fails the test.
    • Android: the phone lost its internet after a desktop session, and Stop didn't fix it: when the desktop disconnected, the VPN stayed established (all phone traffic routed into a dead tunnel), the app never accepted another connection (so desktop auto-reconnect could not work) and kept showing "Connected". The Stop button used stopService(), which cannot stop a VpnService whose VPN is up, so only force-stopping the app restored the phone's network. Sessions now run in a loop that tears the VPN down after every session and waits for the next desktop; Stop sends an explicit stop action. The VPN is also only established after a successful handshake, so a client with a wrong token no longer touches the phone's routing. Found on a real phone (Android 13).
    • Ctrl-C raced the Disconnect message: the retry loop could drop the live session before it told the phone it was leaving; it now lets the session finish (up to 3 s) and never reconnects once stopping.
    • Desktop forwarding stall: the TUN reader held the device mutex during its blocking read, so packets from the phone were only written to the TUN device when the desktop happened to send something. The forwarding loop now uses the async TUN device with independent read/write halves (shared session module).
    • GUI Connect did nothing: the button only changed the status label. It now runs a real session (same runner as the CLI) with live state, stats and logs; Disconnect stops it. GUI-created profiles defaulted to port 8765 instead of 8888.
    • Bluetooth (Linux): the RFCOMM connection was opened and then abandoned with "integration incomplete"; it now completes the handshake (with --token / auth.bluetooth_token) and forwards packets.
    • CLI: --reconnect could not be turned off (it defaulted to true); save-profile --type from the README was rejected; sudo etphonehome connect looked in root's config instead of the invoking user's; WiFi hosts had to be IP literals; Ctrl-C now sends Disconnect; wrong-token rejections now show the phone's reason instead of "Unexpected message type"; saving a profile from the CLI no longer wipes its filter rules; list-profiles output goes to stdout; a silent phone is detected after 90 s so auto-reconnect can kick in.
    • Android: stats, keepalive and packet writers shared one socket stream without locking (frames could interleave); StatsUpdate was 20 bytes instead of the specified 24. One shared ServerHandshake replaces three copies.
    • Release packaging: release.yml stripped/zipped binaries that don't exist (etphonehome-linux, etphonehome-windows.exe) and expected an unsigned APK; CI artifact uploads pointed at the same wrong names.
    • quick-start-linux.sh: failed on kernels with built-in tun (modprobe under set -e) and required Rust even when the client was already built.
    • Docs: Windows needs wintun.dll, not TAP-Windows; Windows Bluetooth is not supported yet.

    Tests / CI

    • New end-to-end CI job runs the real client through a real TUN device against an Android-protocol stand-in (plaintext, TLS, wrong tokens, USB via adb, filters, auto-reconnect, quick-start script).
    • Shared Rust/Kotlin golden wire vectors (protocol/test-vectors.txt); 13 orphaned protocol tests that never compiled now run; TUN tests run with CAP_NET_ADMIN in CI.
    Downloads