docs: vendor the documentation site into the monorepo

Fold the standalone 3x-ui-docs project (Next.js 16 + Fumadocs, deployed to
docs.sanaei.dev) into docs/ so the panel and its documentation share a single
source of truth, the way sing-box keeps its docs in-tree. The old repo becomes
redundant and can be retired.

- Import the full site under docs/ (app, components, content, lib, public,
  scripts, config). The self-contained pnpm project sits alongside the existing
  engineering notes with no filename collisions.
- Re-point "Edit on GitHub" links from MHSanaei/3x-ui-docs to this repo's
  docs/content/docs path (docs/lib/shared.ts, docs/app/.../page.tsx).
- Add docs-ci.yml and docs-deploy.yml under .github/workflows/, scoped to
  docs/** and run with working-directory: docs, since GitHub only runs
  workflows from the repo-root .github/. deploy-static.yml's GitHub Pages
  publish (CNAME docs.sanaei.dev) carries over unchanged.

Follow-up (outside this commit): attach the docs.sanaei.dev custom domain to
this repository's Pages (or set the Vercel project's root directory to docs),
confirm the site is live from the monorepo, then delete MHSanaei/3x-ui-docs.
This commit is contained in:
MHSanaei
2026-07-07 23:07:14 +02:00
parent 2c49dbf54e
commit 9b91f0f42e
283 changed files with 44179 additions and 0 deletions
@@ -0,0 +1,157 @@
---
title: Outbounds & Routing
description: Shape egress in 3x-ui — WARP and NordVPN outbounds, outbound subscriptions (server pools), routing rules, and load balancers.
icon: Route
---
Inbounds accept clients; **outbounds** decide where their traffic goes next.
3x-ui can route traffic through Cloudflare WARP, NordVPN, or arbitrary outbound
pools imported from a subscription, and select between them with routing rules
and balancers.
## Editing outbounds & routing
Outbounds, routing rules, balancers, DNS, and logging all live in the **Xray
configuration** (the config template you edit under Xray Settings). There's no
separate per-rule UI — you edit the JSON, and the panel reloads Xray. The panel
also offers an **outbound connectivity test** and a **route test** (ask the
running core which outbound a given destination would use).
## Build an outbound
Every outbound is a JSON object with up to four parts: a **`tag`** (referenced by
routing rules and balancers), a **`protocol`**, protocol-specific **`settings`**,
and — for proxy protocols — **`streamSettings`** that must match the remote
inbound's transport and security. Two outbounds are almost always present:
- **`freedom`** sends traffic straight to its destination — the default egress.
Optionally set a `domainStrategy` (e.g. `UseIP`) to control how hostnames resolve.
- **`blackhole`** drops traffic. Route unwanted destinations (ads, torrents) here.
```json title="freedom + blackhole"
{
"outbounds": [
{ "tag": "direct", "protocol": "freedom", "settings": {} },
{ "tag": "block", "protocol": "blackhole", "settings": {} }
]
}
```
A **proxy** outbound (VLESS, VMess, Trojan, Shadowsocks) forwards to another
server — handy for chaining or sending select traffic abroad. Mind the wire
shapes 3x-ui uses: **VLESS is the flat form** (`address`/`port`/`id`/`flow`/
`encryption`), **VMess uses `settings.vnext[]`**, and **Trojan/Shadowsocks use
`settings.servers[]`**. The `streamSettings` must mirror the destination's
[transport and security](/docs/config/transports).
Assemble any outbound below and paste the JSON into **Xray Settings → Outbounds**:
<OutboundGenerator />
## Cloudflare WARP
WARP lets your server egress through Cloudflare's network. 3x-ui can register a
WARP account for you and wire it into a WireGuard outbound tagged **`warp`**:
<Steps>
<Step>
### Add a `warp`-tagged outbound
Create a WireGuard outbound with the tag `warp` in your Xray config.
</Step>
<Step>
### Register WARP
From the panel's WARP controls, register an account. 3x-ui fills the outbound's
keys, addresses, reserved bytes, and peer endpoint automatically.
</Step>
<Step>
### (Optional) auto-rotate the IP
Set a WARP update interval (in **days**) to periodically rotate the WARP IP. A
free license can also be applied.
</Step>
</Steps>
Route the traffic you want (for example specific domains) to the `warp` outbound
with a routing rule.
## NordVPN
3x-ui can fetch NordVPN (NordLynx/WireGuard) credentials from an access token (or
accept a private key directly) and list countries/servers, so you can build a
NordVPN outbound.
## Outbound subscriptions (server pools)
An **outbound subscription** imports a remote share-link subscription and injects
its servers as **outbounds** into the running Xray config — without touching your
saved template. This is the recommended way to subscribe to a *pool* of servers.
| Field | Default | Meaning |
| ---------------- | ------- | -------------------------------------------------------------- |
| `url` | — | The remote subscription URL (SSRF-guarded). |
| `tagPrefix` | auto | Prefix for generated outbound tags (e.g. `hk-`); blank = `subN-`. |
| `updateInterval` | `600` | Refresh interval in **seconds**. |
| `prepend` | `false` | Place these outbounds before your manual ones. |
| `priority` | `0` | Merge order (lower first). |
Imported outbounds get **stable tags**: the same server keeps the same tag across
refreshes, so exact-tag routing/balancer selectors stay pinned — while
prefix/wildcard selectors (e.g. `hk-*`) automatically pick up new servers as the
pool changes. Supported link schemes: `vmess`, `vless`, `trojan`, `ss`,
`hysteria2` (`hy2`), and `wireguard` (`wg`). The panel refreshes enabled
subscriptions on a timer and reloads Xray when something changes.
## Routing rules
**Routing rules** decide which outbound (or balancer) each connection uses. Each
rule is a `field`-type matcher: set any of `domain`, `ip`, `port`, `network`,
`protocol`, `inboundTag`, `sourceIP`, … and point it at an **`outboundTag`** or a
**`balancerTag`**. Rules are evaluated **top-to-bottom — the first match wins**, so
put specific rules above general ones.
```json title="route ads to blackhole, private IPs direct"
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{ "type": "field", "domain": ["geosite:category-ads-all"], "outboundTag": "block" },
{ "type": "field", "ip": ["geoip:private"], "outboundTag": "direct" }
]
}
}
```
## Balancers
A **balancer** groups outbounds by a **selector** (tag prefixes, including the
wildcard pools from outbound subscriptions) and spreads or fails traffic over them
with a **strategy**:
| Strategy | Picks… | Needs a monitor |
| ------------- | ----------------------------------------------- | ------------------------ |
| `random` | a random member per connection | no |
| `roundRobin` | members in rotation | no |
| `leastPing` | the lowest-latency member | **`observatory`** |
| `leastLoad` | the most stable member by sampled load | **`burstObservatory`** |
Reference a balancer from a rule via `balancerTag`. `leastPing` and `leastLoad`
need a health monitor, which Xray places at the **top level** of the config
(`observatory` / `burstObservatory`, **not** inside `routing`). The panel can
report balancer status and **override** a balancer to a specific outbound for
testing.
Build the routing block — rules, balancers, and the matching observatory — here:
<RoutingBuilder />
<Callout type="warn">
Outbounds that reach external services are fetched with SSRF protection — by
default private/internal addresses are blocked unless you explicitly allow them
per source.
</Callout>