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. The quick way there is steps 2 and 3 below.
  2. Install whr, as the administrator, from the release archive: download it and checksums.txt, verify, then sudo ./install.sh <tag> (no clone, no gh, no Command Line Tools): Install, upgrade and release. Then check it with the full path, which is not on the administrator’s PATH: /opt/whr/bin/whr version.
  3. Set up, in this order: first /opt/whr/bin/whr setup as the administrator (it also writes the workharbor account’s base configuration), then whr setup again as workharbor in its desktop session, not over SSH, for the steps that need that session: API token, container system, GitHub App keys, tool store, service (put /opt/whr/bin on its PATH first, Prepare the Mac mini, step 13). 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/workharbor/.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). The supervisor refuses an unfinished run and holds preparation against concurrent run ownership and rebuilds. It stops and starts the environment, even when already running, to exclude surviving processes before returning the target, then waits for exec readiness. Stop, start or readiness failure returns no target. The restart keeps the existing agent-home/login volumes and reads the normal proxy configuration again. The environment stays held while the shell is open: a run waits until you leave the shell. whr starts the runtime as its child with the terminal inherited directly and ends that child if the hold connection ends. Opening the shell records only workspace, environment, time and caller in the audit log; no terminal content is recorded.

    whr starts the runtime’s own interactive exec (container exec -t -i) as its child and keeps the environment held until that child exits. Your terminal attaches directly: its bytes pass through no whr relay, supervisor, file, log or web UI. The shell runs as 1000:1000 in /home/workharbor, with CLAUDE_CONFIG_DIR=/home/workharbor/.claude and the proxy variables set as for a run. It selects /bin/bash by absolute image path, falling back to /bin/sh when that bash is not executable. PATH contains only the read-only tool-store CLI directory and /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; the image’s original PATH, agent-home paths, relative paths and empty entries are discarded.

    Bash runs with --noprofile --norc --noediting, avoiding agent-home startup files and readline’s inputrc/terminfo handling; its prompt has no line editing. INPUTRC, HISTFILE and EDITRC point to /dev/null. Variables naming alternative terminfo, termcap, locale or startup files (TERMINFO, TERMINFO_DIRS, TERMCAP, LOCPATH, GCONV_PATH, NLSPATH, BASH_ENV, CDPATH, PROMPT_COMMAND) are unset. The fallback sets ENV=/dev/null; other behaviour depends on the image’s /bin/sh implementation. unverified Target-image fallback behaviour and file accesses have not been measured with a committed harness. The image’s shells, libraries (including libc), system files, environment and system-directory mounts must be trusted: sanitisation runs inside /bin/sh, after its loader and libc initialise, so it cannot protect against their earlier use of hostile image variables. Use an image from your configuration or the repository’s default branch, never a task branch. Programs you start, including the vendor CLI, have their own startup files and terminal handling; their output is unfiltered, and programs such as less or vim may still read the agent-home terminal database.

    unverified Preparation is covered by fake-runtime regressions, not a live sign-in or a committed target-image leftover-process demonstration. Runtime stop/start coverage in TestStopLeavesNoProcessFromBeforeLive is separate from this integration. The dedicated workharbor account’s runtime access outside its desktop session and the runtime services’ treatment of terminal bytes remain unmeasured. Run it as workharbor in its desktop session, like the other host commands. Never mount your home, ~/.ssh or a runtime socket into the environment.

  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/workharbor/.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/workharbor/.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.