Authentication & Connection PINs
Connect’s auth model has two layers, and confusion between them is the single most common source of “Session requires password” errors. This page describes both layers and the cache that ties them together.
The per-machine attach secret is the connection PIN. The CLI
verb (cmdop connect password …), the CMDOP_AGENT_PASSWORD env var,
and the --password flag keep the historical password name on the
wire, but the thing the user sets is a PIN.
The two layers
| Layer | What it authenticates | Where it lives |
|---|---|---|
| Relay credential | ”Is this caller allowed to talk to the relay at all?” | The CLI token from cmdop login, scoped to your active fleet. Held in the OS keyring. |
| Connection PIN | ”Once a session is opened on this machine, is the operator allowed to drive it?” | Per-machine setting on the agent. Cached locally per machine. |
A relay credential is mandatory. A connection PIN is optional — only machines that explicitly set one require it. Both must be satisfied before a streaming session opens.
The relay credential gets you to the relay. The connection PIN gets you into the session. They are two different gates, configured in two different places, even though Connect collapses them into one flow at attach time.
Streaming vs one-shot auth
The relay handles password auth differently for a live, interactive attach than for a one-shot call. Mixing them up is the canonical source of pain; the short version:
Streaming attach
Used by interactive PTY attach and by the one-shot recovery door-opener. The PIN challenge happens inline on the live stream: a challenge is raised, the client answers from its sources below, and on success a session token is handed back. The client caches that token for 24 hours.
The connection PIN is verified on the target agent, not by the relay — the relay never sees the PIN and only brokers the challenge/response frames. The PIN never leaves the agent boundary as a stored secret.
One-shot calls
A one-shot call (send input, resize, run a command) does not carry a password. It presents the cached session token instead, and the relay checks that token. If no valid token is attached, the call fails with:
UNAUTHENTICATED: Session requires password.
Authenticate via ConnectTerminal first.This is the canonical “I’m seeing the password error” trap. The fix is not to install another password somewhere — it is to make sure a streaming attach has authenticated that session before any one-shot call against it fires.
The session token cache
The session token cache is a small process-wide store that ties the two together:
- The streaming attach writes a token into it on a successful login.
- Each later one-shot call reads the token back out and presents it.
- TTL is 24 hours. Expired entries are pruned on access.
- Memory-only — nothing lands on disk.
Practical consequences:
- Inside one daemon process, the cache is shared across
surfaces. A streaming attach by
cmdop connectpopulates the token; subsequent one-shot calls fromcmdop connect execor theconnectagent tool reuse it. - Across daemon restarts, the cache is gone. First call after restart pays the streaming-open cost again.
- Across hosts, the cache is independent. Each machine’s daemon caches its own tokens.
Password sources, in order
When a streaming attach is asked for a password, the client tries password sources in this order:
--passwordflag. Explicit per-call value.- Local store. A per-machine password record cached locally,
keyed by machine ID. Set via
cmdop connect password set, cleared viacmdop connect password clear. CMDOP_AGENT_PASSWORDenv var. Single value, same for every machine. Used by automation and CI.- TTY prompt. If a human is at the terminal, the CLI prompts inline.
The agent-tool surface skips step 4 — bots cannot answer prompts.
If steps 1–3 do not yield a value, the call fails with an
auth_error.
# Set a password locally for one machine.
cmdop connect password set vps-audi
# Password: <typed in>
# Inspect (only that a value is set; never prints the password).
cmdop connect password get vps-audi
# Remove.
cmdop connect password clear vps-audiThe CMDOP_AGENT_PASSWORD env var applies to every password
challenge in the same process. For multi-machine fan-outs where
machines have different passwords, prefer the local store (per-machine)
or pass --password explicitly per call.
One-shot exec recovery (no extra prompts)
cmdop connect exec is a one-shot call. Without help, it would fail
immediately on a password-protected machine because no prior
streaming attach has populated the cache. The CLI handles this
transparently with a recovery path:
- Try the one-shot command.
- If it is rejected for a missing session token, open a brief streaming attach, answer the password challenge, and capture the resulting session token into the cache.
- Retry the one-shot command — this time it succeeds.
Non-password machines pay zero overhead (step 1 just works). Password machines pay one extra streaming open per process. After that, every one-shot call against the same machine is fast.
Relay credential failures
The other half of auth — the relay CLI token — fails earlier and with clearer errors. Common ones:
| Error | Cause | Fix |
|---|---|---|
auth_error: not signed in | No CLI token resolved. | Run cmdop login. |
unauthenticated: token expired | The CLI token expired and could not refresh. | cmdop login again. |
permission_denied: machine X not in workspace Y | You switched fleets; machine belongs to the previous one. | Select the correct fleet in the dashboard, then cmdop login. |
For the single CLI-token credential source, see credential-resolver.
MFA and login
MFA lives on the login flow, not on Connect itself. When you run
cmdop login, the browser-based flow handles whatever multi-factor
your account requires. The CLI token that comes back is the relay
credential — Connect never sees the PIN, TOTP, or webauthn assertion
directly.
The CLI token refreshes silently while it can; if the refresh fails,
you re-run cmdop login. There is no “MFA prompt” in cmdop connect
itself.
Talking to a password-protected machine
Putting it together, here is what happens for a password-protected machine on its first call after daemon restart:
cmdop connect exec prod-api-1 -- uptime- Resolver picks
prod-api-1from the fleet. - The relay credential resolves (the CLI token from
cmdop login). - The one-shot command fires. The relay rejects it for a missing session token.
- CLI opens a streaming attach. The relay asks for the password.
- CLI tries the four password sources. Finds the password in the local store.
- The relay accepts the password and returns a session token. CLI caches it.
- CLI retries the one-shot command, this time presenting the cached token. The relay accepts it.
uptimeruns and returns.
Subsequent calls in the same process skip steps 4–7 entirely.
Self-to-self machines
When the caller and target share the same identity, the permission gate is bypassed (see server-to-server). The PIN gate is not — even on self-to-self, a PIN-protected machine still demands its PIN. The two gates are independent.
Common errors
| Symptom | Layer | Cause | Fix |
|---|---|---|---|
Session requires password. Authenticate via ConnectTerminal first. | One-shot call | No streaming attach has populated the cache for this session. | The CLI’s one-shot recovery path handles this — if you see it from a custom integration, ensure you do a streaming attach first. |
auth_error: no password source | Streaming | All four password sources empty. | Set the password locally or via env; or pass --password. |
unauthenticated: invalid session token | One-shot call | Cached token expired (24h passed) or evicted. | Just rerun the call — recovery re-primes the cache. |
permission_denied | Relay | CLI token valid, but its fleet doesn’t cover this machine. | Switch to the correct fleet and re-run cmdop login. |