Skip to content

fleet-node writes no runtime log: declared launchd sinks frozen at startup, making node incidents undiagnosable #1459

Description

@barryollama

Summary

agent-relay node up under launchd writes no runtime log at all. Its declared stdout/stderr sinks receive the startup banner and then nothing, for the entire life of the process.

This is not a logging nicety. It is the difference between a diagnosable fleet and one that can only be power-cycled.

Evidence

On finn-mini (macOS, launchd com.agentrelay.fleet-node, relay-broker/11.4.0), with the node process running continuously for 25 hours:

fleet-node.stdout.log   3,882 B   mtime Aug 6 12:41:10   <- process start
fleet-node.stderr.log   434 KB    mtime Aug 6 12:41:02   <- process start

Both frozen at startup. Not truncated, not rotated — the node has never written a runtime line.

Corroborating, all read-only:

  • find across ~/.agentworkforce/relay, ~/.agent-relay and ~/Library/Application Support/agent-relay for anything modified that day returned exactly one file, belonging to an unrelated broken service.
  • log show --last 3h --predicate 'eventMessage CONTAINS "agent-relay" OR ... "fleet-node"'completely empty.
  • ~/Library/Logs/agent-relay does not exist. /usr/local/var/log does not exist. No /tmp/agent-relay*.

What it cost

The node silently stopped heartbeating at 2026-08-07T11:52:35Z while the process stayed alive and healthy: no crash report, no jetsam/OOM, no sleep/wake transition, no OS-level signal, and its TCP session to the control plane still ESTABLISHED with Recv-Q 0 / Send-Q 0. An application-level heartbeat stall on an open socket.

There is no host-side record of the heartbeat loop existing at all, so there was no way to distinguish a wedged timer from a rejected request. The only place that evidence existed was the process's own memory. That process has since been restarted and the state is permanently unrecoverable.

If this recurs, the investigation starts from zero again and reaches the same dead end.

Suggested fixes

  1. Log the heartbeat loop — at minimum each send and its outcome, at a level that survives to disk.
  2. Log connection state transitions — reconnect, backoff, auth failure, rate-limit rejection. Any of these would have answered the question in one line.
  3. Log unexpectedly quiet periods. A node that has not successfully heartbeated in N intervals should say so locally, even if it cannot reach the control plane — especially then.
  4. Make the declared launchd sinks actually receive runtime output, or document that they do not and point operators somewhere real.

Note on interpretation

Absence of a log here is not evidence of absence of activity — the process was demonstrably running the whole time. Anyone diagnosing a node by reading these files will conclude the process died at startup. It did not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions