Skip to content

feat: machine rename from SPA + CLI via the encrypted registry (N1) - #67

Merged
frahlg merged 2 commits into
mainfrom
63-machine-rename
Aug 29, 2026
Merged

feat: machine rename from SPA + CLI via the encrypted registry (N1)#67
frahlg merged 2 commits into
mainfrom
63-machine-rename

Conversation

@frahlg

@frahlg frahlg commented Aug 29, 2026

Copy link
Copy Markdown
Member

Closes #63. Slice N1 of the v0.8-beta UX roadmap: rename a machine on one device and the new name appears on every device — relay still blind, still stateless.

The propagation design (and why)

The agent cannot seal registry records — since the v0.7 hardening the owner client seals the discovery blob during pairing and the agent only republishes it opaquely (targets are expendable). So the only sound path for a rename is owner-side:

  1. Local first. The renaming device updates its own store at once (instant effect), stamped with the seal's ts.
  2. Deliver over the session. The client re-seals the record under the owner root and sends {a:"rename-machine", n, blob} as an agent-level CONTROL command through the existing authenticated channel. No new frame type, no new relay route.
  3. Agent applies + republishes. It validates (name 1–64 printable runes; blob ≤ the relay's 16 KiB cap), persists name + the requesting owner's blob, pushes the blob on its live signaling connection (the relay's setRegistry already replaces per registration — zero relay changes), and re-HELLOs the session — the client's acknowledgement.
  4. Everyone else converges via the registry. Records already carry ts, so the name merge is now last-writer-wins on name_ts (Go and JS mirrored, tests on both). A local rename not yet delivered keeps winning; a newer registry record updates local entries.

Alternatives weighed: a client→relay write endpoint (rejected: new route + breaks "the blob rides the live registration"); rename as local alias only (rejected: never propagates); a new frame type (rejected: CONTROL already carries verb-style JSON both languages understand).

Behavior change (deliberate)

MergeMachines used to keep the local display name unconditionally — under rename that would pin stale names forever. Registry names now win on newer-or-equal name_ts. Pairing-time names are identical on both sides, so nothing visibly changes for existing users; a hand-edited machines.json name will revert to the registry name until renamed properly.

Surface

  • CLI: mir machine rename <name> <new-name> — local-first, then delivery + HELLO-ack wait, with honest errors: unreachable machine → "renamed locally, other devices keep the old name"; no ack → "the machine may run an older agent".
  • SPA: ✎ in the terminal topbar (next to ⇄/⊘) — prompt, seal, send, optimistic local upsert. Late HELLOs also update the title live, so a rename made on your phone renames the tab on your laptop mid-session.
  • Multi-owner: a rename updates the shared display name plus the requesting owner's blob; other owners' sealed records keep their own names until they rename (each owner's record is theirs).

Left out (deliberately)

  • Rename from the SPA machine list (needs a dial-only session; the terminal view is where a session already exists).
  • Opportunistic re-delivery on later attaches when the machine was offline — re-running the command when it is back covers it; noted as a possible follow-up.
  • tmux window/session rename is untouched (different, existing controls).

Tests

  • Go: go test ./... green. New: name validation; persist (only the requesting owner's slot); handler happy-path incl. live republish on a fake signaling writer; bad-input swallowing; a full session-level test that the CONTROL round-trips to a fresh HELLO over Noise; merge LWW table; local upsert. One existing merge test updated to the new name rule (it pinned the old one).
  • Web: npm test 119/119. New: merge LWW table; sealMachineRecord → decodeRegistry round-trip carrying ts; existing merge/decode tests updated for name_ts.
  • No testdata/ vectors changed (no handshake/derivation code touched).

Conflict note: touches go/internal/agent/session.go (the CONTROL case + one new parameter) — coordinate with R3 (#57) at merge; the hunks are in different regions.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KeiotDVE94wEzvc7wcvm1y


Note

Medium Risk
Touches authenticated CONTROL handling, local/registry merge semantics (stale hand-edited names can revert), and multi-step rename delivery; crypto and relay paths are unchanged but behavior is user-visible across devices.

Overview
Adds end-to-end machine rename so a new display name on one device propagates to every paired client through the existing encrypted registry—no relay API changes.

Agent: Authenticated sessions accept a CONTROL command {a:"rename-machine", n, blob} (owner-resealed registry blob). The agent validates the name and blob size, persists machine_name plus that owner’s registry slot, republishes the blob on the live signaling WebSocket, and re-sends HELLO as acknowledgement. serveAuthenticated now passes owner id into a per-session renameHandler; HELLO uses a live machineName() that can change mid-session.

Clients (Go + web): mir machine rename and the SPA terminal button update local machines.json first with name_ts, re-seal the discovery record, attach, and wait for confirming HELLO (RenameOverSession). SealRegistryMachine now returns the record timestamp; MergeMachines / mergeMachines pick display names by last-writer-wins on name_ts (registry wins when newer)—replacing the old rule that always kept the local name.

Session plumbing: RunAgentSession takes an optional ControlHandler that runs before tmux CONTROL handling; successful rename triggers an extra HELLO frame. The web attach path handles late HELLOs to refresh the title when another device renames.

Reviewed by Cursor Bugbot for commit b8950ef. Bugbot is set up for automated code reviews on this repo. Configure here.

…istry (N1)

The agent never holds the owner root, so it cannot seal registry records.
A rename therefore starts on an owner client: it renames locally at once
(instant effect), re-seals the discovery record under the owner root, and
delivers name + blob to the machine over the authenticated session as an
agent-level CONTROL command ({a:"rename-machine"}). The agent validates,
persists both, republishes the record on its live relay registration (the
relay replaces the blob it holds — still blind, still stateless), and
re-HELLOs the session as the acknowledgement.

Other devices converge through the registry: records already carry ts, so
the name merge becomes last-writer-wins on a new local name_ts (Go and JS
mirrored). A local rename not yet delivered keeps winning; a newer registry
record (a rename made elsewhere) now updates local entries — previously the
local name always stayed, which would have pinned stale names forever.

- agent: ControlHandler seam in RunAgentSession (rename claimed before tmux
  control); RenameMachine persists name + owner blob; live signaling writers
  registry for mid-run republish; serveAuthenticated learns the owner id
  (relay + LAN paths).
- client: `mir machine rename <name> <new-name>` (local-first, then deliver
  + await the HELLO ack, honest errors when offline/unconfirmed);
  RenameLocalMachine keyed by machine_id; SealRegistryMachine returns its ts.
- web: ✎ rename in the terminal topbar (seal + sendCtl + optimistic local
  upsert); late HELLOs update the title live (renames from other devices);
  mergeMachines carries name_ts.

Closes #63

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeiotDVE94wEzvc7wcvm1y
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T10:53:16.681642Z 59ef0eb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

…ct + gofmt)

session.go keeps both R3's hook-trigger window push and N1's ControlHandler;
app.js's connectOnce takes onLink (R1, 6th) AND onHello (N1, 7th).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeiotDVE94wEzvc7wcvm1y

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59ef0eb3bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if err := json.Unmarshal(data, cfg); err != nil {
return err
}
cfg.MachineName = name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the renamed name across agent restarts

On the normal mir up restart path, cmdUp calls LoadOrInit with the hostname/default --name, and LoadOrInit unconditionally assigns that value to cfg.MachineName. Consequently, the name persisted here is overwritten whenever the agent restarts unless its launch configuration is also manually changed; subsequent HELLOs and newly paired owners see the pre-rename/default name. The startup path needs to preserve a persisted rename when --name was not explicitly supplied.

Useful? React with 👍 / 👎.

Comment thread web/src/app.js
Comment on lines +669 to +672
machine = { ...machine, name: n, name_ts: sealed.ts };
addMachine(machine); // upsert by machine_id — the local rename is instant
titleEl.textContent = n;
ctl({ a: 'rename-machine', n, blob: sealed.blob });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Confirm or queue the SPA rename before committing it locally

In the inspected SPA terminal flow, the rename button remains usable while connecting, reconnecting, or failed, but ctl silently does nothing when current.send is null; an older agent similarly ignores this new command. These lines nevertheless persist and display the new local name without waiting for the promised HELLO acknowledgment, leaving the machine and encrypted registry on the old name indefinitely. Retrying the desired name is also blocked by the earlier n === machine.name return, so delivery must be queued or failed/unconfirmed renames must remain retryable.

Useful? React with 👍 / 👎.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b8950ef. Configure here.

cfg.OwnerRegistry = make(map[string]string)
}
cfg.OwnerRegistry[ownerID] = blob
return save(dir, cfg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisted rename lost on restart

High Severity

RenameMachine writes the new display name into config.json, but every mir up (and mir pair responder / mir enroll) calls LoadOrInit, which always overwrites MachineName with the --name flag. That flag defaults to hostname(), including LaunchAgent and auto-update restarts, so the persisted rename is replaced on the next agent start. New sessions then HELLO the old hostname while the sealed registry blob can still carry the new name.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b8950ef. Configure here.

Comment thread web/src/app.js
machine = { ...machine, name: n, name_ts: sealed.ts };
addMachine(machine); // upsert by machine_id — the local rename is instant
titleEl.textContent = n;
ctl({ a: 'rename-machine', n, blob: sealed.blob });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SPA rename drops when disconnected

Medium Severity

renameMachineUI always upserts the local store and title, then calls ctl, whose sendCtl no-ops when current.send is null (connecting, reconnecting, or a dead session). There is no HELLO wait, unlike the CLI. The receive loop also feeds the agent’s initial HELLO into onHello, so a later attach overwrites the optimistic title with the still-old agent name and never retries delivery.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b8950ef. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot did not complete successfully (check status: skipped), so this automation is not approving. Human review is needed; reviewers were assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@cursor
cursor Bot requested review from miravoss26 and wachtelhund August 29, 2026 10:58

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a non-blocking comment: Cursor Bugbot reported 2 unresolved findings on the current head, so this PR is not approved. Human review is needed; two reviewers are already assigned, so none were added.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@frahlg
frahlg merged commit 7970965 into main Aug 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

N1: machine rename from SPA + CLI, propagated via the encrypted registry

1 participant