Skip to content

[enhancement] Recovering from a hostname change means hand-editing machine.json — the CLI should ask, on first run, which of the account's unclaimed names are this machine #460

Description

@serge-ivo

The recovery from a hostname change is hand-editing a JSON file

Split out of #393, whose own closing note said this belongs in its own issue rather than absorbed
there. #393's three items are delivered; this is the gap its comment thread named and its checkboxes
never covered.

What a user has to do today

#379 gave a machine a stable id and #393 taught the list to fold by it. Both work. But the name
history starts at upgrade time: a fresh ~/.config/proagentstore/machine.json holds
{ id, names: [<current hostname>] }, so claimMachineNames stamps exactly one row, and rows minted
under a name the machine used before it had an id can never be claimed by anything.

That is not hypothetical — it is what happened on this account, and the recovery was:

1. upgrade the CLI
2. open ~/.config/proagentstore/machine.json in an editor and add the other hostnames by hand:
     { "id": "3901c9c4-…", "names": ["Mac", "RLs-MacBook-Air.local", "RLs-MacBook-Air"] }
3. restart `pags up`

Step 2 is the product gap. It is also the only step that matters: with it, all three rows take one
machine_id, aliasNodesFor proves they are one machine, six stranded pins route to the live socket,
and the fold collapses them. Without it the mechanism is correct and does nothing.

The current file on that laptop still carries the hand-written list, which is the evidence: the fix
works, and it only worked because someone edited JSON.

What to do

On first run — when the CLI has just minted an id and is about to register — ASK. The server
already knows the candidate list; GET /v1/terminals/nodes returns every node on the account with
its machineId (null = unclaimed), lastSeenAt and connected. So:

PAGS knows 3 machine names on this account that no machine has claimed.
Is this machine also known as:
  [ ] RLs-MacBook-Air.local   (last seen 2026-08-07, 16 agents)
  [ ] RLs-MacBook-Air         (last seen 2026-07-16,  2 agents)
Selecting a name merges its agents and sessions onto this machine.  [Enter to skip]

Confirmed names are appended to machine.json's names and claimed on the next register. Nothing
else changes — claimMachineNames already does the work.

Constraints that are not negotiable

  • User-confirmed, never automatic. A wrong yes merges two genuinely different machines, and
    fail-closed is the one guarantee [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 exists to provide. Do not infer from MAC address, serial, or
    hostname similarity — RLs-MacBook-Air and RLs-MacBook-Air.local look like the same machine and
    a rule that says so would also merge two laptops an admin named consistently.
  • Only offer unclaimed names. A name already carrying a different machineId is another machine's
    proven identity; it must not appear in the list at all.
  • Skippable and non-blocking. pags up must still come up on a headless box, over SSH, in CI, and
    with no TTY. No prompt when --headless, when stdin is not a TTY, or when there is nothing
    unclaimed to offer — and a skip must be remembered, not re-asked on every start.
  • Offer it once, not forever. Record the decline so a user who genuinely has three machines is not
    interrogated at every launch.

Alternatives considered and rejected

  • A console-side "these are the same machine" merge control. Rejected as the first move: the
    browser cannot prove anything about the machine, so it would be the same guess with a nicer surface.
    The CLI is the only party that is on the machine, which is what makes its answer evidence.
  • Auto-claim any unclaimed name last seen from the same public IP. Rejected — a household NAT
    makes two different laptops indistinguishable, and the cost of a wrong merge is another agent's
    sessions and pins moving to the wrong machine.
  • Fold by name similarity (X and X.local). Rejected: it is right often enough to be trusted and
    wrong exactly where it is expensive. .local is mDNS, not an alias guarantee.
  • A pags machines claim <name> command instead of a first-run prompt. Worth having as well, and
    cheap once the API call exists — but it does not close the gap, because the user who needs it does
    not know the command exists. The whole defect is that the remedy is invisible.

Acceptance criteria

  • On a machine with a fresh machine.json, pags up lists the account's unclaimed node names with
    last-seen and agent counts, and claims only the ones the user selects.
  • Claimed names appear in machine.json names and are stamped with this machine's machine_id
    on the next register; GET /v1/terminals/nodes then folds them into one machine with the others
    in aka.
  • A name already bearing a different machineId is never offered.
  • --headless, a non-TTY stdin, and "nothing unclaimed" each start the runner with no prompt and
    no delay.
  • A decline is remembered and not re-asked.
  • No path claims a name the user did not select.

Regression risk

Files: packages/cli/src/machine.ts (the id + name file), packages/cli/src/up.ts (first-run path),
workers/api/src/routes/terminals.ts:174-259 (the candidate list, already returns machineId/aka/
identityHint), workers/api/src/lib/machine-identity.ts (claimMachineNames, adoptableIdByName).
Follows #393 and #379.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions