C³ CELERITY

April 17, 2026 · View on GitHub

Fast. Simple. Long-lasting.

English | Русский

License: MIT Docker Pulls Docker Image Size Node.js Hysteria Xray Telegram Support

C³ CELERITY by Click Connect — modern web panel for managing Hysteria 2 and Xray VLESS proxy servers with centralized authentication, one-click node setup, and flexible user-to-server group mapping.

Built for performance: Lightweight architecture designed for speed at any scale.

C³ CELERITY Dashboard
Dashboard — real-time server monitoring and statistics

⚡ Quick Start

Updating an existing installation? See Safe Production Updates.

1. Install Docker (if not installed):

curl -fsSL https://get.docker.com | sh

2. Deploy panel (Docker Hub - recommended):

mkdir hysteria-panel && cd hysteria-panel

# Download required files
curl -O https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/docker.env.example

# Create Greenlock SSL config (required for HTTPS)
mkdir -p greenlock.d
curl -o greenlock.d/config.json https://raw.githubusercontent.com/ClickDevTech/hysteria-panel/main/greenlock.d/config.json

cp docker.env.example .env
nano .env  # Set your domain, email, and secrets
docker compose -f docker-compose.hub.yml up -d

Alternative: Build from source (for development or customization)

git clone https://github.com/ClickDevTech/hysteria-panel.git
cd hysteria-panel
cp docker.env.example .env
nano .env  # Set your domain, email, and secrets
docker compose up -d

3. Open https://your-domain/panel

Planning to manage the panel from AI assistants? See MCP Setup Guide.

Required .env variables:

PANEL_DOMAIN=panel.example.com
ACME_EMAIL=admin@example.com
ENCRYPTION_KEY=your32characterkey  # openssl rand -hex 16
SESSION_SECRET=yoursessionsecret   # openssl rand -hex 32
MONGO_PASSWORD=yourmongopassword   # openssl rand -hex 16

🐳 Dokploy (Development and Release)

Use docker-compose.dokploy.yml when deploying through Dokploy with Traefik.

Development Mode (build from current branch)

  1. In Dokploy, create a project from this repository/branch.
  2. Set compose path to docker-compose.dokploy.yml.
  3. Add env vars from docker.env.example and set at least:
    • MONGO_PASSWORD
    • PANEL_DOMAIN
    • ACME_EMAIL
    • ENCRYPTION_KEY
    • SESSION_SECRET
    • DOKPLOY_PANEL_HOST (domain used in Traefik Host(...) rule)
    • DOKPLOY_TRAEFIK_SERVICE_PORT (Traefik target/backend port, default 3000)
  4. Deploy: Dokploy will run build: . and start the stack.

This mode is best when you test branch changes before publishing a release image.

Release Mode (Docker Hub image)

If you want stable deploys from Docker Hub tags (without building), replace backend image source in Dokploy compose with:

backend:
  image: clickdevtech/hysteria-panel:latest
  # or pin a release tag, e.g. clickdevtech/hysteria-panel:v1.2.3
  restart: always
  depends_on:
    mongo:
      condition: service_healthy
    redis:
      condition: service_healthy
  expose:
    - "${DOKPLOY_TRAEFIK_SERVICE_PORT:-3000}"
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.celerity.rule=Host(`${DOKPLOY_PANEL_HOST}`)"
    - "traefik.http.routers.celerity.entrypoints=websecure"
    - "traefik.http.routers.celerity.tls=true"
    - "traefik.http.services.celerity.loadbalancer.server.port=${DOKPLOY_TRAEFIK_SERVICE_PORT:-3000}"
  env_file:
    - .env

Use latest for fast updates, or pin an explicit tag for predictable production rollouts.


✨ Features

  • 🖥 Web Panel — Full UI for managing nodes and users
  • 🔐 Dual Protocol — Hysteria 2 and Xray VLESS on one panel
  • 🛡️ Panel 2FA (TOTP) — Unified TOTP verification flow for admin login and sensitive security actions
  • 🚀 Auto Node Setup — Install Hysteria/Xray, certs, port hopping in one click
  • 👥 Server Groups — Flexible user-to-node mapping
  • ⚖️ Load Balancing — Distribute users by server load
  • 🚫 Traffic Filtering (ACL) — Block ads, domains, IPs; route through custom proxies
  • 🧩 Advanced Hysteria Config — optional ACME challenge options, masquerade modes, resolver, speed test, sniffing, and QUIC tuning
  • 📊 Statistics — Online users, traffic, server status
  • 📱 Subscriptions — Auto-format for Clash, Sing-box, Shadowrocket, Hiddify
  • 🔄 Backup/Restore — Automatic backups with S3 support
  • 💻 SSH Terminal — Direct node access from browser
  • 🔑 API Keys — Secure external access with scopes, IP allowlist, rate limiting
  • 🪝 Webhooks — Real-time event notifications with HMAC-SHA256 signing
  • 🗺 Network Map — Visual cascade topology with Forward/Reverse chain routing (beta)
  • 🤖 MCP Integration — Native AI assistant support (Claude, Cursor, etc.) for panel management

⚠️ Beta Features

Network Map & Cascade Topology

Status: beta — fully functional, but manual verification after deploy is recommended.

Cascade topology allows building server chains where clients connect to one node while traffic exits through another. This is useful in scenarios where the entry point must reside in a specific network or jurisdiction — for example, when connections must originate from local cloud provider IP ranges.

Why Use This

Many corporate and carrier networks apply IP-range filtering. Traffic to well-known local hosting providers may pass unrestricted, while connections to foreign IPs are blocked or throttled. Cascade topology solves this: clients connect to a server in a "trusted" IP range, and traffic is transparently proxied to an external server.

Xray Mechanisms Used

The panel generates Xray-core configurations using the following mechanisms:

MechanismPurpose
Reverse ProxyXray bridge/portal — allows a server behind NAT to initiate a connection to a public node and receive traffic through it
Outbound ChainingSequential proxying through multiple outbounds via proxySettings.tag
REALITYTLS handshake camouflage to look like a connection to a legitimate site; no domain or certificate required
Transport Layer ProxytransportLayer: true mode for correct REALITY application in hop chains

Reverse Proxy — classic Xray reverse scheme. The Bridge server (typically abroad) initiates a persistent connection to the Portal server. Clients connect to Portal, traffic exits through Bridge.

Client ──▶ Portal (entry) ◀── tunnel ── Bridge (exit) ──▶ Internet
                           (bridge initiates connection)
  • Portal can be behind NAT or firewall — no incoming connections required
  • Suitable for scenarios where the entry point must be in a specific network

Forward Chain — direct outbound chain. Portal establishes connections through relay nodes to the exit Bridge.

Client ──▶ Portal ──▶ Relay (opt.) ──▶ Bridge (exit) ──▶ Internet
           (chained outbounds)
  • All nodes in the chain must have a public IP
  • REALITY is supported on each hop to encrypt inter-server traffic

REALITY Between Nodes

Tunnel-REALITY is configured independently from the client-facing REALITY on the Portal node. This enables:

  • Encrypting inter-server traffic without drawing attention
  • Using different SNI/destination for clients vs. tunnels
  • Auto-generating x25519 keys and shortIds when creating links

Post-Deploy Recommendations

  1. Check hop statuses on the Network Map
  2. Verify traffic exits through the expected Bridge (check exit IP)
  3. For Forward Chain — confirm each relay is reachable on its tunnelPort

Limitations

ConstraintReason
REALITY + WebSocketWebSocket doesn't support uTLS fingerprint required by REALITY
Forward Chain without public IPEach hop must accept incoming connections
Mixed modes in one chainReverse and Forward use different Xray mechanisms and cannot be combined
Same port on relay for two hopsA relay that is both bridge and portal requires different ports for incoming and outgoing tunnels

🏗 Architecture

                              ┌─────────────────┐
                              │     CLIENTS     │
                              │ Clash, Sing-box │
                              │   Shadowrocket  │
                              └────────┬────────┘

                     hysteria2:// or vless://

              ┌────────────────────────┼────────────────────────┐
              ▼                        ▼                        ▼
     ┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
     │  Hysteria Node  │      │   Xray Node     │      │  Hysteria Node  │
     │   :443 + hop    │      │  VLESS Reality  │      │   :443 + hop    │
     └────────┬────────┘      └────────┬────────┘      └────────┬────────┘
              │                        │                        │
              │    POST /api/auth      │   CC Agent API         │
              │    GET /online         │                        │
              └────────────────────────┼────────────────────────┘

                          ┌────────────────────────┐
                          │    HYSTERIA PANEL      │
                          │                        │
                          │  • Web UI (/panel)     │
                          │  • HTTP Auth API       │
                          │  • Subscriptions       │
                          │  • SSH Terminal        │
                          │  • Stats Collector     │
                          └───────────┬────────────┘


                          ┌────────────────────────┐
                          │       MongoDB          │
                          └────────────────────────┘

How Authentication Works

Hysteria:

  1. Client connects to node with userId:password
  2. Node sends POST /api/auth to panel
  3. Panel validates user and returns { "ok": true/false }

Xray:

  1. Client connects with UUID (xrayUuid)
  2. CC Agent on node manages user list via API
  3. Panel syncs users to node without restarting Xray

Server Groups

Instead of rigid "plans", use flexible groups:

  • Create group (e.g., "Europe", "Premium")
  • Assign nodes to group
  • Assign users to group
  • User gets only nodes from their groups in subscription

🔧 Node Types

Hysteria 2

Fast UDP protocol based on QUIC with port hopping and obfuscation support.

Advantages:

  • High speed on unstable networks
  • Port hopping to bypass blocks
  • Salamander obfuscation

Settings:

  • Port, port range for hopping
  • ACME or self-signed certificates
  • Obfs (Salamander) with password

Advanced Hysteria settings in panel:

  • Port Hopping interval (hopInterval)
  • ACME advanced options (challenge type, alt ports, DNS challenge provider and config)
  • Masquerade modes: proxy and string
  • Bandwidth limits (up / down) and ignoreClientBandwidth
  • Built-in speedTest, disableUDP, udpIdleTimeout
  • Protocol sniffing (sniff) and QUIC parameters (quic)
  • Custom DNS resolver (resolver)
  • ACL source mode (inline or file) + GeoIP/GeoSite paths
  • Advanced sections are optional and omitted from generated config until enabled in UI

Xray VLESS

Modern protocol with Reality support and various transports.

Advantages:

  • Reality — disguise as legitimate HTTPS traffic
  • Multiple transports (TCP, WebSocket, gRPC, XHTTP)
  • No domain required for Reality

Transports:

TransportDescriptionClient Support
TCPDirect connection, max speedAll clients
WebSocketWorks through CDN and proxiesAll clients
gRPCMultiplexing, good for CDNAll clients
XHTTPNew splithttp transportLimited*

*XHTTP is not supported by all clients (Clash/Sing-box don't support it yet)

Security:

ModeDescription
RealityDisguise as popular site, no domain needed
TLSClassic TLS with certificate
NoneNo encryption (not recommended)

🚀 Xray Node Setup

  1. Add node in panel:

    • Type: Xray
    • IP, SSH credentials
    • Security: Reality (recommended)
    • Transport: TCP (recommended for Reality)
  2. Click "⚙️ Auto Setup"

  3. Panel will automatically:

    • Install Xray-core
    • Generate Reality keys (x25519)
    • Upload config
    • Install CC Agent for user management
    • Open firewall ports
    • Start services

Reality Settings

FieldDescriptionExample
DestDisguise destination (domain:port)www.google.com:443
SNIServer Name Indicationwww.google.com
Private Keyx25519 private keyAuto-generated
Public KeyPublic key (for clients)Auto-generated
Short IDsSession identifiersAuto-generated

CC Agent

CC Agent is a lightweight HTTP service on the node for managing Xray users without restart.

Features:

  • Add/remove users on the fly
  • Traffic stats collection
  • Health check

Agent is installed automatically during Xray node auto-setup.


🔧 Hysteria Node Setup

Understanding Node Configuration

Ports

  • Main port (443) — Port Hysteria listens on
  • Port hopping range (20000-50000) — UDP ports for hopping
  • Stats port (9999) — Internal port for stats collection

Domain vs SNI

FieldPurposeExample
DomainFor ACME/Let's Encrypt certificatesde1.example.com1.2.3.4
SNIFor masquerading (domain fronting)www.google.com

Scenarios:

  1. Simple setup: Set domain, leave SNI empty
  2. Domain fronting: Set domain for certs, SNI as popular domain
  3. No domain: Leave empty — self-signed certificate will be used
  1. Add node in panel (IP, SSH credentials)
  2. Click "⚙️ Auto Setup"
  3. Panel will automatically:
    • Install Hysteria 2
    • Configure ACME or self-signed certificates
    • Set up port hopping
    • Open firewall ports
    • Start service

Obfuscation (Salamander)

Hysteria supports obfuscation to disguise traffic:

  1. Enable Obfs in node settings
  2. Set obfuscation password
  3. Save and update config

Clients will automatically receive obfs params in subscription.

Single VPS Setup (Panel + Node)

You can run panel and node on the same VPS (panel TCP, node UDP on 443).

Option 1: Use panel domain (recommended)

  • Set node domain same as panel domain
  • Panel certificates will be copied automatically

Option 2: No domain (self-signed)

  • Leave domain field empty
  • Self-signed certificate will be generated

📖 API Reference

API Key Authentication

All /api/* endpoints (except /api/auth and /api/files) require authentication.

Create a key: Settings → Security → API Keys → Create Key

Usage:

# Option 1 — header
X-API-Key: ck_your_key_here

# Option 2 — Bearer token
Authorization: Bearer ck_your_key_here

Scopes

ScopeAccess
users:readRead users
users:writeCreate / update / delete users
nodes:readRead nodes
nodes:writeCreate / update / delete / sync nodes
stats:readRead stats and groups
sync:writeTrigger sync, kick users

Rate Limiting

Each key has a configurable rate limit (default: 60 req/min).
Exceeded requests return 429 with X-RateLimit-Limit / X-RateLimit-Remaining headers.


Authentication (for nodes)

POST /api/auth

Validates user on Hysteria node connection.

// Request
{ "addr": "1.2.3.4:12345", "auth": "userId:password" }

// Response (success)
{ "ok": true, "id": "userId" }

// Response (error)
{ "ok": false }

Subscriptions

GET /api/files/:token

Universal subscription endpoint. Auto-detects format by User-Agent.

User-AgentFormat
shadowrocketBase64 URI list
clash, stash, surgeClash YAML
hiddify, sing-box, karingSing-box JSON
BrowserHTML page with QR code
OtherPlain URI list

Query params: ?format=clash, ?format=singbox, ?format=uri

GET /api/files/info/:token

Subscription info (status, traffic, expiry).

Users

Required scope: users:read (GET) / users:write (POST, PUT, DELETE)

MethodEndpointDescription
GET/api/usersList users (pagination, filtering, sorting)
GET/api/users/:userIdGet user
POST/api/usersCreate user
PUT/api/users/:userIdUpdate user
DELETE/api/users/:userIdDelete user
POST/api/users/:userId/enableEnable user
POST/api/users/:userId/disableDisable user
POST/api/users/:userId/groupsAdd user to groups
DELETE/api/users/:userId/groups/:groupIdRemove user from group
POST/api/users/sync-from-mainSync from external DB

Nodes

Required scope: nodes:read (GET) / nodes:write (POST, PUT, DELETE)

MethodEndpointDescription
GET/api/nodesList nodes
GET/api/nodes/:idGet node
POST/api/nodesCreate node
PUT/api/nodes/:idUpdate node
DELETE/api/nodes/:idDelete node
GET/api/nodes/:id/configGet node config (YAML/JSON)
GET/api/nodes/:id/statusGet node status
POST/api/nodes/:id/reset-statusReset status to online
GET/api/nodes/:id/usersGet users on node
POST/api/nodes/:id/syncSync specific node
POST/api/nodes/:id/update-configPush config via SSH
POST/api/nodes/:id/setupAuto-setup node via SSH
POST/api/nodes/:id/setup-port-hoppingSetup port hopping
POST/api/nodes/:id/groupsAdd node to groups
DELETE/api/nodes/:id/groups/:groupIdRemove from group
GET/api/nodes/:id/agent-infoGet CC Agent info (Xray)
POST/api/nodes/:id/generate-xray-keysGenerate Reality keys

Stats & Sync

MethodEndpointScopeDescription
GET/api/statsstats:readPanel statistics
GET/api/groupsstats:readList server groups
POST/api/syncsync:writeSync all nodes
POST/api/kick/:userIdsync:writeKick user from all nodes

🪝 Webhooks

Send real-time event notifications to any HTTP endpoint.

Configure: Settings → Security → Webhooks

Request Format

POST https://your-endpoint.com/webhook
Content-Type: application/json
X-Webhook-Event: user.created
X-Webhook-Timestamp: 1700000000
X-Webhook-Signature: sha256=<hmac>
User-Agent: C3-Celerity-Webhook/1.0

{
  "event": "user.created",
  "timestamp": "2024-01-01T00:00:00.000Z",
  "data": { ... }
}

Signature Verification

const crypto = require('crypto');
const expected = 'sha256=' + crypto
  .createHmac('sha256', YOUR_SECRET)
  .update(`${timestamp}.${rawBody}`)
  .digest('hex');
// compare with X-Webhook-Signature header

Events

EventTrigger
user.createdUser created
user.updatedUser updated
user.deletedUser deleted
user.enabledUser enabled
user.disabledUser disabled
user.traffic_exceededUser traffic limit reached
user.expiredUser subscription expired
node.onlineNode came online
node.offlineNode went offline
node.errorNode error
sync.completedSync cycle finished

📊 Data Models

User

FieldTypeDescription
userIdStringUnique ID
usernameStringDisplay name
subscriptionTokenStringURL token for subscription
xrayUuidStringUUID for Xray VLESS (auto-generated)
enabledBooleanUser active status
groups[ObjectId]Server groups
nodes[ObjectId]Direct node assignments
trafficObject{ tx, rx, lastUpdate } — used traffic
trafficLimitNumberTraffic limit in bytes (0 = unlimited)
maxDevicesNumberDevice limit (0 = group limit, -1 = unlimited)
expireAtDateExpiration date

Node

FieldTypeDescription
typeStringhysteria or xray
nameStringDisplay name
flagStringCountry flag (emoji)
ipStringIP address
domainStringDomain for SNI/ACME
sniStringCustom SNI for masquerading
portNumberMain port (443)
portRangeStringPort hopping range
portConfigsArrayMulti-port: [{ name, port, portRange, enabled }]
obfsObjectObfuscation: { type: 'salamander', password }
statsPortNumberHysteria stats port (9999)
statsSecretStringStats API secret
groups[ObjectId]Server groups
outboundsArrayProxies for ACL: [{ name, type, addr }]
aclRules[String]ACL rules
maxOnlineUsersNumberMax online for load balancing
rankingCoefficientNumberSorting coefficient (1.0)
statusStringonline/offline/error/syncing
trafficObject{ tx, rx, lastUpdate } — node traffic
xrayObjectXray settings (see below)

Xray Settings (node.xray)

FieldTypeDescription
transportStringtcp, ws, grpc, xhttp
securityStringreality, tls, none
flowStringxtls-rprx-vision (for tcp)
fingerprintStringchrome, firefox, safari, etc.
alpn[String]ALPN protocols (h3, h2, http/1.1)
realityDestStringDisguise destination
realitySni[String]Server names
realityPrivateKeyStringx25519 private key
realityPublicKeyStringPublic key
realityShortIds[String]Short IDs
realitySpiderXStringSpider X path (default: /)
wsPathStringWebSocket path
wsHostStringWebSocket host header
grpcServiceNameStringgRPC service name
xhttpPathStringXHTTP path
xhttpHostStringXHTTP host header
xhttpModeStringauto, packet-up, stream-up
apiPortNumberXray gRPC API port (61000)
inboundTagStringInbound tag (vless-in)
agentPortNumberCC Agent port (62080)
agentTokenStringAgent token
agentTlsBooleanTLS for CC Agent

ServerGroup

FieldTypeDescription
nameStringGroup name
descriptionStringDescription
colorStringUI color (#hex)
maxDevicesNumberDevice limit for group
subscriptionTitleStringTitle in subscription profile

🚫 Traffic Filtering (ACL)

Control traffic routing on each Hysteria node. Access: Panel → Node → Traffic Filtering.

Built-in Actions

ActionDescription
reject(...)Block connection
direct(...)Allow through server

Rule Examples

reject(suffix:doubleclick.net)     # Block ads
reject(suffix:googlesyndication.com)
reject(geoip:cn)                   # Block Chinese IPs
reject(geoip:private)              # Block private IPs
direct(all)                        # Allow everything else

Custom Proxy Routing

  1. Add proxy (e.g., my-proxy, SOCKS5, 1.2.3.4:1080)
  2. Use in rules: my-proxy(geoip:ru)

⚖️ Load Balancing

Configure in Settings:

  • Enable balancing — Sort nodes by current load
  • Hide overloaded — Exclude nodes at capacity

Algorithm:

  1. Get user's nodes from groups
  2. Sort by load % (online/max)
  3. Filter overloaded if enabled
  4. Fall back to rankingCoefficient

🔒 Device Limits

Priority:

  1. User's personal limit (maxDevices > 0)
  2. Minimum limit from user's groups
  3. -1 = unlimited

Device Grace Period — delay (in seconds) before counting a disconnected device, to avoid false triggers during reconnections.


📱 Subscription Page Customization

Customize the HTML subscription page in Settings → Subscription:

FieldDescription
Logo URLLogo URL for page header
Page TitlePage title
Support URLSupport link (button at bottom)
Web Page URLProfile URL (profile-web-page-url header)

The subscription page automatically shows:

  • QR code for app import
  • Traffic stats and expiration
  • Location list with copy buttons

💾 Backups

Auto Backups

Configure in Settings → Backups:

  • Interval (in hours)
  • Number of local copies to keep

Manual Backup

Dashboard button — file auto-downloads.

Restore

Upload .tar.gz archive via interface.

S3-Compatible Storage

Backups can be automatically uploaded to S3-compatible storage (AWS S3, MinIO, Backblaze B2, Cloudflare R2, etc.).

Configure: Settings → Backups → S3

FieldDescription
EndpointStorage URL (for MinIO, etc.). Leave empty for AWS S3
RegionRegion (e.g., us-east-1)
BucketBucket name
PrefixPrefix/folder for backups
Access Key IDAccess key
Secret Access KeySecret key
Keep LastHow many backups to keep in S3

Configuration examples:

# AWS S3
Endpoint: (empty)
Region: eu-central-1
Bucket: my-backups

# MinIO
Endpoint: https://minio.example.com
Region: us-east-1
Bucket: backups

# Cloudflare R2
Endpoint: https://<account-id>.r2.cloudflarestorage.com
Region: auto
Bucket: my-backups

🐳 Docker Compose

version: '3.8'

services:
  mongo:
    image: mongo:7
    restart: always
    volumes:
      - mongo_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-hysteria}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}

  backend:
    image: clickdevtech/hysteria-panel:latest
    restart: always
    depends_on:
      - mongo
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./logs:/app/logs
      - ./greenlock.d:/app/greenlock.d
      - ./backups:/app/backups
    env_file:
      - .env

volumes:
  mongo_data:

📝 Environment Variables

VariableRequiredDescription
PANEL_DOMAINPanel domain
DOKPLOY_PANEL_HOSTTraefik host for Dokploy (Host(...) rule)
DOKPLOY_TRAEFIK_SERVICE_PORTTraefik/backend service port in Dokploy (default: 3000)
ACME_EMAILLet's Encrypt email
ENCRYPTION_KEYSSH encryption key (32 chars)
SESSION_SECRETSession secret
MONGO_PASSWORDMongoDB password (for Docker)
MONGO_USERMongoDB user (default: hysteria)
MONGO_URIMongoDB connection URI (for non-Docker)
REDIS_URLRedis URL for cache (default: in-memory)
PANEL_IP_WHITELISTIP whitelist for panel
SYNC_INTERVALSync interval in minutes (default: 2)
API_DOCS_ENABLEDEnable interactive API docs at /api/docs
LOG_LEVELLogging level (default: info)

🤝 Contributing

Pull requests welcome!


📄 License

MIT