Skip to content

Commit 409b5e2

Browse files
committed
docs: README, SECURITY, MAINTENANCE, CHANGELOG
README: ABI table with checksums + GPG verification flow, Requirements section (minSdk 21, AGP 8.5.1+ for 16 KB page alignment), Gradle file- dependency integration snippet, three-method usage example, full foreground-service lifecycle skeleton, CLI argument reference table, troubleshooting matrix, license + bundled-byedpi disclosure, v0.x API maturity statement (low-level binding now; typed Kotlin DSL on the v0.2 roadmap), compatibility pinning policy, build-from-source instructions. SECURITY: private disclosure channel (GitHub advisory + PGP email), 72 h acknowledgment SLA, in/out-of-scope boundary, supported versions aligned with README's 0.x = unstable framing. MAINTENANCE: bus-factor mitigation runbook — pinned toolchain versions, source layout, release procedure, upstream-bump handling, GPG key rotation steps, reproducibility scope (same NDK revision + same submodule SHA → byte-identical .aar), keys.openpgp.org publishing, mirror policy. CHANGELOG: v0.1.0 entry with bundled byedpi pin and built-with stack; v0.2 roadmap.
1 parent fd30749 commit 409b5e2

4 files changed

Lines changed: 344 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Changelog
2+
3+
## v0.1.0 — 2026-05-16
4+
5+
Initial release. First public MIT-licensed standalone Android library for ByeDPI. Low-level argv-style JNI surface; typed Kotlin DSL deferred to v0.2.
6+
7+
- Artifact: single `libbyedpi-android-v0.1.0.aar` with arm64-v8a / armeabi-v7a / x86_64 bundled
8+
- Kotlin stub `io.github.oviron.libbyedpi.ByeDpi` included in the .aar — consumers do not need to declare native methods themselves
9+
- Three JNI methods: `nativeStart(args)`, `nativeStop()`, `nativeForceClose()` (escape hatch for when graceful stop fails)
10+
- Bundled ByeDPI: [hufrea/byedpi@ba53229](https://github.com/hufrea/byedpi/tree/ba532298de7b28cfe854aea83d061369d13ca290) (v0.17.3-38)
11+
- Build: AGP 8.12.2 library project, Kotlin 2.2.10, Android NDK 28.0.13004108, `-Wl,--build-id=sha1` for reproducibility
12+
- Each release artifact ships with SHA-256 checksum and GPG detached signature
13+
14+
### Wrapper hardening over the FlClash-internal predecessor
15+
16+
- Reset the proxy atomic guard if `calloc` fails — previously the guard stuck at 1 forever and every subsequent `nativeStart` returned -1
17+
- Validate every `strdup` return value before invoking byedpi's `main()` — previously a mid-loop OOM passed NULL slots into `getopt_long` and crashed
18+
- Sentinel `server_fd = -1` before `main()` runs; `nativeStop` now refuses to call `shutdown` on the uninitialised fd (which would have targeted stdin of the host process)
19+
- Silence `getopt` stderr noise via `opterr = 0`
20+
21+
## v0.2 (roadmap)
22+
23+
- Typed Kotlin API: `data class Config`, `sealed class Strategy`, `enum class Detector/AutoMode`, builder DSL covering byedpi's full ~40-option surface
24+
- Replaces the argv-style `nativeStart` with a typed entrypoint; `nativeStart` may be demoted to `internal` or kept as a power-user escape hatch

‎MAINTENANCE.md‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Maintenance procedure
2+
3+
Bus-factor mitigation document. If the primary maintainer (oviron) goes silent for 60+ days, fork this repo and continue using these instructions.
4+
5+
## Build environment
6+
7+
- **Android Gradle Plugin**: 8.12.2 (pinned in `build.gradle.kts`)
8+
- **Kotlin**: 2.2.10
9+
- **Gradle**: 8.13 (via wrapper, no system install needed)
10+
- **JDK**: 17+ (Temurin recommended)
11+
- **Android NDK**: 28.0.13004108 (pinned via `ndkVersion` in `build.gradle.kts`)
12+
- **CMake**: 3.22.1+
13+
- **Android SDK**: compileSdk 36, minSdk 21
14+
- **OS**: ubuntu-latest in CI; locally Linux/macOS both fine
15+
16+
## Source layout
17+
18+
- `src/main/cpp/native-lib.c` — our JNI wrapper (~100 LOC, MIT-licensed)
19+
- `src/main/cpp/main.h` — declarations from upstream byedpi
20+
- `src/main/cpp/CMakeLists.txt` — native build script
21+
- `src/main/cpp/byedpi/` — git submodule pinned at `hufrea/byedpi` SHA
22+
- `src/main/kotlin/io/github/oviron/libbyedpi/ByeDpi.kt` — Kotlin stub bundled into the .aar
23+
- `src/main/AndroidManifest.xml` — empty library manifest
24+
- `build.gradle.kts`, `settings.gradle.kts`, `gradle.properties` — AGP library project config
25+
26+
## How a release is made
27+
28+
Tags matching `v*` trigger the `release.yml` workflow:
29+
30+
1. Checks out source with `submodules: recursive`
31+
2. Builds the `.aar` via `./gradlew :assembleRelease`; AGP compiles `libbyedpi.so` for all three ABIs and bundles them with the Kotlin stub
32+
3. Renames the artifact to `libbyedpi-android-<tag>.aar` and generates SHA-256
33+
4. GPG-signs the `.aar` with maintainer key (detached, armored) — public key in `oviron-signing.pub.asc`
34+
5. Creates a GitHub Release with 3 files attached (`.aar` + `.aar.sha256` + `.aar.asc`)
35+
36+
Cut a release manually:
37+
38+
```sh
39+
# Verify main is green
40+
gh run list --limit 3
41+
# Update CHANGELOG.md (mandatory)
42+
# Bump version in this MAINTENANCE.md if conventions change
43+
git tag v0.X.Y
44+
git push origin v0.X.Y
45+
gh run watch
46+
```
47+
48+
## How upstream bumps are handled
49+
50+
When `hufrea/byedpi` releases a new tag:
51+
52+
1. Update the submodule:
53+
```sh
54+
cd src/cpp/byedpi
55+
git fetch origin
56+
git checkout <new-tag-or-commit>
57+
cd ../../..
58+
git add src/cpp/byedpi
59+
git commit -m "Bump byedpi to <version>"
60+
```
61+
2. Verify our wrapper still compiles. If `params`, `clear_params`, or `main` signatures changed upstream → patch `native-lib.c` accordingly. Bump our `0.X.Y` minor.
62+
3. Tag a new release.
63+
64+
ByeDPI changes its public C API rarely (~1-2 times per year). The wrapper is small (~94 LOC) — drift fixes are usually one or two lines.
65+
66+
## GPG signing key
67+
68+
The maintainer's GPG key is RSA 4096 `4A94DA488A4C5033`, fingerprint `1139C91B6525883E6783DCF04A94DA488A4C5033`. Public key committed at repo root as `oviron-signing.pub.asc`. Same key is registered on github.com/oviron for commit verification.
69+
70+
CI uses the same key via GitHub Actions secrets `GPG_PRIVATE_KEY` (armored secret key) and `GPG_PASSPHRASE`. To rotate:
71+
72+
1. Generate new GPG key, e.g. `gpg --full-generate-key`
73+
2. Export armored private: `gpg --armor --export-secret-keys <NEW_KEY_ID> | gh secret set GPG_PRIVATE_KEY --repo oviron/libbyedpi-android`
74+
3. Set new passphrase: `gh secret set GPG_PASSPHRASE --repo oviron/libbyedpi-android`
75+
4. Replace `oviron-signing.pub.asc` with the new public key
76+
5. Announce in next release notes with cross-signature from old key
77+
78+
After cutting any release, also push the maintainer public key to a keyserver so consumers can fetch independently of the repo:
79+
80+
```sh
81+
gpg --keyserver keys.openpgp.org --send-keys 4A94DA488A4C5033
82+
```
83+
84+
## Reproducibility
85+
86+
A given source tree + the same pinned AGP/Kotlin/NDK toolchain + the same byedpi submodule SHA produces byte-identical artifacts across runs. `-Wl,--build-id=sha1` in `src/main/cpp/CMakeLists.txt` makes the BuildID a content hash, AGP applies zip epoch normalization to the `.aar`, and LLD does not embed linker timestamps. Stronger reproducibility (across NDK patch revisions or different host platforms) is not claimed.
87+
88+
## Mirror
89+
90+
GitHub Releases is currently the only distribution channel. Mirror to a non-GitHub host (Codeberg / IPFS / other) is **deferred until concrete need**. If GitHub deletes this repo:
91+
92+
- All consumers downloading via fixed URL lose access
93+
- The git submodule at `hufrea/byedpi` is upstream and unaffected
94+
- Fork: any third party can clone this repo via local checkout, push to alternative host, continue releases — the build is reproducible per the section above
95+
96+
## Contact
97+
98+
- Primary maintainer: oviron (@oviron on GitHub)
99+
- Security disclosures: see `SECURITY.md`
100+
- Issues / questions: GitHub Issues

‎README.md‎

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# libbyedpi-android
2+
3+
Standalone Android library that embeds **ByeDPI** ([hufrea/byedpi](https://github.com/hufrea/byedpi)) as a `.so` per ABI. Suitable for any Android app that needs DPI-bypass functionality without bundling the full ByeByeDPI APK.
4+
5+
**First public MIT-licensed standalone Android byedpi binding.** The upstream byedpi project itself ships only Linux/Windows/macOS binaries — no Android library. Existing Android consumers ([ByeByeDPI](https://github.com/dovecoteescapee/byedpiandroid), [ByeByeDPI fork](https://github.com/romanvht/ByeByeDPI)) are full apps with their own JNI shim, not reusable libraries. This repo fills that gap.
6+
7+
## What's in each release
8+
9+
Each tag attaches three files to its GitHub Release:
10+
11+
| File | Description |
12+
|---|---|
13+
| `libbyedpi-android-v0.1.0.aar` | Android library, all three ABIs bundled (arm64-v8a + armeabi-v7a + x86_64) plus the Kotlin stub |
14+
| `libbyedpi-android-v0.1.0.aar.sha256` | SHA-256 checksum |
15+
| `libbyedpi-android-v0.1.0.aar.asc` | GPG detached signature |
16+
17+
Verify before consuming:
18+
19+
```sh
20+
sha256sum -c libbyedpi-android-v0.1.0.aar.sha256
21+
22+
# One-time: import maintainer public key from this repo
23+
gpg --import oviron-signing.pub.asc
24+
gpg --verify libbyedpi-android-v0.1.0.aar.asc libbyedpi-android-v0.1.0.aar
25+
# Expected: Good signature from "oviron <awdonkin@gmail.com>"
26+
```
27+
28+
Public key fingerprint: `1139 C91B 6525 883E 6783 DCF0 4A94 DA48 8A4C 5033`. Cross-check this fingerprint against the maintainer's GitHub profile (https://github.com/oviron) or keys.openpgp.org before trusting the key.
29+
30+
## Requirements
31+
32+
- **minSdk 21** (Android 5.0+) — the `.so` files are built with `-DANDROID_PLATFORM=android-21`. Older devices will fail at `System.loadLibrary` time.
33+
- **AGP 8.5.1+** — needed so the host APK is itself 16 KB page-aligned on Android 15+ devices. The `.so` files in this library are already built 16 KB-aligned by NDK 28.
34+
35+
## Integration
36+
37+
### Gradle (file dependency)
38+
39+
Download the `.aar` once during your build and reference it as a file dependency. Example:
40+
41+
```kotlin
42+
// app/build.gradle.kts
43+
val byedpiVersion = "0.1.0"
44+
val byedpiAar = layout.buildDirectory.file("libs/libbyedpi-android-v$byedpiVersion.aar")
45+
46+
val downloadByedpi = tasks.register("downloadByedpi") {
47+
inputs.property("byedpiVersion", byedpiVersion)
48+
outputs.file(byedpiAar)
49+
doLast {
50+
val target = byedpiAar.get().asFile
51+
target.parentFile.mkdirs()
52+
val url = "https://github.com/oviron/libbyedpi-android/releases/download/v$byedpiVersion/libbyedpi-android-v$byedpiVersion.aar"
53+
target.outputStream().use { out ->
54+
uri(url).toURL().openStream().use { it.copyTo(out) }
55+
}
56+
}
57+
}
58+
59+
dependencies {
60+
implementation(files(byedpiAar).builtBy(downloadByedpi))
61+
}
62+
```
63+
64+
The `.aar` already includes the Kotlin stub `io.github.oviron.libbyedpi.ByeDpi` with `System.loadLibrary("byedpi")` wired up — no additional source code on your side is needed.
65+
66+
### Usage from Kotlin
67+
68+
```kotlin
69+
import io.github.oviron.libbyedpi.ByeDpi
70+
71+
// On a background thread (the call blocks until the proxy stops):
72+
val exit = ByeDpi.nativeStart(arrayOf(
73+
"byedpi", "-i", "127.0.0.1", "-p", "1080", "--auto=torst",
74+
))
75+
76+
// From any thread, to signal graceful shutdown:
77+
ByeDpi.nativeStop()
78+
79+
// Escape hatch — when the proxy is stuck and graceful stop did not
80+
// unblock the running nativeStart, force-close the listening socket:
81+
ByeDpi.nativeForceClose()
82+
```
83+
84+
The argv-style API mirrors the byedpi CLI 1:1. A typed Kotlin `Config` + `sealed class Strategy` DSL is on the v0.2 roadmap; see the "API maturity" section below.
85+
86+
### End-to-end usage
87+
88+
A minimal lifecycle for a foreground service that hosts the proxy:
89+
90+
```kotlin
91+
class ByeDpiService : Service() {
92+
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
93+
private var job: Job? = null
94+
95+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
96+
startForeground(NOTIFICATION_ID, buildNotification())
97+
job = scope.launch {
98+
val exit = ByeDpi.nativeStart(arrayOf(
99+
"byedpi", "-i", "127.0.0.1", "-p", "1080", "--auto=torst",
100+
))
101+
if (exit != 0) Log.w(TAG, "byedpi exited with code $exit")
102+
}
103+
return START_STICKY
104+
}
105+
106+
override fun onDestroy() {
107+
ByeDpi.nativeStop()
108+
runBlocking { job?.join() }
109+
scope.cancel()
110+
super.onDestroy()
111+
}
112+
}
113+
```
114+
115+
Other components in your app then route traffic through the SOCKS5 endpoint at `127.0.0.1:1080` (for example by configuring a `Proxy` on `OkHttpClient.Builder` or by feeding it into a VPN service `tun` socket protector).
116+
117+
## CLI arguments
118+
119+
`nativeStart(args)` accepts the same CLI as upstream byedpi binary. First element is conventionally `"byedpi"` (argv[0] / program name). See [hufrea/byedpi README](https://github.com/hufrea/byedpi#readme) for the full option list.
120+
121+
Common arguments:
122+
123+
| Flag | Effect |
124+
|---|---|
125+
| `-i 127.0.0.1` | Bind address |
126+
| `-p 1080` | SOCKS5 listen port |
127+
| `--auto=torst` | Auto-detect best DPI-bypass strategy (default) |
128+
| `--no-domain` | Disable hostname check |
129+
| `--hosts FILE` | Whitelist mode: only bypass DPI for these hosts |
130+
| `-d` | Increase debug verbosity |
131+
132+
## Troubleshooting
133+
134+
| Symptom | Likely cause | Fix |
135+
|---|---|---|
136+
| `nativeStart` returns immediately with non-zero | Bind port already in use (another proxy app, or previous instance still listening) | Pick a different `-p` value, or call `nativeStop` and wait for the previous `nativeStart` to return before re-starting |
137+
| `nativeStart` returns -1 instantly | Another call to `nativeStart` is already in flight on the same process | Wait for the running call to return; only one proxy per process is supported |
138+
| ANR on main thread | `nativeStart` called from main thread | Always call from `Dispatchers.IO` or another background thread — `nativeStart` blocks until `nativeStop` is signalled |
139+
| Crash on `System.loadLibrary` | Missing ABI for device, or `minSdk < 21` | Verify the consumer app ships all three ABIs (`arm64-v8a`, `armeabi-v7a`, `x86_64`) and sets `minSdk = 21` |
140+
| Traffic does not bypass DPI | Wrong byedpi strategy for the operator | Try `--auto=torst` (default), then experiment with explicit flags from the [byedpi README](https://github.com/hufrea/byedpi#readme) |
141+
| App lacks network access while proxy runs | App is missing `<uses-permission android:name="android.permission.INTERNET"/>` | Add it to your manifest |
142+
143+
## License
144+
145+
This wrapper code (`src/cpp/native-lib.c`, `src/cpp/main.h`, `src/cpp/CMakeLists.txt`) is MIT-licensed — see `LICENSE`.
146+
147+
The bundled ByeDPI source at `src/cpp/byedpi/` is also MIT-licensed by hufrea — see `src/cpp/byedpi/LICENSE`.
148+
149+
Downstream apps can be **any** license, including proprietary. There is no copyleft obligation propagated through this library.
150+
151+
## API maturity
152+
153+
v0.1.0 is a **low-level JNI binding**. The `nativeStart` method takes a raw argv-style `Array<String>` matching byedpi's CLI surface 1:1. This is intentional — it keeps the wrapper thin, predictable, and trivially in sync with upstream byedpi. The cost is consumer-side boilerplate: you build the argv yourself from typed values.
154+
155+
A higher-level typed Kotlin API (data-class `Config`, `sealed class Strategy`, builder DSL, `.aar` packaging) is planned for **v0.2**. v0.1.0 is shipped first so that FlClash and other early adopters can integrate today and inform what the typed API should look like based on real usage.
156+
157+
## Compatibility note
158+
159+
While the library version is below 1.0, treat the **whole library as API-unstable**: bundled byedpi version, CLI flag semantics, and library packaging may change between minor releases. The two JNI method signatures (`nativeStart(Array<String>): Int`, `nativeStop(): Int`) are not expected to change during 0.x, but no guarantee is made. **Pin an exact version in your build** (`v0.1.0`, not `v0.1.+`) and re-read the CHANGELOG before bumping.
160+
161+
Semver guarantees begin at v1.0.
162+
163+
## Building from source
164+
165+
```sh
166+
git clone https://github.com/oviron/libbyedpi-android.git
167+
cd libbyedpi-android
168+
git submodule update --init --recursive
169+
170+
# Build one ABI (requires Android NDK 28+):
171+
cmake -B build-arm64 \
172+
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
173+
-DANDROID_ABI=arm64-v8a \
174+
-DANDROID_PLATFORM=android-21 \
175+
-DCMAKE_BUILD_TYPE=Release \
176+
-S src/cpp
177+
cmake --build build-arm64
178+
# Output: build-arm64/libbyedpi.so
179+
```
180+
181+
See `MAINTENANCE.md` for the full release procedure.
182+
183+
## Reporting issues
184+
185+
GitHub Issues for bugs and feature requests. For security disclosures see `SECURITY.md`.

‎SECURITY.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Security policy
2+
3+
## Supported versions
4+
5+
Only the latest minor release receives security updates. The whole library is API-unstable below v1.0, as described in the README "Compatibility note" section — security fixes may include breaking changes.
6+
7+
## Reporting a vulnerability
8+
9+
For suspected security issues in this library or its bundled upstream (hufrea/byedpi), use one of:
10+
11+
1. **GitHub private security advisory**: [Report a vulnerability](https://github.com/oviron/libbyedpi-android/security/advisories/new)
12+
2. **Email**: awdonkin@gmail.com (PGP-encrypted preferred — public key at repo root: `oviron-signing.pub.asc`)
13+
14+
**Please do not file public issues for security problems.**
15+
16+
## Response SLA
17+
18+
- Acknowledgment within 72 hours
19+
- Initial assessment within 7 days
20+
- Fix or mitigation timeline communicated within 14 days
21+
22+
## Scope
23+
24+
In scope:
25+
- Memory safety bugs in our wrapper (`src/cpp/native-lib.c`)
26+
- JNI shim issues (boundary handling, ref leaks, race conditions)
27+
- Build-pipeline vulnerabilities (compromised CI artifacts)
28+
29+
Out of scope (upstream byedpi responsibility — but please tell us anyway so we can pin to a fixed release):
30+
- Bugs in `src/cpp/byedpi/` itself
31+
- ByeDPI protocol-level issues
32+
33+
Out of scope (consumer responsibility):
34+
- Misuse of the library (e.g. exposing the SOCKS5 listener publicly)
35+
- Issues in apps that consume this library

0 commit comments

Comments
 (0)