156 lines
6.6 KiB
Markdown
156 lines
6.6 KiB
Markdown
# Core Private Node Routes
|
|
|
|
These routes are consumed only by the node agent and require the `x-node-auth-key` header.
|
|
|
|
## Transport
|
|
|
|
The node uses two transports to communicate with Core:
|
|
|
|
- **HTTP** — registration only (`POST /node/register`).
|
|
- **gRPC** — all other node-to-core calls: heartbeat, usage reporting, config sync, and stats. Core listens on `GRPC_PORT` (default `3002`). The node connects via `CORE_GRPC_URL`. Set `CORE_INSECURE=true` on the node to use plaintext instead of TLS.
|
|
|
|
The HTTP routes for heartbeat, usage, config, and error remain available as a fallback but the node agent uses gRPC by default.
|
|
|
|
## Authentication
|
|
|
|
- `POST /node/register` is the only public route in this group.
|
|
- All other HTTP routes require the `x-node-auth-key` header.
|
|
- gRPC calls pass the auth key as the `x-node-auth-key` metadata header (or in the `authKey` request field as fallback).
|
|
- The auth key is issued by Core during registration and persisted on the node side.
|
|
|
|
## Route table
|
|
|
|
| Method | Route | Input | Output / Notes |
|
|
| --- | --- | --- | --- |
|
|
| `POST` | `/node/register` | `RegisterNodeDto` | Returns `{ authKey, nodeId }` |
|
|
| `POST` | `/node/heartbeat` | `NodeHeartbeatDto` | Returns `{ ok: true, needSync }` |
|
|
| `POST` | `/node/usage` | `NodeUsageDto` | Returns `{ ok: true }` |
|
|
| `POST` | `/node/outbound-usage` | `NodeOutboundUsageDto` | Returns `{ ok: true }` |
|
|
| `POST` | `/node/session-events` | `SessionEventsDto` | Returns `{ ok: true }` |
|
|
| `GET` | `/node/config/latest` | none | Returns `NodeConfig` |
|
|
| `POST` | `/node/config/applied` | `NodeConfigAppliedDto` | Returns `{ ok: true }` |
|
|
| `POST` | `/node/error` | `NodeErrorDto` | Returns `{ ok: true }` |
|
|
|
|
## Request payloads
|
|
|
|
### `RegisterNodeDto`
|
|
|
|
- `code: string`
|
|
- `name: string`
|
|
- `ip: string`
|
|
- `port: number` — agent HTTP port (required; auto-detected from `PORT` env on the node)
|
|
- `region: string`
|
|
- `domain?: string` — optional domain name (omitted on auto-registration; can be set later via admin panel)
|
|
|
|
### `NodeHeartbeatDto`
|
|
|
|
- `ip?: string`
|
|
- `revision?: number`
|
|
|
|
The node sends its current revision in heartbeats. Core uses it to decide whether a sync is needed.
|
|
|
|
### `NodeUsageDto`
|
|
|
|
- `entries: UsageEntryDto[]`
|
|
|
|
`UsageEntryDto` fields:
|
|
|
|
- `userId: number`
|
|
- `subscriptionId?: number | null`
|
|
- `nodeProtocolId: number`
|
|
- `bytesUp: number`
|
|
- `bytesDown: number`
|
|
- `recordedAt: string`
|
|
|
|
### `NodeOutboundUsageDto`
|
|
|
|
- `items: OutboundUsageItem[]`
|
|
|
|
`OutboundUsageItem` fields:
|
|
|
|
- `tag: string`
|
|
- `bytesUp: number`
|
|
- `bytesDown: number`
|
|
|
|
### `SessionEventsDto`
|
|
|
|
- `events: SessionEventEntryDto[]`
|
|
|
|
`SessionEventEntryDto` fields:
|
|
|
|
- `userId: number`
|
|
- `protocol: VpnProtocol`
|
|
- `eventType: SessionEventType`
|
|
- `occurredAt: string`
|
|
- `ipAddress?: string`
|
|
|
|
### `NodeConfigAppliedDto`
|
|
|
|
- `revision: number`
|
|
|
|
### `NodeErrorDto`
|
|
|
|
- `message: string`
|
|
- `code?: string`
|
|
- `details?: Record<string, unknown>`
|
|
|
|
## `NodeConfig`
|
|
|
|
The config returned by `GET /node/config/latest` has this structure:
|
|
|
|
```ts
|
|
{
|
|
revision: number
|
|
clientMap: Record<string, { userId: number; subscriptionId: number | null }>
|
|
mtprotoClientMap: Record<string, { userId: number; subscriptionId: number | null }>
|
|
services: {
|
|
xray?: XrayServiceConfig
|
|
mtproto?: MtProtoServiceConfig
|
|
}
|
|
}
|
|
```
|
|
|
|
Important notes:
|
|
|
|
- `clientMap` maps subscription link UUIDs to the user and subscription — used for xray traffic attribution.
|
|
- `mtprotoClientMap` maps MTProto link UUIDs to the user and subscription — used for telemt traffic attribution.
|
|
- `services.xray` contains the Xray inbounds, routing, and outbounds for the node.
|
|
- `services.mtproto` contains MTProto inbounds for `telemt`.
|
|
- The node stores the current revision and client maps locally after a successful apply.
|
|
|
|
## Route semantics
|
|
|
|
- `POST /node/heartbeat` updates `lastSeenAt` and can request a sync when the node revision is behind.
|
|
- `POST /node/usage` stores per-minute usage rows and increments subscription traffic counters.
|
|
- `POST /node/outbound-usage` increments `node_outbounds.traffic_bytes_up/down`.
|
|
- `POST /node/session-events` stores online/offline events in `session_events`.
|
|
- `POST /node/config/applied` marks the node sync status as `synced`.
|
|
- `POST /node/error` marks the node as `error` and the sync status as `failed`.
|
|
|
|
## gRPC interface (`NodeService`)
|
|
|
|
Core exposes a gRPC service that the node agent uses for all real-time communication. The service name is `nodeservice.NodeService` and is defined in `proto/node-service.proto`.
|
|
|
|
Authentication: pass `x-node-auth-key` as gRPC metadata (or in the `authKey` field of the request as fallback).
|
|
|
|
| Method | Request fields | Response fields | Notes |
|
|
| --- | --- | --- | --- |
|
|
| `Heartbeat` | `authKey`, `revision?`, `ip?` | `ok`, `needSync` | Equivalent to `POST /node/heartbeat` |
|
|
| `ReportUsage` | `authKey`, `entries[]` | `ok` | Equivalent to `POST /node/usage` |
|
|
| `ReportOutboundUsage` | `authKey`, `items[]` | `ok` | Equivalent to `POST /node/outbound-usage` |
|
|
| `GetLatestConfig` | `authKey` | `revision`, `clientMapJson`, `mtprotoClientMapJson`, `servicesJson` | Config fields are JSON-encoded strings |
|
|
| `ConfirmConfigApplied` | `authKey`, `revision` | `ok` | Equivalent to `POST /node/config/applied` |
|
|
| `ReportNodeStats` | `authKey`, `system{}`, `xray{}`, `mtprotoJson` | `ok` | Reports CPU/RAM/disk/network/xray/mtproto snapshot; stored in Redis; drives `GET /admin/nodes/:id/runtime-status` and stats history |
|
|
| `ReportOnlineUsers` | `authKey`, `users[]` | `ok` | Reports currently connected users with their IPs every 5 s; Core stores Redis TTL keys and updates IP history |
|
|
|
|
`ReportNodeStats` nested fields:
|
|
- `system`: `uptimeSeconds`, `cpuPercent`, `cpuCores`, `ramUsed`, `ramTotal`, `diskUsed`, `diskTotal`, `netRxPerSec`, `netTxPerSec`, `netRxTotal`, `netTxTotal`
|
|
- `xray`: `running`, `uptimeSeconds?`
|
|
- `mtprotoJson`: JSON string `Record<tag, MtProtoStatus>`
|
|
|
|
`ReportOnlineUsers.users[]` entry: `userId`, `nodeProtocolId`, `ips[]{ip, lastSeen}`.
|
|
|
|
The node sends `ReportNodeStats` every 2 s. `GET /admin/nodes/:id/runtime-status` returns the latest stored snapshot without polling the node; `?fromNode=true` forces a live HTTP fetch instead.
|
|
|
|
The node sends `ReportOnlineUsers` every 5 s. Core sets `online:user:{userId}` and `online:inbound:{nodeProtocolId}:user:{userId}` Redis keys with TTL 20 s. Existing `(userId, ip)` rows only receive a new `lastSeenAt`; missing pairs are inserted. IP information enrichment supports IPinfo.io and ip-api.com. A daily job refreshes all known addresses, while a job running every 10 minutes processes addresses with `firstSeenAt > now - 10 minutes`. Results are stored in `user_ips.info`. Core also emits a `user.online` domain event when the user had a gap > 10 s.
|