Signing in without a browser
The usual way to sign the Watcher client in is the local UI, which hands off to your identity provider in a browser on the same machine. A headless server has no browser to hand off to.
This page covers the two ways such a machine gets credentials: watcher login,
where a person approves the sign-in from a browser on another device, and an
organization API key, for machines nobody signs in to.
Choose how the machine authenticates
| The machine | Use | Sessions are attributed to |
|---|---|---|
| A server or VM a person reaches over SSH, even if nobody stays logged in | watcher login | The person who approved the sign-in |
| Unattended: CI, a container nobody logs in to, a shared runner | WATCHER_API_KEY | The organization, not a person |
| A container on a developer machine that is already signed in | The host's credentials, bind-mounted | The host's user |
| Any machine on a deployment that authenticates through a reverse proxy | Nothing to do here | The identity your proxy asserts |
The first two are below. For the container cases, see
Containers, which compares all
of them for that setting. On a proxy-authenticated deployment the gateway
signs requests in, so terminal sign-in does not apply and watcher login
says so.
Terminal sign-in
Before you start
- The client is installed on the machine, and either
watcher target set --cloudorwatcher target set --url https://<your-watcher-host>has recorded which backend it talks to. - The deployment uses SSO. Terminal sign-in is a sign-in flow, so it only applies where there is an identity provider to sign in to.
- You have a browser on some other device: a laptop, a phone. It does not need any network route to the headless machine.
Sign in
Run this on the headless machine, over SSH:
watcher login
It prints a URL and a short code, then waits:
Signing in to https://watcher.example.com
On any device with a browser, open:
https://id.example.com/device?user_code=ABCD-EFGH
and confirm it shows this code: ABCD-EFGH
Waiting for approval (the code expires in 5 minutes; Ctrl-C to cancel)...
Open that URL on the other device, sign in as you normally would, and check that the page shows the same code the terminal printed. Confirming the code is what ties your approval to this specific machine, so compare the two rather than clicking through.
The command returns once you approve:
Signed in as person@example.com.
Credentials saved to /home/dev/.apollo_monitor/credentials/production.json.
Those are the same credentials the browser flow writes, in the same place, so the hooks, the background client, and token refresh all behave identically from here on. You do not need to repeat this after a reboot: the credentials are refreshed on use, including after the machine has been off for a while.
Keep the client running
Signing in stores credentials. It does not start the client, and on a server nobody stays logged into, starting it once is not enough either.
Run the client once so it installs the login agent, the service that starts it and keeps it running. It starts in the background and returns control to the terminal:
watcher
On Linux that agent is a systemd user unit, which starts at login and stops with the user's last session. A server running agents unattended needs the user instance to survive logout and reboot. Enable lingering for the user you signed in as, the one whose home holds the credentials:
loginctl enable-linger <user>
Without lingering, sign-in and watcher doctor both look healthy while
someone is logged in, and nothing records once they disconnect.
Verify
watcher doctor
The auth check confirms that credentials exist for this target and that a
live authenticated request succeeds. Then start a coding agent session on the
machine and confirm it appears in the Analyzer.
If it does not complete
- The code expired. It is valid for the few minutes the command prints, so
a delay in getting to the other device runs it out. Run
watcher loginagain for a fresh code. - You cancelled or denied the sign-in in the browser. Run
watcher loginagain. - The command reports that the server predates device login. The backend is older than the client and does not offer the endpoints yet. Update the backend, or sign in from a browser: start the client on a machine that has one, or port-forward this machine's local UI (port 8228 by default) to a machine that does.
- The command reports that the deployment does not use SSO sign-in. It authenticates through a reverse proxy, and the gateway supplies the identity. See Sending additional headers.
Unattended machines
Terminal sign-in needs a person to approve it. A machine nobody signs in to, such as a CI runner or a container started by an agent, authenticates with an organization API key instead:
export WATCHER_API_KEY=<key>
The hooks and the client both read it from the environment, so set it where the machine's processes will actually see it: the runner's environment configuration, the container's run command, or the service definition that starts the client. Exporting it in one SSH session leaves every other process without it.
Create and revoke these keys on the API Keys tab in the Analyzer, described in Organization settings.
Sessions recorded this way are attributed to the organization rather than to a
person, which is the tradeoff for not having anyone to attribute them to. Where
a person does own the machine, prefer watcher login, so its sessions carry
their identity.