Genymobile/gnirehtet
Gnirehtet provides reverse tethering for Android
Stale — last commit 2y ago
weakest axislast commit was 2y ago; top contributor handles 94% of recent commits…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 2y ago; no CI workflows detected
- ✓5 active contributors
- ✓Apache-2.0 licensed
- ✓Tests present
Show all 6 evidence items →Show less
- ⚠Stale — last commit 2y ago
- ⚠Single-maintainer risk — top contributor 94% of recent commits
- ⚠No CI workflows detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: 1 commit in the last 365 days; diversify commit ownership (top <90%)
- →Deploy as-is Mixed → Healthy if: 1 commit in the last 180 days
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/genymobile/gnirehtet)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/genymobile/gnirehtet on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: Genymobile/gnirehtet
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/Genymobile/gnirehtet shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 2y ago
- 5 active contributors
- Apache-2.0 licensed
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Single-maintainer risk — top contributor 94% of recent commits
- ⚠ No CI workflows detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live Genymobile/gnirehtet
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/Genymobile/gnirehtet.
What it runs against: a local clone of Genymobile/gnirehtet — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in Genymobile/gnirehtet | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 665 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of Genymobile/gnirehtet. If you don't
# have one yet, run these first:
#
# git clone https://github.com/Genymobile/gnirehtet.git
# cd gnirehtet
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of Genymobile/gnirehtet and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "Genymobile/gnirehtet(\\.git)?\\b" \\
&& ok "origin remote is Genymobile/gnirehtet" \\
|| miss "origin remote is not Genymobile/gnirehtet (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java" \\
&& ok "relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java" \\
|| miss "missing critical file: relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java"
test -f "relay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java" \\
&& ok "relay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java" \\
|| miss "missing critical file: relay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java"
test -f "app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java" \\
&& ok "app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java" \\
|| miss "missing critical file: app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java"
test -f "relay-java/src/main/java/com/genymobile/gnirehtet/relay/TunnelServer.java" \\
&& ok "relay-java/src/main/java/com/genymobile/gnirehtet/relay/TunnelServer.java" \\
|| miss "missing critical file: relay-java/src/main/java/com/genymobile/gnirehtet/relay/TunnelServer.java"
test -f "relay-java/src/main/java/com/genymobile/gnirehtet/Main.java" \\
&& ok "relay-java/src/main/java/com/genymobile/gnirehtet/Main.java" \\
|| miss "missing critical file: relay-java/src/main/java/com/genymobile/gnirehtet/Main.java"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 665 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~635d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/Genymobile/gnirehtet"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Gnirehtet provides reverse tethering over adb for Android devices, allowing them to use the host computer's internet connection without requiring root access. It relays TCP and UDP over IPv4 traffic through a VPN-based tunnel, with implementations available in both Java and Rust for Linux, Windows, and macOS. Monorepo structure: app/ contains the Android application (Java VPN service and UI), while the relay tunnel logic is implemented in two parallel stacks—a Java version (no separate folder, logic in GnirehtetService.java and RelayTunnel.java) and a Rust version (in a separate tree, compiled separately). The Android app uses adb reverse to tunnel connections back to the relay server running on the host computer.
👥Who it's for
Android developers and QA engineers who need to test applications on devices without cellular connectivity or WiFi, and system administrators managing Android device labs who want to share a single internet connection across multiple devices via USB.
🌱Maturity & risk
Moderately mature but not actively maintained; the README explicitly states 'This project is not actively maintained anymore, only major blockers (like build issues) are fixed.' The codebase is stable (v2.5.1) with both Java and Rust implementations, but lacks recent development activity. Production-ready for its intended use case, but do not expect new features or rapid bug fixes.
Low immediate risk due to stability, but the single-maintainer situation (Genymobile) and lack of active maintenance mean critical bugs may not be resolved quickly. Dependencies are minimal (only junit:4.12 and espresso-core in the Android app build.gradle), reducing supply-chain risk. The project is feature-complete rather than actively developed, so compatibility drift with newer Android APIs is the main long-term risk.
Active areas of work
No active development; this is a maintained-but-not-developed project. The last update was to reach v2.5.1 stability. No open PRs or milestones are visible in the provided data. The focus is on the Rust implementation (README recommends it over Java) as it consumes less CPU and memory.
🚀Get running
Clone and build the Android app: git clone https://github.com/Genymobile/gnirehtet.git && cd gnirehtet && ./gradlew build. For the Rust relay server, download a pre-built binary from the releases page (Rust is recommended over Java). The Android app requires API 21+ and adb debugging enabled on the device.
Daily commands:
To build and install the Android app: ./gradlew installDebug (requires a connected Android device with adb). To run the relay server: download the Rust binary from releases and execute it, or run the Java version if Java 8 JRE is available. Start reverse tethering: adb reverse tcp:31415 tcp:31415 (port number may vary), then enable the VPN in the Android app.
🗺️Map of the codebase
relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java— Core relay server orchestrating packet routing between Android device and host; every networking change flows through hererelay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java— Routes IPv4 packets to appropriate TCP/UDP connections; understanding packet flow requires reading thisapp/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java— Android-side VPN service managing the tunnel; entry point for on-device packet interceptionrelay-java/src/main/java/com/genymobile/gnirehtet/relay/TunnelServer.java— Establishes and manages bidirectional communication tunnel between relay and Android device over ADBrelay-java/src/main/java/com/genymobile/gnirehtet/Main.java— Relay process entry point; handles CLI arguments, ADB integration, and initializationapp/src/main/java/com/genymobile/gnirehtet/VpnConfiguration.java— Configures Android VPN interface settings; critical for reverse tether functionalityrelay-java/src/main/java/com/genymobile/gnirehtet/relay/IPv4Packet.java— Represents and parses IPv4 packets; fundamental data structure for all networking operations
🛠️How to make changes
Add support for a new IP protocol (e.g., ICMP)
- Create a new header class extending TransportHeader (similar to TCPHeader.java) to parse protocol headers (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/ICMPHeader.java) - Create a new Connection subclass to handle protocol-specific state and forwarding logic (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/ICMPConnection.java) - Register the protocol in Router.route() and Router.getConnection() to dispatch packets to the new connection type (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java) - Add unit tests for header parsing and connection state transitions (
relay-java/src/test/java/com/genymobile/gnirehtet/relay/ICMPHeaderTest.java)
Add a new relay server configuration option (e.g., custom DNS)
- Add the CLI argument definition and parser logic (
relay-java/src/main/java/com/genymobile/gnirehtet/CommandLineArguments.java) - Pass configuration to Relay constructor and store in Relay class (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java) - Implement configuration application logic (e.g., in TunnelServer or Router) (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/TunnelServer.java) - Add test case to verify argument parsing (
relay-java/src/test/java/com/genymobile/gnirehtet/CommandLineArgumentsTest.java)
Add a new network statistic or diagnostic metric
- Define a new metric counter or gauge in the Router or Relay class (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/Relay.java) - Increment/update the metric in appropriate handling methods (e.g., Router.route(), TCPConnection.handle()) (
relay-java/src/main/java/com/genymobile/gnirehtet/relay/Router.java) - Expose metrics via a new diagnostic endpoint or logging output in Main (
relay-java/src/main/java/com/genymobile/gnirehtet/Main.java)
🔧Why these technologies
- Java for relay server — Cross-platform (runs on macOS, Linux, Windows); avoids JVM dependency on host; existing mature socket/networking libraries
- Android VPN API (VpnService) — Only non-root mechanism to intercept all device traffic; requires min API 21; provides NetworkInterface abstraction
- ADB tunnel as transport — Leverages existing USB debugging connection; no extra setup required; handles automatic reconnection
- Raw IPv4 packet processing — Fine-grained control over routing logic; allows custom TCP/UDP state machines; avoids system socket overhead
⚖️Trade-offs already made
- Java relay server instead of native binary (though Rust version exists)
- Why: Simplicity, rapid prot
- Consequence: undefined
🪤Traps & gotchas
The relay protocol (port 31415 by default) is custom and not publicly documented; you must match the host relay implementation (Java or Rust) to the Android app version. The VpnConfiguration CIDR parsing (CIDR.java) is strict and will throw InvalidCIDRException for malformed ranges—test carefully. adb reverse must be set up before the VPN connects, or tunneling will silently fail. The app requires the 'BIND_VPN_SERVICE' permission in AndroidManifest.xml; it will not work on devices with VPN permission restrictions. ProGuard minification is disabled in release builds (minifyEnabled false), which may increase APK size.
🏗️Architecture
💡Concepts to learn
- Reverse Tethering — The core problem Gnirehtet solves; understanding the difference between forward tethering (device shares its internet with a host) and reverse tethering (host shares its internet with a device) is essential to understanding the architecture.
- Android VPN Service (VpnService API) — Gnirehtet uses the Android VPN Service to intercept all traffic from the device at the network layer; this is the foundational Android API that makes reverse tethering possible without root.
- adb Reverse Tunneling — The transport mechanism that routes packets from the Android app to the relay server on the host; understanding how adb reverse creates bidirectional tunnels is critical for debugging connectivity issues.
- CIDR Notation (Classless Inter-Domain Routing) — Gnirehtet uses CIDR notation to specify which IP ranges the VPN should intercept; the CIDR.java parser and InvalidCIDRException show this is a critical part of VPN configuration.
- IPv4 Packet Structure — IPPacketOutputStream.java directly serializes and deserializes IPv4 packets; understanding packet headers (TTL, protocol type, checksums) is essential for contributing to the packet forwarding logic.
- TCP/UDP Multiplexing over a Single Tunnel — Gnirehtet relays both TCP and UDP flows over a single adb connection; the relay protocol (implemented in RelayTunnel.java and the host relay) must multiplex multiple connections with proper state management.
- ProGuard Code Obfuscation & Minification — The app/build.gradle disables minification (minifyEnabled false) in release builds; understanding why (likely to preserve debugging and compatibility) helps explain design choices and future optimization opportunities.
🔗Related repos
android/platform_system_core— Contains the adb source code and VPN service framework that Gnirehtet depends onshadowsocks/shadowsocks-android— Similar Android VPN-based tunneling architecture for routing traffic; good reference for VPN service patternsGenymobile/scrcpy— Same organization's complementary tool for Android device control over adb; shares adb infrastructure and may reuse relay patternsopenvpn/openvpn-android— Reference implementation of Android VPN services with packet I/O patterns; useful for understanding VpnService lifecycle and VpnConfiguration best practices
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for CIDR.java and Net.java networking utilities
The repo has minimal test coverage - only TestIPPacketOutputSteam.java exists. CIDR.java and Net.java are critical networking components that handle IP parsing and validation. Adding unit tests for edge cases (invalid CIDR notation, IPv4 boundary conditions, malformed packets) would prevent regressions and make the codebase more maintainable. This is especially important given the project is in maintenance mode.
- [ ] Create app/src/test/java/com/genymobile/gnirehtet/TestCIDR.java with tests for InvalidCIDRException cases, valid CIDR parsing, and boundary conditions
- [ ] Create app/src/test/java/com/genymobile/gnirehtet/TestNet.java with tests for IP address validation and parsing edge cases
- [ ] Run tests via ./gradlew test and verify coverage of both happy paths and error conditions
Add GitHub Actions CI workflow to detect build failures and deprecation warnings
The project has outdated Android dependencies (compileSdkVersion based on gradle.properties, espresso-core:2.2.2 from 2016, support-annotations deprecated). There's no CI preventing build breakage when Android tooling updates. Adding a GitHub Actions workflow to build both the Android app and relay-java modules would catch incompatibilities early, especially critical since maintenance is limited.
- [ ] Create .github/workflows/android-build.yml that runs ./gradlew build for app and relay-java modules on push/PR
- [ ] Add build step that captures deprecation warnings to identify outdated dependencies (espresso-core, testInstrumentationRunner)
- [ ] Document findings in DEVELOP.md about which dependencies need upgrading (e.g., migrate from support-test to androidx.test)
Extract and document relay protocol handler from AbstractConnection.java into a dedicated interface
relay-java/src/main/java/com/genymobile/gnirehtet/relay/AbstractConnection.java is referenced but appears to be a large handler managing the relay tunnel protocol. Given the README mentions both Java and Rust implementations exist, abstracting the relay protocol contract would clarify the interface that both implementations must conform to. This would make it easier for contributors to understand the protocol and implement the Rust version correctly.
- [ ] Review AbstractConnection.java and identify core relay protocol methods (likely socket handling, packet forwarding, connection lifecycle)
- [ ] Create relay-java/src/main/java/com/genymobile/gnirehtet/relay/RelayProtocolHandler.java interface documenting the contract
- [ ] Add comments documenting the protocol exchange flow in DEVELOP.md with reference to the new interface and the Rust implementation location
🌿Good first issues
- Add unit tests for CIDR.java parsing: The existing test file TestIPPacketOutputSteam.java suggests a test suite exists, but CIDR.java (which handles IP range parsing) has no visible tests. Add parametrized tests covering edge cases like invalid netmask values and boundary conditions.
- Document the relay protocol wire format: The code in RelayTunnel.java and IPPacketOutputStream.java implements a custom packet format, but no DEVELOP.md or code comments explain the protocol structure (packet headers, message types, etc.). Write a clear protocol specification document for contributors wanting to implement relay servers in other languages.
- Add support for IPv6 (partial): The README explicitly notes IPv6 is not supported yet. Start by adding Inet6Address parsing to CIDR.java and creating IPv6PacketOutputStream.java alongside the existing IPv4 version, then expose a checkbox in the UI (GnirehtetActivity.java) to enable/disable IPv6 tunneling (relay server support can follow).
⭐Top contributors
Click to expand
Top contributors
- @rom1v — 94 commits
- @jstevenson — 3 commits
- @SalvatoreT — 1 commits
- @In-line — 1 commits
- [@James O'Hara](https://github.com/James O'Hara) — 1 commits
📝Recent commits
Click to expand
Recent commits
1eb2e58— Update links to v2.5.1 in README (rom1v)67e1faf— Bump version to 2.5.1 (rom1v)848a3ca— Mention that gnirehtet is not actively maintained (rom1v)8eeed20— Cargo update (rom1v)bfb1faa— Remove useless borrowing (rom1v)2f69550— Prefix unused field by _ (rom1v)537b3d8— Fix trailing semicolon warning (rom1v)9ad9bc8— Read process output before waiting (rom1v)211db45— Fix datagram buffer remaining size (rom1v)6d2a4b9— Fix typo in comment (rom1v)
🔒Security observations
The Gnirehtet codebase has several moderate to high-severity security concerns, primarily around outdated dependencies, disabled code obfuscation in release builds, and low target API levels. The project uses espresso-core 2.2.2 and junit 4.
- High · Outdated Espresso Test Dependency —
app/build.gradle - androidTestImplementation. The project uses espresso-core:2.2.2, which is significantly outdated (released in 2015). This version contains known security vulnerabilities and lacks security patches. Modern versions should be used. Fix: Update to the latest stable version of espresso-core (e.g., 3.5.1 or newer). Run dependency vulnerability scanning tools to identify and patch known CVEs. - High · Outdated JUnit Dependency —
app/build.gradle - testImplementation. The project uses junit:junit:4.12, which is outdated (released in 2014). While primarily a test dependency, outdated versions may contain security issues and lack important bug fixes. Fix: Update to junit:junit:4.13.2 or the latest stable version. - High · Disabled ProGuard Minification in Release Build —
app/build.gradle - release buildType. The release build has minifyEnabled set to false, which means the APK contains unobfuscated code. This makes reverse engineering easier and exposes application logic, constants, and potentially sensitive information. Fix: Set minifyEnabled to true and configure appropriate ProGuard rules to obfuscate the code in release builds. Review and test the ProGuard configuration thoroughly to prevent application breakage. - Medium · Low Target SDK Version —
app/build.gradle - defaultConfig. The targetSdkVersion is set to 29 (Android 10, released in 2019). Google Play requires regular updates to target recent API levels. Using an older target SDK may miss security hardening features and cause compatibility issues. Fix: Update targetSdkVersion to 34 or the latest available stable Android API level. Ensure the application is tested for compatibility with newer Android versions and implement necessary changes for new permission models. - Medium · Low Minimum SDK Version without Security Justification —
app/build.gradle - defaultConfig. minSdkVersion is set to 21 (Android 5.0, released in 2014). This ancient API level lacks modern security features including proper TLS enforcement, secure random number generation hardening, and other security improvements. Fix: Increase minSdkVersion to at least 24 or higher to leverage modern security features. If legacy device support is required, document the business justification and implement additional security mitigations. - Medium · No Explicit Dependency Version Management —
app/build.gradle - dependencies. The build.gradle uses fileTree(dir: 'libs', include: ['*.jar']) which includes all JARs without version pinning. Unknown or outdated dependencies in the libs directory could contain vulnerabilities. Fix: Migrate all JAR dependencies to explicit Maven/Gradle dependencies with pinned versions. Remove the fileTree dependency inclusion. Use dependency scanning tools (e.g., OWASP Dependency-Check, Snyk) to identify vulnerabilities. - Low · Absence of ProGuard Exception Handling Documentation —
app/proguard-rules.pro. The proguard-rules.pro file is referenced but no content is provided in the analysis. Without proper ProGuard configuration, important classes may be improperly obfuscated or exposed. Fix: Review and maintain comprehensive ProGuard rules that protect sensitive classes while preserving necessary functionality. Document any exceptions with security justifications. - Low · No Dependency Vulnerability Scanning in Build —
build.gradle and app/build.gradle. The project does not appear to include dependency vulnerability scanning plugins (e.g., OWASP Dependency-Check, Snyk, or Gradle dependency-check plugin) in the build configuration. Fix: Integrate a dependency vulnerability scanner into the build pipeline. Examples: apply the OWASP dependency-check plugin or configure Gradle security scanning. Run it as part of CI/CD to catch vulnerabilities early.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.