Production-oriented Hysteria 2 VPN management platform with a FastAPI backend, a React admin/user interface, PostgreSQL persistence, traffic accounting, and Ansible-based deployment automation.
HtrBox provides a web control plane for operating Hysteria 2 VPN access across one or more servers. It includes account management, role-based access, connection URL generation, server lifecycle controls, traffic aggregation, and background maintenance jobs.
The repository contains everything needed for local development and deployment:
backend- FastAPI API, authentication, database access, Hysteria integration, traffic collection, maintenance workers, and rate limiting.frontend- React 19, Vite, TypeScript application for administrators and end users.hysteria- local Hysteria 2 configuration used by Docker Compose.ansible- deployment playbooks and templates for the main application host and regional Hysteria nodes..github/workflows- GitHub Actions workflow for building images and running Ansible deployment.
- JWT access tokens with refresh tokens stored in
HttpOnlycookies. - Role-based access control for
adminanduseraccounts. - User registration, activation, blocking, password changes, and subscription status management.
- Hysteria user secret regeneration via
hyPassword. - Server inventory management: create, update, deactivate, and delete nodes.
hysteria2://client URL generation.- Admin dashboard with user, server, online, subscription, and traffic signals.
- User profile with account status, traffic usage, expiry data, selected server, and connection information.
- Traffic collection from the Hysteria management API with 5-minute buckets.
- Background maintenance for account state and traffic retention.
- Per-endpoint rate limiting and brute-force protection.
- Healthcheck endpoint and baseline security headers.
- GitHub Actions build pipeline with GHCR image publishing.
- Ansible deployment for the main application host and regional VPN servers.
Backend Frontend Infrastructure
--------------------------------------------------------
Python 3.11 React 19 Docker / Docker Compose
FastAPI TypeScript Hysteria 2
PostgreSQL Vite GitHub Actions
psycopg2 TanStack Query GitHub Container Registry
PyJWT Zustand Ansible
bcrypt React Hook Form Certbot + Cloudflare DNS
httpx Zod
Uvicorn Tailwind CSS v4
Radix UI
Recharts
lucide-react
Browser
|
| /api/*
v
Frontend dev server / production nginx
|
| proxied API requests
v
FastAPI backend
|
+--> PostgreSQL
|
+--> Hysteria 2 management API
|
+--> background traffic collector
|
+--> background maintenance worker
In local development, the Vite server proxies /api/* and /api/ws to the
backend through Docker service names configured in frontend/vite.config.ts.
.
├── ansible/
│ ├── deploy.yml
│ ├── inventory.yml
│ └── templates/
├── backend/
│ ├── source/
│ │ ├── main.py
│ │ ├── config.py
│ │ ├── database.py
│ │ ├── routers/
│ │ └── ...
│ ├── Dockerfile
│ ├── requirements.txt
│ └── requirements-dev.txt
├── frontend/
│ ├── src/
│ │ ├── api/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── pages/
│ │ ├── stores/
│ │ └── styles/
│ ├── Dockerfile
│ ├── package.json
│ └── vite.config.ts
├── hysteria/
│ ├── certs/
│ └── config.yaml
├── docker-compose.yaml
└── README.md
Docker Compose is the recommended local development path. It starts PostgreSQL, the FastAPI backend, the Vite frontend, and a local Hysteria 2 server.
Create a .env file in the repository root:
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change_me
JWT_SECRET=replace_with_a_long_random_secret_at_least_32_chars
HYSTERIA_AUTH=Bearer change_me
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=htrbox
ALLOWED_ORIGINS=http://localhost:5173
COOKIE_SECURE=false
COOKIE_SAMESITE=lax
DOCS_ENABLED=trueFor a strong JWT secret, use:
openssl rand -hex 32The backend validates required configuration at startup and fails fast if a
required value is missing or if JWT_SECRET is too short.
The local Compose stack expects these files to exist:
hysteria/.envhysteria/config.yamlhysteria/certs/server.crthysteria/certs/server.key
The repository already includes a development Hysteria configuration and local certificates. Replace them for real deployments.
docker compose up --buildServices:
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:8000 - Backend healthcheck:
http://localhost:8000/health - Swagger UI:
http://localhost:8000/docswhenDOCS_ENABLED=true - PostgreSQL:
localhost:5432 - Hysteria management API:
localhost:8080
Running everything through Compose is simpler because the frontend proxy points to Docker service names. Manual startup is still useful when working on one layer at a time.
cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cd source
uvicorn main:app --reload --host 0.0.0.0 --port 8000cd frontend
npm ci
npm run devWhen running the frontend outside Docker, update the Vite proxy target from
backend:8000 to localhost:8000, or keep the frontend in Compose.
cd frontend
npm run buildConfiguration is loaded from environment variables in backend/source/config.py.
Required variables:
| Variable | Description |
|---|---|
ADMIN_USERNAME |
Initial administrator username, used for first-run seeding. |
ADMIN_PASSWORD |
Initial administrator password, used for first-run seeding. |
HYSTERIA_AUTH |
Authorization header value used for Hysteria API requests. |
JWT_SECRET |
Secret for signing JWT tokens. Must be at least 32 characters. |
POSTGRES_USER |
PostgreSQL username. |
POSTGRES_PASSWORD |
PostgreSQL password. |
POSTGRES_DB |
PostgreSQL database name. |
Important optional variables:
| Variable | Default | Description |
|---|---|---|
POSTGRES_HOST |
postgres |
PostgreSQL host. |
POSTGRES_PORT |
5432 |
PostgreSQL port. |
ALLOWED_ORIGINS |
http://localhost:80 |
Comma-separated CORS origins. |
COOKIE_SECURE |
true |
Enables secure cookies and HSTS. Use false for local HTTP. |
COOKIE_SAMESITE |
strict |
Cookie SameSite policy: strict, lax, or none. |
DOCS_ENABLED |
false |
Enables /docs, /redoc, and /openapi.json. |
LOG_LEVEL |
INFO |
Backend logging level. |
TRAFFIC_POLL_INTERVAL |
30 |
Seconds between Hysteria traffic polls. |
TRAFFIC_BUCKET_SECONDS |
300 |
Traffic aggregation bucket size. |
TRAFFIC_RETENTION_DAYS |
7 |
Retention for aggregated traffic rows. |
MAINTENANCE_INTERVAL |
600 |
Seconds between maintenance runs. |
Rate limiting is also configurable with RT_* variables. See
backend/source/config.py for the full list.
/admin- dashboard/users- user management/servers- server management/settings- account settings
/profile- account status, usage, server selection, and connection URL/manual- onboarding and usage guide/chekavo- additional user page/settings- account settings
/login/register
Main API groups:
/auth- login, refresh, logout, registration, and Hysteria auth callback./users- admin user management and self-service user actions./servers- VPN server inventory and lifecycle operations./traffic- traffic metrics by user and server./kick- disconnect a Hysteria user./online- online Hysteria users./generate-url/{username}- Hysteria connection URL generation./health- public liveness/readiness probe.
The OpenAPI schema is available only when DOCS_ENABLED=true.
On startup, the backend:
- Configures logging.
- Validates required environment variables.
- Initializes the PostgreSQL connection pool.
- Creates missing database tables.
- Seeds the first administrator account from
ADMIN_USERNAMEandADMIN_PASSWORD. - Starts rate limiter cleanup.
- Starts the traffic collector.
- Starts the maintenance worker.
The application intentionally fails startup if critical background workers do not start, because traffic accounting and account maintenance are part of the core service contract.
- Account passwords are hashed with bcrypt.
- Access tokens are kept in frontend memory.
- Refresh tokens are stored in
HttpOnlycookies. - Roles are read from the database on authenticated requests, so role changes take effect without waiting for the access token to expire.
COOKIE_SECURE=trueis recommended for production and enables HSTS headers.- API documentation is disabled by default in production.
- Request bodies are globally capped at 1 MB.
- Sensitive endpoints are protected by per-endpoint rate limits.
hyPasswordis stored as a VPN credential required by the Hysteria protocol, not as an account login password. Seebackend/source/database.pyfor the design note.- Never commit production secrets, real certificates, or private deployment credentials.
The repository includes a GitHub Actions workflow and Ansible playbook for building and deploying the platform.
.github/workflows/deploy.yml can:
- Build backend and frontend Docker images.
- Push images to GitHub Container Registry.
- Run
ansible/deploy.ymlagainst the selected target.
Workflow inputs include:
- Deployment target:
all,yc,vps, or a specific regional VPS target. - Task tags:
prepare,certbot,deploy,status,logs, orhealthcheck. - Build control: normal build or deployment from an existing image tag.
Required GitHub secrets:
| Secret | Purpose |
|---|---|
SSH_PRIVATE_KEY |
Shared SSH deployment key for application and VPN hosts. |
JWT_SECRET |
Production JWT signing secret. |
ADMIN_USERNAME |
Initial admin username. |
ADMIN_PASSWORD |
Initial admin password. |
POSTGRES_PASSWORD |
Production PostgreSQL password. |
HYSTERIA_AUTH |
Shared Hysteria management API authorization value. |
CF_API_TOKEN |
Cloudflare API token for DNS-based certificate issuance. |
EMAIL |
Email used for Certbot registration and renewal notices. |
The Ansible playbook renders environment files and Docker Compose files from templates, checks Docker availability, manages certificates, pulls GHCR images, starts containers, and performs health checks.
Useful commands for local operators:
ansible-playbook ansible/deploy.yml \
-i ansible/inventory.yml \
--private-key ~/.ssh/deploy_key \
--tags deployLimit deployment to a specific group:
ansible-playbook ansible/deploy.yml \
-i ansible/inventory.yml \
--private-key ~/.ssh/deploy_key \
--limit yc \
--tags healthcheckHigh-value directories when changing the application:
backend/source/routers- HTTP endpoints.backend/source/schemas.py- API request and response models.backend/source/database.py- schema initialization and persistence helpers.backend/source/traffic_collector.py- traffic polling and aggregation.backend/source/maintenance.py- scheduled account and data maintenance.frontend/src/pages- route-level screens.frontend/src/components- reusable UI and domain components.frontend/src/api- frontend API clients.frontend/src/hooks- data fetching and application hooks.frontend/src/stores- Zustand stores.frontend/src/styles- tokens, shared variants, and CSS.
# Start the complete local stack
docker compose up --build
# Stop local containers
docker compose down
# Start backend manually
cd backend/source
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# Start frontend manually
cd frontend
npm run dev
# Build frontend assets
cd frontend
npm run build- Create
.envwith required variables. - Start the local stack with
docker compose up --build. - Check
http://localhost:8000/health. - Open
http://localhost:5173. - Log in with the initial administrator account.
- Add the first Hysteria server in
/servers. - Create a test user.
- Generate and test a Hysteria connection URL.







