helpContent returns the full help document. Kept as a function so future versions can interpolate version info or conditional notes.
()
| 40 | // helpContent returns the full help document. Kept as a function so future |
| 41 | // versions can interpolate version info or conditional notes. |
| 42 | func helpContent() string { |
| 43 | s := func(ss ...string) string { return strings.Join(ss, "") } |
| 44 | return s( |
| 45 | "# snix — client-side DPI bypass via SNI spoofing\n\n", |
| 46 | |
| 47 | "## The one-paragraph explanation\n", |
| 48 | "When your browser opens an HTTPS connection, the very first TLS packet\n", |
| 49 | "— the ClientHello — contains the server name in plain text (the SNI\n", |
| 50 | "field). Nation-state DPI systems read this field to decide whether to\n", |
| 51 | "block your connection. snix exploits a quirk: most DPI devices don't\n", |
| 52 | "reassemble TCP streams, they inspect individual packets. We inject a\n", |
| 53 | "fake ClientHello with an ALLOWED SNI immediately after the TCP\n", |
| 54 | "handshake. The DPI sees \"allowed domain, let this flow through.\" Our\n", |
| 55 | "fake packet has a deliberately wrong TCP sequence number (or a\n", |
| 56 | "corrupted checksum), so the real server silently drops it. Meanwhile,\n", |
| 57 | "the real TLS handshake proceeds normally — and because the DPI has\n", |
| 58 | "already whitelisted the flow, it passes.\n\n", |
| 59 | |
| 60 | "## Prerequisites\n", |
| 61 | "- Linux or Windows (macOS / Android planned).\n", |
| 62 | "- On Linux: root (iptables + NFQUEUE).\n", |
| 63 | "- On Windows: Administrator + WinDivert.dll + WinDivert64.sys\n", |
| 64 | " next to snix.exe. See docs/windows.md.\n", |
| 65 | "- A TLS-based proxy server reachable on port 443 (Xray, v2ray, etc.).\n\n", |
| 66 | |
| 67 | "## Screens\n", |
| 68 | "1. Home — overview + config status.\n", |
| 69 | "2. Profiles — list configured profiles, switch active, view details.\n", |
| 70 | "3. Scan — test SNIs + IPs against the live network; paste results.\n", |
| 71 | "4. Run — start/stop the bypass engine as a subprocess.\n", |
| 72 | "5. Settings — toggle randomization knobs in the active profile.\n", |
| 73 | "6. Help — this screen.\n", |
| 74 | "7. About — version + credits.\n\n", |
| 75 | |
| 76 | "## Navigation keys (global)\n", |
| 77 | " 1–7 jump to tab by number\n", |
| 78 | " ] / [ cycle tabs right / left\n", |
| 79 | " ↑↓ / j k scroll\n", |
| 80 | " ? help (you are here)\n", |
| 81 | " q quit (Ctrl+C everywhere)\n\n", |
| 82 | |
| 83 | "## Scan screen\n", |
| 84 | " s probe SNIs against the target IP\n", |
| 85 | " i probe the default Cloudflare IP pool\n", |
| 86 | " a run both in parallel\n", |
| 87 | " r re-run last scan\n", |
| 88 | " e save top results into the active profile (writes config.yaml)\n\n", |
| 89 | |
| 90 | "## Run screen\n", |
| 91 | " s start the snix engine in a subprocess (opens platform backend)\n", |
| 92 | " x stop the engine (SIGINT → graceful shutdown)\n", |
| 93 | " r restart\n", |
| 94 | " The Run screen tails the engine's stdout live.\n\n", |
| 95 | |
| 96 | "## Profiles screen\n", |
| 97 | " ↑↓ move cursor\n", |
| 98 | " ⏎ mark as active (writes config.yaml immediately)\n", |
| 99 | " e open this profile in $EDITOR (exits TUI temporarily)\n\n", |