Skip to content

[bug] A machine's identity is os.hostname(), so one laptop is three machines — and a pin to a name it no longer uses answers every call with "run pags up" while pags up is running #379

Description

@serge-ivo

Reported

pags up is running, but the agent's Settings shows RLs-MacBook-Air twice, both offline.
Why doesn't it show online? Should we limit clients to one per machine?

Answer to the second question first: no — one-per-machine is already enforced (the RelayDO
rejects a second runner on the same hostname with 4409 unless --force). There are not two clients.
There is one machine wearing three names, and the agent is pinned to one it stopped using.

Measured, live, on this account

$ hostname                     Mac
$ scutil --get ComputerName    RL's MacBook Air
$ scutil --get LocalHostName   RLs-MacBook-Air
$ ps                           pags runner connect <17 instance ids>   (PID 30591, started 06:39 local)
                               16 ESTABLISHED sockets → 2606:4700:…:443

GET /v1/terminals/nodes — four "machines", which are really two:

node connected last seen
Mac true (16 instances) now
RLs-MacBook-Air.local false (16 instances) 2026-08-07 09:20
RLs-MacBook-Air false (2 instances) 2026-07-16
Sergeys-Mac-mini.local true (4 instances) now

The first three are the same laptop. It has minted a new identity every time os.hostname()
changed — which on macOS it does, because hostname follows the DHCP/network-supplied name and
flips between the .local mDNS form and whatever the network hands out. ComputerName and
LocalHostName are stable; hostname() is the one field the runner uses and the one that moves.

GET /v1/instances/cda75e28…/runner-node:

{ "runnerNode": "RLs-MacBook-Air.local",
  "nodesDetail": [ { "node": "Mac", "connected": true,  "nodeOnline": true },
                   { "node": "RLs-MacBook-Air.local", "connected": false, "nodeOnline": false } ] }

GET /v1/relay/cda75e28…/status?node=Mac{"connected":true}.

So the socket is up. The pin names a machine that no longer exists.

What that costs

getBoundRunnerConn (lib/runner-client.ts:80-87) treats a pin as authoritative and deliberately
does not fall back
— correct, and its comment says why: falling back would run the agent "on a
machine the user did not choose, in a different checkout". Pinned + dead → null.

Every runner-backed call on that instance therefore returns:

terminal_list_targets → "No runner is connected for this agent — run `pags up` on the machine
                         whose terminal you want to control."

…while pags up is running, on that machine, with a live socket, 16 instances attached. The one
remedy the platform offers is the one thing the user has already done.
This is the root cause of
#378 (the Terminal tab); that ticket is about how badly the state is presented, this one is why
the state exists at all.

Nothing heals it and nothing reaps the dead rows: a node row from 2026-07-16 is still in the picker
three weeks later, indistinguishable from a machine that is merely asleep.

Why "both duplicates are offline" is the honest reading of the screen

machineTile (store/console/src/lib/runnerPanel.ts) renders one tile per runner_node string.
The two tiles the user recognises as their laptop — RLs-MacBook-Air and RLs-MacBook-Air.local
are both genuinely offline. The tile that is their laptop is called Mac, which reads as some
other machine. The picker has no notion that a machine can be renamed, so it cannot say so.

Suggested direction

  1. Give a machine a stable identity. Mint a UUID once, persist it next to the session
    (~/.config/proagentstore/), and send it as the node key; keep hostname() as a mutable
    display label. normalizeRunnerNode (lib/runtime-nodes.ts) is a trim+slice over a
    user-visible string being used as a primary key — that is the whole defect in one line.
  2. Heal or flag a pin whose machine is gone. /runner-node already returns everything the
    sentence needs: pinned node offline, exactly one live node. Either auto-repoint with a notice, or
    say it out loud — "pinned to RLs-MacBook-Air.local, last seen 21h ago; this machine now reports
    as Mac — repin?"
    . Silent-and-authoritative is right for routing and wrong for the UI.
  3. Reap or fold stale nodes. A node not seen for N days should leave the picker (or offer
    "forget this machine"). Merging on a stable id makes this mostly unnecessary.

Workaround today: Settings → Runner → Runs on → pick Mac, or set it to Automatic (unpinned
routing already scans for whichever node holds a live socket — getLiveRunnerConn).

Related: #378 (the surface that shows this state), #238 (/runtime/status and the stale default
row), #241 (a stale offline verdict outliving its sessions).

Files: packages/cli/src/commands/runner/relay.ts:27,213, packages/cli/src/commands/runner/http.ts:53,
workers/api/src/lib/runtime-nodes.ts, workers/api/src/lib/runner-client.ts:73-116,
workers/api/src/routes/terminals.ts, store/console/src/lib/runnerPanel.ts,
store/console/src/components/RunnerPanel.tsx.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions