Skip to content

Connect Remote Nodes

Open Settings → Nodes → Add node. The wizard checks public Control Plane reachability, then asks about the target network and installation state. Prefer a connection initiated by whichever side can make outbound requests, minimizing public ingress.

Compare the Methods

MethodInitiatorBest forPublic requirement
Reverse WSSNode AgentRemote node can reach the Control PlaneExpose only the Control Plane
Direct HTTPSControl PlaneControl Plane can reach the Node AgentExpose the Node Agent HTTPS API
Trusted Control Plane proxyCurrent Control PlaneAnother reachable Control Plane already manages the nodeCurrent plane can reach the proxy plane

Reverse WSS is normally preferred. It avoids public Node Agent listening, NAT, and TLS configuration, and the Node Agent persists the connection and reconnects after service restarts.

Reverse-connect to the Control Plane

Node Agent is not installed

  1. Ensure the target can reach the public Control Plane URL.
  2. In the wizard, select that the remote node can reach this Control Plane and that the agent is not installed.
  3. The wizard generates a one-time join token and a complete installation command.
  4. Run the command on the target Debian or Ubuntu host.
  5. Keep the wizard open until the node reports connected.

The command has this shape. Use the actual URL, token, and version generated by the UI:

sh
curl -fsSL 'https://control.example.com/install-node-agent.sh' | sudo sh -s -- \
  --control-plane 'https://control.example.com' \
  --join-token 'ONE_TIME_JOIN_TOKEN' \
  --npm-package @task-handoff/node-agent \
  --controlled-instance-package @task-handoff/controlled-instance \
  --version 'TARGET_VERSION'

The installer creates and starts task-handoff-node-agent.service, completes pairing, stores a dedicated connection credential, and opens the reverse WebSocket. A fresh supported host does not need Node.js or npm preinstalled.

Node Agent is already installed

After the wizard generates the token, run this on the target:

sh
sudo task-handoff-node-agent connect \
  --control-plane 'https://control.example.com' \
  --join-token 'ONE_TIME_JOIN_TOKEN'

If another Control Plane already manages the target, you can instead open that plane's Node → Connections, select Add outbound connection, and enter the new Control Plane URL and join token.

Join tokens expire and are used only for initial pairing. Select Generate new invite when the wizard reports expiration. The persisted reverse connection uses a separate credential and does not need the token to remain online.

Connect Directly to a Public Node Agent

Use direct access only when the target cannot reach the Control Plane but the Control Plane can reach the Node Agent.

  1. On the Node Agent host, change the TCP listener from 127.0.0.1 to the required interface. All IPv4 interfaces corresponds to 0.0.0.0.
  2. Configure firewall rules, DNS, NAT, and TLS termination so the Control Plane can reach the Node Agent over HTTPS.
  3. Generate a pairing token from the source Control Plane's node detail. For a standalone Node Agent, run:
sh
sudo task-handoff-node-agent invite \
  --ipc-path /run/task-handoff/node-agent.sock
  1. Enter the Node Agent HTTPS endpoint and one-time pairing token in the new Control Plane's wizard.
  2. Verify the node ID, connection method, and health after connection.

Public Node Agent endpoints must use HTTPS. Changing the listener to 0.0.0.0 only expands the bind scope; it does not configure TLS, firewall rules, or authentication. A port change is rejected while running instances still reference the old callback port. Stop those instances, change the port, then restart them. Existing direct Control Planes must also update their endpoints manually.

Use a Trusted Control Plane Proxy

Let A be the current Control Plane, R the trusted proxy Control Plane, and B the target node already managed by R:

  1. On R, open B's Connections → Trusted Control Plane proxy and create a one-time invite bound to B.
  2. On A, select Neither side is publicly reachable → Use trusted Control Plane in the add-node wizard.
  3. Enter R's canonical HTTPS origin and the proxy invite token.
  4. Read and select the trust confirmation, then connect.
  5. On A, inspect the proxy path, binding state, target state, and observation time.

R can read, modify, and perform every proxied Node Agent operation on A's behalf. This is not end-to-end encrypted and must stay within one trusted administrative boundary. The origin must be an HTTPS origin with no userinfo, path, query, or fragment.

The proxy binding is fixed to the node selected when the invite was created. Disabling the proxy node on A stops A from initiating operations but leaves the binding on R. Normal deletion asks R to revoke first. Force deletion while R is unreachable can leave a remote credential behind, which must be revoked manually on R after recovery.

Account-based node relay

The add-node wizard describes a Thandoff account option, but account-based Node Agent relay is not yet supported by the protocol. Use a trusted Control Plane proxy here; Thandoff account remote access is a different client-to-Control Plane path.

State and Troubleshooting

State or symptomMeaning and action
connecting / handshakingTransport is being established; check initiator logs, URL, DNS, and certificates
reconnectingA configured connection was interrupted; fix the network or remote service without re-pairing
Node online, instance offlineNode transport works; inspect the instance process and runtime
Proxy reachable, target offlineA can reach R, but R does not currently observe B online
Invite expired or invalidGenerate another one-time token and ensure another request did not consume it
Port change rejectedStop running instances that reference the old callback port and retry

Stopping or restarting the Control Plane should not stop instances managed by a remote Node Agent. Preserve the node record and pairing while troubleshooting. Inspect the last error and diagnostics under Node → Overview/Connections instead of deleting the node to test connectivity.