devmachine

How it works

Why a login cannot be automated

It is the first question everybody asks, so here is the answer before you go looking.

Because a person is the point

gh auth login opens a browser, or prints a device code and waits. claude /login does the same. The step in the middle is a human proving they are themselves, to a service that will not accept a script doing it for them. A CLI that automated this would either store your password — what the whole flow exists to avoid — or hold a token it got some other way, the same problem with an extra step.

So devmachine login does not try. It knows which command to run, because the package declared it; where it has to happen — that workspace’s account, that machine, a real terminal so a prompt or code actually reaches you; and what to do with the result: a machine-scoped login is copied to /etc/devmachine/<name>/ and spread from there. You sit through one login; the CLI handles the rest.

A real terminal, not a captured one

login runs the system ssh with a TTY, the same path devmachine ssh takes — a device code you cannot read is one that expires.

One consequence worth knowing: that path reads ~/.ssh/known_hosts, which the Go client the rest of the CLI uses does not. So the first devmachine login against a machine ssh has never seen can fail with Host key verification failed, while doctor and sync worked fine a moment earlier. Connect once with devmachine ssh and accept the key, or add it yourself.

stored_at is a claim, not a guarantee

A package says where its tool keeps the result:

stored_at: ~/.claude/.credentials.json

That is how doctor and credentials list can check, and how a shared login knows what to copy. It is a claim by whoever wrote the package, checked against one version of one tool.

A tool that moves its session file breaks the claim in the wrong direction: the CLI reports missing what is in fact present, and logging in again changes nothing. Worth having anyway — the alternative is no check at all — and worth knowing before you meet it. A credential with no stored_at reports unknown rather than missing, because “I cannot tell” and “it is not there” are different claims.