Add WARP control endpoints for nodes#40
Conversation
|
Are you serious? Maybe you'll also add an API for updating apt packages, and for a server reboot? Is Ansible a joke to you? |
3f93582 to
fca1b18
Compare
|
Production smoke test update (2026-06-03):
This validates the non-interactive node-side installer path used by this PR through the full backend action flow. |
Greptile SummaryThis PR adds five WARP lifecycle endpoints (status, install, enable, disable, uninstall) to the node, implemented as a new
Confidence Score: 3/5The feature itself works as described, but two structural issues in the core service should be addressed before this merges to avoid hard-to-diagnose production failures. The src/modules/warp/warp.service.ts needs the concurrency guard and parallel trace fix; src/modules/xray-core/xray.service.ts inherits the latency from the sequential probes. Important Files Changed
Sequence DiagramsequenceDiagram
participant Panel
participant WarpController
participant WarpService
participant Shell as Shell (wgcf/wg-quick/ip)
participant CF as Cloudflare Trace
Panel->>WarpController: POST /warp/install
WarpController->>WarpService: install()
WarpService->>WarpService: withOperation('installing')
WarpService->>Shell: bash WARP_INSTALL_SCRIPT
WarpService->>WarpService: normalizeWarpConfig()
WarpService->>CF: curl -4 --interface warp /cdn-cgi/trace
WarpService->>CF: curl -6 --interface warp /cdn-cgi/trace
WarpService-->>Panel: TWarpStatus
Panel->>WarpController: POST /warp/enable
WarpController->>WarpService: enable()
WarpService->>Shell: wg-quick up warp
WarpService-->>Panel: TWarpStatus
Panel->>WarpController: POST /warp/disable
WarpController->>WarpService: disable()
WarpService->>Shell: wg-quick down warp
WarpService-->>Panel: TWarpStatus
Panel->>WarpController: POST /warp/uninstall
WarpController->>WarpService: uninstall()
WarpService->>Shell: stopRunningInterface()
WarpService-->>Panel: TWarpStatus
Panel->>WarpController: GET /stats
WarpController->>WarpService: getStatus() + getHostConnectivity()
WarpService->>CF: curl probes (WARP + host interfaces)
WarpService-->>Panel: NodeSystemStats
Reviews (1): Last reviewed commit: "fix: stabilize dual-stack warp install" | Re-trigger Greptile |
| public async uninstall(): Promise<TWarpStatus> { | ||
| if (process.platform !== 'linux') { | ||
| return this.getStatus('WARP is supported only on Linux nodes'); | ||
| } | ||
|
|
||
| return this.withOperation('uninstalling', 'Preparing WARP uninstall', async () => { | ||
| try { | ||
| if (await this.isInterfaceRunning()) { | ||
| this.appendOperationLog('Stopping WARP before uninstall'); | ||
| await this.stopRunningInterface(); | ||
| } | ||
|
|
||
| this.removeFileIfExists(WARP_CONFIG_PATH); | ||
| this.removeFileIfExists(WARP_TOOL_PATH); | ||
| this.finishOperation('WARP uninstalled'); | ||
| return await this.getStatus(); | ||
| } catch (error) { | ||
| const message = this.toSafeError(error); | ||
| this.logger.warn(`Failed to uninstall WARP: ${message}`); | ||
| this.failOperation(message); | ||
| return this.getStatus(message); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
No concurrency guard on lifecycle operations
withOperation immediately overwrites this.operation without checking whether a previous operation is still in-flight. Two concurrent HTTP POST requests — e.g., POST /warp/install and POST /warp/enable arriving simultaneously — will both enter the method, interleave their mutations to this.operation, and may both invoke installIfMissingOrSingleStack (running wgcf register/wg-quick up in parallel), producing corrupted WireGuard state. A simple early-return guard checking this.operation.state !== 'idle' in each public method would prevent this.
| const hasWireGuardHandshake = running ? await this.hasWireGuardHandshake() : false; | ||
| const traceIpv4 = running ? await this.getTrace('4') : null; | ||
| const traceIpv6 = running ? await this.getTrace('6') : null; |
There was a problem hiding this comment.
Sequential Cloudflare trace probes block the xray start path
getTrace('4') and getTrace('6') are sequential await calls, each with an 8-second execFixed timeout. When WARP is installed and running but the Cloudflare endpoint is slow or unreachable, getStatus() alone can stall for up to 16 seconds. This is then called unconditionally inside startXray(), which means every xray restart now inherits that latency. Running the two traces in parallel would cut the worst case in half.
| const hasWireGuardHandshake = running ? await this.hasWireGuardHandshake() : false; | |
| const traceIpv4 = running ? await this.getTrace('4') : null; | |
| const traceIpv6 = running ? await this.getTrace('6') : null; | |
| const hasWireGuardHandshake = running ? await this.hasWireGuardHandshake() : false; | |
| const [traceIpv4, traceIpv6] = running | |
| ? await Promise.all([this.getTrace('4'), this.getTrace('6')]) | |
| : [null, null]; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
wgcf updateafter registration so fresh one-click installs reliably handshakeVerification
node --test test/warp-status.test.mjsnpm run buildnpm run lintghcr.io/rickeyren/remnawave-node:warp-node-control@sha256:957eea53bfbdcb8446867ebe0e4a1054a8d4acc96b2aecada5426702be7f178ewarp=onon HK, GB, Bandwagon, and RackNerdwarp=off; Bandwagon correctly reports no native host IPv6