@@ -278,9 +278,16 @@ Frontend is a single-page React app served by nginx.
278278- QUIC (UDP/443) blocking — forces TCP fallback for protocols TPROXY
279279 can intercept
280280- Tunnel chaining — VLESS-inside-WireGuard, etc.
281+ - ** Recursive multi-hop chains** — ` config_gen ` follows ` chain_node_id `
282+ transitively, wiring every hop (exit → mid → entry) with cycle
283+ detection and a depth cap (WireGuard is exit-hop only)
281284- ** Proxy Chains** (multi-panel, two-hop VLESS+Reality across two
282285 x-ui panels with independent channels per chain; managed clients
283286 + per-channel delete + live healthcheck)
287+ - ** TLS ClientHello fragmentation (anti-DPI)** — a Settings toggle
288+ splits the outgoing ClientHello across several packets so a DPI box
289+ can't match the SNI in a single read; entirely client-side, tunable
290+ packet mode / length / interval, off by default (needs bundled xray 26.x)
284291- Kill switch — drop all forwarded traffic if xray crashes
285292
286293** Routing**
@@ -295,38 +302,64 @@ Frontend is a single-page React app served by nginx.
295302 gambling, "Work" set routes corp domains direct). Assign devices
296303 individually or in bulk; per-set rules apply first, then fall through
297304 to global rules. DHCP-resistant (MAC-based via dedicated per-set
298- loopback TPROXY ports + xray ` inboundTag ` matching)
305+ loopback TPROXY ports + xray ` inboundTag ` matching). Set-aware
306+ ** export/import** — pick scopes, resolve conflicts, import into
307+ Global / an existing / a new set
308+ - ** Route Explainer** — paste a domain, URL or IP and see which rule
309+ wins and whether it exits proxied, direct or blocked
299310
300311** Health & resilience**
301312- Background liveness probe with two-tier auto-failover: if the failed
302313 node belongs to an enabled NodeCircle, the failover handler delegates
303314 recovery to the circle (which skips dead siblings via pre-ping +
304315 retry); otherwise it walks a configurable fallback list
305- - Speed test per node via short-lived isolated xray instance
316+ - ** Unified speed test** — gates on reachability first (Google /
317+ Cloudflare 204 with a retry, so a dead node fails in ~ 1s instead of
318+ grinding every fallback), then measures the ** average after warm-up
319+ plus the peak** ; streams live, persists both numbers, and flags a
320+ reading older than 6h as stale
321+ - ** Automatic background speed checks** — sweep a chosen scope (all /
322+ a subscription / a group / specific nodes) on an interval so ` best ` /
323+ ` min_speed ` and the UI stay fresh without manual testing; sequential,
324+ staleness-guarded, with per-node error isolation
325+ - One-tap ** reachability check** — confirms a node actually carries
326+ traffic to the internet (204 through the live tunnel), separate from
327+ raw link speed
306328- Naive sidecar supervisor — auto-restarts crashed Naive containers
307329 with a sliding-window rate limiter
308330- Recent Events feed on Dashboard surfaces failovers, sidecar
309331 restarts, geo updates, circle rotations
310332
311333** Balancing & rotation**
312334- Balancer groups (xray's ` leastPing ` or ` random ` strategies)
313- - Node Circles — automatically rotate the active node on a schedule,
314- seamlessly via xray's gRPC API (no dropped connections); each
315- candidate is TCP-pinged with a single retry before switching, so
316- dead siblings are skipped without a connection blip
335+ - Node Circles — schedule seamless active-node rotation via xray's gRPC
336+ API (no dropped connections). A ** ` best ` ** mode plus ** ` max_latency_ms ` **
337+ and ** ` min_speed_mbps ` ** candidate filters pick from real speed data
338+ (never-tested nodes get the benefit of the doubt), and a ** smart-skip**
339+ keeps a scheduled rotation from moving off a healthy, low-latency node
340+ — manual "rotate now" always rotates
317341
318342** Subscriptions**
319343- Periodic refresh from VLESS / VMess / Trojan / SS / Hysteria2 /
320344 Clash YAML / xray JSON subscription URLs
321- - Per-subscription User-Agent (v2ray, clash, sing-box, happ, …),
322- optional regex filter, configurable interval
345+ - ** User-Agent templates** — an editable table (add / edit / delete)
346+ replacing the old hardcoded presets; each row carries a UA string and
347+ optional custom request headers, with catalogue export / import
348+ - Optional ** GeoIP country flags** on imported node names (` 🇳🇱 vless-nl ` )
349+ — opt-in and licence-clean (drop a MaxMind ` GeoLite2-Country.mmdb `
350+ next to the geo data; absent it's a silent no-op)
351+ - Optional regex filter, configurable interval
323352
324353** Devices & DNS**
325354- LAN discovery via ` arp-scan ` , OUI vendor lookup
326355- Per-device routing policy (default / always-include / always-bypass)
327356- Per-domain DNS rules (plain, DoH, DoT)
328357- FakeDNS pool for sniffing-friendly geoip resolution
329358- DNS query log with stats
359+ - ** Host network controls** — change the box's own gateway / DNS from
360+ Settings → Network with an auto-backup + one-click rollback; warns on
361+ a routing self-loop (gateway pointing at the box itself) or a
362+ double-hop through another PiTun
330363
331364** Servers & deployments**
332365- Inventory of remote VPS hosts (host, SSH credentials, tags) separate
@@ -336,22 +369,35 @@ Frontend is a single-page React app served by nginx.
336369- One-click auto-deploy over SSH for ** NaiveProxy** , ** WireGuard** ,
337370 ** x-ui** (3x-ui / x-ui-pro) — live log streaming, status badges,
338371 cascade-clean on uninstall
372+ - ** Per-page Direct switch** — every server / panel / chain SSH
373+ operation runs * through the active node* by default (the same tunnel
374+ the LAN uses); a Direct toggle flips one operation back to a straight
375+ dial for reaching a box while the active node is down
339376- Dedicated ** X-ui Panels** page — full inbound + client management
340377 (6 wired presets covering Reality / TLS / domain modes), live
341378 healthcheck (panel API, xray, nginx, UFW, TLS cert, disk, mem),
342379 cache↔panel sync for hand-added clients, random / custom
343380 fakesite rotation
381+ - ** SNI / REALITY-dest scanner** in the node form — probe a candidate
382+ host for TLS 1.3 + HTTP/2 before saving it as the masquerade target
344383
345384** Operations**
346385- One-click GeoIP / GeoSite refresh — three switchable upstream
347386 profiles: Loyalsoldier (CN-focused community list), runetfreedom
348387 (Russian-internet curated list), v2fly (vanilla baseline)
388+ - ** In-UI self-update** — Settings → Updates checks GitHub (through the
389+ active node, so a throttled direct route isn't a blocker), shows
390+ what's new and applies it with live progress via a host-side agent
349391- Full-fidelity JSON Export/Import for Nodes and Servers — versioned
350392 bundle envelope, append/replace modes, optional secret redaction
351393 (separate from URI/subscription import which is single-node only)
352394- Plain-text URI export (` .txt ` , one ` vless://… ` per line) — share
353395 your node list with any v2rayN-compatible client; symmetric
354- ` Import ` button auto-detects URI list vs JSON bundle
396+ ` Import ` button auto-detects URI list vs JSON bundle. Single-node URI
397+ export straight from a node card too
398+ - ** Login lockout** — five consecutive failed logins lock an account
399+ for 15 minutes (HTTP 429 + ` Retry-After ` ); the primary LAN-only
400+ brute-force guard
355401- Built-in diagnostics page (DNS reachability, gateway, xray status,
356402 resource usage)
357403- Streaming xray log viewer
@@ -411,30 +457,30 @@ curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh |
411457> ``` bash
412458> curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh \
413459> -o /tmp/pitun-install.sh
414- > sudo bash /tmp/pitun-install.sh --version v1.3.0-beta.8
460+ > sudo bash /tmp/pitun-install.sh --version v1.4.12
415461> ` ` `
416462>
417463> ** (B) Pipe with ` bash -s --` separator** (the ` -s --` is ** required** ):
418464> ` ` ` bash
419465> curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh \
420- > | sudo bash -s -- --version v1.3.0-beta.8
466+ > | sudo bash -s -- --version v1.4.12
421467> ` ` `
422468>
423469> ** (C) Environment variable** (no ` -s --` voodoo needed):
424470> ` ` ` bash
425471> curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh \
426- > | sudo PITUN_VERSION=v1.3.0-beta.8 bash
472+ > | sudo PITUN_VERSION=v1.4.12 bash
427473> ` ` `
428474>
429- > ❌ ** Do NOT do this:** ` curl ... | sudo bash --version v1.3.0-beta.8 ` — bash
475+ > ❌ ** Do NOT do this:** ` curl ... | sudo bash --version v1.4.12 ` — bash
430476> swallows ` --version` as its own flag (prints bash' s version + exits)
431477> before our installer ever runs. Common copy-paste trap.
432478
433479Useful flags (work via any of the three forms above; examples use form B):
434480
435481```bash
436- # Pin a specific version (current: v1.3.0-beta.8 )
437- ... | sudo bash -s -- --version v1.3.0-beta.8
482+ # Pin a specific version (example tag — see GitHub Releases for the latest )
483+ ... | sudo bash -s -- --version v1.4.12
438484
439485# Force rebuilding from source (no published release available, or
440486# you' re testing local changes). Slower, needs reliable internet
0 commit comments