Skip to content

Connecting Agents

Writ edited this page Aug 6, 2026 · 3 revisions

Connecting agents

The coordinator never launches a browser. Agents do. This page is about getting them connected and keeping them healthy.

The shape of it

An agent is a single writ-agent binary. It dials out to the coordinator over a WebSocket and holds that socket open, so:

  • it needs no inbound port and works behind NAT, on a laptop, on a home connection;
  • it can live anywhere the coordinator's URL is reachable — which is why WRIT_PUBLIC_URL matters so much (see Configuration);
  • the coordinator dispatches work down the socket it already has.

Nothing runs until at least one agent is connected. A workflow with no agent online does not fail — it waits.

The one-liner

Fleet → Connect a new agent mints a single-use pairing code and shows you:

curl -fsSL https://writ.example.com/agent.sh | sh -s -- WRIT-4K2P-9XQ

Run it on the machine that should do the browsing. It detects the platform, downloads the matching release asset, exchanges the code for real credentials, and starts the agent.

The code is single-use and expires in 15 minutes. It is deleted the moment it is redeemed, so a code captured from a shell history or a proxy log is already spent. The exchange endpoint is rate-limited per IP, and a spent code is indistinguishable from a wrong one in the response.

The code is redeemed before the download, so a wrong or expired one fails in a second rather than after tens of megabytes. The archive's SHA-256 is then checked against the checksum published beside the release, and the installer refuses to run on a mismatch.

If the one-liner shows http://localhost:8000 and you are enrolling a remote machine, stop: set your public URL first (Settings → Network, or WRIT_PUBLIC_URL). The agent bakes in whatever address it was handed, so it will enrol happily and then fail to dial back.

Doing it by hand

The same modal has Binary and Docker tabs carrying the raw token, for air-gapped hosts and for scripted enrolment where an interactive single-use code is the wrong shape.

WRIT_SERVICE_TOKEN=<token> ./writ-agent-fleet start --headless
docker run -d --name writ-agent \
  -e WRIT_SERVICE_TOKEN=<token> \
  -e WRIT_SAAS_URL=https://writ.example.com \
  ghcr.io/usewrit/writ-agent:latest

Sources for the binary: the writ-agent releases, the ghcr.io/usewrit/writ-agent:latest image, or build from source.

The archive matters — the Playwright driver ships beside the executable and the agent cannot launch a browser without it. Unpack the whole thing, do not copy out just the binary.

Session slots

Each agent advertises how many browser sessions it will run at once. The stock agent reports 2, which is conservative for most hardware — one slot is roughly one browser, so a machine with real RAM and cores can do far more.

Fleet → click an agent's slot meter shows the three numbers that decide it:

The agent reports what the machine says it can handle, from its heartbeat
Its token allows the ceiling issued in that agent's token — the most it may claim for itself
In effect now what the scheduler actually uses

Set your own number to override the agent's self-limit. The pin is stored on the agent's record, so it survives reconnects, and applies immediately if the agent is online (or on next connect if it is not). Clear it to hand control back to the agent.

Document extraction

Agents call the doc-extract service directly, with bytes they already fetched — the coordinator never calls it. That is what makes PDFs, office files and scanned pages work.

The address an agent is handed defaults to http://127.0.0.1:8092, which is correct for an agent on the coordinator's own host and unreachable from anywhere else. The failure is silent: an agent that cannot reach the service does not error, it just skips every non-HTML resource, exactly as if the service were absent.

If any agent runs elsewhere, give the service a route of its own — see Production deployment. GET /api/fleet/connect-info reports whether the lane is enabled and which URL agents are being handed.

Health

  • Fleet shows each agent's online state, last-seen clock, and slot usage.
  • An agent that goes quiet past the silence timeout is dropped and shows offline.
  • Removing an agent from Fleet is durable: it revokes the bound token, evicts the live socket, and deletes the record. The agent cannot reconnect with the old credential.

When an agent will not connect

Symptom Cause
Installer says the code is invalid Already used, or older than 15 minutes. Mint a new one.
Agent starts, never appears in Fleet It is dialing the wrong address — check WRIT_PUBLIC_URL, then re-enrol.
Appears, then drops after ~60s A reverse proxy is timing out the WebSocket. See Troubleshooting.
Connects but never picks up work It has no free slots, or the run is queued for a different agent. Check the slot meter.
PDFs come back empty doc-extract is not reachable from that agent.

Clone this wiki locally