API Reference - FrankoonG/hy2scale GitHub Wiki

🌐 English | δΈ­ζ–‡ | ν•œκ΅­μ–΄

API Reference

HY2 SCALE exposes a REST API under http://<host>:5565/scale/api/. Every endpoint except POST /api/login requires a session token obtained from login.

Authentication

Login

POST /api/login

Body:

{
  "username": "admin",
  "password": "<sha256-hex-of-plaintext>"
}

The password is SHA-256 hashed on the client before transmission; the server only ever stores and compares the hash.

Response:

{
  "token": "hex-session-token",
  "force_password_change": false
}

force_password_change: true indicates you are still on the default admin/admin credentials β€” the web UI will force a password change before showing any page.

Using the token

Subsequent requests pass the token as a Bearer header:

Authorization: Bearer <token>

Long-lived EventSource endpoints (notably /api/graph-layout/stream) additionally accept the token as a ?token=<hex> query parameter because EventSource can't set custom headers.


Node

GET /api/node

Returns this node's identity and Hysteria 2 server configuration:

{
  "node_id": "ea1b2adb",
  "name": "sg-home",
  "exit_node": true,
  "hy2_user_auth": false,
  "compat": false,
  "limited": false,
  "server": {
    "listen": "0.0.0.0:5565",
    "password": "<hex>",
    "tls_cert": "/data/tls/default.crt",
    "tls_key": "/data/tls/default.key"
  },
  "version": "1.3.0"
}

PUT /api/node

Replaces the node config. Only the fields you include are modified.

GET /api/stats

{ "tx_bytes": 1234567890, "rx_bytes": 9876543210 }

Topology

GET /api/topology

Returns a flat array of top-level peers, each with a nested children tree for peers visible through nested discovery. Self is always first with direction: "local" and is_self: true.

[
  {
    "name": "ea1b2adb",
    "direction": "local",
    "latency_ms": 0,
    "is_self": true
  },
  {
    "name": "jp",
    "direction": "outbound",
    "connected": true,
    "latency_ms": 35,
    "nested": true,
    "version": "1.3.0",
    "tx_rate": 0,
    "rx_rate": 0,
    "children": [
      { "name": "jp-r1", "direction": "outbound", "latency_ms": 73 }
    ]
  }
]

Clients (outbound peers)

Method Path
GET /api/clients
POST /api/clients
PUT /api/clients/{name}
DELETE /api/clients/{name}
PUT /api/clients/{name}/disable β€” {"disabled": bool}

Create body:

{
  "name": "jp",
  "addr": "jp.example.com:5565",
  "addrs": ["jp.example.com:5565", "jp2.example.com:5565"],
  "password": "<hy2-password>",
  "sni": "jp.example.com",
  "insecure": false,
  "ca": "",
  "max_tx": 0,
  "max_rx": 0
}

Peers (nested discovery)

Method Path Purpose
GET /api/peers/{name}/peers fetch a peer's cached sub-peer list
PUT /api/peers/{name}/nested toggle nested flag for this peer

Nested body:

{ "enabled": true }

{name} may be a bare peer name (direct peer) or a /-separated path for sub-peers (us/us-east, kr/kr-r1/kr-r1-a …).


Proxies

Generic proxy list (SOCKS5, HTTP, Shadowsocks):

Method Path
GET /api/proxies
POST /api/proxies
PUT /api/proxies/{id}
DELETE /api/proxies/{id}

Create body (SOCKS5 / HTTP / SS share this schema):

{
  "id": "socks5",
  "protocol": "socks5",
  "listen": "0.0.0.0:1080",
  "enabled": true,
  "tls_cert": ""
}

For Shadowsocks also include "method": "aes-256-gcm".

L2TP / IKEv2 / WireGuard

Each has a dedicated config endpoint pair:

Protocol GET / PUT
L2TP /api/l2tp
IKEv2 /api/ikev2
WireGuard /api/wireguard

PUT triggers hot reload β€” the service stops and restarts with the new config.

WireGuard peers

Method Path
GET /api/wireguard (peers inside)
POST /api/wireguard/peers
PUT /api/wireguard/peers/{name}
DELETE /api/wireguard/peers/{name}
GET /api/wireguard/peers/{name}/config (text/plain WireGuard .conf)
GET /api/wireguard/qr?peer={name} (PNG QR code)
POST /api/wireguard/generate-key (returns {private_key, public_key})

Users

Method Path
GET /api/users
POST /api/users
PUT /api/users/{id}
DELETE /api/users/{id}
PUT /api/users/{id}/toggle
PUT /api/users/{id}/reset-traffic

Create body:

{
  "username": "alice",
  "password": "<plaintext or hashed>",
  "exit_via": "us/us-east",
  "exit_paths": ["us/us-east"],
  "exit_mode": "direct",
  "traffic_limit_gb": 500,
  "expiry": "2026-12-31T00:00:00Z",
  "enabled": true
}

Sessions

Method Path
GET /api/sessions
DELETE /api/sessions/{id} β€” kick, 60 s reconnection lockout

Routing rules

Method Path
GET /api/rules
POST /api/rules
PUT /api/rules/{id}
DELETE /api/rules/{id}
PUT /api/rules/{id}/toggle

Create body:

{
  "id": "netflix",
  "name": "Netflix",
  "type": "domain",
  "targets": ["netflix.com"],
  "exit_via": "us",
  "exit_paths": ["us"],
  "exit_mode": "direct",
  "enabled": true
}

type is "ip" or "domain".

TUN mode

Method Path
GET /api/rules/tun-mode
PUT /api/rules/tun-mode

Body:

{ "enabled": true, "mode": "mixed" }

mode: "mixed" (rule-matched targets via TUN, others via proxy) or "full" (all traffic via TUN).


TLS

Method Path
GET /api/tls
POST /api/tls/import (PEM in body)
POST /api/tls/import-path (file paths in body)
POST /api/tls/generate (self-signed or CA)
POST /api/tls/sign (sign with existing CA)
GET /api/tls/{id}/pem (text/plain cert PEM)
DELETE /api/tls/{id}

POST /api/tls/generate body:

{
  "id": "vpn-cert",
  "name": "vpn.sg.example",
  "domains": ["vpn.sg.example"],
  "days": 730,
  "is_ca": false
}

Graph layout

The Nodes-page graph stores node coordinates server-side so every logged-in session sees the same layout.

Method Path Purpose
GET /api/graph-layout fetch current layout { key: {x, y} }
PUT /api/graph-layout replace layout with a new map
GET /api/graph-layout/stream SSE stream β€” emits the current layout on connect, then every mutation

SSE events are JSON objects with the same shape as the GET response.


Backup / restore

Method Path
GET /api/backup
POST /api/restore

Build info

Method Path Auth Purpose
GET /api/build-id no sha256 of the embedded index.html; used by the SPA auto-reload on deploy
GET /api/build-info yes full build info: {version, license, repository, go_deps[], natives[]}

/api/build-info drives the License + native components panel in Settings β†’ Upgrade.


Settings

Method Path Body
PUT /api/settings/password {current_password, new_username?, new_password?} (all SHA-256)
GET /api/settings/ui returns port, base path, DNS, session timeout
PUT /api/settings/ui update UI settings

Upgrade

Method Path Purpose
POST /api/upgrade multipart upload of a .tar.gz containing the new binary. Server swaps the file and restarts.

Utilities

POST /api/check-ports

{ "ports": [5565, 1701, 500] }

Response:

{ "results": { "5565": true, "1701": false, "500": false } }

true means the port is free on the host.


Internal / relay-plane endpoints

The following endpoints are used by the relay protocol itself for inter-node coordination. They do not use web-UI auth β€” access control is the Hysteria 2 tunnel password.

GET /api/internal/peers    β€” peer list for reverse nested discovery

These aren't intended for external automation β€” they're documented only for protocol implementers.

⚠️ **GitHub.com Fallback** ⚠️