Skip to content

Commit ae7c87c

Browse files
authored
feat: Hysteria2 Gecko obfs via bundled official-binary sidecar (#17)
* feat: Hysteria2 Gecko obfs via bundled official-binary sidecar Adds Hysteria2 'Gecko' obfuscation by bundling the official apernet/hysteria Android client as a sidecar, instead of re-platforming the sing-box core. - HysteriaBean: add hysteria2ObfsType (none/salamander/gecko) + gecko min/max packet size; kryo version 7->8 with backward-compatible deserialize (old HY2 profiles with a password derive as Salamander). obfuscation stays the password. - HysteriaFmt: canUseSingBox() returns false for HY2+Gecko (forces the sidecar); native HY2 outbound still emits Salamander; URI parse/export handle obfs type + gecko packet sizes; new buildHysteria2SidecarConfig() emits official hysteria client JSON with obfs.gecko + quic.sockopts.fdControlUnixSocket (reuses libcore's existing protect_path server) + loopback SOCKS5. - BoxInstance: init/launch branches run the bundled libhysteria2.so (hysteria client --config <file>) for HY2+Gecko; Salamander/HY1 unchanged. - buildScript/lib/hysteria2.sh: download official hysteria-android-* (v2.9.2) as app/executableSo/<abi>/libhysteria2.so (all 4 ABIs). - Executable: add libhysteria2.so to the kill-set. - UI: obfs type selector + gecko min/max packet-size fields (HY2 only). - Routing reuses the existing external-proxy machinery (pluginId hysteria2-plugin). No sing-box core change; starifly core + SSR/Snell/Juicity untouched. * fix: import buildHysteria2SidecarConfig in BoxInstance * ci: bundle Hysteria2 sidecar binary alongside Mieru Combine the per-workflow Mieru native-build job into a 'sidecars' job that builds both libmieru.so (cross-compiled) and libhysteria2.so (downloaded from apernet/hysteria v2.9.2) into app/executableSo, cached as one unit and restored in the build job with a verify-all-ABIs-and-both-binaries guard. Adds HYSTERIA_VERSION env. Applied across ci/build/preview/release workflows. * ci: verify Hysteria2 binary SHA256 against official hashes.txt * fix: clamp gecko packet sizes; clarify obfs password visibility - buildHysteria2SidecarConfig: clamp gecko min/max to hysteria's accepted bounds (1 <= min <= max <= 2048) to avoid emitting an invalid config. - Document why the obfs password field is shown for both Salamander and Gecko (Gecko requires a password too). * ci/docs: address review - least-privilege permissions, curl retry, doc accuracy - Add top-level permissions blocks: contents:read for ci/build/preview, contents:write for release (it publishes via ghr). Caps the default GITHUB_TOKEN scope for the jobs that run ./run sidecar scripts. - hysteria2.sh: add curl --retry/--max-time to the hashes.txt fetch and binary downloads. - Fix the design doc to describe the combined 'sidecars' CI job and the actual sidecar launch flags (--disable-update-check --config <file> --log-level <level> client).
1 parent 42094a3 commit ae7c87c

16 files changed

Lines changed: 514 additions & 81 deletions

File tree

.github/workflows/build.yml

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ on:
88
- 'nb4a.properties'
99
env:
1010
MIERU_VERSION: v3.34.0
11+
HYSTERIA_VERSION: v2.9.2
12+
permissions:
13+
contents: read
1114
jobs:
1215
libcore:
1316
name: Native Build (LibCore)
@@ -34,21 +37,21 @@ jobs:
3437
- name: Native Build
3538
if: steps.cache.outputs.cache-hit != 'true'
3639
run: ./run lib core
37-
mieru:
38-
name: Native Build (Mieru)
40+
sidecars:
41+
name: Native Build (Sidecars)
3942
runs-on: ubuntu-latest
4043
steps:
4144
- name: Checkout
4245
uses: actions/checkout@v5
43-
- name: Mieru Status
44-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
45-
- name: Mieru Cache
46+
- name: Sidecars Status
47+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
48+
- name: Sidecars Cache
4649
id: cache
4750
uses: actions/cache@v5
4851
with:
4952
path: |
5053
app/executableSo
51-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
54+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
5255
- name: Install Golang
5356
if: steps.cache.outputs.cache-hit != 'true'
5457
uses: actions/setup-go@v6
@@ -57,12 +60,15 @@ jobs:
5760
- name: Mieru Build
5861
if: steps.cache.outputs.cache-hit != 'true'
5962
run: ./run lib mieru
63+
- name: Hysteria2 Download
64+
if: steps.cache.outputs.cache-hit != 'true'
65+
run: ./run lib hysteria2
6066
build:
6167
name: Build OSS APK
6268
runs-on: ubuntu-latest
6369
needs:
6470
- libcore
65-
- mieru
71+
- sidecars
6672
steps:
6773
- name: Checkout
6874
uses: actions/checkout@v5
@@ -76,22 +82,24 @@ jobs:
7682
path: |
7783
app/libs/libcore.aar
7884
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
79-
- name: Mieru Status
80-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
81-
- name: Mieru Cache
82-
id: mieru-cache
85+
- name: Sidecars Status
86+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
87+
- name: Sidecars Cache
88+
id: sidecars-cache
8389
uses: actions/cache@v5
8490
with:
8591
path: |
8692
app/executableSo
87-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
88-
- name: Verify Mieru Artifacts
93+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
94+
- name: Verify Sidecar Artifacts
8995
run: |
9096
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
91-
if [ ! -f "app/executableSo/$abi/libmieru.so" ]; then
92-
echo "Error: missing app/executableSo/$abi/libmieru.so (Mieru cache miss)" >&2
93-
exit 1
94-
fi
97+
for so in libmieru.so libhysteria2.so; do
98+
if [ ! -f "app/executableSo/$abi/$so" ]; then
99+
echo "Error: missing app/executableSo/$abi/$so (sidecar cache miss)" >&2
100+
exit 1
101+
fi
102+
done
95103
done
96104
- name: Gradle cache
97105
uses: actions/cache@v5

.github/workflows/ci.yml

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ on:
88
pull_request:
99
env:
1010
MIERU_VERSION: v3.34.0
11+
HYSTERIA_VERSION: v2.9.2
12+
permissions:
13+
contents: read
1114
jobs:
1215
libcore:
1316
name: Native Build (LibCore)
@@ -39,21 +42,21 @@ jobs:
3942
- name: Native Build
4043
if: steps.cache.outputs.cache-hit != 'true'
4144
run: ./run lib core
42-
mieru:
43-
name: Native Build (Mieru)
45+
sidecars:
46+
name: Native Build (Sidecars)
4447
runs-on: ubuntu-latest
4548
steps:
4649
- name: Checkout
4750
uses: actions/checkout@v5
48-
- name: Mieru Status
49-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
50-
- name: Mieru Cache
51+
- name: Sidecars Status
52+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
53+
- name: Sidecars Cache
5154
id: cache
5255
uses: actions/cache@v5
5356
with:
5457
path: |
5558
app/executableSo
56-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru-ci
59+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars-ci
5760
- name: Install Golang
5861
if: steps.cache.outputs.cache-hit != 'true'
5962
uses: actions/setup-go@v6
@@ -62,12 +65,15 @@ jobs:
6265
- name: Mieru Build
6366
if: steps.cache.outputs.cache-hit != 'true'
6467
run: ./run lib mieru
68+
- name: Hysteria2 Download
69+
if: steps.cache.outputs.cache-hit != 'true'
70+
run: ./run lib hysteria2
6571
build:
6672
name: Build OSS APK
6773
runs-on: ubuntu-latest
6874
needs:
6975
- libcore
70-
- mieru
76+
- sidecars
7177
steps:
7278
- name: Checkout
7379
uses: actions/checkout@v5
@@ -86,22 +92,24 @@ jobs:
8692
path: |
8793
app/libs/libcore.aar
8894
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}-ci
89-
- name: Mieru Status
90-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
91-
- name: Mieru Cache
92-
id: mieru-cache
95+
- name: Sidecars Status
96+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
97+
- name: Sidecars Cache
98+
id: sidecars-cache
9399
uses: actions/cache@v5
94100
with:
95101
path: |
96102
app/executableSo
97-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru-ci
98-
- name: Verify Mieru Artifacts
103+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars-ci
104+
- name: Verify Sidecar Artifacts
99105
run: |
100106
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
101-
if [ ! -f "app/executableSo/$abi/libmieru.so" ]; then
102-
echo "Error: missing app/executableSo/$abi/libmieru.so (Mieru cache miss)" >&2
103-
exit 1
104-
fi
107+
for so in libmieru.so libhysteria2.so; do
108+
if [ ! -f "app/executableSo/$abi/$so" ]; then
109+
echo "Error: missing app/executableSo/$abi/$so (sidecar cache miss)" >&2
110+
exit 1
111+
fi
112+
done
105113
done
106114
- name: Gradle cache
107115
uses: actions/cache@v5

.github/workflows/preview.yml

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
inputs:
55
env:
66
MIERU_VERSION: v3.34.0
7+
HYSTERIA_VERSION: v2.9.2
8+
permissions:
9+
contents: read
710
jobs:
811
libcore:
912
name: Native Build (LibCore)
@@ -30,21 +33,21 @@ jobs:
3033
- name: Native Build
3134
if: steps.cache.outputs.cache-hit != 'true'
3235
run: ./run lib core
33-
mieru:
34-
name: Native Build (Mieru)
36+
sidecars:
37+
name: Native Build (Sidecars)
3538
runs-on: ubuntu-latest
3639
steps:
3740
- name: Checkout
3841
uses: actions/checkout@v5
39-
- name: Mieru Status
40-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
41-
- name: Mieru Cache
42+
- name: Sidecars Status
43+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
44+
- name: Sidecars Cache
4245
id: cache
4346
uses: actions/cache@v5
4447
with:
4548
path: |
4649
app/executableSo
47-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
50+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
4851
- name: Install Golang
4952
if: steps.cache.outputs.cache-hit != 'true'
5053
uses: actions/setup-go@v6
@@ -53,12 +56,15 @@ jobs:
5356
- name: Mieru Build
5457
if: steps.cache.outputs.cache-hit != 'true'
5558
run: ./run lib mieru
59+
- name: Hysteria2 Download
60+
if: steps.cache.outputs.cache-hit != 'true'
61+
run: ./run lib hysteria2
5662
build:
5763
name: Build OSS APK
5864
runs-on: ubuntu-latest
5965
needs:
6066
- libcore
61-
- mieru
67+
- sidecars
6268
steps:
6369
- name: Checkout
6470
uses: actions/checkout@v5
@@ -72,22 +78,24 @@ jobs:
7278
path: |
7379
app/libs/libcore.aar
7480
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
75-
- name: Mieru Status
76-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
77-
- name: Mieru Cache
78-
id: mieru-cache
81+
- name: Sidecars Status
82+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
83+
- name: Sidecars Cache
84+
id: sidecars-cache
7985
uses: actions/cache@v5
8086
with:
8187
path: |
8288
app/executableSo
83-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
84-
- name: Verify Mieru Artifacts
89+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
90+
- name: Verify Sidecar Artifacts
8591
run: |
8692
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
87-
if [ ! -f "app/executableSo/$abi/libmieru.so" ]; then
88-
echo "Error: missing app/executableSo/$abi/libmieru.so (Mieru cache miss)" >&2
89-
exit 1
90-
fi
93+
for so in libmieru.so libhysteria2.so; do
94+
if [ ! -f "app/executableSo/$abi/$so" ]; then
95+
echo "Error: missing app/executableSo/$abi/$so (sidecar cache miss)" >&2
96+
exit 1
97+
fi
98+
done
9199
done
92100
- name: Gradle cache
93101
uses: actions/cache@v5

.github/workflows/release.yml

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ on:
1010
required: false
1111
env:
1212
MIERU_VERSION: v3.34.0
13+
HYSTERIA_VERSION: v2.9.2
14+
permissions:
15+
contents: write
1316
jobs:
1417
libcore:
1518
name: Native Build (LibCore)
@@ -36,21 +39,21 @@ jobs:
3639
- name: Native Build
3740
if: steps.cache.outputs.cache-hit != 'true'
3841
run: ./run lib core
39-
mieru:
40-
name: Native Build (Mieru)
42+
sidecars:
43+
name: Native Build (Sidecars)
4144
runs-on: ubuntu-latest
4245
steps:
4346
- name: Checkout
4447
uses: actions/checkout@v5
45-
- name: Mieru Status
46-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
47-
- name: Mieru Cache
48+
- name: Sidecars Status
49+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
50+
- name: Sidecars Cache
4851
id: cache
4952
uses: actions/cache@v5
5053
with:
5154
path: |
5255
app/executableSo
53-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
56+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
5457
- name: Install Golang
5558
if: steps.cache.outputs.cache-hit != 'true'
5659
uses: actions/setup-go@v6
@@ -59,12 +62,15 @@ jobs:
5962
- name: Mieru Build
6063
if: steps.cache.outputs.cache-hit != 'true'
6164
run: ./run lib mieru
65+
- name: Hysteria2 Download
66+
if: steps.cache.outputs.cache-hit != 'true'
67+
run: ./run lib hysteria2
6268
build:
6369
name: Build OSS APK
6470
runs-on: ubuntu-latest
6571
needs:
6672
- libcore
67-
- mieru
73+
- sidecars
6874
steps:
6975
- name: Checkout
7076
uses: actions/checkout@v5
@@ -78,22 +84,24 @@ jobs:
7884
path: |
7985
app/libs/libcore.aar
8086
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
81-
- name: Mieru Status
82-
run: cat buildScript/lib/mieru.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > mieru_status
83-
- name: Mieru Cache
84-
id: mieru-cache
87+
- name: Sidecars Status
88+
run: cat buildScript/lib/mieru.sh buildScript/lib/hysteria2.sh buildScript/init/env.sh buildScript/init/env_ndk.sh | sha1sum > sidecars_status
89+
- name: Sidecars Cache
90+
id: sidecars-cache
8591
uses: actions/cache@v5
8692
with:
8793
path: |
8894
app/executableSo
89-
key: ${{ hashFiles('.github/workflows/*', 'mieru_status') }}-${{ env.MIERU_VERSION }}-mieru
90-
- name: Verify Mieru Artifacts
95+
key: ${{ hashFiles('.github/workflows/*', 'sidecars_status') }}-${{ env.MIERU_VERSION }}-${{ env.HYSTERIA_VERSION }}-sidecars
96+
- name: Verify Sidecar Artifacts
9197
run: |
9298
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
93-
if [ ! -f "app/executableSo/$abi/libmieru.so" ]; then
94-
echo "Error: missing app/executableSo/$abi/libmieru.so (Mieru cache miss)" >&2
95-
exit 1
96-
fi
99+
for so in libmieru.so libhysteria2.so; do
100+
if [ ! -f "app/executableSo/$abi/$so" ]; then
101+
echo "Error: missing app/executableSo/$abi/$so (sidecar cache miss)" >&2
102+
exit 1
103+
fi
104+
done
97105
done
98106
- name: Gradle cache
99107
uses: actions/cache@v5

app/src/main/java/io/nekohasekai/sagernet/Constants.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ object Key {
129129
const val SERVER_DISABLE_MTU_DISCOVERY = "serverDisableMtuDiscovery"
130130
const val SERVER_HOP_INTERVAL = "hopInterval"
131131

132+
const val SERVER_HY2_OBFS_TYPE = "serverHy2ObfsType"
133+
const val SERVER_HY2_GECKO_MIN_PACKET = "serverHy2GeckoMinPacket"
134+
const val SERVER_HY2_GECKO_MAX_PACKET = "serverHy2GeckoMaxPacket"
135+
132136
const val SERVER_PRIVATE_KEY = "serverPrivateKey"
133137
const val SERVER_INSECURE_CONCURRENCY = "serverInsecureConcurrency"
134138

app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import androidx.core.text.isDigitsOnly
1010

1111
object Executable {
1212
private val EXECUTABLES = setOf(
13-
"libtrojan.so", "libtrojan-go.so", "libnaive.so", "libtuic.so", "libhysteria.so", "libmieru.so"
13+
"libtrojan.so", "libtrojan-go.so", "libnaive.so", "libtuic.so", "libhysteria.so", "libmieru.so", "libhysteria2.so"
1414
)
1515

1616
fun killAll(alsoKillBg: Boolean = false) {

0 commit comments

Comments
 (0)