Skip to content

feat(singbox): bump to v1.13.9 + fix VLESS xtls-rprx-vision flow for injected users#41

Open
abpestov-cpu wants to merge 3 commits into
marzneshin:masterfrom
abpestov-cpu:feat/singbox-vless-flow
Open

feat(singbox): bump to v1.13.9 + fix VLESS xtls-rprx-vision flow for injected users#41
abpestov-cpu wants to merge 3 commits into
marzneshin:masterfrom
abpestov-cpu:feat/singbox-vless-flow

Conversation

@abpestov-cpu
Copy link
Copy Markdown

Problem

Two distinct issues addressed by this branch:

1. VLESS xtls-rprx-vision flow not propagated to injected users

sing-box strictly validates that VLESS user flow matches inbound flow (sagernet/sing-vmess vless/service.go:68-69). When an inbound advertises flow: xtls-rprx-vision but marznode injects a VLESSUser entry without it, the handshake fails with "flow mismatch".

Previously, marznode's sing-box backend never set flow on users. This left REALITY+Vision inbounds unusable via the sing-box backend — only the xray backend was fixed (#30).

This PR mirrors that behaviour for sing-box.

2. sing-box 1.11.3 (from dawsh/marznode:latest) cannot serve modern Xray-core REALITY clients

The old overlay base image ships sing-box 1.11.3, which is incompatible with recent Xray-core REALITY clients — they receive [EOF] right after TLS handshake, and the server log shows REALITY: processed invalid connection. sing-box 1.13.x reworked the TLS stack and fixes this.

The official ghcr.io/sagernet/sing-box:v1.13.9 image is built without with_v2ray_api (needed for FetchUsersStats) and with_grpc (v2ray gRPC transports), so we build from source keeping full tag parity with dawsh/marznode, minus tags that 1.13.x absorbed:

  • with_reality_server → merged into with_utls
  • with_ech → migrated to Go stdlib

Changes

Code

  • _resolve_inbounds: detect VLESS+REALITY over raw TCP, set the inbound's flow to xtls-rprx-vision (mirrors xray _config.py).
  • append_user: when the resolved inbound carries a flow, pass it as kwarg to the account constructor so sing-box sees it on the injected VLESSUser.
  • _runner.start(): add sing-box config pre-check — fail fast with a clear error instead of silent crash-loop on invalid configs.

Build / Dockerfile

  • Multi-stage Dockerfile.patch: builder compiles sing-box v1.13.9 from source with full tag set, stage-1 layers patched Python on top of dawsh/marznode:latest.
  • Build flags learned the hard way:
    • -checklinkname=0 is required — sing-box uses //go:linkname into crypto/tls internals (handlePostHandshakeMessage) which Go 1.23+ rejects by default.
    • -X internal/godebug.defaultGODEBUG=multipathtcp=0 matches upstream release/LDFLAGS.
    • GOTOOLCHAIN=local pins to the image's Go (go.mod may request a newer patch version).

Consumer-side effect (BREAKING for 1.13.x)

sing-box 1.13.x drops the legacy WireGuard outbound format. It must be migrated to top-level endpoints:

"endpoints": [
  { "type": "wireguard", "tag": "warp", "address": [...], "peers": [...] }
]

Refs


Commits:

  • 0573b82 fix(sing-box): support VLESS xtls-rprx-vision flow for injected users
  • 65fc377 feat(singbox): bump to v1.13.9, build from source with required tags
  • 1943032 refactor(singbox): simplify VLESS flow injection before upstream PR

abpestov-cpu and others added 3 commits April 20, 2026 16:18
sing-box strictly validates that VLESS user flow matches inbound flow
(sagernet/sing-vmess vless/service.go:68-69). When an inbound advertises
"flow: xtls-rprx-vision" but the VLESSUser entry is injected without it,
the handshake fails with "flow mismatch".

Previously, marznode's sing-box backend never set flow on users. This
meant REALITY+Vision inbounds were unusable via marznode — only the
xray backend was fixed (PR marzneshin#30). This patch mirrors that behavior:

- In _resolve_inbounds(): detect VLESS+REALITY over raw TCP and set
  the inbound's flow to "xtls-rprx-vision" (mirroring xray _config.py).
- In append_user(): when the resolved inbound carries a flow, pass it
  as kwarg to the account constructor so sing-box sees the flow on the
  injected VLESSUser.

Also: add sing-box config pre-check in _runner.start() — fail fast with
a clear error instead of a silent crash-loop on invalid configs.

Overlay Dockerfile (Dockerfile.patch) provided for deploying patched
files on top of dawsh/marznode:latest — upstream Dockerfile pulls
jklolixxs/sing-box:latest which is no longer publicly available.

Refs: marzneshin#29 (report), marzneshin#30 (xray fix)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The old overlay layered dawsh/marznode:latest (sing-box 1.11.3) with
patched Python only. 1.11.3 is incompatible with recent Xray-core
REALITY clients — they get "[EOF]" right after the TLS handshake and
the server log shows "REALITY: processed invalid connection". The fix
is to move to v1.13.9, where the TLS stack has been reworked.

The official ghcr.io/sagernet/sing-box:v1.13.9 image ships without
`with_v2ray_api` (needed for FetchUsersStats) and `with_grpc` (v2ray
gRPC transports), so we build our own from source — keeping full tag
parity with the base dawsh image, minus tags that 1.13.x absorbed:
  - with_reality_server → merged into with_utls
  - with_ech            → migrated to Go stdlib

Build specifics learned the hard way:
  - -checklinkname=0 is required: sing-box uses `//go:linkname` into
    crypto/tls internals (`handlePostHandshakeMessage`) which Go 1.23+
    rejects without the flag.
  - -X internal/godebug.defaultGODEBUG=multipathtcp=0 matches upstream
    release/LDFLAGS.
  - GOTOOLCHAIN=local pins us to the image's Go (go.mod may request a
    newer patch version).

Consumer-side effect: 1.13.x drops the legacy WireGuard `outbound`
format — it must be migrated to the new top-level `endpoints: [{ type:
wireguard, address, peers: [...] }]`. Our Router/marzneshin configs
have been updated in a parallel commit.
- drop redundant isinstance(inbound.config, dict) guard: models.Inbound
  declares `config: dict` via pydantic, so the type is enforced at parse
  time and the isinstance branch was dead.
- fold the two nested ifs into a single walrus expression.
- extend the comment on settings["flow"]="xtls-rprx-vision" to name the
  affected clients (xray-core, v2rayN, NekoBox) and the concrete
  rejection path (sagernet/sing-vmess "flow mismatch"), plus the
  precedent marznode#30 (fix(xray): consider flow when transport is raw).

No functional change — just prep for an upstream PR.
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

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