# Node Node is the agent installed on each VPN/proxy server. It registers with Core, receives configuration revisions, applies local services, reports usage, and exposes authenticated runtime actions. This page is public operator documentation. It describes setup, behavior, environment variables, Docker deployment, and the node HTTP API without requiring source-code access. ## Capabilities - One-time registration with Core. - Persistent auth key storage. - Config sync by revision. - Xray install, restart, validation, GeoIP update, and key generation. - MTProto install and restart. - Nginx masking site management. - System status reporting with CPU, RAM, disk, network, and process state. - Heartbeat reporting. - Per-user and per-subscription traffic reporting. - Outbound traffic reporting for proxy chains. - Cloudflare WARP outbound config generation. - Session event reporting. - Runtime error reporting. - Crash watchdog for local services. ## Environment Variables | Variable | Required | Default | Description | | --- | --- | --- | --- | | `PORT` | No | `3001` | Local node agent HTTP port. | | `NODE_ENV` | No | `development` | Runtime mode. | | `CORE_URL` | Yes | `http://localhost:3000` | Core API URL reachable from the node server. | | `REGISTRATION_CODE` | First start only | empty | One-time node registration code generated in the panel. | | `NODE_AUTH_KEY` | No | empty | Optional fallback auth key when no key file exists. | | `HEARTBEAT_INTERVAL_SEC` | No | `30` | Heartbeat interval in seconds. | Persistent state is stored under `/var/lib/vpnnode`. Keep this directory on a persistent Docker volume. It contains the auth key, generated configs, binaries, stats state, and masking-site files. ## Docker Compose Create a `docker-compose.yml` on the VPN/proxy server: ```yaml version: '3.9' services: node: image: nexuma/node:latest container_name: nexuma-node restart: unless-stopped environment: PORT: 3001 CORE_URL: https://core.example.com REGISTRATION_CODE: paste-one-time-code-here HEARTBEAT_INTERVAL_SEC: 30 volumes: - node_data:/var/lib/vpnnode ports: - "3001:3001" # Add protocol ports here, for example: # - "443:443" # - "443:443/udp" cap_add: - NET_ADMIN healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:3001/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 15s volumes: node_data: ``` Run: ```bash docker compose up -d ``` After successful registration, remove `REGISTRATION_CODE` and redeploy: ```bash docker compose up -d ``` ## Registration 1. In the panel, open `Nodes`. 2. Generate a registration code. 3. Set `CORE_URL` and `REGISTRATION_CODE` on the node server. 4. Start the node container. 5. Wait until the node appears as registered. 6. Remove `REGISTRATION_CODE`. 7. Keep the `node_data` volume for future restarts. If the persistent volume is deleted, the node loses its auth key and must be registered again or started with `NODE_AUTH_KEY`. ## Local HTTP API Public: | Method | Route | Purpose | | --- | --- | --- | | `GET` | `/health` | Health check. | All other routes require `x-node-auth-key`. | Method | Route | Purpose | | --- | --- | --- | | `GET` | `/node/status` | Runtime status and resource usage. | | `POST` | `/node/apply-config` | Trigger config pull and apply. | | `POST` | `/node/xray/restart` | Restart Xray. | | `POST` | `/node/xray/install` | Install an Xray version. | | `POST` | `/node/geoip/update` | Update GeoIP/geosite files. | | `POST` | `/node/crypto/x25519` | Generate X25519 keys. | | `POST` | `/node/crypto/mldsa65` | Generate an ML-DSA-65 seed. | | `POST` | `/node/mtproto/restart` | Restart MTProto processes. | | `POST` | `/node/mtproto/install` | Install an MTProto version. | | `GET` | `/node/nginx/sites` | List masking sites. | | `GET` | `/node/nginx/sites/:domain` | Get one masking site. | | `PUT` | `/node/nginx/sites` | Replace masking sites. | | `PUT` | `/node/nginx/site` | Upsert one masking site. | | `GET` | `/node/nginx/site` | Get current masking site status. | | `DELETE` | `/node/nginx/sites/:domain` | Delete one masking site. | | `DELETE` | `/node/nginx/site` | Delete current masking site. | | `POST` | `/node/nginx/restart` | Restart nginx. | | `POST` | `/node/warp/generate` | Register an anonymous Cloudflare WARP account and return a WireGuard outbound config. | These endpoints are intended to be called by Core. Protect the agent port with firewall rules or private networking. ## Config Sync - The node keeps the latest applied revision locally. - Core can ask the node to apply config immediately. - Heartbeat responses can also request sync. - The node pulls the latest config from Core. - If config apply succeeds, the node confirms the applied revision. - If config apply fails, the node reports an error and keeps the previous valid config where possible. ## Traffic and Events The node reports: - user traffic - subscription traffic attribution - per-protocol usage - outbound usage - online/offline session events - service crash and config errors Traffic is flushed periodically. If reporting fails temporarily, the node keeps pending stats for the next flush where possible. ## Status Output `GET /node/status` returns operational status including: - system uptime - CPU usage and core count - RAM usage - disk usage - network transfer rates and totals - Xray running state and uptime - MTProto process state and connection counts ## Operations - Use node sync from the panel after changing protocols, routing, or outbounds. - Open every configured inbound port in Docker and the server firewall. - Keep `/var/lib/vpnnode` persistent. - Keep the node agent API private. - Remove one-time registration codes after registration. - Rotate auth keys when moving or rebuilding a server.