mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-06 04:44:21 +00:00
e11e587c6062318be151572006fa2ba31b571dc6
82 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f90e4a6962 |
fix(panel): use the hosting node address for WireGuard client configs (#5679)
* fix(panel): use the hosting node address for WireGuard client configs The clients page rendered a node-managed WireGuard inbound's config with the master panel's host in Endpoint instead of the hosting node's address, so the copied/QR config pointed at the wrong server. The subscription path already resolves this via resolveInboundAddress; the UI generator did not. Expose the share-host resolution inputs (node address, listen, share-address strategy/address) on InboundOption and route buildWireguardClientConfig through the same canonical resolver the inbounds-page share links use, extracted as resolveShareHost. This also brings local inbounds with a shareable listen or a listen/custom share strategy into parity with the subscription Endpoint; the common listen=0.0.0.0 case still falls back to the panel host. * fix(frontend): keep a raw fallback host and refresh node-fed inbound options Code review of the WireGuard node-endpoint change surfaced two gaps. resolveShareHost normalized its last-resort fallbackHostname, so a panel reached via a hostname the share-host grammar rejects (underscore label, trailing-dot FQDN) emitted a broken 'Endpoint = :51820'; the fallback now stays verbatim when normalization empties it. Node mutations only invalidated the nodes query, leaving the staleTime-Infinity inbound options cache serving an edited node address until the sync job broadcast (never, for disabled/offline nodes); they now invalidate the options key too. Also folds the ShareHostFields projections into direct structural passes, elides the default node shareAddrStrategy so omitempty drops it, and replaces the nullable node-address scan with COALESCE. --------- Co-authored-by: STRENCH0 <17428017+STRENCH0@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
258d8b7344 |
feat(frontend): add targetStrategy field to the outbound editor
Xray-core added a top-level targetStrategy to OutboundObject that controls how the destination domain is resolved before dialing (AsIs/UseIP*/ForceIP*, any protocol). The panel neither offered a control for it nor preserved the key across the modal's JSON round trip, so hand-written values were silently dropped on save. The form now carries targetStrategy next to sendThrough as a select of the 11 canonical values; the adapter normalizes wire values to canonical case (the core matches case-insensitively) and omits the key when unset. Freedom settings additionally read the new settings-level targetStrategy with domainStrategy as fallback, mirroring the core, while still emitting the legacy domainStrategy key so configs keep working on older cores. |
||
|
|
97e2c9e7ba |
fix(web): sync the VLESS generate-key dropdown with the encryption field
The auth-kind dropdown in the VLESS "Generate Key" block was hardcoded to x25519 on mount, while the "Already selected" text next to it was derived independently from settings.encryption. Editing an inbound whose encryption uses another kind (e.g. ML-KEM-768) showed a mismatched dropdown, and clicking Generate without noticing would produce a keypair of the wrong kind for the inbound. Extract the encryption-string parsing into a shared pure helper (lib/xray/vless-encryption), use it both for the selected-auth label and to initialize/sync the dropdown, so the two can no longer diverge. When the encryption is none or unparseable the dropdown keeps its x25519 default. Closes #5744 |
||
|
|
c8ef1b1f68 |
feat(reality): derive a stable per-client spiderX for shared links
The inbound's spiderX now acts as a per-client seed: exports emit sha256(seed|subKey) truncated to a 15-hex "/path", so a client's spx no longer changes on every subscription fetch (#5718) while different clients stop sharing one fingerprintable value. The form gains a regenerate button that rotates every client's path at once. The frontend link builders derive through the same function (lib/xray/spider-x.ts, @noble/hashes) keyed on subId-then-email like the Go subKey, so panel QR/copy links and subscription output agree — cross-language vector tests lock both sides byte-for-byte. streamData now tolerates malformed stored stream settings (unparseable JSON, null tls/reality settings) instead of panicking the subscription request. |
||
|
|
539bcc897c |
fix(inbounds): apply the legacy xhttp session-key migration when editing
rawInboundToFormValues injected the stored xhttpSettings blob into the form store without running it through XHttpStreamSettingsSchema, so the sessionPlacement/sessionKey -> sessionIDPlacement/sessionIDKey rename from xray-core v26.6.22 (and the v3.4.0 field defaults) never applied on the edit path. Inbounds saved before the rename opened with blank session fields, and the stale keys could ride back on save even though the core no longer reads them. Parse the sub-object through the schema on load, and lift any stale legacy keys in normalizeXhttpForWire as a backstop. Closes #5621 |
||
|
|
8332ba67ae |
chore(deps): bump antd to 6.5 and migrate deprecated component props
Upgrade frontend deps (antd 6.4.5 -> 6.5.0, Ant Design icons, TanStack Query, i18next, eslint) and fasthttp 1.71 -> 1.72. AntD 6.5 deprecated several Input/Card/Space props, so adapt the panel UI: - Input/InputNumber addonBefore/addonAfter -> prefix/suffix - Card bordered -> variant="outlined" - Space direction -> orientation - swap the hand-rolled Telegram SVG for the new TelegramFilled icon - guard SettingListItem against cloning aria-labelledby onto a Fragment, which only accepts key/children |
||
|
|
d8221a8153 |
fix(sub): bake Host VLESS Route into subscription UUIDs
The Host VLESS Route field was stored and shown in the panel but never applied to any generated subscription (raw, JSON, Clash), so the UUID was emitted unmodified (#5655). Xray reads the route from the UUID's 3rd group (bytes 6-7, net.PortFromBytes) and masks those bytes to zero before authenticating, so a value can be baked into the share/JSON/Clash UUIDs without breaking the user match. A shared applyVlessRoute helper encodes a single 0-65535 value as the 3rd group; empty/invalid/non-UUID input is left unchanged, so legacy data never yields a broken link and no DB migration is needed. The field was wrongly validated as a multi-segment port spec (that form belongs to the separate server-side routing rule). It is now a single value 0-65535, with frontend validation, link-preview parity (genVlessLink/hostToExternalProxyEntry), hint + error translations across all 13 locales, and tests on every path. Closes #5655 |
||
|
|
71aca2018a |
feat(a11y): screen-reader & keyboard accessibility across the panel (#5486) (#5652)
* feat(a11y): label list, toolbar & dashboard actions for screen readers Phase 1 of #5486 (Android TalkBack support). Icon-only controls across the management surfaces previously announced only their untranslated icon name (e.g. "edit", "ellipsis") or nothing at all. - Add aria-label to icon-only row-action and toolbar buttons across inbounds, clients, groups, hosts, nodes and xray (outbounds/routing/dns/balancers) lists, plus the dashboard cards. - Make clickable bare icons and AntD Card actions keyboard-operable via role/tabIndex + Enter/Space (new activateOnKey helper); convert mobile dropdown triggers to buttons so they open from the keyboard. - Fix the sidebar hamburger's mislabeled aria-label (was the dashboard label) and translate previously-hardcoded outbound menu labels. New i18n keys in all 13 locales: sort, menu.openMenu, pages.xray.outbound.moveToTop. * feat(a11y): label modal, QR and copy/download controls for screen readers Phase 2 of #5486. Modal and overlay controls relied on tooltips (not a reliable accessible name) or were bare clickable icons with no keyboard or screen-reader support. - Add aria-label to copy/QR/download/info icon buttons in the inbound and client info modals, sub-links modal, QR panel, backup/log modals, and to the bare search/select inputs of the attach/detach client modals. - Make click-to-copy QR codes and the IP-log refresh/clear, geofile reload and log refresh icons keyboard-operable (role/tabIndex + Enter/Space) with translated labels. - Label the 2FA code input; drop the QrPanel download-image string fallback now that the key exists. New i18n key in all 13 locales: downloadImage. * feat(a11y): label form fields and shared form components for screen readers Phase 3 of #5486. Form controls and shared form widgets were largely unlabelled, and several remove controls were not keyboard-operable. - SettingListItem now ties its title to the control via aria-labelledby, giving accessible names to the ~90 settings-tab inputs at once. - InputAddon gains button semantics (role/tabIndex/Enter+Space) and an ariaLabel prop when used as an interactive remove control. - Sparkline charts expose a role="img" summary of their latest values. - Add aria-label to add/remove/regenerate icon buttons and bare inputs/selects across inbound, client and xray (dns/routing/balancer/ outbound) forms; make clickable remove icons keyboard-operable; mark decorative help/target icons aria-hidden; label the JSON editor, date-time clear button, header-map remove, notification select-all and remark token chips. New i18n keys in all 13 locales: regenerate, jsonEditor, pages.xray.balancer.{costMatch,costValue,costRegexp}. * chore(a11y): add eslint-plugin-jsx-a11y harness and fix flagged interactions Phase 4 of #5486. Adds eslint-plugin-jsx-a11y (recommended ruleset, scoped to .tsx) so screen-reader/keyboard regressions fail lint. - Make the mobile node-card header a proper keyboard disclosure (role=button, aria-expanded, Enter/Space activation that ignores clicks on the nested action buttons) and drop the now-redundant stop-propagation click handlers the linter flagged on card-action wrappers in the node, client and inbound mobile cards. - Disable jsx-a11y/no-autofocus: the autofocus on the login field and modal primary inputs is intentional focus management that helps screen-reader and keyboard users land on the right control. make lint passes with the a11y ruleset enforced. * feat(a11y): cover remaining deferred spots (settings tabs, sockopt, API docs) Completes the panel sweep for #5486 by labelling the spots previously left out of phases 1-4: - NotifyTimeField (Telegram notifications): the mode, interval, unit and custom-cron inputs now carry aria-labels. - The Sockopt toggle in transport options. - Settings category tabs in icons-only (mobile) mode now expose the tab name as the icon's aria-label instead of the raw icon name. - The Swagger API-docs view is wrapped in a labelled region landmark. New i18n keys in all 13 locales: pages.settings.notifyTime.{interval,unit}. * feat(a11y): label shared xray form components and remark field Code review surfaced frontend/src/lib/xray/forms/ — shared form components used by the host and inbound JSON forms — which the initial audit missed. - FinalMaskForm (TCP/UDP final-mask editor): label the icon-only add and regenerate buttons and make all six remove icons keyboard-operable (role/tabIndex/Enter+Space); adds useTranslation to its sub-components. - CustomSockoptList: the remove icon is now keyboard-operable. - SniffingFields: aria-label on the otherwise label-less destOverride select. - RemarkTemplateField: aria-label on the remark-variable picker button. New i18n key in all 13 locales: pages.inbounds.sniffingDestOverride. * feat(a11y): label client info modal and WireGuard config block After rebasing onto the WireGuard client-config feature, re-apply the ClientInfoModal copy/QR/IP-log aria-labels (the modal was restructured upstream, so the original labels did not carry over) and label the new ConfigBlock component's copy/download/QR actions. ConfigBlock's action wrapper keeps its stop-propagation handler (a non-interactive guard for the Collapse header) under a scoped jsx-a11y exception. * fix(frontend): let npm install jsx-a11y under ESLint 10 eslint-plugin-jsx-a11y@6.10.2 declares a peer range that stops at ESLint 9, but the panel is on ESLint 10, so `npm ci` aborts with ERESOLVE even though the plugin runs fine on ESLint 10 with flat config. Add an npm override so jsx-a11y accepts the project's ESLint version. This keeps normal peer resolution (recharts' react-is peer still auto-installs) — no global legacy-peer-deps and no manual react-is pin needed. * fix(a11y): size mobile row triggers and move node expand role to chevron Address automated review on #5652: - add size="small" to the inbound/client/node mobile-card "more" dropdown triggers so they match the adjacent small Switch and the established desktop RowActions pattern. - move the node card-head disclosure semantics (role/tabIndex/aria-expanded/ keyboard) onto the chevron affordance so the expand control is no longer a role="button" wrapping the Switch, info button and dropdown. Mouse click-anywhere-to-expand is preserved on the header div. |
||
|
|
a329882e0e |
feat(wireguard): client config UX, collapsible config card, configurable DNS
Land the WireGuard client-config UX work on main (the upstream PR #5642 branch could not be pushed to). - Reusable collapsible ConfigBlock (copy/download/QR, actions aligned right) for the client .conf, used by client info and the public sub page. - Correct .conf: canonical PresharedKey casing and DNS sourced from the inbound (configurable per-inbound, default 1.1.1.1, 1.0.0.1). - Configurable per-inbound DNS for WireGuard (schema + form + backend hint via InboundOption.WgDns); inert at the Xray layer. - Public sub page now shows the WireGuard config, rebuilt from the share link; the Go wireguard:// link carries dns/presharedkey/keepalive for completeness. - QR enabled for the wireguard:// link; link rows are compact like other protocols. - Client information order is subscription, copy URL, WireGuard config; the redundant config tab is removed from the add/edit client modal. - Drop the Inbound Information and QR Code row actions for WireGuard inbounds. |
||
|
|
9c8cd08f90 |
feat(wireguard): multi-client support
WireGuard inbounds now manage per-client peers using xray-core's native WireGuard users (AddUser/RemoveUser). Each client lives in settings.clients (canonical, like every other protocol) and is projected to peers[] only when emitting the xray config, at level 0 so the dispatcher's per-user traffic/online counters work with no extra plumbing. Backend: internal/util/wireguard gains KeyToHex (base64 to hex for the gRPC path), PublicKeyFromPrivate and GenerateWireguardPSK; xray/api.go builds a wireguard account in AddUser with hex keys (RemoveUser already worked); client CRUD generates a keypair and allocates a unique tunnel address per client and never rotates keys on edit; an idempotent migration converts legacy settings.peers into managed clients; WireGuard is included in the raw subscription. Frontend: WireGuard in the add-client modal with keys on the credential tab, client schema, per-client QR/link/.conf, inbound form reduced to server settings; i18n added across 13 locales. Fix: guard the settings[clients] assertion in add/update so a legacy WireGuard inbound stored without a clients key no longer panics. |
||
|
|
e4b881e58a |
feat(panel): surface dev-build version in UI, bot, and CLI
A dev build now shows its `dev+<commit>` identity instead of a misleading stable-looking version in the sidebar badge, dashboard card, update modal, Telegram status report, startup log, and `x-ui -v`. Adds a shared formatPanelVersion helper (single v prefix; dev labels shown verbatim) and fixes the mobile-tag double-v. Renames the version getters for clarity: config.GetVersion to GetBaseVersion (raw embedded version), config.GetReportedVersion to GetPanelVersion (advertised/displayed), and the xray process GetVersion to GetXrayVersion. |
||
|
|
11c5b53fac | feat(sub): add PROTOCOL, TRANSPORT, SECURITY remark template variables | ||
|
|
bd60e770f4 |
fix(outbound): preserve custom headers for HTTP outbounds (#5519)
The Outbounds form routed HTTP through the SOCKS-shared simpleAuth adapter, which only knew address/port/user/pass, so xray's top-level settings.headers was dropped on both load and save. Opening and re-saving an HTTP outbound destroyed its headers. Add headers to the HTTP wire/form schemas, round-trip it via dedicated httpFromWire/httpToWire helpers, and expose a HeaderMapEditor in the form. Only settings-level headers round-trip; xray-core ignores per-server headers. |
||
|
|
fea3c94b11 |
feat(xhttp): support sessionID* rename + sessionIDTable/Length (xray v26.6.22) (#5506)
* feat(xhttp): support sessionID* rename + sessionIDTable/Length (xray v26.6.22) xray-core v26.6.22 (PR #6258) renamed the XHTTP session config keys sessionPlacement/sessionKey to sessionIDPlacement/sessionIDKey (no fallback kept in core) and added sessionIDTable (predefined charset name or literal ASCII) and sessionIDLength (range, e.g. 16-32, lower bound > 0). Panel changes: - Schema (xhttp.ts): rename the two keys, add sessionIDTable/sessionIDLength, and a z.preprocess that lifts legacy keys off stored configs so an upgraded panel never silently drops a saved session setting. - Wire normalize + share-link build/parse: rename keys, emit the two new fields, and accept legacy sessionPlacement/sessionKey from old share links. - Inbound + outbound XHTTP forms: rename field paths, add a sessionIDTable autocomplete (9 predefined tables + free ASCII) and a sessionIDLength range input shown only when a table is set, with light client validation (ASCII table, length min > 0; xray enforces the room-size minimum server-side). - Subscription (service.go) and Clash (clash_service.go) builders: emit the renamed + new keys, with a legacy fallback for not-yet-resaved inbounds. - Locales: add sessionIDTable/sessionIDLength labels + hints in all 13 files. Two sibling v26.6.22 XHTTP commits need no panel change and are covered by the core bump alone: #6332 (XHTTP/3 closes QUIC/UDP) and #6320 (udpHop honors the existing dialerProxy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(xhttp): add Session ID Table to inbound form-blocks snapshot The new sessionIDTable input renders by default in the inbound XHTTP form, so its label joins the field-structure snapshot. sessionIDLength stays conditional (only shown when a table is set), so it does not appear here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(xhttp): migrate legacy session keys in the running xray config The Zod preprocess plus the subscription/Clash fallbacks only covered the panel UI and share-link output. The config handed to the running xray-core process is built from the raw stored streamSettings in GetXrayConfig, which did not rewrite the renamed XHTTP session keys — so a pre-upgrade inbound (or template outbound) stored with a non-default sessionPlacement was emitted unchanged and dropped by xray-core v26.6.22, until the admin re-saved it. Lift sessionPlacement/sessionKey onto sessionIDPlacement/sessionIDKey at config-generation time, in the existing inbound stream-rewrite block (next to the tls/reality/externalProxy handling) and across template outbounds. The lift is idempotent and leaves unchanged configs byte-identical so the hot-reload diff never sees a spurious change. Also tighten validateSessionIDLength to reject an inverted range (e.g. 32-16) in addition to the existing lower-bound > 0 check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(xray): avoid summed-capacity allocation in mergeSubscriptionOutbounds CodeQL go/allocation-size-overflow flagged the pre-sized make() whose capacity was a sum of three slice lengths. Grow the slice via append on a nil slice instead; same result, no overflow-prone capacity expression. |
||
|
|
b07fad0e69 |
refactor(wireguard): drop removed workers field (xray v26.6.22) (#5509)
* v3.4.0 * refactor(wireguard): drop removed `workers` field (xray v26.6.22) xray-core v26.6.22 (PR #6287) removed the WireGuard `workers` (num_workers) config field; the engine now relies on wireguard-go's internal worker fallback and no longer reads it. Remove it from the panel so it stops emitting a key xray ignores. Removed from the inbound/outbound/outbound-form WireGuard schemas, both WireGuard forms, the outbound form adapter (both directions) and defaults, the two affected tests, and the `workers` label in all 13 locales. Existing configs that still carry workers are simply dropped on parse — no migration needed since the field had no runtime effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Update version --------- Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
852b53db79 |
feat(xray): add loopback sniffing and per-segment fragment masks
- Loopback outbound: add sniffing support (xray-core #6320) - FinalMask fragment: support per-segment lengths/delays arrays with legacy length/delay migration (xray-core #6334) - Consolidate sniffing into a shared SniffingFields component and the canonical SniffingSchema across inbound, VLESS reverse, and loopback |
||
|
|
39774a6a38 |
fix(tls): default OCSP stapling to off for new inbound certs
Certs without an OCSP responder URL (e.g. Let's Encrypt, which dropped OCSP in 2025) made xray log 'ignoring invalid OCSP: no OCSP server specified in cert' on every refresh. Default the per-cert ocspStapling interval to 0 (disabled) so new inbounds stay quiet; the field is kept for certs that do support stapling. |
||
|
|
7c8889466b |
feat(tls,reality): port xray TLS/REALITY fields, cert-hash helpers, fallback UX
TLS: add verifyPeerCertByName (vcn) to inbound settings + emit in both share-link generators (frontend + Go sub) and outbound parser; the allowInsecure replacement xray removed after 2026-06-01. Add server-side curvePreferences, masterKeyLog, echSockopt (passthrough + form) at tlsSettings top-level so they survive the panel-only settings strip. REALITY: add limitFallbackUpload/Download (afterBytes/bytesPerSec/burstBytesPerSec) with per-field tooltips, plus masterKeyLog. Verified field names/semantics against pinned xray v1.260327.1 (bytesPerSec=0 disables). Hosts: fix verify_peer_cert_by_name column bool->string (xray expects comma-separated names) with an idempotent, history-gate-free migration (SQLite typeof blank; Postgres ALTER once); emit vcn for hosts/external proxies. Server: add getCertHash (local cert DER SHA-256) and getRemoteCertHash (xray tls ping) endpoints + api-docs; wire pinned-cert field buttons. Drop the meaningless random-hash button. Xray UI: metrics endpoint (listen/tag) config in Basics; import/export for routing rules and outbounds. Fallbacks card: compact empty state, header-aligned actions, responsive labeled grid rows. i18n: add all new keys to every locale; drop unused generateRandomPin. |
||
|
|
a5bc71a6f1 |
fix(sub): SS2022 share links must not base64-encode userinfo (#5432)
Per SIP022, ss:// links for 2022-blake3-* methods must NOT base64-encode the userinfo; method and password are percent-encoded instead. Clients like Hiddify reject the base64 form. Fix both the server-side subscription path and the client-side panel link, plus the matching parsers for round-trip import. |
||
|
|
0a40ec5f13 |
fix(sub): re-add xhttp mode to extra JSON for Karing (#5446)
Regression of #4364. Karing parses the `extra` JSON and ignores the flat `mode=` param, so when extra was present without `mode` it stored the transport with no mode and the handshake failed. The `mode` field that #4365 added to buildXhttpExtra was dropped during the share-link refactor; restore it in both the backend and frontend generators. |
||
|
|
d01d9867e4 |
fix(sub): preserve non-default scMinPostsIntervalMs and use per-inbound xmux in JSON subscriptions (#5393)
* fix(sub): preserve non-default scMinPostsIntervalMs in inbound wire payload
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 (#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.
* fix(sub): use per-inbound xmux instead of global subJsonMux in JSON subscriptions
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
* feat(ui): add scMinPostsIntervalMs to inbound XHTTP form
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 #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
* fix(ui): show packet-up fields for auto mode in inbound XHTTP form
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.
* fix(outbound): show scMinPostsIntervalMs for auto mode, update placeholder
- 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
* fix(inbound): show scMaxEachPostBytes for all modes, gate scMaxBufferedPosts 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.
* fix(inbound): correct XHTTP field visibility per xray-core source verification
- 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.
---------
Co-authored-by: w3struk <w3struk@gmail.com>
Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com>
|
||
|
|
21e9b94bb4 |
fix(sub): emit Shadowsocks http-header links as SIP002 obfs-local plugin
v2rayN's SS parser only reads the SIP002 `plugin` query param; it ignores the xray-native type/headerType/host/path, so an SS link with a TCP http header imported as plain SS and failed to connect. Re-encode the http header as `plugin=obfs-local;obfs=http;obfs-host=<host>`, which v2rayN maps to an xray tcp/http-header outbound. Mirrored in the frontend link generator. Note: v2rayN carries only the host and forces request path "/", so this matches an inbound whose header path is "/" (the default); xray validates path, not host. |
||
|
|
709b332d17 |
feat(hosts): managed Hosts for per-host subscription link overrides (#5409)
* test(sub): characterize current link output (externalProxy + single-link baselines)
Phase 0 of the Hosts feature. Locks current subscription-link output for the
externalProxy paths (vless/vmess/trojan/ss exact, reality/hysteria by Contains)
so the upcoming ShareEndpoint refactor can be proven behavior-preserving. These
must stay green and unedited through every later phase.
* refactor(sub): unify external-proxy link building behind ShareEndpoint (TDD, snapshot-locked)
Phase 1 of the Hosts feature. Collapse the duplicated externalProxy link
builders (param-form for vless/trojan/ss, object-form for vmess) onto a single
ShareEndpoint abstraction so Phase 4 can add Host-driven links with ~zero new
branching.
Design: an externalProxy-derived endpoint carries the original entry map and
applies it through the UNCHANGED applyExternalProxyTLS{Params,Obj} helpers, so
output is provably byte-identical. buildExternalProxyURLLinks /
buildVmessExternalProxyLinks become thin adapters; the genVless/Trojan/SS/Vmess
call sites are untouched. genHysteriaLink is deliberately left on its own path
(hex pinSHA256, not pcs). The no-externalProxy default tails are unchanged.
TDD: N1-N4 (externalProxyToEndpoint, inboundDefaultEndpoint, buildEndpointLinks,
buildEndpointVmessLinks) written failing-first against stubs, then implemented.
Mutation sanity (performed + reverted): dropping the ep-carry in
externalProxyToEndpoint makes the Phase-0 C1/C2 characterization snapshots go
red (TLS overrides vanish), proving the snapshots guard the emitted output.
Gate: go test ./internal/sub/... and go test ./... green with ZERO edits to the
Phase-0 snapshots; go build ./... green on linux and windows; go vet clean.
* feat(model): Host entity + automigrate + openapi codegen (TDD)
Phase 2 of the Hosts feature. Adds the Host GORM model: an override endpoint
attached to an inbound (address/port + TLS/transport/clash overrides + sub
scoping), superseding the legacy externalProxy array functionally while leaving
it intact.
- model.Host with snake_case column tags, json serializer for slices, text for
free-JSON (mux/sockopt/xhttp), validate tags (remark 1-40, port 0-65535,
security + mihomoIpVersion enums); TableName "hosts". NodeGuids column is added
now but unused (host->node scoping deferred to v2).
- Registered in BOTH initModels() (db.go) and migrationModels() (migrate_data.go);
the latter is required for cross-DB migration and is easy to miss. PG sequence
resync iterates the initModels slice, so it is covered automatically.
- pruneOrphanedHosts() deletes hosts whose inbound_id has no inbound, called
alongside pruneOrphanedClientInbounds().
- openapigen manifest: Host added to StructAllow with MuxParams/SockoptParams/
XhttpExtraParams -> KindAny; regenerated frontend/src/generated/* + openapi.json.
TDD: TestHostTableName, TestHostValidation, TestHostAutoMigrateCreatesColumns
(+ _Postgres), TestPruneOrphanedHosts written failing-first against a wrong-name,
untagged, unregistered stub, then implemented.
Gate: go test ./... green on SQLite AND a real Postgres DSN (local container);
go build/vet/gofmt clean; npm run gen succeeds with the new Host type/schema/
example/zod; npm run typecheck + npm run test (542) green.
* feat(api): Host CRUD service + controller + routes (TDD)
Phase 3 of the Hosts feature.
- service/host.go (HostService, empty struct + database.GetDB() like
ClientService): GetHosts, GetHostsByInbound, GetHost, AddHost (verifies the
inbound exists — no hard FK), UpdateHost (inbound + sort order immutable here),
DeleteHost, SetHostEnable, SetHostsEnable, DeleteHosts, ReorderHosts (single
driver-safe transaction), GetAllTags.
- controller/host.go mirrors NodeController: routes under /panel/api/hosts
(list/get/byInbound/tags + add/update/del/setEnable/reorder + bulk/setEnable,
bulk/del), binds via middleware.BindAndValidate so the model validate tags are
enforced, {success,msg,obj} envelopes.
- Wired the hosts group into api.go after nodes (inherits checkAPIAuth + CSRF).
- DelInbound now cascades: deleting an inbound deletes its hosts.
- Documented all 11 routes in api-docs endpoints.ts (referencing the generated
Host schema) and regenerated openapi.json; extended TestAPIRoutesDocumented's
controller->basePath switch for host.go. Backend en toast keys added.
TDD: service tests (Add/GetByInbound, RejectsUnknownInbound, Reorder, Set/Bulk
enable, DeleteHosts, DeleteInboundCascadesHosts, GetAllTags) written failing-
first against a nil-returning stub; controller test (AddListGetDelete envelope
round-trip + AuthInherited 401) added.
Gate: go test ./internal/web/... + go test ./... green; npm run gen + typecheck
+ lint + test (542) + build green.
* feat(sub): render subscription links from hosts; legacy fallback when none (TDD, mutation-checked)
Phase 4 of the Hosts feature. Inserts host resolution between inbound and link
across all three subscription formats.
Mechanism: hostEndpoints(inbound, format) loads the inbound's enabled hosts
(filtered by ExcludeFromSubTypes, ordered by sort_order then id) and projects
each onto the externalProxy entry shape the raw/json/clash renderers already
consume. So a host fans out one link/proxy reusing the exact existing rendering
(address/port/security/sni/fp/alpn/pins/ech) with zero new TLS code. Host header
and path overrides are applied additively in the raw builders (no-op for legacy
externalProxy, which never carries those keys — characterization snapshots stay
green). Clash ip-version (MihomoIpVersion) is set last on the proxy.
Integration points:
- getSubs (raw): per inbound, hostEndpoints AFTER projectThroughFallbackMaster;
len>0 -> linkFromHosts (renders only the hosts), else legacy GetLink.
- GetJson/GetClash: inject the host endpoints into the inbound's externalProxy
before the existing getConfig/getProxies loop.
- Precedence: hosts win over any legacy externalProxy (injection replaces it).
Backward compat: a zero-host inbound takes the legacy path -> byte-identical
output (all Phase-0 characterization snapshots unchanged).
TDD: 9 cycles (zero-hosts identical, N-links-ordered with host/path override,
disabled skipped, host-vs-externalProxy precedence, no-dedup, sort composes with
SubSortIndex, host-over-fallback, resolve-via-client-inbounds, ExcludeFromSubTypes
per format) written failing-first against unwired helpers, then wired green.
Mutation sanity (performed + reverted, documented here):
- zero-hosts fallback: flipping the len(hostEps)>0 guard to >=0 makes
TestSub_ZeroHosts_IdenticalOutput go red (host path yields "" for no hosts).
- no-dedup: adding a remark-dedup in hostEndpoints makes TestSub_NHosts_NoDedup
go red (two distinct hosts collapse to one link).
Gate: go test ./internal/sub/... + go test ./... green with ZERO edits to the
Phase-0 snapshots; go build green on linux and windows; go vet + gofmt clean.
* feat(migration): seed hosts from inbound externalProxy (TDD, idempotent, dual-driver)
Phase 5 of the Hosts feature. One-time migration so existing installs surface
their legacy externalProxy entries as first-class Host rows.
- seedHostsFromExternalProxy() is self-gated on a HistoryOfSeeders
"HostsFromExternalProxy" row (run-once) and wired into runSeeders. For each
inbound it parses StreamSettings, reads externalProxy[], and creates one Host
per entry: forceTls->Security (unknown->same), dest->Address, port->Port,
remark->Remark (generated when blank, capped at 40), sni/fingerprint/alpn/
pinnedPeerCertSha256/echConfigList copied; SortOrder=index; InboundId set.
- Additive: externalProxy is left intact in StreamSettings (rollback-safe; the
sub layer prefers hosts when present, §Phase 4).
- Postgres: GORM db.Create advances hosts_id_seq via the sequence, so no extra
resync is needed beyond the existing startup resync.
TDD: field-mapping, idempotency (second run no-op), no-externalProxy->no-hosts,
externalProxy-kept-intact written failing-first against a stub; plus a
Postgres counterpart that skips without XUI_DB_DSN.
Gate: go test ./internal/web/service/... ./internal/database/... green on SQLite;
the *_Postgres tests green against a real Postgres container; go build green on
linux and windows; go vet + gofmt clean. (Running the whole database package
under XUI_DB_TYPE=postgres is not supported — the SQLite-path tests share the one
DSN — so only the t.Skip-gated *_Postgres tests run with the env set.)
* feat(ui): Hosts page + schema + query hooks + link preview helper (TDD on schema/helpers)
Phase 6 of the Hosts feature — the admin UI.
- schemas/api/host.ts: HostFormSchema (validation: remark 1-40, tags ^[A-Z0-9_:]+$
≤10×≤36, port 0-65535, security/mihomoIpVersion enums, alpn/fingerprint reused
from the shared primitives) + a loose HostRecordSchema/HostListSchema for reads.
- lib/hosts/host-link.ts: hostToExternalProxyEntry — the frontend mirror of the
backend hostToExternalProxyMap (security->forceTls, sni override rules, port
inherit), for share-link previews.
- api/queries/useHostsQuery.ts + useHostMutations.ts (mirror the node hooks):
list/get + add/update/del/setEnable/reorder/bulk; queryKeys.hosts.* added;
mutations invalidate keys.hosts.root().
- pages/hosts/{HostsPage,HostList,HostFormModal}.tsx (+CSS) mirroring pages/nodes:
list with remark · address:port · inbound · security · tags · enable Switch ·
per-inbound move up/down (reorder) · bulk enable/disable/delete; form grouped
into Basic / Advanced / Clash / Subscription-scope sections.
- Route '/hosts' + sidebar item (Global icon); menu.hosts + pages.hosts.* added to
the en-US bundle (other locales fall back to English until translated).
TDD: HostFormSchema (10 cases) and hostToExternalProxyEntry (6 cases) written
failing-first, then implemented. UI verified by lint/typecheck/test/build.
Deferred (documented enhancement): the live in-form share-link preview (needs
inbound+client context) and a per-host host/path override in JSON/Clash output
(raw already overrides; JSON/Clash inherit the inbound's host/path).
Gate: cd frontend && npm run lint && npm run typecheck && npm run test (557) &&
npm run build all green; go build ./... + go test ./... still green.
* refactor(ui): remove the External Proxy form from the inbound stream settings
Hosts supersede the legacy externalProxy: the subscription renders from hosts
(hosts win when both exist) and the migration converts existing externalProxy
entries to hosts. externalProxy's only real consumers were the subscription
(now covered) and this form's preview — the backend per-client copy-link never
used it — so removing the editor has no functional regression.
- Drop ExternalProxyForm + toggleExternalProxy from InboundFormModal and delete
the orphaned form component + its export; remove its block test + snapshot.
- KEEP the externalProxy schema field and backend parsing/link-generation: an
existing inbound's externalProxy still round-trips through the form (not
silently destroyed on edit) and still renders if a host was removed.
Gate: cd frontend && npm run typecheck + lint + test (556) + build green.
* fix(ui): use Alert `title` instead of deprecated `message` (antd 6)
Ant Design 6 deprecated <Alert message=> in favor of <Alert title=>; the panel
was mid-migration (21 Alerts already on title). Renamed the 7 remaining stragglers
across 5 files (SubLinksModal, InboundFormModal, sockopt, EmailTab, TelegramTab),
silencing the runtime deprecation warning. description= is unchanged.
Pre-existing warning, surfaced while testing Hosts — not introduced by it.
Gate: npm run typecheck + lint + test (556) + build green.
* style(ui): align Hosts page with Clients/Inbounds cards + reorder columns
- page-shell.css never listed .hosts-page, so the Hosts page got no content
padding / transparent-layout / summary-card spacing. Add a .hosts-page shell
block (background, dark/ultra vars, content-area + summary-card padding). This
is the actual "card spacing" bug.
- HostList: match the Clients/Inbounds list card — hoverable + the toolbar moved
into the card title as a .card-toolbar (Add when nothing selected; selected
count + bulk enable/disable/delete on selection). Re-declare .card-toolbar in
HostList.css since the shared rule lives in a lazily-loaded page stylesheet.
- Reorder table columns as requested: Actions, Enable, then Remark, Endpoint,
Inbound, Security, Tags. Added scroll x for narrow screens.
- HostsPage: add a summary card (Total / Enabled / Disabled) like the other
pages. New i18n keys: pages.hosts.selectedCount + pages.hosts.summary.*.
Gate: npm run typecheck + lint + test (556) + build green.
* style(ui): use Tabs instead of Collapse in the Add/Edit Host form
The Basic / Advanced / Clash / Subscription-scope sections are now tabs. Each
pane sets forceRender so all fields stay mounted — required because the form
uses preserve=false, so an unmounted tab's values would otherwise be dropped on
submit (and a required field on a hidden tab still blocks submit).
Gate: npm run typecheck + lint + test (556) + build green.
* style(ui): split Host form into Security + Advanced tabs; drop unused JSON fields
- Remove the Mux/Sockopt/XHTTP raw-JSON fields from the Host form: they were not
wired into link generation and the inbound's structured editors are inbound-
specific (not reusable). The DB columns + read schema + generated type stay, so
they can get proper editors later. (HostFormSchema drops them; HostRecordSchema
keeps them.)
- Reorganize tabs to Basic / Security / Advanced / Clash / Subscription scope:
Security holds the TLS/cert fields (security, sni, sni-overrides, alpn,
fingerprint, pins, verify-by-name, ech); Advanced now holds the transport
overrides (host header, path).
- i18n: add pages.hosts.sections.security; drop the 3 unused field labels.
Gate: npm run typecheck + lint + test (556) + build green.
* style(ui): restore Mux/Sockopt/XHTTP fields in the Host Advanced tab
Put the three free-JSON override fields back, in the Advanced tab next to host
header / path (as JSON inputs — the inbound's structured editors aren't reusable
here). Re-added to HostFormSchema + defaults + the i18n labels.
Gate: npm run typecheck + lint + test (556) + build green.
* feat(hosts): add allowInsecure (rendered) + serverDescription/mihomoX25519/vlessRouteId fields
Closes most of the Remnawave-host gap analysis.
- model.Host: + allowInsecure, serverDescription (≤64), vlessRouteId (0-65535),
mihomoX25519. Auto-migrated (SQLite + Postgres verified); openapi regenerated.
- allowInsecure is fully RENDERED into subscription output (TDD):
- raw link: allowInsecure=1 (TLS/Reality, skipped for none) via the endpoint
builder;
- JSON/Clash: applyExternalProxyTLSToStream writes tlsSettings.settings.
allowInsecure, and clash applySecurity now emits skip-cert-verify for the tls
case (it previously only did so for Hysteria — a pre-existing gap, so inbound
allowInsecure now renders for vless/trojan/ss clash too).
- Frontend: the four fields added to the Host form (allowInsecure → Security,
serverDescription → Basic, vlessRouteId → Advanced, mihomoX25519 → Clash);
serverDescription shown under the remark in the list. Schema + i18n updated.
serverDescription / vlessRouteId / mihomoX25519 are stored + editable; their
deeper rendering (and per-host mux/sockopt/xhttp into JSON/Clash, plus a per-host
xray JSON template) are tracked as follow-ups.
Gate: go test ./... green (SQLite + Postgres for the host schema/migration);
go build linux+windows; go vet + gofmt clean; npm run gen + typecheck + lint +
test (556) + build green; generated files in sync.
* feat(sub): render host sockopt + xhttp-extra params into JSON/Clash output (TDD)
A host's sockoptParams and xhttpExtraParams (free-JSON) now take effect:
applyHostStreamOverrides injects sockopt into the per-host stream (re-added since
the base stream strips it) and merges xhttpExtraParams into xhttpSettings, called
in both getConfig (JSON) and getProxies (Clash) right after the per-host TLS
apply. No-op for legacy externalProxy entries (keys absent) — characterization
snapshots unchanged.
mux rendering is outbound-level (overrides outbound.Mux) and needs a genVless/
genVnext/genServer signature change — deferred, along with the per-host xray
JSON template.
Gate: go test ./internal/sub/... + go test ./... green (snapshots unchanged);
go build + vet + gofmt clean.
* feat(sub): render host muxParams as a per-host JSON outbound mux override (TDD)
genVnext/genVless/genServer take a muxOverride: a host's muxParams (when valid
JSON) overrides the global mux on its JSON outbound; empty falls back to the
panel mux (behavior unchanged for non-host configs). Completes the host
mux/sockopt/xhttp trio. Test call sites updated for the new signature.
Gate: go test ./internal/sub/... + go test ./... green (snapshots unchanged);
go build + gofmt clean.
* style(ui): show Host security fields conditionally per security (like externalProxy)
* feat(sub): apply host SNI + fingerprint override for reality (TDD)
A reality host now overrides SNI and fingerprint while inheriting publicKey/
shortId from the inbound (reality keys can't be host-supplied). Previously the
reality link kept the inbound's serverName because the TLS appliers are gated to
security=="tls".
- raw: applyEndpointRealityParams sets sni/fp on the params for reality;
- JSON/Clash: applyHostStreamOverrides sets realitySettings.serverName +
serverNames from the host SNI.
Gated to host endpoints via an isHost marker on the synthesized ep, so the legacy
externalProxy path stays byte-identical (characterization snapshots unchanged).
The marker is internal and never emitted.
Gate: go test ./internal/sub/... + go test ./... green; go build + vet + gofmt clean.
* fix(ui): start the Host inbound select unselected instead of showing 0
A new host left inboundId defaulting to 0, so the Select rendered "0". inboundId
is now optional in the form (undefined until chosen), so it shows its
placeholder ("Select an inbound"); the required rule still enforces a choice on
save. Port keeps 0 (means "inherit the inbound's port").
Gate: npm run typecheck + lint + build green.
* fix(ui): drop redundant :port suffix from the Host inbound select label
The inbound tag (e.g. in-59303-tcp) already carries the port, so the appended
":59303" was duplicated. Show just the remark/tag.
Gate: npm run typecheck + lint + build green.
* style(ui): apply the shared card hover shadows to the Hosts page
page-cards.css scoped its card styling + hover shadows to each page class but
not .hosts-page, so Hosts fell back to antd's default hoverable (a larger/blurry
shadow + pointer cursor). Add a .hosts-page block matching the other pages.
Gate: npm run build green.
* feat(hosts): move Tags to Basic tab, add Nodes field, accept VLESS route ranges
- Move the Tags field into the Host form's Basic tab and add a Nodes
multi-select (visual-only assignment, backed by the existing node_guids
column) so the Basic tab matches the reference layout.
- Replace the single-port vlessRouteId integer with a free-form vlessRoute
string that accepts comma-separated ports/ranges (e.g. 53,443,1000-2000);
format-validated on the frontend, stored verbatim on the backend.
- Regenerated frontend types/openapi from the changed model.
* feat(hosts): structured editors for Mux/Sockopt/XHTTP + new Final Mask
Replace the raw JSON textareas in the Host form's Advanced tab with the same
structured editors used elsewhere, under a nested tabbed layout (General / Mux /
Sockopt / XHTTP / Final Mask), mirroring the Sub-JSON settings tab:
- Mux: the Sub-JSON mux editor (enable + concurrency/xudpConcurrency/xudp443).
- Sockopt + XHTTP: reuse the outbound SockoptForm / XhttpForm, wrapped in an
isolated form that serializes the edited subtree back to the host's JSON
string (pruned so the override stays sparse).
- Final Mask: new host field (model + column + JSON-render wiring that merges
the masks into the host's JSON-subscription stream), edited via the shared
FinalMaskForm like the Sub-JSON Final Mask editor.
Each editor stays a controlled value/onChange component bound to its existing
host JSON string field; backend rendering of mux/sockopt/xhttp is unchanged.
* feat(hosts): drop XHTTP + Xray-JSON-template overrides; fix mobile form layout
Remove the host's XHTTP extra-params and Xray-JSON-template overrides entirely
(model fields + columns, JSON-subscription render paths incl. hostTemplateOutbound,
schema, form tab/field, i18n, openapi codegen, and their tests) — they did not
fit the host model. Mux, Sockopt and Final Mask stay as structured editors.
Mobile fixes for the Edit Host modal:
- responsive width (95vw on mobile, was a fixed 760px that overflowed the
viewport and clipped the tabs/labels) + a scrollable body so the footer stays
on screen;
- Mux fields use responsive Row/Col (stack on mobile) instead of a fixed-width
label grid.
* fix(hosts): hide the spurious horizontal scrollbar in the Edit Host modal
Setting overflowY:auto on the modal body forced overflow-x to auto too (CSS
rule), so antd Row's negative gutter margins triggered a horizontal scrollbar.
Pin overflowX:hidden.
* feat(hosts): inbound-style responsive field layout + icon empty state
- Host form (main form + Mux/Sockopt/Final Mask editors) now use the inbound
form's label layout: label beside the input on desktop (labelCol sm span 8 /
wrapperCol sm span 14, right-aligned), stacked label-above-input on mobile.
Rewrote HostMuxForm onto an internal antd Form so it follows the same layout
instead of a manual grid.
- Empty hosts table now shows the host icon + the shared 'Nothing here yet'
(noData) text, matching Nodes/Inbounds/Clients, replacing the bespoke
'No hosts yet…' string.
* fix(hosts): avoid nested <form> in the Edit Host modal
The Mux/Sockopt/Final Mask editors each render their own antd Form inside the
host's main Form, producing an invalid nested <form> DOM node (hydration
warning). Render those inner forms with component={false} so they keep the form
instance/context but emit no <form> element.
* fix(hosts): make the Mux enable toggle work
The Switch's checked state came from Form.useWatch('mux'), but the mux object
field had no registered Form.Item while disabled, so setFieldValue never
notified the watcher and the toggle stayed off. Bind the Switch to a real
name='enabled' field (antd drives its checked state directly) and keep the
sub-fields registered via hidden={!enabled}, serialized to the flat mux JSON.
* refactor(hosts): reuse the outbound MuxForm instead of a bespoke Mux editor
The Mux fields duplicated the outbound MuxForm. Reuse it through the same
wrapper as Sockopt: generalize OutboundSubtreeJsonForm with defaultSubtree
(pre-fill on enable) and a serialize hook, and have HostMuxForm render MuxForm
at the ['mux'] path. The host keeps its inherit-when-off semantics by storing ''
unless mux.enabled. Also drops the now-unused enableSwitch path from the
wrapper (only the removed XHTTP editor used it).
* style(hosts): use default-width Port input like the inbound form
The host Port used width:100% (full width); the inbound's numeric inputs use
antd's default width. Drop the override so Port matches. The Mux number inputs
already use the default width via the reused MuxForm.
* refactor(sockopt): readable customSockopt editor as a shared component
The customSockopt rows were a single cramped Space.Compact line and duplicated
verbatim in the inbound and outbound sockopt forms. Extract a shared
CustomSockoptList that renders each entry as a titled group of labeled fields
(System / Level / Opt / Type / Value), matching the rest of the form, and use it
in both (and thus the host Sockopt editor).
* fix(finalmask): drop the empty Custom Tables tag on a new sudoku mask
The sudoku TCP-mask default seeded customTables: [''] (one empty string), which
rendered as a blank removable tag. Seed [] instead.
* fix(sockopt): make the outbound (and host) Sockopt client-only
Per the XTLS sockopt docs, tproxy / acceptProxyProtocol / V6Only /
trustedXForwardedFor only apply to an inbound (listening socket); they are
meaningless on an outbound/dialer. Drop them from the outbound SockoptForm
(which the host reuses). The Sockopt default object still seeds those keys, so
the host also strips them on serialize, keeping its override honest to the
server/client split. The inbound SockoptForm is left unchanged.
* fix(sockopt): make the inbound Sockopt server-only
Complete the server/client split: drop the outbound/dialer-only fields from the
inbound SockoptForm — dialerProxy, domainStrategy, interface, addressPortStrategy,
happyEyeballs, tcpMptcp (client-only since Go 1.24 auto-enables MPTCP on listen).
mark stays (xray applies SO_MARK on inbound sockets too). Update the form-blocks
snapshot to the server-side field set (intentional spec change).
* feat(hosts): populate Sockopt dialerProxy with the panel's outbound tags
The host Sockopt editor reused the outbound SockoptForm with outboundTags=[],
so the dialerProxy dropdown was empty. Feed it the panel's outbound tags via
the existing useOutboundTags hook (shares the cached xray-config query;
blackhole excluded), so a host can chain through a subscription outbound by tag.
* fix(hosts): empty-state styling on direct load + exclude balancers from dialerProxy
- .card-empty was only defined in lazily-loaded Clients/Inbounds/Nodes
stylesheets, so a direct /hosts refresh rendered the empty table state
unstyled (faint + uncentered) until another page was visited. Re-declare it
in HostList.css so it's correct on first load.
- The Sockopt dialerProxy dropdown listed balancer tags (useOutboundTags merges
them in for mtproto egress). dialerProxy chains a single outbound, so balancers
aren't valid — switch to useOutboundTagGroups and use only the outbound group.
* fix(outbounds): icon + 'Nothing here yet' empty state; stop fading other pages
The Outbounds empty state was a faint '—', and OutboundsTab.css set the global
.card-empty to opacity:0.4 — which leaked onto whichever page's empty state was
shown after the Outbounds CSS had loaded (e.g. Hosts went faint after visiting
Outbounds). Render the icon + noData ('Nothing here yet') like the other lists,
and align .card-empty to the shared centered/secondary style (no opacity).
* fix(outbounds): custom empty state on the desktop table too
The desktop Outbounds Table had no locale.emptyText, so it showed antd's
default 'No data' box. Add the same ExportOutlined + noData empty state as the
card (mobile) view.
* style(sidebar): use ExportOutlined for the Outbounds nav item
The Outbounds sidebar item used UploadOutlined (an upload tray). Switch to
ExportOutlined, matching the outbound icon now used in the routing target and
the outbounds empty states.
* feat(hosts): icons on the form tabs (icon-only on mobile)
Wrap every Host form tab label (Basic/Security/Advanced/Clash/Subscription
scope and the nested General/Mux/Sockopt/Final Mask) with catTabLabel, so the
tabs show icon + text on desktop and just the icon (with a tooltip) on mobile,
matching the Settings/Xray tab bars.
* refactor(hosts): fold Exclude-from-formats into Advanced, drop the one-field tab
The Subscription scope tab held only excludeFromSubTypes after Tags moved to
Basic — a niche per-format scoping knob. Move it into the Advanced > General
sub-tab and remove the standalone tab (and its now-unused subScope label/icon).
* feat(sub): per-client remark template variables; drop the remark model & Show Usage Info
* fix(migration): cap seeded host remark at the model's 256-char limit, not 40
|
||
|
|
9385b6c609 |
feat(nodes): per-node client IP attribution for IP-limit
Record each panel's own Xray IP observations under its panelGuid and merge each node's guid-keyed report on the master, so the panel can tell which node a client IP is connecting through (the flat inbound_client_ips union is pushed back to every node and cannot attribute). Adds the NodeClientIp model + migration, the clientIpsByGuid endpoint and node-sync merge, node-name labels in the client IP log, and cleanup on node deletion. |
||
|
|
c1fbfd0510 |
fix(outbound): parse xmux from imported share links (#5353)
The inbound link generator bundles xmux and downloadSettings as nested objects inside the `extra=` JSON blob, but the outbound link parser only pulled scalar fields and headers from it, silently dropping xmux on import. Extract the nested objects too so they round-trip into the outbound XMUX sub-form. |
||
|
|
f00512d12e |
fix(frontend): TProxy schema, VLESS+XHTTP flow links, clearable Jalali date picker (#5339, #5322, #5313)
- #5339: accept transportless tunnel/TProxy streamSettings that carry no `security` key by adding a transportless branch to SecuritySettingsSchema, mirroring NetworkSettingsSchema. Fixes "streamSettings.security Invalid input". - #5322: emit XTLS Vision `flow` in panel VLESS share links for XHTTP+vlessenc via the shared canEnableTlsFlow predicate, so panel links match the form and the subscription output. - #5313: give the Jalali expiry date picker a working clear (X) button (remount on clear, since the library reads `value` only on mount) and a blank placeholder instead of the library's hardcoded Persian text. |
||
|
|
dab0add191 |
feat(finalmask): support Salamander packetSize (Gecko) and Realm tlsConfig for Hysteria2 (#5278)
* feat(finalmask): support salamander packetSize (Gecko) and realm tlsConfig Hysteria v2.9.1/v2.9.2 added two finalmask features that the pinned Xray-core (26.6.1, 94ffd50) already supports but the panel UI did not expose: Salamander's packetSize range (Gecko, XTLS/Xray-core#6198) and the Realm UDP hole-punching mask's optional tlsConfig (XTLS/Xray-core#6137). Add typed schemas and form fields for both, keeping UdpMaskSchema.settings permissive per the existing finalmask design note. packetSize reuses the existing dash-range preprocess (like udpHop.ports) so it round-trips under the fm= share-link param with no new URI key; realm tlsConfig emits xray's flat TLSConfig shape (serverName/alpn/fingerprint/allowInsecure). Verified against the bundled Xray 26.6.1: configs with packetSize and realm tlsConfig validate (Configuration OK.), plain salamander stays backward-compatible, and a malformed packetSize is correctly rejected by the salamander mask builder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(finalmask): add snapshots for salamander-gecko and realm-tls fixtures vitest run does not auto-create missing snapshots in CI mode, so the two new fixtures need committed snapshot entries. Verified under node:22 that finalmask.test.ts passes (6/6) with these snapshots. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(finalmask): polished Gecko UX with core-grounded validation Fold PR #5281's Gecko work into the Realm tlsConfig base: - Replace the plain packetSize input with a Salamander/Gecko mode selector and validated Min/Max number inputs. - parseGeckoPacketSize enforces xray-core's real bound (1 <= min <= max <= 2048, the gecko buffer size) so the panel rejects configs core would reject at runtime. - Accurate Gecko description; add parser unit tests. - Drop the unused Salamander/Realm settings schemas; settings stay permissive and are validated at the form level. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
7c737820d1 |
fix(links): bracket ipv6 hosts in share links and qr codes (#5310)
* fix(sub): bracket ipv6 hosts in share links * fix(frontend): bracket ipv6 hosts in share links |
||
|
|
41cb0b8ae7 |
fix(inbounds): show remark first, else inbound tag, in client labels
Revert formatInboundLabel to the pre-#5151 behavior: display the inbound remark when set, otherwise the inbound tag, instead of "tag (remark)". Affects the Attach clients / Attached inbounds views and client lists. Routing keeps its own tag (remark) formatting. |
||
|
|
0766e16684 |
feat: implement inbound XMUX form fields (#5211)
* feat: implement inbound XMUX form fields
* fix: replace any cast to satisfy eslint
* test: update xhttp form snapshot for XMUX
* fix(inbound): persist xmux on save so the XMUX form actually round-trips
The inbound wire normalizer unconditionally deleted xhttpSettings.xmux,
so the new inbound XMUX form was stripped on save and never reached the
stored config — the subscription extra blob (buildXhttpExtra) could
never see it. Gate the deletion on the enableXmux toggle, mirroring the
outbound adapter, and add regression tests for both on/off cases.
* fix(xmux): enforce xray-core's maxConnections/maxConcurrency exclusivity
xray-core's XmuxConfig rejects a config that sets both maxConnections
and maxConcurrency. The panel pre-fills maxConcurrency ('16-32') whenever
XMUX is enabled, so an explicit maxConnections would always collide and
make xray refuse the config. Mirror core's semantics in the wire
normalizer: when maxConnections is set (>0, an explicit opt-in since it
defaults to 0), drop the leftover default maxConcurrency. Applies to both
inbound and outbound xhttp.
---------
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
|
||
|
|
f1a4286e2f |
feat(sub): per-inbound sort order for subscription links
Add a subSortIndex field to inbounds that controls the order of links in subscription output only: the raw sub body, the HTML sub page, and the JSON/Clash formats (all served from the same query). Lower values come first; ties keep id order. The panel inbound list is unaffected. The value is editable in the inbound form next to the share-address fields, propagates to nodes via wireInbound, and follows the usual node-sync rules (copied on import, mirrored while not dirty, never a structural change). Rescoped from #5214 by @Ponywka. |
||
|
|
d04cb10971 |
feat(wireguard): per-peer comments for identifying devices (#5168)
WG peers were only identifiable by their keys. Add an optional panel-side comment per peer: editable in the inbound form (echoed next to "Peer N" in the section header), stored in the settings JSON alongside the panel-only privateKey (xray-core ignores unknown peer fields), and appended to the share link / .conf remark so the device is identifiable in client apps too. |
||
|
|
bade1fcef6 |
feat(ui): allow custom fragment packets ranges, not just presets (#5075)
The fragment "packets" field was a locked dropdown (tlshello / 1-3 / 1-5) in both the finalmask TCP-mask form and the Freedom outbound form, while xray-core accepts any "n-m" packet range. Replace both with an AutoComplete that keeps the presets as suggestions and validates free input as "tlshello" or a numeric range. |
||
|
|
60da6bed15 |
fix(xhttp): stop injecting scMaxEachPostBytes/scMinPostsIntervalMs defaults (#5141)
The panel seeded xhttp configs with scMaxEachPostBytes=1000000 and scMinPostsIntervalMs=30 — xray-core''s own defaults — and emitted them into every generated config and share link. The literal scMinPostsIntervalMs=30 is a stable DPI fingerprint that Russia''s TSPU keys on to block connections on mobile networks. New configs no longer seed these values (empty schema/template defaults, so xray-core applies its internal defaults). For configs already stored with the old defaults, the link/subscription builders now drop values equal to xray-core''s defaults instead of advertising them — covering panel share links, the raw subscription, and the JSON subscription without requiring every inbound to be re-saved. Non-default values the user set deliberately are still emitted. |
||
|
|
2a7342baa9 |
feat: add inbound share address strategy (#5162)
* feat: add inbound share address strategy Allow node-managed inbounds to choose whether exported share links use the node address, routable listen address, or a custom endpoint. Preserve locally configured share address fields during remote node traffic sync. Refs #5161 Refs #4891 * fix: preserve inbound share address settings Forward share address fields to remote nodes, keep existing values when older update payloads omit them, align localhost handling between frontend and subscriptions, and preserve share address settings when cloning inbounds. * fix: keep share address strategy out of subscriptions Limit the new share address strategy to direct exported share links and QR codes. Restore subscription address resolution to the existing panel-owned behavior and update the UI help text accordingly. * fix: address share address review feedback * fix: validate custom share address * fix --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
8f408d2d6a |
feat(routing): show tag (remark) in routing rules list (#5151)
* feat(routing): show tag (remark) in routing rules list Rules table and mobile cards showed raw inboundTag while the form already used remarks. Display "tag (remark)" when a remark exists; saved rules still store tags only. Signed-off-by: aleskxyz <39186039+aleskxyz@users.noreply.github.com> * feat(inbounds): show "tag (remark)" consistently wherever an inbound is listed Add a shared formatInboundLabel/formatInboundTag helper and apply the "tag (remark)" format across the routing rules table, mobile cards, the rule form and route tester, plus the client attach/detach/filter modals and the attached-inbounds column. Falls back to the bare tag when no distinct remark exists. Also fix the routing rules list mis-rendering inbounds whose remark contains a comma: formatted entries are now carried as an array end to end instead of being joined and re-split on commas. --------- Signed-off-by: aleskxyz <39186039+aleskxyz@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
941eba546d |
feat(clients): restore traffic usage progress bars on Clients page (#5150)
Bring back the v2.9.x traffic column UX: used amount, color-coded progress bar, limit/infinity label, and hover popover with upload/download/remaining breakdown. Adds a shared ClientTrafficCell component, traffic display helpers, and unit tests. |
||
|
|
c7a76e9626 |
fix: enable XTLS vision flow for VLESS+XHTTP+vlessenc in UI and share links (#5157) (#5185)
* fix: enable XTLS vision flow for VLESS+XHTTP+vlessenc in UI and share links (#5157) * fix: enable xtls-rprx-vision flow for VLESS XHTTP with vlessenc encryption (#5157) The flow selector was hidden and the vless:// link omitted flow= because: 1. The backend gate (inboundCanEnableTlsFlow) only accepted tcp+tls/reality. 2. The PR #5185 frontend check used `encryption === 'vlessenc'`, which never matches — the stored value is a generated ML-KEM dotted string, not the CLI subcommand name. Fix: extend inboundCanEnableTlsFlow to also return true for XHTTP when a non-none vlessenc encryption/decryption value is present. Update all three call-sites (inbound.go TlsFlowCapable field, client_crud.go clientWithInboundFlow, inbound_clients.go copy-flow path) and the sub/service.go link generator. Scope is XHTTP-only: TCP without tls/reality is intentionally excluded. Add inbound_protocol_test.go covering the new and existing gate combinations, extend client_flow_isolation_test.go with xhttp+vlessenc cases, and add frontend tests for canEnableTlsFlow with real ML-KEM key values. --------- Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
1ad483ede6 |
fix: expose streamSettings for Tunnel inbounds to support TProxy (#5171)
* fix: expose streamSettings for Tunnel inbounds to support TProxy * fix(ui): hide security tab for tunnel inbounds when stream is enabled tunnel (dokodemo-door) does not support TLS or Reality, so showing the security tab only results in a fully-disabled radio group. Exclude tunnel alongside wireguard from the security tab. * fix(tunnel): restrict stream tab to sockopt-only and fix transportless schema Tunnel (dokodemo-door) only needs sockopt.tproxy for TProxy mode — no user-selectable transport. Add hasSelectableTransport flag to hide the network picker, per-network sub-forms, ExternalProxy, and FinalMask for both tunnel and wireguard, matching the pattern already used for Hysteria. Fix a pre-existing Zod schema bug where NetworkSettingsSchema was a bare discriminatedUnion requiring `network` to be present. Wireguard and tunnel submit streamSettings without a `network` key, causing "Invalid discriminator value. Expected 'tcp' | ..." on every save. Fix by adding a transportless union branch (z.never().optional()) alongside the transport DU; also add ?? 'tcp' fallback in inbound-link.ts where stream.network is now string | undefined. Three regression tests added. --------- Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com> Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com> |
||
|
|
4002be4ade |
feat: support latest Wireguard features from Xray-core (PRs #5643, #5833, #5850) (#5131)
* feat: support latest Wireguard features from Xray-core Implements support for Xray-core PRs #5833, #5643, and #5850 for Wireguard Inbounds: - Adds 'domainStrategy' and 'workers' to Wireguard inbound configuration. - Enables the Stream Settings tab for Wireguard inbounds to configure 'sockopt' and 'finalmask', hiding the irrelevant 'network' transmission dropdown. - Adds the 'randRange' field to the 'noise' UDP Finalmask obfuscation settings. * fix --------- Co-authored-by: Rqzbeh <Rqzbeh@example.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com> |
||
|
|
fe62c39a53 |
fix: inbound edit validation failure and legacy copy to clipboard (#5132)
* fix: auto-enable clients when resetting traffic When a client's traffic is exhausted, the panel automatically disables the client and pushes enable: false to the nodes. However, when an admin clicked 'Reset Traffic' or used bulk reset, the counters were zeroed but the client was left disabled. This forced administrators to manually re-enable the client across the central panel and remote nodes. This patch updates ResetTrafficByEmail and BulkResetTraffic to automatically set Enable: true for any previously disabled client and push the updated settings to nodes, ensuring the client is instantly restored upon traffic reset. * fix: inbound edit validation failure and legacy copy to clipboard |
||
|
|
6c1594693d |
feat(mtproto): add domain-fronting and essential mtg options
Expose mtg's [domain-fronting] section (ip/port/proxy-protocol) plus proxy-protocol-listener, prefer-ip, and debug on MTProto inbounds. Each key is written to the generated mtg-<id>.toml only when set, so mtg's own defaults apply otherwise. The instance fingerprint now covers these fields, so editing an option restarts the sidecar. Since MTProto is mtg-served (not Xray), sniffing does not apply: hide the Sniffing tab and the Advanced sniffing sub-editor, drop it from the Advanced "All" JSON view, and emit empty sniffing in the wire payload, all gated by a new canEnableSniffing predicate. |
||
|
|
f8e89cc848 |
fix(mtproto): reap orphaned mtg, fix SysLog viewer, mtg log visibility, export remark (#5105) (#5107)
* fix(logs): render journalctl output in the SysLog viewer The log viewer's parseLogLine only understood the app-log format (2006/01/02 15:04:05 LEVEL - body). With SysLog ticked the backend returns journalctl lines (Mon DD HH:MM:SS host ident[pid]: LEVEL - body), so the parser mistook the journal time for the level and dropped the body, leaving only timestamps. Detect and strip the journald prefix, keep the journal timestamp as the stamp, then parse the real level and body from the remainder. * feat(mtproto): surface mtg output and add status reporting mtg's stdout/stderr was captured by a writer that kept only the last line and showed it nowhere, so the reason a proxy could not reach Telegram was invisible. Stream mtg output line-by-line into the x-ui log, tagged per inbound, so it appears in the panel log viewer and journald. Also fix mangled log lines: logger.Info uses fmt.Sprint, which drops the space between adjacent string operands, producing output like 'inbound3on0.0.0.0:8443'. Switch the affected mtproto calls to the formatted (*f) variants. Add show_mtproto_status to x-ui.sh so 'x-ui status' reports each mtproto inbound's mtg process state and bind address. * fix(logs): parse all journalctl message shapes in SysLog viewer Real journalctl output mixes four message shapes after the 'Mon DD HH:MM:SS host ident[pid]:' prefix: go-logging 'LEVEL - msg' (x-ui/xray), Go std-log with an embedded date (net/http, runtime), telego's '[timestamp] LEVEL msg', and systemd lines. The viewer only understood the first, so std-log and telego lines — which never contain ' - ' — collapsed to a bare timestamp (e.g. the 8s telego 409 spam). Extract the parser into a pure, testable module and teach it the other shapes: strip the redundant Go std-log date, lift the level out of telego brackets, and always keep the message body. Add a unit test covering each shape with real captured lines. * fix(mtproto): reap orphaned mtg sidecars so a stale one can't break new clients On Linux x-ui does not kill its mtg children when it dies (no kill-on-exit, unlike the Windows job object). After a crash, OOM, kill -9, or update, a stale mtg keeps holding the inbound port with an OLD secret, so new clients fail the FakeTLS handshake and get silently domain-fronted to the fakeTLS domain instead of proxied to Telegram (a few MB of traffic, never connects). Sweep orphans at startup: on the first reconcile, before x-ui starts any of its own mtg, scan /proc and SIGKILL any process whose executable is our mtg-<goos>-<goarch> binary. x-ui is the sole owner of mtg, so anything alive then is an orphan. Runs once per process (swept guard), survives the binary-deleted-during-update case via /proc/<pid>/cmdline, and is a no-op on Windows (job object) and other platforms. Also clear stray mtg in update.sh/install.sh after stopping x-ui, anchored to the 'mtg-linux-<arch> run ' invocation so the pattern can't match unrelated command lines (e.g. x-ui.sh's own 'grep mtg-linux'). * fix(logs): drop dead body initializer flagged by eslint no-useless-assignment * fix(mtproto): drop remark fragment from tg://proxy export link The mtproto export link appended the inbound remark as a URL fragment (tg://proxy?server=...&port=...&secret=...#remark). Telegram Desktop rejects a proxy deep link with a trailing fragment as 'This proxy link is invalid', breaking one-click import, and a remark is meaningless for proxy links across clients. Stop adding it in both the panel link (genMtprotoLink) and the subscription service. Fixes #5105. * fix(x-ui.sh): remove unused check_mtproto_status helper show_mtproto_status does its own process check, so check_mtproto_status was dead code. Drop it (per Copilot review on #5107). |
||
|
|
1ca5924a44 |
feat(mtproto): add MTProto (FakeTLS) protocol via managed mtg sidecar (#5076)
* feat(mtproto): add MTProto (FakeTLS) protocol via managed mtg sidecar Xray-core has no mtproto proxy, so mtproto inbounds run as standalone mtg (9seconds/mtg) sidecar processes managed by the panel — one per inbound — and are excluded from the generated Xray config entirely. - model: MTProto protocol constant, validator, and FakeTLS secret helpers (GenerateFakeTLSSecret/HealMtprotoSecret) - mtproto package: per-inbound mtg process manager with reconcile, graceful stop, and best-effort Prometheus traffic scraping - runtime: delegate mtproto inbounds to the mtg manager instead of the Xray gRPC API; skip mtproto when building the Xray config - web: boot reconcile + StopAll wiring, periodic reconcile/traffic job, port-conflict transport, secret healing on inbound add/update - sub: tg:// proxy share-link generation - frontend: protocol option, Zod schema, Protocol tab (FakeTLS domain + regenerable secret), info-modal link, and i18n - provisioning: fetch mtg v2.2.8 in install.sh, DockerInit.sh, and the Linux + Windows release workflows * fix * fix * fix: address Copilot review comments on mtproto PR - web/web.go: create NewMtprotoJob once and reuse for cron + initial run - mtproto/manager.go: StopAll cleans up per-inbound config files on shutdown - mtproto/manager.go: CollectTraffic releases mutex before HTTP scrapes to avoid blocking Ensure/Reconcile/Remove during network I/O - database/model/model.go: panic on crypto/rand failure in mtprotoRandomMiddle instead of silently producing a weak all-zero secret - install.sh: fix chmod to handle renamed bin/mtg-linux-arm on armv5/v6/v7 |
||
|
|
af3c808444 | fix: default hysteria tls to no utls fingerprint | ||
|
|
483952cfa0 |
fix(finalmask): validate fragment mask length so empty/zero-min can't crash xray
A fragment TCP finalmask with an empty length (the form's default for a newly added mask) serializes to a 0-0 range, and xray-core rejects LengthMin == 0 with a fatal config error that aborts the whole process, taking every inbound offline. Default a new fragment mask to length 100-200 and add a form validator rejecting an empty value or a zero minimum range before save. Verified against xray 26.6.1 (#4998). |
||
|
|
6ed6f57b5c |
fix(panel): normalize XHTTP/sockopt/Reality wire output and validate REALITY target (#4988)
* fix(panel): normalize XHTTP/sockopt/Reality wire output and validate REALITY target Strip mode-specific XHTTP fields for stream-one, reset harmful sockopt defaults to 0, split server/client Reality fields on save, validate target host:port in the inbound form, and expose Happy Eyeballs for the direct freedom outbound. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(panel): keep REALITY public key on the wire, guard freedom noises The REALITY server/client wire split deleted realitySettings.settings on save, but the panel stores the REALITY public key there and every share-link / subscription generator reads it back from that path (frontend inbound-link.ts, Go subService/subJsonService/subClashService). Stripping it produced empty pbk= links, breaking client connectivity after save+reload. Revert the reality normalization (drop normalizeRealityForWire and the key sets), restore the inbound REALITY form fields (uTLS, spiderX, publicKey, mldsa65Verify) while keeping the new validated target field, and restore the mldsa65Verify clear handler. Also guard freedomToWire against undefined noises/finalRules (same defensive treatment as the existing fragment guard, issue #4686) which the new freedom-outbound test surfaced as a crash. Tests now assert the public key is preserved. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com> |
||
|
|
5b9db13e55 |
fix(finalmask): treat sudoku customTables as array of tables
customTables is the plural array form of customTable, so default it to [""] and edit it with a tags Select instead of binding a text Input to an array value. |
||
|
|
e7ffae5329 |
fix(outbound): import ech and pcs from TLS share links
The vless/trojan link parser's TLS branch read only sni/fp/alpn, so the ech (echConfigList) and pcs (pinnedPeerCertSha256) query params were dropped on import even though buildStream allocates both fields. Read them in applySecurityParams to match the inbound link generator and the hysteria2 parser. |
||
|
|
a8d5d0dfab |
fix(external-proxy): relabel "Host" as "Address", add per-entry ECH (#4935)
The external proxy "Host" field was bound to dest (the connection address that becomes the link host) but labeled "Host", misleading users into thinking it set a transport host header. Relabel it to "Address" to match what it actually controls. Add per-entry ECH (echConfigList) to the external proxy schema, form (shown under Force TLS = TLS), the TS link generator, and the Go sub services: ech is emitted on share links and vmess objects, and written into the stream so the JSON subscription picks it up via the existing tlsData reader. |