Machines & Identity
Every Connect operation — interactive attach, one-shot exec, share-link creation, agent-to-agent calls, remote filesystem reads — funnels through a single resolver that turns the string you type into a concrete machine in the active workspace. This page describes what that resolver knows about a machine and how it picks one.
The four identifiers
A machine carries four kinds of names. They are not interchangeable:
| Field | Source | Stable? |
|---|---|---|
ID | server-assigned UUID | Yes — survives renames, OS reinstalls, reboots. |
Hostname | os.Hostname() on the agent host | No — changes if the OS hostname changes; may include .local on macOS. |
Name | friendly label (defaults to Hostname) | Editable in the desktop app and dashboard. |
| Prefix | first unique characters of Hostname or Name | Convenience only — not a real identifier. |
The MachineInfo struct lives in internal/connect/machines/types.go:14–60
and is what every Connect tool sees:
type MachineInfo struct {
ID string // UUID, stable
Hostname string // OS-reported
Name string // friendly label
Online bool // cached, may lag
HeartbeatAgeSeconds int64 // freshness signal — preferred over Online
WorkspaceID string
WorkspaceName string
AgentVersion string
ActiveSession *ActiveTerminalSession
}Resolution priority
machines.Resolve() (in internal/connect/machines/filter.go) tries the
input string against the machine list in this order, stopping at the first
match:
- UUID short-circuit. If the input matches the canonical
8-4-4-4-12UUID shape (case-insensitive), it is matched againstMachineInfo.ID. No fall-through — if no UUID matches, you get an error, not a fuzzy guess. - Exact hostname. Equal to
Hostnameafter lowercasing. - Exact display name. Equal to
Nameafter lowercasing. Useful when the friendly label diverges from the OS hostname. - Unique fuzzy prefix. A substring or prefix match across both
HostnameandName. If exactly one machine matches, it wins. - Ambiguous. Two or more candidates → error with a candidate list.
- No match. Error with the input echoed.
All consumers — cmdop connect, cmdop connect exec, ask_agent,
ask_agents, remotefs.Open — funnel through one machines.Resolve().
You only need to learn the precedence once.
Online flag vs heartbeat age
MachineInfo.Online is a cached boolean populated by the relay. It
reflects what the dashboard saw at the last list refresh, which can lag
real state by seconds to minutes. For freshness-sensitive logic the
authoritative signal is HeartbeatAgeSeconds:
0–30s— recent heartbeat, machine is live.30–90s— possibly degraded; the daemon may be running but its relay link is flaky.>90s— server will mark the machine offline on the next sweep.
The agent tool surface uses Online as the gate (online_only=true is the
default for ask_agents, false for connect.list per tool.go:34), but
when you need to debug a flaky machine, HeartbeatAgeSeconds is the
number to look at.
The whoami operation
The connect agent tool exposes operation=whoami, which is the simplest
way to ask “where am I?” from inside an agent prompt:
{
"operation": "whoami"
}The implementation calls os.Hostname() and matches against the workspace
list (case-insensitive). The result is one of:
{ "matched": true, "hostname": "mac-studio", "machine_id": "8f23..." }
{ "matched": false, "hostname": "mac-studio", "reason": "not in workspace" }
{ "matched": false, "hostname": "mac-studio", "reason": "api unreachable" }If the API is unreachable, whoami degrades gracefully: it still returns
the local hostname so prompts can use it as context. See
internal/connect/CLAUDE.md §whoami.
Listing machines
The CLI surfaces three views of the machine list:
# All machines in the active workspace.
cmdop connect --list
# Only the ones currently online.
cmdop connect --list --online
# JSON for scripting.
cmdop connect --list --jsonA typical row in the human-readable output looks like:
NAME HOSTNAME ID ONLINE AGE SESSION
mac-studio mac-studio.local 8f23... yes 4s —
prod-api-1 prod-api-1 a142... yes 12s active (jane@bmw)
vps-audi vps-audi c9f8... no 4m12s —
vps-bmw vps-bmw f200... yes 3s —The SESSION column is populated from MachineInfo.ActiveSession. It
shows whether somebody else is currently attached.
Fuzzy matching pitfalls
Two patterns cause confusion:
-
Ambiguous prefixes. If you type
prodand the workspace containsprod-api-1,prod-api-2,prod-db-1, the resolver returns:ambiguous machine "prod" — matches: prod-api-1, prod-api-2, prod-db-1The fix is to type more characters until exactly one machine matches, or to use the UUID prefix instead.
-
macOS
.localsuffix. On macOS,os.Hostname()often returnsmac-studio.local. The resolver matches this permissively — bothmac-studioandmac-studio.localresolve to the same machine.
A single-character prefix is rejected outright to avoid surprises. Use at least two characters when matching by prefix.
Renaming a machine
The OS hostname and the friendly name can both change. MachineInfo.ID
cannot. Rename flow:
- Edit the friendly name from the desktop app’s machines tab or the dashboard.
- Or change the OS hostname; the agent re-registers under the new value on its next heartbeat.
For the duration of the legacy reconciliation window (server-side), the old hostname continues to resolve. Long-running scripts should switch to UUID before assuming the rename has propagated everywhere.
For unattended scripts (CI, scheduled jobs, fan-out automations), prefer the UUID. It is immune to renames and never ambiguous. Save hostnames and prefixes for interactive flows where a human is reading the output.
Active session info
When somebody is attached to a machine, MachineInfo.ActiveSession
carries enough information for the dashboard and TUI picker to show
“already in use” hints:
type ActiveTerminalSession struct {
SessionID string
StartedAt time.Time
Operator string // hostname of the client that opened the session
}This is purely informational — multiple clients can attach to the same session. The desktop app uses it to dim machines that already have an operator. See interactive-attach for the multi-client model.
Cross-workspace addressing
A machine belongs to exactly one workspace. To talk to a machine in a different workspace, switch first:
cmdop connect workspace use staging
cmdop connect exec vps-bmw -- uptimeThere is no syntax for “machine X in workspace Y” in a single call — that is by design, because the relay enforces workspace scoping at the auth layer.
Related
Where machines are scoped, and how to switch between them.
The picker UI uses the same resolver under the hood.
Common entry point that surfaces resolver errors directly.
ask_agent and ask_agents use the same fuzzy matcher.