Remote browsers and devices
Run tests in a browser or on a device agent-qa does not start, such as a grid, a cloud service or another machine.
By default, agent-qa launches the browser on this machine and finds devices here. A remote is somewhere else tests run: a Playwright server, a Chrome DevTools Protocol (CDP) endpoint, an Appium server, or a service a plugin connects to. You list remotes once under registry.remotes, and your files decide which tests run where.
| Remote | What it is | Runs |
|---|---|---|
protocol: playwright | A Playwright server, at a ws:// or wss:// URL. | Web tests, on any engine the server has. |
protocol: cdp | A Chromium DevTools endpoint, at a ws://, wss://, http:// or https:// URL. | Web tests, on Chromium only. |
protocol: appium | An Appium server agent-qa does not start, at an http:// or https:// URL. | Mobile tests. |
plugin: <entry> | A service a plugin creates sessions on, such as Kernel or BrowserStack. | What the plugin serves. |
Add a web remote
registry:
remotes:
- name: grid
protocol: playwright
url: wss://grid.example.com/playwright
query:
token: "{{secret:GRID_TOKEN}}"
use:
browser:
name: chromium
headless: true
remote: gridPut GRID_TOKEN in your secrets file, .env.secrets.local by default. With use.browser.remote in the config, every web test runs on grid.
| Key | Default | Meaning |
|---|---|---|
name | A lowercase slug, unique in the list. local is reserved for this machine. | |
protocol | playwright, cdp or appium. | |
url | The endpoint. {{secret:NAME}} and {{playwright:version}} may appear in its path and query, never as user:password@. | |
query | Parameters added to the URL when agent-qa connects. A value is text, or a map sent as JSON. Not for appium. | |
headers | Headers sent when agent-qa connects. | |
basicAuth | user and password, the password a {{secret:NAME}} reference. Use it or an Authorization header, not both. | |
connectTimeout | 5m | How long the connection may take. It bounds the connect only, never the test. |
context | new | cdp only. new opens agent-qa's own browser context; reuse uses the browser's first context. |
capabilities | appium only. Appium capabilities for every device on this remote. |
{{playwright:version}} becomes the Playwright version agent-qa runs, for services that ask the client for it, such as url: wss://browsers.example.com/connect?playwright={{playwright:version}}.
Pick where tests run
use.browser.remote picks a remote for web tests, and local means this machine. Set it in agent-qa.config.yaml for the project, or in the use block of a suite or a test:
# In a suite: run its tests on the grid's Firefox
use:
browser:
name: firefox
remote: grid- A suite's pick wins, then the test's, then the config's. With none, the test runs here.
- A suite runs in one place. When it names none, its tests must agree, or it is refused before it starts.
- No run flag or environment variable moves a test. The files decide, so a run in CI and a run on your laptop place tests the same way.
- A mobile test runs where its device is: a device with a
remoteruns there. See Mobile testing.
The dashboard's Config page lists the remotes read-only and sets the project's default, and each test's and suite's settings show where it runs. Coding agents can change use.browser.remote through agent_qa_change_config, but nothing except your own edit changes registry.remotes.
Credentials
- Write every credential as a
{{secret:NAME}}reference, quoted in YAML, to a name in the fileworkspace.secretsFilenames.validaterefuses a literal value under a header or query name that holds a credential word:authorization,cookie,token,secret,key,password,jwt,bearer,credential,signature,passwdorpwd. That covers nested maps, JSON values and the URL's own query.true,falseandnullare allowed there. - Credentials travel encrypted. A remote that carries a credential must use
wss://orhttps://, unless it points at this machine. - A remote's secrets are used only for its connection. Steps, expected values, target URLs and hooks cannot use them, and
validatesays so when one does. - The address stays in the file. The dashboard, MCP and
config showshow a remote by its name, protocol and origin only. Its path, query, headers and credentials are masked in every log and record. - Some settings refuse a remote test. A
DEBUGvalue that turns on Playwright's own logging would print the remote's address and requests, so remote tests refuse to start while it is set.SELENIUM_REMOTE_URLwould send a local launch to another machine, so local tests refuse it. Unset either one.
CDP endpoints
A CDP remote drives Chromium only. With context: new, agent-qa opens its own context and closes only what it opened. With context: reuse, it uses the browser's first context, which holds a signed-in profile but is shared with anything else using that browser.
registry:
remotes:
- name: signed-in-chrome
protocol: cdp
url: http://127.0.0.1:9222
context: reuseWhat a remote browser cannot give
A remote browser starts with the service's settings, not agent-qa's. The test's evidence records what the remote could not give, so a missing value never reads as an empty result:
- launch flags, and
use.browser.headless: the service decides whether its browser shows a window; - clipboard read-back;
- downloaded files, on a CDP remote;
- the clipboard probe, on a CDP remote with
context: reuse; - the live screencast for Firefox and WebKit, which show a snapshot each second instead.
A target on this machine's loopback, such as http://127.0.0.1:3000, is reachable from a remote browser only through a tunnel you run. doctor warns when a remote's tests point at one. BrowserStack Local is one such tunnel.
Add an Appium remote
registry:
remotes:
- name: farm
protocol: appium
url: https://farm.example.com/wd/hub
basicAuth:
user: "{{secret:FARM_USER}}"
password: "{{secret:FARM_KEY}}"
devices:
galaxy-farm:
platform: android
remote: farm
model: Samsung Galaxy S24
os: "14"
targets:
shop-android:
platform: android
appPackage: com.example.shop
device: galaxy-farm
app:
ref: https://builds.example.com/shop-latest.apk- The server picks the device. agent-qa sends
modelasappium:deviceName,osasappium:platformVersionandidasappium:udid, and the server matches them its own way. For a server that selects devices by another key, leavemodelout and set that key incapabilities. - The server installs the app. The target's
app.ref, anhttpsURL or the server's own reference, is sent asappium:app; anappium:appin capabilities wins. agent-qa uploads nothing to an Appium remote, so a device there whose target has onlyapp.pathis refused. A plugin remote that uploads, such as BrowserStack's, takesapp.path. - Capabilities merge in one order: agent-qa's own, then the remote's, then the device's. Maps merge key by key, and
nullremoves a key. - No Appium starts here. agent-qa starts no local Appium for a device on a remote, and
doctorskips the local mobile tools when every device that needs them is remote. - An Appium remote takes no query parameters. Send credentials as headers or
basicAuth.
Android emulators and devices, and iOS simulators, are covered by agent-qa's own tests. Real iPhones behind a remote server work through capabilities, but agent-qa's tests do not cover them.
Plugin remotes
A plugin remote names a plugins entry instead of a URL, and holds the settings for one place on that service:
plugins:
- name: kernel
package: "@vostride/agent-qa-plugin-kernel"
options:
apiKey: "{{secret:KERNEL_API_KEY}}"
registry:
remotes:
- name: kernel
plugin: kernel
settings:
headless: trueIt has no url, query, headers or basicAuth, because the plugin supplies the connection, and it adds uploadTimeout, ten minutes by default, for app uploads. See Plugins.
During a run
- Each test says where it ran. The run prints each remote test's place, and the run record and the dashboard's run page name it: this machine, a remote by name and origin, or a remote and its plugin entry.
- Sessions stay alive. While a test holds a remote session, agent-qa sends it a heartbeat every 30 seconds, never in the middle of an action.
- A remote that cannot connect is refused once. The test fails with one sentence naming the remote, and agent-qa does not retry it. Only the tests placed on that remote fail; the rest run, and the run exits 2. The analysis classes it as an environment problem.
- A dropped session fails its test with
Remote <name> ended the session while this test ran.orThe connection to remote <name> was lost while this test ran. - Cleanup is bounded and closes only what agent-qa opened.
- Stopping releases sessions. On Ctrl+C, agent-qa releases every open remote session within a few seconds and exits 130.
- The live editor uses the same place. A dashboard live-editor session connects where its files say, and a remote session ends after 30 minutes without activity.
Check your remotes
| Command | Checks |
|---|---|
agent-qa validate | Every remote, credential rule and pick, including picks a suite or test cannot honour. |
agent-qa doctor | One row per remote. For a URL remote, it asks whether the server answers, with a plain request that sends no credentials, and names the Appium version when it is an Appium server. It also warns about undeclared secrets. It opens no session. |
agent-qa doctor --remote <name> | Only that remote's rows. |
agent-qa devices | Each device, and the remote it runs on. |
doctor skips the local browser check when every web test runs on a remote.