Summary
A fleet node's control-plane connection blackholes silently: the node process stays alive, keeps its TCP sockets ESTABLISHED, continues spawning workers successfully — and stops heartbeating. The control plane then reports it offline / handlersLive: false / activeAgents: 0 while ps shows it running N agents, and every targeted spawn to that node fails with placement TTL expired.
This is not a dead node. It is a node the scheduler can no longer see.
Measurement, finn-mini, 2026-08-20
| signal |
value |
| node process |
alive, pid 21570, uptime 22h30m, launchd com.agentrelay.fleet-node |
| ESTABLISHED sockets to Cloudflare |
2 (104.18.13.48:443, 104.18.12.48:443) |
| workers actually running |
5, all healthy, all doing real work |
last line written to fleet-node.stderr.log |
19:28 |
control-plane lastHeartbeatAt |
19:29:33Z |
control-plane status / live / handlersLive / activeAgents |
offline / false / false / 0 |
| time observed in this state |
2h21m and counting |
The node stopped writing to its own log and stopped heartbeating at the same moment, while its sockets stayed open. No error is logged at the transition — the last line is an ordinary success:
[agent-relay] spawned worker 'relay-1579-rebase-0820b' via relaycast
That spawn succeeded — the worker is alive and has since rebased a PR and answered review threads. So the node completed the operation and then went silent.
Impact
- New work cannot be placed on the node. Two targeted
fleet spawn --node finn-mini attempts returned Placement queued: target node "finn-mini" is offline; placement TTL expired. Placement had to be moved to another host, including creating duplicate git worktrees there.
- Existing workers keep running, so the loss is invisible unless you
ssh in. The fleet looks like it has capacity it cannot use.
fleet agent list reports the node's agents as count only / names unavailable, because the node can no longer publish its roster — compounding relay#1585.
- Capacity concentrates on the remaining nodes with no signal that it has.
Why the existing signals do not catch it
- An
ESTABLISHED socket is not a live session. Both sockets are open; nothing is flowing.
- Worker spawn success is not control-plane health. The node kept spawning right up to the silence.
activeAgents: 0 is confidently wrong — there are five. So the field a scheduler would trust is not merely stale, it is inverted.
- Nothing is logged. A grep of the node's stderr for
provider websocket|connection closed|failed registering|reconnect|heartbeat returns only entries from a previous incident hours earlier, nothing at the transition.
Asks
- Detect it. The node should notice its own control-plane link has gone quiet — an application-level ping with a deadline, not TCP liveness — and reconnect. relay#1462 was merged to detect this class; this is either a gap in it or a regression against it.
- Log the transition. A node that stops heartbeating must say so in its own log. Today the only way to discover this is to compare
lastHeartbeatAt against ps on the host.
- Do not report
activeAgents: 0 for an unreachable node. Zero is a claim. unknown is the truth, and the distinction decides whether a scheduler routes work there.
- Consider failing the placement fast and loudly.
placement TTL expired after a wait reads as congestion; node unreachable would have pointed straight here.
Environment
- broker
11.8.0, node launchd-managed (com.agentrelay.fleet-node), --state-dir ~/.agentworkforce/relay/finn-mini-node/state
- Same host previously logged
failed registering agent with RELAY_WORKSPACE_KEY workspace key → Invalid response: Max retries exceeded during the D1 saturation window, so a registration/handshake path is already known to be fragile here.
Summary
A fleet node's control-plane connection blackholes silently: the node process stays alive, keeps its TCP sockets
ESTABLISHED, continues spawning workers successfully — and stops heartbeating. The control plane then reports itoffline / handlersLive: false / activeAgents: 0whilepsshows it running N agents, and every targeted spawn to that node fails withplacement TTL expired.This is not a dead node. It is a node the scheduler can no longer see.
Measurement,
finn-mini, 2026-08-20com.agentrelay.fleet-node104.18.13.48:443,104.18.12.48:443)fleet-node.stderr.loglastHeartbeatAtstatus/live/handlersLive/activeAgentsoffline/false/false/ 0The node stopped writing to its own log and stopped heartbeating at the same moment, while its sockets stayed open. No error is logged at the transition — the last line is an ordinary success:
That spawn succeeded — the worker is alive and has since rebased a PR and answered review threads. So the node completed the operation and then went silent.
Impact
fleet spawn --node finn-miniattempts returnedPlacement queued: target node "finn-mini" is offline; placement TTL expired. Placement had to be moved to another host, including creating duplicate git worktrees there.sshin. The fleet looks like it has capacity it cannot use.fleet agent listreports the node's agents ascount only/ names unavailable, because the node can no longer publish its roster — compounding relay#1585.Why the existing signals do not catch it
ESTABLISHEDsocket is not a live session. Both sockets are open; nothing is flowing.activeAgents: 0is confidently wrong — there are five. So the field a scheduler would trust is not merely stale, it is inverted.provider websocket|connection closed|failed registering|reconnect|heartbeatreturns only entries from a previous incident hours earlier, nothing at the transition.Asks
lastHeartbeatAtagainstpson the host.activeAgents: 0for an unreachable node. Zero is a claim.unknownis the truth, and the distinction decides whether a scheduler routes work there.placement TTL expiredafter a wait reads as congestion;node unreachablewould have pointed straight here.Environment
11.8.0, node launchd-managed (com.agentrelay.fleet-node),--state-dir ~/.agentworkforce/relay/finn-mini-node/statefailed registering agent with RELAY_WORKSPACE_KEY workspace key→Invalid response: Max retries exceededduring the D1 saturation window, so a registration/handshake path is already known to be fragile here.