Skip to content

feat: native Hysteria2 Gecko obfs (drop the sidecar)#26

Merged
hawkff merged 4 commits into
mainfrom
feature/hy2-gecko-native
Jun 18, 2026
Merged

feat: native Hysteria2 Gecko obfs (drop the sidecar)#26
hawkff merged 4 commits into
mainfrom
feature/hy2-gecko-native

Conversation

@hawkff

@hawkff hawkff commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

Hysteria2 Gecko obfuscation now runs through the native sing-box core instead of the bundled apernet/hysteria binary sidecar — fewer moving parts, no subprocess/SOCKS hop, and direct sing-box routing.

Changes

  • HysteriaFmt.kt: emit obfs.type=gecko with min_packet_size/max_packet_size for Gecko profiles; canUseSingBox() no longer forces Gecko onto the external sidecar path.
  • SingBoxOptions.Hysteria2Obfs: add min_packet_size / max_packet_size (flattened into the obfs object by the core's serializer).
  • buildScript/lib/core/get_source_env.sh: point COMMIT_SING_BOX at the gecko-enabled sing-box (d003dcc).
  • libcore/go.mod: pin sing-quic to the hawkff fork gecko backport.

Native dependency chain

Verification

  • libcore + OSS APK build clean.
  • Verified end-to-end on Android: native Gecko Hysteria2 outbound (no sidecar) connects; egress confirmed.

Salamander and no-obfs Hysteria2 behavior is unchanged.

Greptile Summary

This PR migrates Hysteria2 Gecko obfuscation from an external sidecar binary (apernet/hysteria + SOCKS5 hop) to the native sing-box core, by wiring in the hawkff/sing-quic gecko backport. The result is one fewer subprocess, no loopback SOCKS hop, and direct sing-box routing for Gecko profiles.

  • HysteriaFmt.kt: canUseSingBox() no longer short-circuits on Gecko; buildSingBoxOutboundHysteriaBean now emits a type = \"gecko\" obfs block with clamped min_packet_size/max_packet_size (1–2048, min ≤ max).
  • BoxInstance.kt: The Gecko sidecar launch path and its buildHysteria2SidecarConfig call are removed; the external-path HysteriaBean handler is now Hysteria v1–only, which is correct since v2 no longer requires the sidecar.
  • go.mod/go.sum: sagernet/sing-quic replaced with the hawkff/sing-quic gecko fork; COMMIT_SING_BOX updated to the matching gecko-enabled core.

Confidence Score: 5/5

Safe to merge — the sidecar removal is clean, the native gecko path is correctly wired, and bounds clamping is in place.

The change is well-scoped: canUseSingBox() loses one guard, the sing-box outbound builder gains a gecko branch with correct min/max clamping (1–2048, min ≤ max), and the sidecar launch code is deleted entirely. Salamander and no-obfs Hysteria2 paths are untouched. The go.mod replace directive and updated COMMIT_SING_BOX are consistent with the claimed dependency chain. No logic errors found in the changed paths.

No files require special attention.

Important Files Changed

Filename Overview
app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaFmt.kt Core change: canUseSingBox() removes the Gecko sidecar gate; buildSingBoxOutboundHysteriaBean emits native gecko obfs with properly clamped packet-size bounds; KDoc on the retained sidecar builder updated to reflect it is no longer called for Gecko.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt Gecko sidecar launch block and buildHysteria2SidecarConfig call removed; the remaining HysteriaBean external-path branch now correctly handles Hysteria v1 only, consistent with canUseSingBox() semantics.
app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java Adds nullable Integer min_packet_size / max_packet_size to Hysteria2Obfs; null fields are omitted by the badjson serializer, keeping Salamander and no-obfs paths unaffected.
buildScript/lib/core/get_source_env.sh COMMIT_SING_BOX bumped to the gecko-enabled hawkff/sing-box commit; straightforward dependency pin update.
libcore/go.mod Adds replace directive pointing sagernet/sing-quic at hawkff/sing-quic gecko fork; consolidates go/toolchain directives to a single go 1.26.4 line, raising the minimum toolchain requirement.
libcore/go.sum Adds hawkff/sing-quic checksum entries and removes the upstream sagernet/sing-quic v0.5.2 entries; consistent with the go.mod replace directive.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App as Android App
    participant SB as sing-box core
    participant SQ as sing-quic (hawkff fork)
    participant Srv as Hysteria2 Server

    Note over App,Srv: Before PR - Gecko via sidecar
    App->>App: canUseSingBox() returns false (Gecko)
    App->>App: launch hysteria2-plugin binary
    App->>App: sing-box socks outbound to 127.0.0.1:port
    App-->>Srv: SOCKS5 hop Gecko QUIC

    Note over App,Srv: After PR - Gecko native
    App->>App: canUseSingBox() returns true (Gecko)
    App->>SB: "buildSingBoxOutboundHysteriaBean() obfs type=gecko"
    SB->>SQ: Gecko obfuscator (backport)
    SQ-->>Srv: Direct Gecko QUIC (no subprocess)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App as Android App
    participant SB as sing-box core
    participant SQ as sing-quic (hawkff fork)
    participant Srv as Hysteria2 Server

    Note over App,Srv: Before PR - Gecko via sidecar
    App->>App: canUseSingBox() returns false (Gecko)
    App->>App: launch hysteria2-plugin binary
    App->>App: sing-box socks outbound to 127.0.0.1:port
    App-->>Srv: SOCKS5 hop Gecko QUIC

    Note over App,Srv: After PR - Gecko native
    App->>App: canUseSingBox() returns true (Gecko)
    App->>SB: "buildSingBoxOutboundHysteriaBean() obfs type=gecko"
    SB->>SQ: Gecko obfuscator (backport)
    SQ-->>Srv: Direct Gecko QUIC (no subprocess)
Loading

Comments Outside Diff (1)

  1. app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaFmt.kt, line 413-414 (link)

    P2 The KDoc for buildHysteria2SidecarConfig still states that Gecko obfs is a reason the function is called — "used for Hysteria2 profiles with Gecko obfs (which the native sing-box core can't do)" — which is now the opposite of the truth. This dead-code comment will mislead anyone tracing the sidecar code path later.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (5): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Hysteria2 Gecko obfuscation now runs through the native sing-box core (via the
hawkff/sing-quic gecko backport) instead of the bundled apernet/hysteria binary
sidecar.

- HysteriaFmt: emit obfs.type=gecko + min/max packet size for Gecko profiles;
  canUseSingBox() no longer forces Gecko onto the external sidecar path.
- SingBoxOptions.Hysteria2Obfs: add min_packet_size / max_packet_size (flattened
  into the obfs object by the core's serializer).
- buildScript: point COMMIT_SING_BOX at the gecko-enabled sing-box (d003dcc).
- libcore go.mod: pin sing-quic to the hawkff fork gecko backport.

Verified end-to-end on Android: native Gecko Hysteria2 outbound (no sidecar)
connects, egress confirmed.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes the Hysteria2 Gecko sidecar dependency by enabling native sing-box support. Hysteria2Obfs gains packet-size fields, canUseSingBox() removes Gecko exclusion, the outbound builder conditionally generates Gecko or Salamander obfs, and BoxInstance consolidates all Hysteria instances to a single hysteria-plugin path. Build dependencies are also updated: sing-box commit hash, Go version directive, and sing-quic replace directive.

Changes

Native Gecko Obfs for Hysteria2

Layer / File(s) Summary
Hysteria2Obfs schema and outbound config generation
app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java, app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaFmt.kt
Hysteria2Obfs gains min_packet_size and max_packet_size integer fields. canUseSingBox() removes the Gecko-specific exclusion for hysteria2. buildSingBoxOutboundHysteriaBean() branches on OBFS_GECKO to emit type="gecko" with password and clamped packet sizes (min in-range, max clamped with min as lower reference), falling back to type="salamander" otherwise. Sidecar comment updated to reflect native Gecko support in sing-box core.
BoxInstance: remove Hysteria2 Gecko sidecar, consolidate to hysteria-plugin
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
Removes the buildHysteria2SidecarConfig import. In init(), removes the protocolVersion==2 && OBFS_GECKO conditional; all HysteriaBean instances now initialize hysteria-plugin with Hysteria v1 config and CA-file tracking. In launch(), removes the dedicated Hysteria2 Gecko sidecar branch (hysteria2-plugin with hysteria2_*.json); all instances now use a single hysteria-plugin path with hysteria_*.json, --no-check flag, dynamic log level, and su -c wrapping for FAKETCP.

Dependency and Build Updates

Layer / File(s) Summary
sing-box commit, Go version, and sing-quic replace directive
buildScript/lib/core/get_source_env.sh, libcore/go.mod
COMMIT_SING_BOX is updated to a new hash, the go directive is bumped from 1.24.7 to 1.26.4, and an active replace directive is added redirecting github.com/sagernet/sing-quic to github.com/hawkff/sing-quic v0.5.3-0.20260618035506-f2f558c5aaba.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • hawkff/NekoBoxForAndroid#17: Introduced the sidecar-based Gecko fallback in HysteriaFmt and BoxInstance that this PR removes in favor of native sing-box core support.

Poem

🐇 The sidecar path grows quiet now—Gecko hops home to core,
No more forking off to plugins; sing-box handles it all!
Packet sizes clamped and ordered, outbound flows so clean,
One hysteria-plugin to rule them, the finest we've seen.
The Go compiler whispers thanks, dependencies align—
This rabbit celebrates the merger: Gecko native, by design! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely and accurately summarizes the main change: enabling native Hysteria2 Gecko obfuscation support and removing the external sidecar dependency.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is directly and clearly related to the changeset, detailing the migration of Hysteria2 Gecko obfuscation from an external sidecar to native sing-box core.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@hawkff

hawkff commented Jun 18, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

hawkff added 2 commits June 18, 2026 07:09
The native sing-box Gecko path passed user-supplied min/max packet sizes
directly; an inverted or out-of-range pair (e.g. min=1500, max=800) would be
rejected by the core at connect time. Clamp to 1..2048 and enforce min <= max,
matching the sidecar config builder's existing bounds. (Greptile P1)
Since Gecko now runs natively (canUseSingBox() == true for all UDP Hysteria2),
the OBFS_GECKO sidecar branches in BoxInstance init/launch are unreachable.
Remove them (the external path is Hysteria v1 only) and drop the now-unused
buildHysteria2SidecarConfig import. Correct the buildHysteria2SidecarConfig
KDoc which still claimed Gecko needs the sidecar. (Greptile)
@hawkff

hawkff commented Jun 18, 2026

Copy link
Copy Markdown
Owner Author

Addressed both items from the latest review in a9e4943:

  • Dead Gecko sidecar branches: removed the unreachable OBFS_GECKO branches in BoxInstance init/launch (since canUseSingBox() is now true for all UDP Hysteria2, the external path is Hysteria v1 only) and dropped the now-unused buildHysteria2SidecarConfig import.
  • Stale KDoc (P2): corrected buildHysteria2SidecarConfig's doc which still claimed Gecko needs the sidecar.

# Conflicts:
#	app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
@hawkff hawkff merged commit 972db68 into main Jun 18, 2026
5 checks passed
@hawkff hawkff deleted the feature/hy2-gecko-native branch June 23, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant