fix(sub): preserve non-default scMinPostsIntervalMs and use per-inbound xmux in JSON subscriptions#5393
Merged
MHSanaei merged 8 commits intoJun 19, 2026
Conversation
added 2 commits
June 16, 2026 23:50
…ayload
The frontend wire normalizer unconditionally deleted scMinPostsIntervalMs
from inbound configs before persisting to the database, so JSON
subscriptions could never include it — even when the admin set a
non-default value like "50-150".
Only strip the xray-core default ("30") or empty values. The literal
"30" is a known DPI fingerprint (MHSanaei#5141) and must still be removed, but
custom tuning knobs must survive the round-trip so that buildXhttpExtra
and the JSON subscription generator can propagate them to clients.
Add tests for non-default preservation and empty-value stripping.
…ubscriptions The JSON subscription generator always used the global subJsonMux panel setting for outbound.Mux, even when the inbound carried per-inbound xmux inside xhttpSettings. This meant XHTTP outbounds that configured their own multiplexing via xmux still got the legacy mux.cool block injected — and the inbound's own xmux was silently ignored. Now getConfig() checks whether xmux is present in the inbound's xhttpSettings. When it is, the per-inbound xmux handles multiplexing and the legacy outbound.Mux is suppressed. When xmux is absent, the global subJsonMux is used as before. The mux selection is threaded through genVless, genVnext, genServer, and genHy as an explicit parameter so each protocol handler can decide independently. Add tests: - xmux present → outbound.Mux suppressed, xmux survives streamData() - no xmux → global subJsonMux used as outbound.Mux
|
btw, currently ui missing scMinPostsIntervalMs parameter at all |
added 4 commits
June 17, 2026 09:17
The inbound XHTTP form was missing scMinPostsIntervalMs, making it impossible for admins to configure this client-only tuning knob through the panel. The field already existed in the Zod schema and outbound form, and the wire normalizer (PR MHSanaei#5393) now preserves non-default values for subscription propagation. Add Form.Item for scMinPostsIntervalMs in the packet-up section of the inbound XHTTP form, after scMaxEachPostBytes. Use the existing translation key and a placeholder that shows the range format without endorsing the DPI-fingerprinted default (30). Update the Zod schema comment to clarify that scMinPostsIntervalMs is now preserved on inbound for subscriptions, while uplinkChunkSize and noGRPCHeader remain outbound-only. Add two integration tests: - Non-default value (50-150) preserved through formValuesToWirePayload - Default value (30) stripped through the full pipeline
When mode is 'auto', the server accepts all three XHTTP modes including packet-up. The packet-up-specific fields (scMaxBufferedPosts, scMaxEachPostBytes, scMinPostsIntervalMs) are therefore relevant and should be configurable. Change the conditional from 'packet-up' only to 'packet-up || auto' so admins using the default 'auto' mode can configure these fields.
…older - Show scMinPostsIntervalMs field when mode is 'auto' in addition to 'packet-up', since auto+TLS resolves to packet-up client-side - Change placeholder from '30' (DPI fingerprint) to 'e.g. 50-150' for consistency with inbound form
…edPosts behind packet-up/auto scMaxEachPostBytes is used by xray-core in every mode (both handlePacketUp and handleStreamUp validate it) and must be visible regardless of mode. scMaxBufferedPosts is only used by handlePacketUp, so it remains gated behind the packet-up/auto conditional. Also show scMinPostsIntervalMs for auto mode in outbound form and change placeholder from '30' (DPI fingerprint) to 'e.g. 50-150'. Update snapshot to reflect the new field order.
04c31d1 to
a218d8b
Compare
…ification - scMaxEachPostBytes: move behind packet-up/auto gate (server only checks it in handlePacketUp, not handleStreamUp) - scMaxBufferedPosts: show for packet-up, stream-up, and auto (server uses uploadQueue in both handlePacketUp and handleStreamUp) - scStreamUpServerSecs: already correct (stream-up only) Verified against xray-core hub.go and dialer.go source code.
This was referenced Jun 17, 2026
|
Thx |
# Conflicts: # internal/sub/json_service.go # internal/sub/json_service_test.go # internal/sub/mutation_audit_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
JSON subscriptions generated by 3x-ui had two bugs that prevented XHTTP client tuning knobs from reaching subscribers, and the inbound/outbound forms had mode-conditional visibility issues that hid important fields.
1.
scMinPostsIntervalMsnever reaches the DBThe frontend wire normalizer (
normalizeXhttpForWire) unconditionally deletedscMinPostsIntervalMsfrom inbound configs before persisting to the database:This meant that even when an admin set a custom value like
"50-150", it was silently dropped on save. JSON subscriptions could never include it because it was never stored.The xray-core default
"30"is a known DPI fingerprint (#5141) and must still be stripped, but non-default tuning values must survive the round-trip so thatbuildXhttpExtraand the JSON subscription generator can propagate them to clients.2. JSON subscriptions ignore per-inbound
xmuxThe JSON subscription generator always used the global
subJsonMuxpanel setting foroutbound.Mux, even when the inbound carried per-inboundxmuxinsidexhttpSettings. This meant:xmuxstill got the legacymux.coolblock injectedxmuxwas silently ignored in JSON subscriptionsvless://) already read per-inboundxmuxviabuildXhttpExtra— JSON subscriptions were the only format that didn't3. Inbound form hides server-side fields behind mode gates
scMaxEachPostBytesis a server-side field used by xray-core in every mode (bothhandlePacketUpandhandleStreamUpvalidate it). It was hidden behind thepacket-up/autoconditional, making it invisible instream-upandstream-onemodes.scMaxBufferedPostsis only used byhandlePacketUpand correctly belongs behind thepacket-up/autogate.scMinPostsIntervalMsis client-only and packet-up/auto-specific — it was missing from the inbound form entirely.4. Outbound form hides
scMinPostsIntervalMsformode: "auto"Because xray-core resolves client
mode: "auto"with TLS topacket-up, thescMinPostsIntervalMsfield must be visible forautomode too — otherwise users cannot configure it for the most common TLS+XHTTP setup.Commits
Commit 1: Preserve non-default
scMinPostsIntervalMsin inbound wire payloadFile:
frontend/src/lib/xray/stream-wire-normalize.tsInstead of unconditionally deleting
scMinPostsIntervalMs, only strip the xray-core default ("30") and empty values. Custom tuning values like"50-150"now survive the round-trip.Commit 2: Use per-inbound
xmuxinstead of globalsubJsonMuxin JSON subscriptionsFile:
internal/sub/json_service.gogetConfig()now checks whetherxmuxis present in the inbound'sxhttpSettings. When it is, the per-inboundxmuxhandles multiplexing and the legacyoutbound.Muxis suppressed. Whenxmuxis absent, the globalsubJsonMuxis used as before (backward compatible).Commit 3: Add
scMinPostsIntervalMsto inbound XHTTP formFile:
frontend/src/pages/inbounds/form/transport/xhttp.tsxAdds the missing UI field for
scMinPostsIntervalMsin the inbound form's packet-up section, so admins can set anti-DPI intervals directly from the panel.Commit 4: Show packet-up fields for
automode in inbound XHTTP formFile:
frontend/src/pages/inbounds/form/transport/xhttp.tsxBecause xray-core resolves client
mode: "auto"with TLS topacket-up, the packet-up fields (includingscMinPostsIntervalMs) are now also shown when the inbound mode isauto.Commit 5: Show
scMinPostsIntervalMsforautomode in outbound form, update placeholderFile:
frontend/src/pages/xray/outbounds/transport/xhttp.tsxSame consistency fix for the outbound form:
scMinPostsIntervalMsis now visible formode: "auto". Placeholder changed from"30"(DPI fingerprint) to"e.g. 50-150".Commit 6: Show
scMaxEachPostBytesfor all modes in inbound formFile:
frontend/src/pages/inbounds/form/transport/xhttp.tsxscMaxEachPostBytesis used by xray-core in every mode (bothhandlePacketUpandhandleStreamUpvalidate it). It is now always visible regardless of mode.scMaxBufferedPostsremains gated behindpacket-up/autosince it is only used byhandlePacketUp.Commit 7: Update XhttpForm snapshot
File:
frontend/src/test/__snapshots__/inbound-form-blocks.test.tsx.snapSnapshot updated to reflect
scMaxEachPostBytesbeing unconditionally visible.Why these changes matter
scMinPostsIntervalMs: 30is a known fingerprint. Allowing users to set ranges like"50-150"and propagating them to subscriptions reduces fingerprinting.mux.cooland XHTTPxmuxare different multiplexers. Using the globalsubJsonMux(mux.cool) for XHTTP outbounds caused many short-lived TCP connections, which is also fingerprintable. Per-inboundxmuxfixes this.stream-upin our deployment template becauseautowith TLS resolves topacket-upin xray-core, contrary to what the public docs claim.Backward Compatibility
xmux, globalsubJsonMuxsetoutbound.Muxfrom globalxmux, globalsubJsonMuxsetoutbound.Muxfrom global (double-mux)outbound.Muxsuppressed,xmuxinxhttpSettingsscMinPostsIntervalMs = "30"(default)scMinPostsIntervalMs = "50-150"(custom)scMinPostsIntervalMs = ""(empty)Testing
Deployed to a test server and verified:
scMinPostsIntervalMs: "50-150"appears in JSON subscriptionxmux: {"maxConcurrency": "16-32", "hMaxRequestTimes": "600-900"}appears insidexhttpSettingsoutbound.Muxis suppressed whenxmuxis presentxmux, globalsubJsonMuxis used asoutbound.MuxscMaxEachPostBytesis visible in all modes in the inbound formscMaxBufferedPostsis visible only forpacket-upandautomodesscMinPostsIntervalMsis visible forpacket-upandautomodes in both inbound and outbound forms