Skip to Content

Interactive Attach

cmdop connect opens an interactive shell on a remote CMDOP-registered machine. There is no SSH key, no port to expose, and no IP to remember — the relay handles transport, the resolver handles “which machine”, and the session model handles disconnects.

The picker flow

Run cmdop connect with no argument from a TTY and you get an interactive picker over the machines in the active workspace:

cmdop connect

The picker shows hostname, friendly name, online flag, and heartbeat age. It honors Ctrl-C to cancel. After you select a machine, a confirm prompt appears so a typo cannot drop you into the wrong shell. Select “yes” and the attach loop begins.

The picker, confirm, and attach loop live in internal/connect/picker/model.go and go/cmd/cmdop/cmds/connect/connect_attach.go. They share one resolver with every other Connect surface.

The picker is TTY-only. Running cmdop connect from a piped or non-interactive context errors out and tells you to pass an explicit hostname (or use --no-interactive).

Direct attach

If you already know which machine you want, skip the picker:

# By friendly name or hostname. cmdop connect vps-audi # By UUID prefix (or full UUID). cmdop connect 8f23a4b0

The string is run through machines.Resolve() — see machines & identity for the precedence rules. If it matches a unique machine, the attach starts immediately.

What you see on attach

A successful attach prints a banner then drops you into the remote shell:

$ cmdop connect prod-api-1 [connect] resolving prod-api-1 ... ok (id 8f23..., online 3s) [connect] dialing relay ... ok [connect] auth ... ok (session abcd1234) [connect] attached — Ctrl-D to disconnect, Ctrl-C forwards as SIGINT prod-api-1 $

The banner is printed only after WaitReady() succeeds, so if you do not see it, the dial or auth failed and you should read the error.

Ctrl-D vs Ctrl-C

Two control characters do different things:

  • Ctrl-D (byte 0x04). Intercepted locally by the CLI. It does not reach the remote shell — it tells the CLI to close the stream. This means you cannot send EOF to a program running on the far side with Ctrl-D while attached. Pipe input via stdin or use cmdop connect exec instead.
  • Ctrl-C (byte 0x03). Forwarded verbatim to the remote shell as SIGINT. Use it the way you would in any normal shell — to cancel a long-running command.

This contract is enforced in internal/connect/client/attach.go. See also client/CLAUDE.md §interactive attach contract.

If you need a literal Ctrl-D byte to reach the remote, use one-shot exec with --stdin or run the program in a persistent session. Interactive attach reserves Ctrl-D for local disconnect.

Window resize

Resize signals (SIGWINCH) are forwarded to the remote shell. Resizing your terminal while attached re-runs ioctl(TIOCSWINSZ) on the remote PTY, so tput, top, htop, and full-screen TUIs all behave normally.

Disconnect, reattach

A session is an object, not a connection. When you press Ctrl-D:

  • The CLI sends a graceful close and exits.
  • The remote PTY is not killed. The session enters a 30-second grace window during which any client can reattach.
  • If nobody reattaches within the grace window, the session closes and the PTY is torn down.

Practical consequence: a flaky network or laptop sleep does not lose your shell as long as you reconnect within the grace period.

# Reattach to the same machine and pick up where you left off. cmdop connect prod-api-1

The CLI loops the picker on ErrUserDisconnected, so pressing Ctrl-D inside the picker exits cleanly while pressing it inside an attached shell drops you back to your previous prompt.

Multiple clients on one session

CMDOP sessions are not single-client. If you attach to a machine that already has an operator, you both see the same PTY:

  • Both clients see all output (full ringbuf replay on join).
  • Both clients can send input.
  • Either client pressing Ctrl-D only disconnects that client; the session lives on.

The SESSION column in cmdop connect --list shows the operator that opened the session, but extra observers are not enumerated. See concepts/multi-client for the broader model.

Password-protected machines

If the remote agent has a machine password set (see auth-and-passwords), the attach flow gains an auth challenge:

[connect] auth ... password required Password for prod-api-1:

The CLI tries, in order:

  1. The --password flag if you passed one.
  2. The locally stored password from internal/security/machinepw.
  3. The CMDOP_AGENT_PASSWORD environment variable.
  4. A TTY prompt.

After a successful AuthSuccess, the session token is cached process-wide for 24 hours so subsequent unary RPCs against the same session do not re-prompt. See internal/connect/client/CLAUDE.md §streaming vs unary auth.

Scripted, no-prompt attach

For CI and unattended automation, force the no-prompt path:

cmdop connect --no-interactive prod-api-1

This disables the picker, the confirm step, and any TTY password prompt. If anything is missing (hostname, password, API key), the call fails fast with a structured error rather than blocking on a prompt.

--json implies --no-interactive and emits machine-readable output suitable for piping into other tools. See exec for the non-interactive workflow that is usually what you actually want for scripts.

Common errors

ErrorCauseFix
ambiguous machine "prod"Prefix matches multiple hosts.Type more characters or use the UUID.
machine "vps-audi" is offlineHeartbeat older than the relay TTL.Check the daemon on the target with cmdop agent status.
Session requires password. Authenticate via ConnectTerminal first.A unary RPC ran before streaming auth cached the session token.Re-attach interactively once; the cache fixes it for the rest of the process lifetime.
no API key for workspace "production"Resolver bottomed out.Run cmdop connect key set <key> or cmdop login.

Run a single command without holding an interactive PTY.

The streaming auth gate, password sources, and session token cache.

Persistent multi-command sessions for long-running work.

The resolver behind every cmdop connect <host> call.

Last updated on