3x-ui/docs
YoungReckless4 8f162994ef
Some checks are pending
CI / go-test (push) Waiting to run
CI / postgres-durable-first (push) Waiting to run
CI / codegen (push) Waiting to run
CI / govulncheck (push) Waiting to run
CI / race (push) Waiting to run
CI / fuzz-smoke (push) Waiting to run
CI / golangci (push) Waiting to run
CI / frontend (push) Waiting to run
CodeQL Advanced / Analyze (go) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Docs CI / build (push) Waiting to run
Docs Deploy (GitHub Pages) / build (push) Waiting to run
Docs Deploy (GitHub Pages) / deploy (push) Blocked by required conditions
Release 3X-UI / build (386) (push) Waiting to run
Release 3X-UI / build (amd64) (push) Waiting to run
Release 3X-UI / build (arm64) (push) Waiting to run
Release 3X-UI / build (armv5) (push) Waiting to run
Release 3X-UI / build (armv6) (push) Waiting to run
Release 3X-UI / build (armv7) (push) Waiting to run
Release 3X-UI / build (s390x) (push) Waiting to run
Release 3X-UI / Build for Windows (push) Waiting to run
Release 3X-UI / Publish rolling dev release (push) Blocked by required conditions
feat(clients): let admins set PersistentKeepalive on tunnel clients (#6377)
* feat(clients): let admins set PersistentKeepalive on tunnel clients

model.Client already carries KeepAlive, and every AmneziaWG/WireGuard client
config emitter already writes PersistentKeepalive when it is above zero -- but
nothing in the UI could set it, so it stayed 0 and the line was never emitted.

Without it a peer that goes quiet has nothing to trigger a handshake: WireGuard
only initiates when it has data to send. An idle client stays disconnected
after any interruption -- a NAT mapping timing out, a device sleeping, the
panel restarting -- until the user generates traffic themselves.

New clients default to 25, the conventional value, which also keeps the NAT
mapping open. Existing clients keep whatever they have, and 0 remains valid and
means "do not send keepalives".

* fix(clients): let an explicit 0 actually disable PersistentKeepalive

Addresses review feedback on the previous commit.

UpdateInboundClient carries a stored keepalive forward whenever the incoming
one is zero, so the settings JSON and the running peer survive a metadata-only
edit that omits the field. That was a 0 -> 0 no-op while no UI could set a
nonzero value. Now that the client form can, the carry-forward became reachable
in the other direction: a client created at the form's default of 25 could
never be returned to 0, and the hint text shipped to all 13 locales -- "0
disables it" -- described something the backend silently refused. The save even
reported success, because a settings blob that came back byte-identical skips
the transaction entirely.

The zero value cannot carry that distinction, so model.Client.KeepAlive becomes
*int: nil means the field was never sent, &0 means "send no keepalives". The
pointer survives the internal marshal in ClientService.Update, which is where an
explicit 0 was being erased by omitempty before UpdateInboundClient ever saw it.
ClientRecord.KeepAlive stays a plain int -- it is the stored column, where
"unset" has no meaning -- and the conversions bridge the two.

Two tests, both red before this change in the direction they cover: an explicit
0 must reach wg_keep_alive, and an update that omits the field must still leave
a stored 25 alone.

Also adds the output transform every other numeric field in the client form
already has, so a cleared box sends 0 rather than null.

* fix(clients): repair the keepalive pointer conversion after the main merge

Merging main brought buildAmneziaWGProxy (#6326) in beside the
Client.KeepAlive int -> *int change without reconciling the new call site,
so internal/sub stopped compiling and took every package importing it with
it. The two sides touched different lines, so git merged them without a
conflict -- the green `make verify` on 112b19a8 predates the break.

ToClient also wrapped a stored 0 in a pointer, so omitempty stopped
omitting: a VLESS client's settings JSON gained "keepAlive": 0 on the
attach and bulk-attach paths, and that JSON reaches xray-core verbatim
through GenXrayInboundConfig. wg_keep_alive cannot tell "off" from "never
set", so a stored 0 now stays nil.

Also copies the regenerated openapi.json over the docs mirror, which
nothing in CI checks, and trims two comment blocks to the two-line cap.

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-09-10 22:32:42 +02:00
..
app chore(docs): update dependencies and adapt to zbsearch 4 2026-08-19 18:38:40 +02:00
components fix(sub): randomize fresh panel subscription paths (#6375) 2026-09-03 16:34:37 +02:00
content/docs feat(sub): add legacy Clash subscription endpoint (#6338) 2026-09-10 21:58:55 +02:00
lib fix(sub): bind JSON local inbounds to 127.0.0.1 and keep mux.cool off Vision outbounds (#6418) 2026-09-05 20:44:48 +02:00
public feat(clients): let admins set PersistentKeepalive on tunnel clients (#6377) 2026-09-10 22:32:42 +02:00
scripts docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
.gitattributes docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
.gitignore docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
.oxfmtrc.json Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
.oxlintrc.json Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
architecture.md feat(amneziawg): add AmneziaWG as an outbound protocol (#6320) 2026-09-10 14:50:48 +02:00
CONTRIBUTING.md Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
custom-subscription-templates.md Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
LICENSE docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
next.config.mjs docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
package.json chore(deps): bump docs and frontend deps 2026-09-02 21:37:55 +02:00
pnpm-lock.yaml chore(deps): bump docs and frontend deps 2026-09-02 21:37:55 +02:00
pnpm-workspace.yaml chore(deps): bump docs and frontend deps 2026-09-02 21:37:55 +02:00
postcss.config.mjs docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
proxy.ts docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
pwa-installability-verification.md feat(web): add network-only PWA installability (#6190) 2026-08-18 15:33:20 +02:00
README.md Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
real-client-ip.md Move to TypeScript 7 and the oxc toolchain (oxlint + oxfmt) (#6262) 2026-08-19 15:36:27 +02:00
source.config.ts docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
tsconfig.json docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
vercel.json docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
vitest.config.ts docs: vendor the documentation site into the monorepo 2026-07-07 23:07:14 +02:00
xray-dns.md Add encrypted DNS presets (#5837) 2026-07-09 00:45:35 +02:00

3x-ui

3x-ui Documentation

The official documentation and product site for 3x-ui — an advanced web panel for managing Xray-core servers.

Live site CI License: GPL-3.0 Next.js 16 Fumadocs 16

Read the docs →


Overview

This directory (docs/ in the 3x-ui monorepo) contains the source for docs.sanaei.dev — a static-first documentation and marketing site built with Fumadocs on Next.js. It has no backend, no database, and no auth: every page is prerendered and every tool runs entirely in the browser.

What's inside

The documentation walks you through 3x-ui from first install to day-to-day operation:

  • Getting Started — installation, first login, and updating or uninstalling the panel.
  • Configuration — the panel, inbounds, REALITY, transports, clients, subscriptions, and share links.
  • Operations — reverse proxy, multi-node setups, outbounds & routing, backup/restore, the Telegram bot, and security.
  • Reference — environment variables, the database, ports & firewall, and the HTTP API.
  • Help — troubleshooting, FAQ, migration, and how to contribute.

Interactive tools

The site ships with in-browser helpers that generate configuration for you — no data ever leaves your browser:

Tool What it does
REALITY Config Generator Build a valid REALITY inbound configuration.
Share Link Inspector Decode and inspect vless:// / vmess:// share links.
Install Command Builder Assemble the right install command for your setup.
Reverse Proxy Generator Generate reverse-proxy configs (Nginx / Caddy).
Protocol Wizard Pick and configure the right protocol for your needs.
Firewall Rules Generator Produce firewall rules for your ports.

Tech stack

Layer Technology
Framework Next.js 16 (App Router) · React 19
Docs Fumadocs (-ui / -core / -mdx)
Styling Tailwind CSS v4
Search Orama static index
Language TypeScript (strict)
Tests Vitest for the pure lib/xray logic
Tooling pnpm · oxlint · oxfmt

Quick start

This project uses pnpm (npm lockfiles are gitignored). Run everything from the docs/ directory:

cd docs
pnpm install
pnpm dev        # http://localhost:3000

Useful scripts:

Script Description
pnpm dev Start the dev server
pnpm build Production build (also typechecks)
pnpm typecheck Generate MDX/route types and tsc --noEmit
pnpm lint Run oxlint (.oxlintrc.json)
pnpm test Run unit tests (Vitest)

See CONTRIBUTING.md for the full list and project conventions.

Project structure

app/             # Next.js App Router — layouts, home, docs, OG images, search, llms.txt
components/      # React components — interactive tools, home sections, MDX bindings
content/docs/    # MDX documentation, one folder per locale (en · fa · ru · zh)
lib/             # source config, i18n, GitHub stats, and the unit-tested lib/xray logic
public/          # static assets — logos, favicon, openapi.json, CNAME
scripts/         # build-time scripts (API reference generation)
source.config.ts # Fumadocs MDX schema & collection config
next.config.mjs  # Next.js config (static-export gating)
proxy.ts         # i18n middleware

Internationalization

Documentation is authored in English. Persian (fa, RTL), Russian (ru), and Chinese (zh) locales are wired up; untranslated pages fall back to English so they never 404. English URLs are unprefixed; other locales live under /fa, /ru, /zh.

Deployment

The site builds for two targets:

  • Vercel / Node — pnpm build (static search index + prerendered OG images).
  • GitHub Pages (static export) — DEPLOY_TARGET=static pnpm build → out/.

Contributing

Contributions are welcome! Setup, scripts, and project conventions live in CONTRIBUTING.md.

License

Licensed under GPL-3.0.