Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ github.token }}
run: |
args=(--base "$BASE_SHA" --head "$HEAD_SHA" --github-output "$GITHUB_OUTPUT" --summary "$GITHUB_STEP_SUMMARY")
args=(--base "$BASE_SHA" --head "$HEAD_SHA" --run-attempt "$GITHUB_RUN_ATTEMPT" --github-output "$GITHUB_OUTPUT" --summary "$GITHUB_STEP_SUMMARY")
if [[ "$EVENT_NAME" == push ]]; then args+=(--push); else args+=(--history); fi
python3 scripts/ci_changes.py "${args[@]}"

Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ branch names, credentials, signing material, or other secrets.
Failed/skipped/cancelled jobs do not advance coverage. Fall back to the full PR diff when
history is unavailable; unknown paths and shared build/CI inputs enable all suites. Require `Change scope` and `Python tests and style`
alongside native/Android checks when this workflow is adopted.
- Every push to main runs all suites without diff/history filtering. The README CI badge is
pinned to main/push; selective checks apply to initial PR runs.
- A full CI rerun disables change filtering when Change scope executes on run attempt > 1,
so previously skipped suites run too. Failed-only reruns reuse scope unless that job also reruns.
- PR builds may publish debug and unsigned APK artifacts. They must never have access to release
signing material and must never produce or publish a signed release APK.
- Surface downloadable APK artifacts in the GitHub Actions job summary in addition to uploading
them through `actions/upload-artifact`.
- Prefer extracting UI-facing decisions into small production contracts and testing those with
deterministic JVM unit tests. Resource parity, navigation destination wiring, preference
serialization/defaults, formatting, and state transitions should not require a device.
- Compose interaction tests may run in `app/src/test` using Robolectric with a pinned SDK and
plain test Application. Inject platform operations; do not load Go JNI or real Keystore in
those tests. They run through the existing Fastlane Android checks without an emulator.
- Keep device-only tests out of required GitHub CI unless the project later adopts a dependable
device farm or controlled self-hosted runner. Do not reintroduce a software-emulated Android
fallback.
Expand Down Expand Up @@ -77,6 +84,9 @@ branch names, credentials, signing material, or other secrets.
- `ProxyVpnService` extends Android's standard `android.net.VpnService`. It owns VPN lifecycle,
creates the TUN interface, coordinates profiles/reconnects/status, and hands the TUN file
descriptor to the native networking layer. Native code performs the actual proxy forwarding.
- JNI calls use the generated gomobile types as a compile-time dependency. Native `Start` borrows
the JVM TUN descriptor only for the call, duplicates it internally, and receives the Android MTU
explicitly. Keep callback exceptions inside the JVM boundary and reject stale-session callbacks.
- Navigation uses a single activity/back stack. Keep route and settings-destination definitions in
shared production contracts whose completeness and uniqueness can be checked by JVM tests.

Expand Down
66 changes: 48 additions & 18 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,67 @@
# Privacy Policy

**Last updated: September 2, 2026**
**Last updated: September 8, 2026**

MegaProxy does not collect, transmit, store, or share personal information or user data.
MegaProxy is an Android VPN client for proxy servers you choose. It does not provide a proxy
service, require an account, or automatically send usage data or crash reports to the developer.
It contains no advertising, analytics SDKs or tracking services.

The application does not use analytics, telemetry, advertising services, or user tracking.
## Data used on your device

## Diagnostic logs
MegaProxy stores your connection profiles, credentials, settings and trusted SSH host keys to
connect to your servers. Passwords and imported private keys are encrypted with a key held by Android Keystore. Android backup and device transfer are disabled for app data.

The application may create diagnostic logs locally on the user's device for troubleshooting purposes.
For per-app routing, MegaProxy reads the applications visible to it on your device and stores
which applications you select. This information is used locally to configure routing, not
uploaded as an application inventory. Profile exports can include these routing selections.

These logs are not automatically transmitted to the developer or any third party.
Connection statistics and diagnostic/crash logs are processed locally to display connection
status and help troubleshoot failures. Logs contain operational events and error details;
filtering is designed to remove credentials and sensitive addresses. Review reports before sharing.

The user may choose to export or share a diagnostic log using the sharing functionality provided by the device. Sharing a log is entirely voluntary and is initiated explicitly by the user.
## Data sent to network services

The application is designed not to include personal information in diagnostic logs.
MegaProxy forwards selected application traffic to your configured proxy servers and sends the
authentication information needed to connect. SSH authentication does not transmit private keys. Proxy
operators can see connection metadata and destinations, and unencrypted application content.
Choose operators you trust.

## Data sharing
DNS providers receive the names being resolved. Before connecting, MegaProxy may resolve your
proxy's hostname directly through Cloudflare, Yandex, Google or Quad9; these resolvers can see
your source IP address and the proxy hostname. DNS queries through the tunnel use the configured
provider and permitted fallbacks.

MegaProxy does not automatically send user data to the developer or to third parties.
When you run a connection test, MegaProxy contacts a test website and external IP/country lookup
services through the proxy. They receive the exit IP and test requests to check connectivity and
identify the proxy's apparent country, not your GPS location. The current services are listed in
[Network privacy details](README.md#privacy-and-security). These providers handle requests under
their own policies; MegaProxy does not control their retention practices.

## Data retention and deletion
## Sharing and contacting support

The developer does not maintain a database or other server-side storage containing user data collected by the application.
Exporting or sharing a profile can disclose its settings and, if explicitly included, passwords
or private keys to the destination you choose. Copying diagnostics places them on the clipboard.

Locally stored application data and diagnostic logs can be removed by clearing the application's data or uninstalling the application.
If you choose to share a feedback or crash report, MegaProxy passes it to the application you
select. The prepared report contains device model, Android/app versions, a summary of connection settings and
a diagnostic log attachment. You control whether to send it. The receiving application handles
the shared copy under its own policies.

## Third-party services
If you email support, the developer receives your sender address, message and any attachments
you send for handling your request. Uninstalling MegaProxy does not delete that correspondence.

MegaProxy does not use third-party analytics, advertising, telemetry, or tracking services.
## Retention and deletion

## Contact
Profiles and settings remain locally until you change/delete them or clear app data. Diagnostic
logs rotate within a configurable size limit; you can clear them in the diagnostic-log screen.
Clearing app data or uninstalling removes local app files, including cached report attachments.
Exported files and copies shared with other applications must be deleted separately.

Support emails, including the sender address, message and attachments, are retained until the
reported problem is fixed, then deleted. You can contact the developer about your correspondence
at the address below.

If you have questions about this Privacy Policy, you can contact:
## Contact

megaproxy-feedback@hotmail.com
For privacy questions or requests about information you sent to support, contact the MegaProxy
developer at megaproxy-feedback@hotmail.com.
59 changes: 33 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MegaProxy

[![CI](https://github.com/andre487/AndroidMegaProxy/actions/workflows/ci.yml/badge.svg)](https://github.com/andre487/AndroidMegaProxy/actions/workflows/ci.yml)
[![CI](https://github.com/andre487/AndroidMegaProxy/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/andre487/AndroidMegaProxy/actions/workflows/ci.yml?query=branch%3Amain+event%3Apush)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Android 8+](https://img.shields.io/badge/Android-8.0%2B-3DDC84?logo=android&logoColor=white)](https://developer.android.com/about/versions/oreo)

Expand All @@ -22,8 +22,8 @@ statistics and diagnostic logs stay on the device unless you explicitly choose t

- **Private by design.** No account, ads, analytics, tracking identifiers, or background telemetry.
- **Your infrastructure.** Connect to your HTTPS or SSH servers, directly or through a jump server.
- **End-to-end application encryption.** HTTPS proxying uses CONNECT without intercepting or
decrypting application traffic.
- **Preserves application TLS.** HTTPS proxying uses CONNECT without intercepting or
decrypting application TLS; plain application protocols still need their own encryption.
- **Flexible routing.** Route the whole device or only selected applications through the VPN.
- **Resilient connections.** Profile failover, encrypted DNS fallback, SSH keepalives, and
connection health reporting help recover from network and server failures.
Expand Down Expand Up @@ -51,6 +51,7 @@ statistics and diagnostic logs stay on the device unless you explicitly choose t
- Android Always-on VPN integration and a persistent foreground-service notification.
- Automatic reconnect when the active profile or pending connection settings change.
- Approximate upload speed, download speed, proxy latency, and recent connection-error rate.
- Session traffic totals with selectable IEC/SI units, connection start time and elapsed duration.

### DNS and transport

Expand All @@ -63,7 +64,7 @@ statistics and diagnostic logs stay on the device unless you explicitly choose t

### Diagnostics

- A staged connection test for proxy setup, `example.com`, and the observed exit IP.
- A staged connection test for proxy setup, `example.com`, and the observed exit IP and country.
- Local, size-limited, rotating diagnostic and crash logs designed to omit credentials and traffic
content.
- On-device connection visibility checks and actionable connection warnings.
Expand All @@ -75,8 +76,10 @@ statistics and diagnostic logs stay on the device unless you explicitly choose t

MegaProxy does not operate a proxy service and does not send configuration or usage data to the
project author. Network traffic is sent only where required by the selected profile, destination,
and DNS configuration. The explicit connection test additionally contacts `example.com` and
`ifconfig.me`.
and DNS configuration. Proxy-hostname bootstrap may contact Cloudflare, Yandex, Google or Quad9
DoH resolvers directly before the tunnel exists. The explicit connection test contacts `example.com`
and uses fallback providers for exit IP (`ifconfig.me`, `api.ipify.org`, `icanhazip.com`) and country
(`ifconfig.co`, `ipapi.co`, `api.country.is`) through the proxy. See [PRIVACY.md](PRIVACY.md).

- HTTPS proxy certificates are checked against the Android trust store, including hostname and
validity. Normal CA certificate renewal does not require certificate pinning.
Expand Down Expand Up @@ -104,8 +107,8 @@ vendors may impose additional background-execution restrictions.
MegaProxy requires Android 8.0 (API 26) or newer. Download the latest signed build from
[GitHub Releases](https://github.com/andre487/AndroidMegaProxy/releases/latest), expand the
**Assets** section, and download the file ending in `universal.apk`. It is the recommended build:
it supports every architecture listed below and is also the artifact independently rebuilt and
verified for F-Droid distribution.
it supports every architecture listed below and is the artifact intended for reproducible
F-Droid verification.

**[Download the recommended universal APK](https://github.com/andre487/AndroidMegaProxy/releases/latest/download/mega-proxy-universal.apk)**

Expand Down Expand Up @@ -180,7 +183,7 @@ After installation:
1. Create or import a connection profile.
2. Choose global routing or select applications for split tunneling.
3. Review DNS and fingerprint settings if the defaults are not appropriate for your server.
4. Tap **Test** to validate the connection, then tap **Connect**.
4. Open the main-screen menu and choose **Test**, then tap **Connect**.
5. Optionally enable Always-on VPN in Android settings.

Server configurations and setup instructions are maintained separately in
Expand Down Expand Up @@ -233,16 +236,15 @@ exports support single HTTPS proxies only and omit chain profiles.
The command-line build does not require Android Studio. It requires:

- JDK 21
- Go 1.26 or newer
- `gomobile`
- Android SDK Platform 35
- Go 1.26.3 or newer (see `native/go.mod`)
- Network access to download the pinned `gomobile`/`gobind` tools during native builds
- Android SDK Platform 36 and Build Tools 36.0.0
- Android NDK 29.0.14206865

Example environment on macOS:

```shell
export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
export JAVA_HOME="$(/usr/libexec/java_home -v 21)"
export ANDROID_HOME="$HOME/Library/Android/sdk"
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/29.0.14206865"
export PATH="$JAVA_HOME/bin:$HOME/go/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$PATH"
Expand All @@ -269,22 +271,26 @@ adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n net.megaproxy487/.MainActivity
```

The project pins Gradle 8.9 through the checked-in wrapper. Use `./gradlew` rather than a globally
The project pins Gradle 8.11.1 through the checked-in wrapper. Use `./gradlew` rather than a globally
installed Gradle version. See [native/README.md](native/README.md) for Go data-plane details.

## Development workflow

English is the project language for source code, comments, documentation, commit messages, UI
copy, logs, and tooling.
English is the project language for source code, comments, commit messages, logs, and tooling.
User-visible UI strings are provided in English and Russian. Developer guides are maintained in
both languages; numbers and dates follow the system locale independently of the app language.

### Emulator

Create the API 35 Google APIs ARM64 emulator:
For optional local device testing on an ARM64 host, create the API 35 Google APIs ARM64 emulator
(the script installs the emulator and system image if missing):

```shell
./scripts/create-android-emulator.sh
```

Required CI and Robolectric Compose tests do not need an emulator.

The script installs missing components, configures host keyboard and mouse input, and can be run
more than once. It creates `MegaProxy_API_35` by default; set `MEGAPROXY_AVD_NAME` to override the
name.
Expand Down Expand Up @@ -318,6 +324,9 @@ logging.

### Signed release builds

Release scripts require `gomobile` on `PATH`. The `debug_artifact` and `android_checks` lanes
install the pinned native tools; run either once when preparing a fresh build environment.

Build optimized and signed APKs for `arm64-v8a`, `armeabi-v7a`, `x86_64`, and `x86`, plus the
universal APK used for reproducible F-Droid verification:

Expand All @@ -335,26 +344,24 @@ provided as `mega-proxy-native-debug-symbols.zip` for upload in Play Console.

Pushing a version tag runs the same Fastlane release lane in GitHub Actions, builds and verifies
every APK and the App Bundle, and attaches the artifacts to a GitHub Release. The tag must match
`versionName` exactly:

```shell
git tag v0.0.4
git push origin v0.0.4
```
`v` followed by the current `versionName` in `app/build.gradle.kts`. Create that tag with
`git tag` and push the specific tag with `git push origin`; do not reuse a historical release tag.

## Contributing

Bug reports and focused pull requests are welcome. Please avoid including proxy credentials,
private keys, destination history, or other personal data in issues and logs. Run both the Go and
Android unit-test suites before opening a pull request.
Android checks with `bundle exec fastlane android test` before opening a pull request. For Python
changes, also run `bundle exec fastlane android python_checks`; the `test` lane does not include Python.

## License

MegaProxy is released under the [MIT License](LICENSE).

### CI tools

CI selects checks from changes since each suite’s last successful ancestor check, with a full PR diff fallback. Use `python3 scripts/github_actions.py` to choose an open
PR and rerun all CI jobs or only failed jobs through GitHub CLI. Supports `--dry-run` and `--yes`/`-y`.
Every push to main runs all Android/Compose UI, Go and Python checks; the CI badge tracks these runs.
PR CI selects checks from changes since each suite’s last successful ancestor check, with a full PR diff fallback. Use `python3 scripts/github_actions.py` to choose an open
PR and rerun all CI jobs (including skipped checks) or only failed jobs through GitHub CLI. Supports `--dry-run` and `--yes`/`-y`.
See the [English](docs/en/fastlane.md) or [Russian](docs/ru/fastlane.md) reference for scope rules,
Python formatting/tests and launcher setup.
23 changes: 22 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ android {
}
}

testOptions {
unitTests.isIncludeAndroidResources = true
unitTests.all {
it.systemProperty("robolectric.dependency.repo.url", "https://repo.maven.apache.org/maven2")
it.jvmArgs(
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.net=ALL-UNNAMED",
"--add-opens=java.base/java.security=ALL-UNNAMED",
"--add-opens=java.base/java.text=ALL-UNNAMED",
"--add-opens=java.base/jdk.internal.access=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.font=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
)
}
}

buildFeatures {
compose = true
buildConfig = true
Expand Down Expand Up @@ -116,9 +134,12 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
implementation("androidx.navigation:navigation-compose:2.9.5")
testImplementation("org.robolectric:robolectric:4.16")
testImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")
testImplementation("junit:junit:4.13.2")
testImplementation("org.json:json:20250107")
runtimeOnly(files("libs/megaproxy.aar"))
implementation(files("libs/megaproxy.aar"))
}

// fwcd.kotlin does not understand Android Gradle Plugin variants reliably. Its language server
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SshHostKeyReviewActivity"
android:exported="false"
android:excludeFromRecents="true"
android:noHistory="true" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
Expand Down
Loading