Skip to main content
Working with an agent? Give them a link to this page as markdown.

Client

The Watcher client is a single watcher binary installed on each developer machine. It hooks into the supported coding agents (Claude Code, Codex) to record sessions, upload them to your Watcher deployment, and enforce blocking review. This page covers installing it, where its settings live, the command reference, and diagnosing problems with watcher doctor. To run the client inside a container alongside a coding agent, see Containers, or Dev containers for a dev container that reuses the sign-in already on the developer's machine. For an untrusted workspace, review the dev container hardening options.

Install

The client runs on macOS (Apple Silicon) and x86_64 Linux. Install it with the published installer, telling it which backend this machine reports to:

# Apollo's hosted cloud
curl -fsSL https://github.com/ApolloResearch/watcher-bin/releases/latest/download/install.sh | bash -s -- --cloud

# A backend your organization hosts (see Self-hosting)
curl -fsSL https://github.com/ApolloResearch/watcher-bin/releases/latest/download/install.sh | bash -s -- --watcher-api-url https://watcher.example.com

The installer reads the chosen backend's version and installs the client release matching the lower of that server or the installer itself. It verifies the release checksum, installs the binary as ~/.local/bin/watcher, records the chosen backend, and registers the agent hooks. The one consent prompt is for the Codex config.toml changes described on Supported agents; hook entries themselves, including Claude Code's, are installed without prompting. If ~/.local/bin is not on your PATH, the installer prints the command that adds it. Open a new terminal afterwards so watcher is found.

Client and server are mutually compatible within one minor version of each other (while on 0.x). The latest installer automatically caps the client at the configured server's version. To impose a lower cap, use that release's installer:

curl -fsSL https://github.com/ApolloResearch/watcher-bin/releases/download/v<version>/install.sh | bash -s -- --watcher-api-url https://watcher.example.com

To update, run watcher update. The command reads the configured server's version and installs that release in either direction. It keeps your sign-in and settings. A packaged client running through its login agent invokes the same reconciliation automatically every two to two-and-a-half hours. Foreground and mirror-backed clients remain externally managed. The installer refuses to overwrite an existing installation. Clients from releases that predate watcher update must uninstall and reinstall once to move onto this update flow. To remove everything (hooks, autostart, and the binary), run watcher uninstall.

To move back to a specific release during a server rollback, run watcher downgrade --version <version>. The requested version must be lower than the installed client and must match the configured server. Releases predating watcher update are unsupported downgrade targets.

First run and sign-in

Run watcher with no arguments to start the client's local services. The first such run installs the login agent and turns start-on-login on, then starts in the background and returns control to the terminal. It restarts after a crash and starts again when you log in. The local UI is at http://localhost:8228 (or the port set by WATCHER_BACKEND_PORT), where escalated tool calls wait for approval and per-machine settings are edited. Use watcher --foreground to keep it attached to the current terminal without enabling start-on-login.

Later runs read the start-on-login setting rather than turning it back on, so watcher autostart disable holds. While it is off, watcher starts in the current terminal and says so.

Any run that starts the client in the current terminal first stops a client the login agent is already running on that same port, so the terminal one can take it. The login agent stays installed, and it starts the client again at your next login unless start-on-login is off. A terminal pointed at some other port with WATCHER_BACKEND_PORT leaves the background client alone, since that client is not the one holding the port being asked for.

How you sign in depends on your deployment's authentication mode, which the client discovers from the backend:

  • SSO deployments (Apollo cloud, and self-hosted in SSO mode): sign in once from the local UI (the same address as above), which hands off to your identity provider in the browser. Starting watcher does not open the browser for you; on macOS the menu bar icon shows a "Sign in" entry until you have signed in. Recording starts once you have, and the running client keeps the credentials refreshed.
  • Header-proxy deployments: the client attaches headers taken from WATCHER_HEADER_* environment variables, which must be set in the environment that runs the installer (or watcher hooks install); there is no browser sign-in. Setup captures the values into a per-deployment file that later launches read, so they persist until you re-run watcher hooks install with new values, or remove the variables from the environment and run watcher hooks clear-headers (the command refuses while the variables are still set, because the next launch would re-capture them). See self-hosting.

Agent hooks

watcher hooks install (run by the installer, and idempotent to re-run) registers Watcher with every supported agent on the machine. What the hooks do in each agent, the consent involved, and how blocking review coverage differs between agents is covered on Supported agents.

Hooks stay registered until you run watcher hooks remove or watcher uninstall; they are not tied to the client running. A re-run of watcher hooks install also repairs hooks that a doctor check reports as out of date.

How sessions reach your deployment

Recorded sessions do not stay on the machine. The hooks upload the conversation directly to the backend recorded at install time: after tool calls and at the end of each agent turn, so a session is visible in the Analyzer while it is still running. There is no periodic batch export to wait for.

Which backend that is, is a per-install setting:

watcher target set --url https://watcher.example.com # your own deployment
watcher target set --cloud # Apollo's hosted cloud

watcher build-info prints the version and the backend target a launch would resolve, and watcher doctor (below) verifies the target is reachable and accepting this machine's requests.

Per-machine settings

Blocking review resolves from settings on each machine: which graders run, the per-tool flag and deny thresholds, and the enforcement mode. Admins can distribute and lock these org-wide with managed client settings instead of configuring every machine by hand. The concept pages explain what each setting does; on the machine they live in layered YAML files under ~/.apollo_monitor/:

  • settings.yaml is the admin baseline. Values set here apply machine-wide, and the enforcement mode is read only from this file. On deployments that distribute managed client settings via Watcher, the running client pulls the org's document every few minutes and replaces this file with it, so hand edits do not stick; otherwise an administrator or an MDM profile writes it. Admins can lock individual values so local edits cannot override them.
  • settings.local.yaml holds the developer's own overrides. Most settings, thresholds included, are edited from the local Watcher UI rather than by hand.
  • Each settings profile is an entry under profiles in whichever of those two files defines it, so a profile's admin values live in settings.yaml. A deployment may still have an admin-managed settings.<profile>.yaml beside them; that form is read but deprecated, and watcher doctor says where its contents belong. A developer's own edits made while a non-default profile is active still land in settings.<profile>.local.yaml.

A settings profile is a named bundle of these review settings that can be switched as a unit. The client ships with Default, Auto, Human in the Loop, and Skip Permissions profiles, and a deployment can add its own. watcher profile list shows what is available, watcher profile current the active one, and watcher profile set <name> switches; it accepts a profile's id, its display name, or any of its alternative names. Human in the Loop's id is permissive; you can also write human-in-the-loop. Both work in a settings file's active_profile, though a profile an organization pins for every machine is named there by its id.

Beyond settings, ~/.apollo_monitor/ holds the client's other local state: a local database of sessions and pending decisions backing the live UI, client logs, and the sign-in credential and auth-header files, written with owner-only (0600) permissions. watcher uninstall intentionally keeps this directory and prints a reminder; remove it manually to wipe a machine's local Watcher data (nothing already uploaded to the deployment is affected).

Environment variables

Almost everything about the client is configured through the local UI or the settings files above. A few things are not, because they have to be in place before the client can read a settings file at all: which backend to talk to, which port to listen on, and how to trust your TLS.

Where you set them decides whether they take effect. The agent hooks run as short-lived subprocesses that the coding agent spawns, and they inherit nothing from your shell. A variable reaches them only because watcher hooks install bakes it onto the hook command lines as it writes them. So for anything in the first group below, set the variable in the environment that runs the installer (or watcher hooks install): your shell's startup file, or ~/.config/environment.d/watcher.conf on Linux. Re-run watcher hooks install after you change it. Changing the variable alone leaves the already-installed hooks on the old value.

Read by the hooks, so they must be set when hooks are installed:

VariableDefaultWhat it does
WATCHER_API_URLthe recorded targetThe backend this machine reports to. Overrides whatever watcher target set recorded, both for this launch and for the hooks installed by it. A value with no scheme is read as https://. Prefer watcher target set for a lasting change; use this to point one launch somewhere else.
WATCHER_BACKEND_PORT8228The port the local services listen on, which is also the port the local UI is served from. Change it when something else on the machine already owns 8228.
SSL_CERT_FILEthe system trust storePath to a CA bundle, for a deployment behind an internal CA or a self-signed certificate. Forwarded to the hooks only when it is set.
WATCHER_API_KEYnoneAn organization API key for the machine to authenticate with, instead of a browser sign-in. Mainly for unattended installs such as containers; a developer machine signs in instead. Unlike the variables above it is not baked onto the hook command lines, so it must be set in the environment the agent runs in, where the hooks inherit it.
WATCHER_HEADER_*noneOne variable per header the client sends to your deployment, on header-proxy deployments. These are the exception to the rule above: rather than being baked onto the command lines, where they would show up in the process list, they are captured into an owner-only file that later launches read. See Sending additional headers.

Read by the client itself, so it must be set in whatever starts it:

VariableDefaultWhat it does
WATCHER_MENUBAR1Set to 0 and the macOS menu bar icon never starts. No effect on Linux.

The menu bar icon's own "Hide Menu Bar Icon" entry closes it for the current run only; it is back at the next start. WATCHER_MENUBAR=0 is how you keep it away.

When the client starts on login it is given almost no environment: by default the login agent runs the binary directly with little beyond PATH. If the running client needs environment variables (WATCHER_MENUBAR, and also WATCHER_BACKEND_PORT and SSL_CERT_FILE where you have changed them, since the client uses those at runtime as well as at hook-install time), install the login agent with --launcher pointing at a wrapper script that exports them. See MDM rollout § Start on login.

Command reference

Running watcher with no arguments starts the client under its login agent, enabling start-on-login on the first run. Every command supports --help.

CommandWhat it does
watcherStart the client in the background under the login agent, installing it and turning start-on-login on if this is the first run. Starts in the current terminal instead while start-on-login is off. watcher live start and watcher all start remain foreground aliases.
watcher --foregroundStart the client in the current terminal without installing or enabling the login agent. Stops a client the login agent is already running so this one can take the local port.
watcher --versionShow the installed client version.
watcher updateInstall the client release matching the configured server.
watcher downgrade --version <version>Install the configured server's explicitly selected older release.
watcher build-infoShow build details and the backend target a launch would resolve.
watcher target set --cloud / --url <url>Record the backend this install reports to.
watcher hooks installInstall or repair the agent hooks for Claude Code and Codex. --yes skips the consent prompt (for unattended installs).
watcher hooks removeRemove the agent hooks for both agents.
watcher hooks clear-headersRemove the captured WATCHER_HEADER_* headers for the selected backend target (header-proxy deployments). Refuses while WATCHER_HEADER_* variables are still set in the environment: remove them first.
watcher profile list / current / set <name>List, show, or switch settings profiles.
watcher autostart enable / disableTurn starting-on-login on or off in settings.
watcher autostart start / stopStart or stop the login agent now, without waiting for login.
watcher autostart enabledExit 0 if autostart is enabled in settings, 1 otherwise (for scripts).
watcher autostart install [--launcher <script>] / uninstallInstall or remove the login agent itself (macOS LaunchAgent, Linux systemd user unit). By default the binary is its own launcher (client 0.16.0 and later); pass --launcher to run a wrapper script that sets environment variables at login instead.
watcher doctorRead-only health checks for this install (see below; client 0.14.0 and later).
watcher doctor dumpWrite a support bundle with best-effort redaction.
watcher uninstallRemove hooks, autostart, and the binary.

watcher doctor

watcher doctor is the first thing to run when sessions stop appearing in the Analyzer or blocking review stops intervening. It is read-only: it makes no changes to the machine. The command ships with client 0.14.0 and later; on older clients it does not exist, so if your server line predates 0.14 the version-matched client will not have it.

What it checks

The report has four sections, and each can be run alone (watcher doctor auth):

SectionChecks
targetA backend target is recorded, and any environment override agrees with it.
authThe backend is reachable, which authentication mode it runs, sign-in credentials exist and are being refreshed, and a live authenticated request succeeds.
hooksThe installed agent hook config matches what the current client would install.
daemonThe local services respond on their port, and whether autostart is installed and enabled.

Reading the output

Each check reports ok, a warning, or an error, with a suggested fix in the message. By default only problems are shown; --verbose shows passing checks too, and --json emits the full report for scripts. The exit code summarizes the report: 0 healthy, 1 at least one error, 2 warnings only.

watcher X.Y.Z — target: https://watcher.example.com — auth mode: workos
Auth ✗
✗ No sign-in credentials for this target. Start watcher and sign in via the
browser.

Agent Hooks ⚠
⚠ Agent hook config is out of date. Run: watcher hooks install

6 checks: 4 ok, 1 warning(s), 1 error(s)

Common failures

FindingWhat it meansFix
No backend target recordedThe install never had a backend selected, so nothing can upload.watcher target set --url https://<your-watcher-host> (or --cloud).
Cannot reach the backendWrong URL, no network route, or TLS fails against an internal CA.Check the URL and network. For an internal CA, set SSL_CERT_FILE to your CA bundle.
No sign-in credentials for this targetNobody has signed in on this machine (or the target changed, which needs a fresh sign-in).Start watcher and sign in from the local UI.
Authenticated probe failed with 401The backend rejects this machine's requests: expired or revoked credentials, or stale auth headers overriding the sign-in.Sign in again. If the finding names a hook-headers file, follow its instructions to remove the stale headers.
Credentials last refreshed N minutes agoThe running client should refresh credentials every few minutes and has stopped.Restart watcher; if it recurs, capture a watcher doctor dump.
Agent hook config is out of dateThe hooks in the agent config predate the current client or were edited.watcher hooks install.
No backend responding on localhostThe client services are not running, so there is no UI and no sign-in refresh.Start watcher (or watcher autostart start if the login agent is installed).

Support bundle

watcher doctor dump writes a text bundle: the full doctor report plus recent client logs. No session content is collected, and known secrets are redacted, but redaction is best-effort: review the file before sharing it, as the command's own output reminds you. --since 2h widens the log window (default one hour) and --output sets the file path. Attach it when reporting a client problem.