VLESS / XHTTP / WS‑TLS subscription & proxy‑relay panel FastAPI panel for Railway + Cloudflare Workers. Manage users, inbounds, Reality / WS‑TLS configs, browser‑side IP scanning and a multi‑location Cloudflare Worker proxy — all from one dashboard.
فارسی: پنل مدیریتی و فروش سرویس VLESS با قابلیت ساخت سابسکریپشن، مدیریت کاربران و اینباندها، اسکنر آیپی (سمت مرورگر) و مدیریت Worker کلادفلر برای پروکسی چندمنطقهای.
- VLESS Reality — keys (x25519 + ML‑DSA‑65 post‑quantum) generated by the Xray binary itself, never by Python crypto. Fresh
pbk/sid/spxper inbound. - VLESS + WS + TLS — the config always points
address/host/sniat the real panel domain automatically (no manual domain entry, nolocalhost). - XHTTP (packet‑up / stream‑up / stream‑one) — 3x‑UI style XHTTP transports.
- Multi‑inbound per user — one user can belong to several inbounds → each produces its own config in the subscription.
- Sub page — clean black + teal page, iOS system font, status config always first, main configs on top and custom‑IP configs below with a labeled separator.
- Custom‑IP (scanner) configs — up to 10 extra WS‑TLS configs whose
addressis a scanned CF/Railway IP, whilehost/snistay on the real domain. - Browser‑side IP scanner — Cloudflare / Railway / TCP. Pings run from the user's own internet via
fetch(..., { mode: 'no-cors' })— no server bandwidth, works on mobile. Cleared results stay cleared (seq‑guarded writes). - Cloudflare Worker manager — connect the panel to your Cloudflare account, deploy a proxy worker, manage a country → proxy‑IP pool, and route only the users who opt in through
Worker Domain + /route/{code}. Railway is never in the VPN data path. - Map + proxy IP selector — pick up to 3 proxy IPs per user with live ping.
- Full dashboard — users, inbounds, groups, live traffic/CPU/RAM, panel music, light/dark mode, FA/EN (sidebar stays put on language switch), SVG‑only icons.
┌─────────────────────────┐
│ Railway (this panel) │ Panel / API / user mgmt / config generator / worker manager
└────────────┬────────────┘
│ (only management + config serving — NOT in the VPN path)
│
┌─────────┐ ┌────▼──────────┐ ┌──────────────────┐ ┌───────────┐
│ Client │ ───▶ │ Worker Domain │ ───▶ │ Cloudflare Worker │ ───▶ │ Proxy IP │ ──▶ Internet
└─────────┘ └───────────────┘ └──────────────────┘ └───────────┘
(users with proxy_ip_enabled=true)
- Normal users connect straight to the panel's Reality / WS inbounds.
- Worker users (
proxy_ip_enabled = true) get a config addressed to the Worker domain with path/route/{country-code}. The Worker looks up the country → proxy IP and forwards the connection. Traffic never crosses Railway.
- Push this repo to GitHub (or use the template directly).
- On Railway: New Project → Deploy from GitHub → select the repo.
- Railway auto‑detects the Python app (
python main.py) and readsPORT. - Add the environment variables below.
- Open the generated
*.up.railway.appURL → sign in with the default password (admin) → change it immediately.
| Variable | Default | Purpose |
|---|---|---|
PORT | 8080 | HTTP port Railway forwards to |
ADMIN_PASSWORD | admin | Initial dashboard password (change after first login) |
SECRET_KEY | spider-panel-secret-key-v2 | Session / hashing secret — set a strong random one |
RAILWAY_PUBLIC_DOMAIN | — | Auto‑set by Railway; used as the real panel domain in configs |
DATA_DIR | /data | Persistence dir for spider_state.json |
WORKER_SYNC_INTERVAL | 3600 | Seconds between auto proxy-source syncs (default: hourly) |
RAILWAY_PUBLIC_DOMAINis provided automatically. The WS‑TLS config generator uses it (or an optional manually set domain) foraddress/host/sni— no manual domain typing required.
The Worker tab in the sidebar walks you through connecting the panel to a Cloudflare Worker. The deployed Worker is a VLESS WS relay (edgetunnel-style): it accepts WebSocket connections from VLESS clients, authenticates them via UUID in its KV store, and forwards traffic through a TCP proxy to the target.
Supported auth: Global API Key (cfk_... + email) or Bearer token.
- Open Worker in the sidebar → Setup form.
- Cloudflare API Token — create a Global API Key at https://dash.cloudflare.com/profile/api-tokens or a scoped token with
Workers Scripts: Edit+KV Storage: Editpermissions. - Email — your Cloudflare account email (required for Global API Key auth).
- Account ID — your Cloudflare account UUID (dashboard overview / right sidebar).
- Click Connect & Deploy. The panel:
- Verifies the token/email
- Auto-discovers your Worker subdomain from the Cloudflare API
- Creates a KV namespace (
spider-worker-kv) and binds it to the Worker - Deploys the VLESS WS relay script (from
worker/_worker.jsin this repo) - Pushes all active panel users (uuid + traffic limit + expiry) to the Worker's KV
- In the Worker tab, the Proxy IP Pool shows all available country proxies (fetched daily from the GitHub source). Use the dropdown to manage them.
How it works: When a user picks a Worker inbound, the panel syncs their
config_uuid→limit_bytes+expireto the Worker's KV. The Worker authenticates by checkinguser:{uuid}in KV. Users whose quota is exceeded or whose expiry has passed get a 403.
Traffic flow: Client → Worker Domain (wss://) → Cloudflare Worker → target IP → Internet. Railway is only the control plane (panel / API / config generation / user management).
worker/_worker.js is the deployed script. The panel reads it at deploy time and injects __PANEL_DOMAIN__ and __PANEL_TOKEN__ before uploading. You can edit the Worker logic with a normal git push — no need to touch Python code.
The panel can pull a fresh country → proxy list from a daily GitHub source and push it to the deployed Worker automatically:
- Every hour (configurable via
WORKER_SYNC_INTERVAL, default3600), if the worker is connected and auto-sync is on, the panel fetches the source, parses it and re-deploys the worker with the updated map. - Source defaults to the NiREvil
ProxyIP-Daily.mdlist (62 countries, best-risk IPs first). You can change the URL in the Worker tab. - The parser decodes each section's flag emoji → ISO country code, keeps the first 3 IPs per country, and the Worker round-robins across them per request.
- Manual entries you add/edit in the panel carry a
manualflag and survive source refreshes (your overrides are never wiped). - In the Worker tab you get: auto-update toggle, source URL, a "Update Now" button, and a country/IP preview that works even before the worker is connected.
Update flow:Worker tab → set source → Save → Update Now (or just wait for the hourly sync).
| Type | Security | Transport | Notes |
|---|---|---|---|
| VLESS Reality | reality | tcp / xhttp | x25519 + ML‑DSA‑65 keys from the Xray binary |
| VLESS WS TLS | tls | ws | auto panel domain for address/host/sni |
| VLESS XHTTP | tls | xhttp | packet‑up / stream‑up / stream‑one |
| VMess / Trojan / Shadowsocks | tls | ws/grpc/tcp | legacy protocols |
| Custom‑IP WS TLS | tls | ws | address = scanned IP, host/sni = panel domain |
Sub page layout (per user):
[Status config] ← always first (shows days left + traffic %)
[Main config #1] ← one per selected inbound
[Main config #2]
─────────────────────
[Custom IP (Railway) + 10 configs]
[Custom #1] [Custom #2] …
Runs entirely from the visitor's browser using the fastest, simplest method:
conststart=performance.now();awaitfetch("https://1.2.3.4/",{mode: "no-cors",cache: "no-store"});constping=performance.now()-start;- Pings 16 targets at a time with
Promise.all, sorts by latency, live‑saves the best 10. - Cloudflare / Railway tabs generate random candidate IPs from known ranges.
- TCP tab resolves a domain to its IPs and reports connect latency.
- Uses the user's own network & IP — no server bandwidth, works on phones and computers.
- Clearing stops the scan, wipes the list, and the cleared state persists (stale in‑flight writes are dropped server‑side, so results never “come back”).
Saved scanned IPs are consumed when you assign Custom IP (scanner) to a user → up to 10 extra WS‑TLS configs in their sub.
All /api/* endpoints (except the public sub/config ones) require the auth cookie.
| Method | Path | Body |
|---|---|---|
| POST | /api/login | { password } |
| POST | /api/logout | — |
| POST | /api/change-password | { current_password, new_password } |
| Method | Path | Notes |
|---|---|---|
| GET/POST | /api/users | list / create (proxy_ip_enabled, custom_ip_type, proxy_country, …) |
| GET/PATCH/DELETE | /api/users/{id} | read / update / delete |
| GET | /api/users/{id}/config | generated VLESS config |
| GET | /api/users/{id}/qr | QR code |
| GET | /api/sub/{username} | public sub data — configs + custom_configs (split for the sub page) |
| GET | /sub/{identifier} | user subscription page / raw link |
| Method | Path | Notes |
|---|---|---|
| GET/POST | /api/inbounds | list / create (Reality keys auto‑generated via the Xray binary) |
| PATCH/DELETE | /api/inbounds/{id} | update / delete |
| POST | /api/inbounds/{id}/generate-reality-keys | fresh pbk/sid/spx |
| POST | /api/inbounds/{id}/generate-short-id | new short id |
| Method | Path | Notes |
|---|---|---|
| GET | /api/scanner/ips/{ctype} | cf or railway → saved list + seq |
| POST | /api/scanner/save | write list; pass current seq — stale writes rejected |
| GET | /api/scanner/resolve?host= | DNS resolve for TCP tab |
| POST | /api/scanner/ping-batch | server‑side TCP latency probe (fallback) |
| Method | Path | Notes |
|---|---|---|
| GET | /api/worker | status + proxy map (no token) |
| POST | /api/worker/setup | verify token, deploy worker, save connection |
| POST | /api/worker/sync | re‑deploy after proxy changes |
| POST | /api/worker/sync-source | fetch daily source now, update pool + deploy |
| POST | /api/worker/settings | set source_url / auto_sync |
| DELETE | /api/worker | remove connection |
| POST | /api/worker/proxies | add/update a country → proxy |
| DELETE | /api/worker/proxies/{code} | remove a country |
| GET | /api/worker/locations | location status for the map tab |
| Method | Path | Notes |
|---|---|---|
| GET/POST | /api/groups | group management |
| POST | /api/ips/assign | assign IPs to a user |
| GET | /api/tools/reality-settings | Reality presets |
| POST | /api/tools/config-generator | ad‑hoc config builder |
| GET | /api/server/stats | live CPU/RAM/disk/net |
main.py FastAPI app — users, inbounds, config generator, scanner, worker manager, relay
pages.py public page HTML helpers
relay_vless.py VLESS/WS/XHTTP relay logic
xhttp_siz10.py XHTTP streaming helpers
fetch_proxies.py legacy proxy‑list fetcher (no longer used by the UI)
static/
index.html dashboard SPA (sidebar: Dashboard / Users / Inbounds / IP Scanner / Worker / Settings)
sub.html per‑user subscription page
login.html login page
worker/_worker.js Cloudflare Worker template (deployed by the panel, not under /static)
xray/ Xray binary + generated config (installed at runtime)
data/ spider_state.json, scanned/ (gitignored)
- Never share the dashboard URL with users; change the default password right away.
- The Cloudflare API token is kept server‑side and stripped from every API response.
- Use a strong
SECRET_KEY; it protects sessions and password hashing. - Reality private keys are generated by the Xray binary and stored in state; keep
data/private.
pip install -r requirements.txt
python main.py # http://localhost:8080 (login: admin / admin)Set RAILWAY_PUBLIC_DOMAIN or an in‑panel domain to see real domains in WS‑TLS configs locally.
MIT — use it, fork it, build on it. For a production deployment you are responsible for your own infrastructure, users and applicable laws.