Skip to content

First run

A short path through the manual, in order. Every command is provisional and has not been run against a release yet (unverified); it was checked against whr --help of a build from main (issue #65). Each step names the page that holds the detail.

  1. Prepare the host, as the administrator: Prepare the Mac mini.
  2. Install whr, as the administrator: Install, upgrade and release. Then check it: whr version.
  3. Set up: first whr setup host as the administrator, then whr setup as whr in its desktop session, not over SSH. whr setup --dry-run shows every fix first. The GitHub App comes from whr github app create, see Prepare the Mac mini.
  4. Check with whr doctor; fix what it reports, and treat “not verified” as not ok.
  5. Start the supervisor: whr service install, then whr service status. See Run the supervisor.
  6. Sign in to the web UI on your phone or laptop through the forwarder, with the API token (see Run the supervisor). Then enrol a passkey on the host with whr passkey add; from the first passkey on, the token no longer signs in to the web UI.
  7. Add a workspace and an agent: whr ws add <name> --path <folder> --repo <owner/name> --role <role>. It seeds the clone and starts the environment, which takes a while. The workspace rebases onto the repository’s publication target (the configuration’s integration_branch, develop for the integration preset, or, for a published repository, the default branch the forge reports now; only main or develop are accepted so far, #229); leave --branch out, because a different one is refused.
  8. Sign in to the agent inside its environment yourself, with the vendor’s own flow; whr never sees the login (Sign in to the agent below, and Agent vendor terms).
  9. Start a task: whr run <issue-url> --agent <workspace>/<role>, then whr logs <task> -f, whr inbox and whr approve. See Daily use.
  10. Before you rely on it, read Security notes.

Sign in to the agent

This is step 8 in detail, for Claude Code with a subscription (unverified: no step below has been run on the real setup; the hosts and the flow are from the sign-in spike, which measured them in a test environment). You sign in once per workspace, in that workspace’s own environment. The login stays on the environment’s home volume, so it survives a stop, a start and whr ws rebuild, and it is lost with whr ws rm.

Which environment. The workspace’s, not the console. whr console and whr ssh open the console, which has none of the agents’ credentials, so a login made there is of no use to an agent. The Claude adapter reads its login from /home/agent/.claude in the workspace’s environment (it sets CLAUDE_CONFIG_DIR to that directory for every run), so the login has to land there. The spike used /root/.claude (HOME=/root), so its paths differ from these. A run’s own claude is the unmodified vendor binary from the tool store.

Never route the login through whr. You type nothing about it into whr, into the configuration, into a file or into the web UI. You open the vendor’s page yourself, in your own browser, and the CLI in the environment keeps the result.

  1. Let the sign-in hosts through. The default allowlist admits api.anthropic.com only. Add claude.com, platform.claude.com, auth.anthropic.com and statsig.anthropic.com to environment.egress_allow in the configuration (verified as the hosts the flow needs, in the spike; see the allowlist paragraph in Prepare the Mac mini, step 13), restart the supervisor so it reads the configuration (Run the supervisor), and recreate the environment with whr ws rebuild <workspace> so it picks the list up. A host a repository asks for is a Decision in whr inbox; these are not, because they come from your own configuration.
  2. Make sure no run is live. whr ls shows the unfinished tasks; whr ws rebuild is refused while a run of the workspace is live.
  3. Open a terminal in the workspace’s environment, as the agent’s user: whr ws shell <workspace> (provisional, issue #281). whr asks the supervisor only which environment and variables to use, then replaces itself with the runtime’s own interactive exec (container exec -t -i), so your terminal is attached to the environment directly: what you type, and what the agent’s CLI writes, pass through no whr relay, supervisor, file, log or the web UI, and whr adds no secret and types nothing for you. The shell runs as the agent’s user (1000:1000) in /home/agent, with the agent’s CLI first on PATH, CLAUDE_CONFIG_DIR=/home/agent/.claude and the proxy variables set as for a run, and, because the agent writes that directory, reads none of its startup files (bash runs with --noprofile --norc, sh with ENV=/dev/null), no ~/.inputrc and no ~/.terminfo (bash runs with --noediting, so it loads no readline: a planted terminfo entry could otherwise make every keystroke at the prompt send bytes of the agent’s choosing, such as a clipboard write, to your terminal) and writes no history (HISTFILE=/dev/null), so what you type does not reach the next run’s agent. The price is that the bash prompt has no line editing (no arrow keys or history; backspace works). That does not matter for the login: you paste the code into the vendor CLI’s own prompt, not into bash (unverified: not run in a real environment). The variables that name another place to read from (TERMINFO, TERMINFO_DIRS, TERMCAP, LOCPATH, GCONV_PATH, NLSPATH, BASH_ENV, CDPATH, PROMPT_COMMAND) are unset. Two things are not blocked: the image’s own /etc files (/etc/inputrc, /etc/bash.bashrc, /etc/terminfo), which are acceptable only while the image comes from your own configuration or the repository’s default branch, never from a task branch; and any program you start in the shell, such as less or vim, which still reads ~/.terminfo from the agent’s home, so do not use one there. The sh fallback, used only when the image has no bash, has no readline (unverified: reasoned from dash and busybox ash, not measured). The command is refused while a run of the workspace is unfinished, and checks that when the shell opens only: do not start a run while it is open. unverified The command was tested with a fake runtime and its command line was not run against a real environment; two parts are not measured: that the container CLI reaches the environment’s services from the dedicated whr account outside its desktop session (the sign-in spike ran it from a developer’s own session), and what the runtime’s services do with the terminal’s bytes on their way into the VM. Run it as whr in its desktop session, like the other host commands. Never mount your home, ~/.ssh or a runtime socket into an environment to get around it.
  4. Sign in with the vendor’s command. In that shell run claude auth login (unverified; the spike measured claude auth login in an attached terminal, with the CLI from the tool store). The shell already has CLAUDE_CONFIG_DIR and the egress proxy variables, which a plain container exec shell lacks; echo $HTTPS_PROXY shows that the proxy is set (unverified: it is read from the running sidecar, not seen on a host). The command prints a long address (https://claude.com/cai/oauth/authorize?...) and the prompt Paste code here if prompted >. Copy the address into a browser on your phone or laptop, sign in to your Claude account there, and copy the one-time code the page shows. Paste the code at the prompt. Claude Code cannot finish from the phone alone: it needs the terminal for the paste (spike §5).
  5. Verify. Still in the shell, run claude auth status: it should report "loggedIn": true with "authMethod": "claude.ai" (unverified; the spike saw this output). Check that the login is where the adapter looks: ls -l /home/agent/.claude/.credentials.json shows the file with mode -rw-------. Do not print or copy its contents. Then leave the shell with exit. Last, start a small task (step 9): a run that stops at once with a sign-in error means the login did not land in /home/agent/.claude.
  6. Close the sign-in window again if you like. The login does not need the extra hosts after it is made, except api.anthropic.com, but the CLI may refresh it through auth.anthropic.com, so removing them is untested (unverified); leave them in until you have seen a refresh work.

When the allowlist blocks the sign-in. The CLI prints no address, hangs after it, or fails at the paste with a network error. First check that HTTPS_PROXY is set in the shell (echo $HTTPS_PROXY, step 4): without it every host fails the same way, allowed or not. If it is set, your configuration is the likely cause: one of the hosts above is missing, or you did not rebuild the environment after adding it. Compare the list with environment.egress_allow, correct it, and repeat step 1 and the steps after it; an entry is one exact host, so claude.com does not admit platform.claude.com. whr doctor does not check this list (unverified). Never work around a block by adding * entries for the vendor or by signing in somewhere else and copying files in: both leave the vendor’s flow, and the second is the one thing D40 rules out.

Do not clone the home volume. Two environments on one account sign in separately; a copied login shares one refresh token and the second environment fails to refresh (spike §4).

Codex has a different flow, codex login --device-auth, which the phone alone can finish. There is no Codex adapter yet, so no procedure here.