/ docsConnect your own devicesllms.txt
On this page
What the agent isGetting the binaryThe tokenThe commandsWhat doctor answersWhen your phone is shown liveNetwork capture on your own phoneWhat stays running on your phoneWhat a run costsRunning on your own iPhoneNot supported yet

Connect your own devices

A phone on your desk becomes a device this workspace can run tests on when the NthCase agent runs on the machine it is plugged into. This page is what the agent is, what it needs, what it will not do yet, and what a run on your own hardware costs.

What the agent is

nth-agent is a small program you run on your own machine. It reports which phones are attached, registers the machine with your workspace, and — when you point it at a local Appium — lets runs reach those phones through an outbound tunnel it opens itself. Nothing dials in to you.

It is not the platform's own node software. That is a different program which authenticates with a pre-shared platform key and runs only on the platform's hardware; it is never installed on a customer's machine. Everything below is true of the agent, and only of it.

It installs nothing on your machine. Appium and the platform tools it needs are yours to install and start — in the agent's own words, we do not put software we do not own on hardware we do not own. doctor tells you exactly what is missing instead.

Start Appium as yourself, on 127.0.0.1. The agent uses only an Appium, an adb server and screen servers that your own account (or root) runs on this machine. One that another account runs is not used, and neither is an Appium at an address on another machine or one listening only on ::1. npx nthcase-agent doctor says whose it found.

Only phones on the machine's USB cable. An emulator, or a phone the machine reaches over Wi-Fi or the network (adb connect, wireless debugging, an iPhone paired over Wi-Fi), is left out, and your workspace is not told about it at all. npx nthcase-agent connect names each one once, with how it is reached; npx nthcase-agent devices lists it as left out, and npx nthcase-agent doctor has a line for it. Earlier releases offered these phones too, so after an update a phone like that is no longer listed in your workspace. To offer them as well, start the agent with one more flag:

bash
npx nthcase-agent connect --farm https://farm.nthcase.com --appium http://127.0.0.1:4723 --offer-uncabled-devices

npx nthcase-agent devices and npx nthcase-agent doctor take the same flag, to show what it would offer.

Keep the phone unlocked with its screen on while it is offered for tests, or turn off the screen lock on a phone you keep for testing. A run wakes a phone whose screen is off, and opens its lock when the lock has no PIN, pattern or password. A run cannot enter a PIN, pattern or password, so on a phone locked with one it stops before its first step and says the phone was locked.

Renting devices from a cloud lab instead? The same agent offers those too, and on that path the machine needs no phone and no Appium: Devices you rent from a cloud lab.

Getting the agent

One command, and there is nothing to download.

bash
npx nthcase-agent --version

The package carries the binary for your machine and runs it. You need Node 18 or newer — that is what npx itself needs — and nothing else. The agent is a Go binary; npm is only how it reaches you.

It ships for darwin-arm64, darwin-x64 and linux-x64 — those are the platforms it claims, because those are the platforms it compiles. On anything else it prints a sentence saying so and naming what is built, rather than failing at a file that is not there.

Every command on this page runs the same way — npx nthcase-agent doctor, npx nthcase-agent connect …. If you install it once with npm i -g nthcase-agent, it is on your PATH as nth-agent, and you can type that in place of npx nthcase-agent in every command here.

--version prints the release you are running, and the package and the binary always agree: publishing stamps the package from the release tag, runs the binary it is about to publish, and refuses if the two disagree.

npx nthcase-agent notices prints the licences of the third-party code the agent carries — the same text the package ships as THIRD_PARTY_NOTICES.txt — and the package's CHANGELOG.md says what changed in each release.

npm checks what it downloads, so there is no checksum step and no Gatekeeper dialog. Every tarball the registry serves carries an integrity hash npm verifies before it unpacks anything. And macOS attaches its quarantine flag to what a browser writes — npm is not a browser, so the "developer cannot be verified" dialog never appears on this path.

There is no self-update. The agent has no update code path and never replaces itself. What does fetch a newer version is npx, when you type the command — and npx nthcase-agent@0.4.1 pins exactly one. That difference is the whole point: a self-updating binary rewrites itself unasked and has to be told where to fetch from, and whoever can answer that owns every device the machine drives. This fetches when you ask, from a registry you can inspect, list and audit.

npx nthcase-agent connect says when your workspace knows of a newer release than the one running: once, with how to update for the way you started it. It keeps working as it is, and nothing updates by itself.

The token

A machine joins a workspace with an agent token, minted in Settings → Agent tokens by a workspace owner whose email address is verified. It is shown once, at mint time: the platform stores only a hash, so nothing — not the screen, not the API, not a support conversation — can show it to you again. Lose it and mint another.

An agent token is minted for one machine in Settings → Agent tokens, with the token itself blurred out. 0:11

Set it at a prompt, and paste the line on its own: read takes whatever follows it as the value. As an argument the token is visible to every process on the machine for as long as the agent runs, and typed into an inline export it is written verbatim into your shell's history file, which outlives the process, the terminal and the reboot. The agent never writes the token to disk or into any of its output.

bash
printf 'workspace agent token: ' && read -rs NTH_AGENT_TOKEN && echo && export NTH_AGENT_TOKEN  # paste this line ALONE
bash
npx nthcase-agent connect --farm https://farm.nthcase.com --appium http://127.0.0.1:4723

The farm URL must be https:// — a plain http:// address would send your workspace token in cleartext. The agent refuses one unless it is exactly localhost, in lower case, or a loopback address such as 127.0.0.1, and it never reaches a plain http:// farm through a proxy.

One connect at a time. On one machine, your account runs one npx nthcase-agent connect. A second one refuses to start, with exit status 1, until the first is stopped: two would offer the same phones twice. To join another workspace from the same machine, stop the first one before you start the next.

Stopping it hands the phones back. However npx nthcase-agent connect ends — Ctrl-C, closing the terminal it runs in, or a token that is refused — it hands back the phones it was using before it exits. Closing the terminal ends it with exit status 129. When you stop it — Ctrl-C, or closing its terminal — it then tells your workspace that this machine is leaving, so its phones stop being offered at once instead of 30 to 40 seconds later. It waits three seconds at most for the answer. If your workspace does not take it, connect says so in one line, and the phones go off offer within a minute instead.

Revoking is immediate, not eventual. Revoke from the same screen; the check runs on every call the agent makes, so the machine's next heartbeat — at most ten seconds later — is refused. The row stays in the list, struck and dated, because "this machine was disconnected on the 22nd" is a fact somebody looking at a fleet needs.

The commands

CommandWhat it does
npx nthcase-agent devicesWhat is attached to this machine, what is not, and what to do about it. Add --json for the same facts, machine-readable.
npx nthcase-agent doctorWhat this machine needs to run tests, what it has, and what to do about each gap. It installs nothing, changes nothing and starts nothing.
npx nthcase-agent connectRegister this machine with your workspace and keep it updated — the standing process. With --appium your usable devices are offered for real runs.
npx nthcase-agent setup-iosThe one-time WebDriverAgent build a real iPhone needs. It states what it will do, prints the exact command, and waits for your yes — and on failure it shows xcodebuild's own error, not an exit code.
npx nthcase-agent uploadPut a build on your own cloud-device account and print the id a run needs. --file sends the bytes from this machine — use it for anything not published on the internet — and --url asks the lab to fetch an address reachable from THEIR network. It needs your lab credentials and nothing else: no workspace token.
npx nthcase-agent captureNetwork capture on this machine, off until you turn it on: enable (it explains what capture does and asks you to type ENABLE), setup [serial] (it copies this machine's certificate to a test phone and checks the phone trusts it), disable (it hands every phone back and deletes the certificate's key and every capture file).
npx nthcase-agent noticesThe licences of the third-party code the agent carries, as the THIRD_PARTY_NOTICES.txt the npm package ships beside it.
In a terminal, npx nthcase-agent connect starts, and the Devices page shows the phone go from offline to Ready. 0:29
In a terminal, npx nthcase-agent connect names the devices it left out, an emulator and a phone reached over Wi-Fi, and the flag that offers them too. 0:18

The platform is never a verb: there is no ios-devices and no required --platform — the platform is a field on each device. --skip android or --skip ios tells a machine not to ask about a platform at all, and that platform then reports not asked, which is a decision rather than a failure.

upload is for a device you rent rather than one on your desk: it puts your build into your own cloud-lab account, where the lab's devices can run it. Your key, your build, the devices you name and what a run on one costs are on Devices you rent from a cloud lab.

What doctor answers

npx nthcase-agent doctor checks 15 requirements and reports each in one of 6 states. It is scoped by what this machine can actually see: a machine with no iPhone attached is not broken, it is an Android machine, so its iOS gaps come back not-required — and a check reported not-required is still a check, which is why all 15 are listed below.

In a terminal, npx nthcase-agent doctor answers each of its checks. 0:16
StateMeans
satisfiedit is there AND answering. Nothing to do.
missingit is not on this machine at all.
not-runningit is installed and is not answering — the state that used to be reported as either of the others.
unreachablesomething IS answering somewhere, but not from here (a wrong host, a firewall, a container).
unknownthis check could not establish its answer. Never a failure and never a pass.
not-requiredthis machine does not need it — an Android-only host asked about an iOS toolchain.

Missing, not-running and unreachable stop a run from happening on this machine. Unknown and not-required do not — "I could not tell" is not a fault, and neither is "you do not need this".

RequirementWhose it is
a device your probe could hand overThe probe's, per device. A device that is attached but stuck — unauthorized, or any state the platform names — is invisible to every run until that changes, and it used to reach no check at all: a machine whose only phone sat unauthorized read every toolchain line green and “Nothing here is stopping a run”. It BLOCKS only where a platform has no usable device left; beside a working one it is advisory. The remedy shown is the probe’s own sentence, carried verbatim.
a phone on this machine's USB cableThe agent's own rule: it offers your workspace only phones on this machine’s USB cable. An emulator, or a phone reached over Wi-Fi or the network, is named here with how it is reached, and left out. It never stops a run, because the phones on the cable still run. Plug it in by cable, or start connect with --offer-uncabled-devices to offer it too.
adbThe agent's own — its Android probe shells out to adb by name, so PATH resolution is the requirement.
adb serveradb's, and it has to be yours: the agent uses an adb server only when your own account (or root) runs it. Whether one would START cannot be established without starting it, which doctor may not do — so it reports whether one is already listening, whose it is, and marks the rest unknown.
appium (installed)Yours. Installed-ness is a separate observation from answering, which is exactly the distinction a real machine needed: Appium was installed and was not answering.
appium (answering)Yours, run as your own account on this machine. The same GET /status the connect loop probes before it offers any device — so doctor and the drive path cannot disagree about what “up” means. An Appium another account runs, or one at an address on another machine, is reported unreachable and is not used.
uiautomator2Appium's — the driver an Android session needs.
xcuitestAppium's — the driver an iOS session needs. Same instrument as uiautomator2, for the same reason: a session for X needs X's driver.
Xcode command-line toolsNOT the agent's. Appium's xcuitest driver builds and signs WebDriverAgent with xcodebuild before it can drive a real iPhone; it is stated as the driver's requirement so nobody reads it as ours.
Developer ModeThe phone's own (iOS 16+). It refuses to install a developer-signed app until a person switches it on in Settings. The agent READS the setting; it does not change it.
iOS signing identityThis Mac's keychain. xcodebuild signs WebDriverAgent with a certificate from it, and the ten-character code in a certificate's name is NOT the team id a build wants — this check reads your real one. It says nothing about which teams your Apple account owns, and a certificate here can still be revoked at Apple.
iOS provisioning profileApple's, and yours to obtain once. xcodebuild refuses to build WebDriverAgent for a real iPhone without a profile for the WDA bundle id's .xctrunner that belongs to the team it builds as, and Appium does not create one. Reading a profile off this disk is not running the build.
iOS WebDriverAgent projectYours, once setup-ios has run. xcodebuild signs EVERY target it builds, so a WebDriverAgent target carrying no team — or another team — fails the build. This reads what the project stores; passing the team on the command line, which setup-ios does, overrides all of it.
iOS WebDriverAgent portThis machine's. The port a phone's WebDriverAgent listens on is derived from its udid, so it is a fixed number something else here could already hold. Checked here and only right now — a port free at this moment can be taken before the next run starts.
live viewThis machine's, one check per usable phone: whether a run on it can be watched, and if not, why — the same sentence the devices command prints under the phone. Its missing is the one that never stops a run: the phone is still driven, only not shown. It reads, right now, whether the phone's port is taken and what Appium's driver list says; whether the screen server starts is only known when a session asks for it.

When your phone is shown live

A run on your own phone can be watched live, the way a run on one of ours can, when three things are true on the machine it is plugged into: the agent runs with a local Appium (--appium); that Appium has the driver that can show the phone — uiautomator2 for an Android phone, xcuitest 8.1.0 or later for an iPhone, because earlier versions never pass a real iPhone's screen to the Mac; and a port for the phone's screen was free on that machine. Each phone gets a port of its own, checked free when the agent first offers it, so two phones on one machine are never asked for the same one.

npx nthcase-agent devices says under each phone whether it will be shown and, when it will not, why and what to do; npx nthcase-agent doctor says the same in its live view check, one per phone. A phone that cannot be shown is still driven: its run says there is no mirror and why, and its screenshots, step results, timings and device logs arrive as they always do.

It needs version 0.2.0 of the agent or later. An older agent says at registration that it cannot show any phone, and the run says so rather than handing you a live view that would never produce a frame.

Network capture on your own phone

A run can record your test app's network traffic on a phone plugged into your own machine: an Android phone, with the agent on macOS or Linux. It needs version 0.3.0 of the agent or later.

An older agent cannot capture. npx nthcase-agent --version says which version a machine runs. An agent older than 0.3.0 tells us when it connects that it cannot capture, so a run on a phone plugged into that machine records no traffic, and the run's Network tab says that machine's agent cannot capture. To get 0.4.1, run the agent as npx nthcase-agent@0.4.1 … — with npx, keep @0.4.1 on every command below — or update a global install with npm i -g nthcase-agent@latest; then stop the running npx nthcase-agent connect and start it again with the new version.

Capture is off until you turn it on, on that machine, with the agent:

  1. npx nthcase-agent capture enable explains what capture does and asks you to type ENABLE. It makes one certificate authority for that machine, valid for one year. The agent never sends its private key anywhere, and keeps the folder that holds it out of backups: out of Time Machine on a Mac, and marked with a CACHEDIR.TAG on Linux, which backup tools that honour it skip. A backup made before that, or by a tool that ignores it, may still hold a copy; removing the certificate from every phone is what ends a copied key's use.
  2. npx nthcase-agent capture setup copies the certificate to a test phone, shows where to install it (Samsung and stock Android), and checks that the phone's browser trusts it.
  3. Turn on Capture network traffic in the scenario's device profile.

During a capture run the whole phone's traffic, not only the app's, goes through your machine — use a test phone. Only debug builds that trust user certificates can be read: in a release build, every host it reaches over HTTPS is listed as not readable. In a debug build, a host that does not accept the certificate (the app pins it, or its library trusts only its own certificates) is listed as not readable and passed through untouched; the rest of the app's traffic is still read. Everything is masked on your machine before it is sent — credentials in headers, cookies, fields and query parameters, and credential-shaped values, in compressed bodies too — and masked again when it reaches us. A body the agent cannot read is never sent, and the run says why it is missing. We keep the masked record with the run for one hour; nothing raw is ever kept on your machine.

A capture keeps to the internet. During a capture the agent does not connect the phone to that machine itself or to its local network — loopback, link-local, private and carrier-grade NAT addresses, judged on the address it actually dials. The phone gets an error, and the run's Network tab lists the request as refused. If your app under test talks to a backend on that machine or on your office network, start npx nthcase-agent connect … --capture-allow-local-network; while that is on, connect says so.

On a machine other people log into, the machine's administrator (root) could send traffic through the capture proxy while a capture runs; a connection from any other account is refused unread.

A machine with capture off, and a phone nobody set up there, are known before the run starts: the run's Network tab says which, and what to type on that machine. npx nthcase-agent doctor on that machine says whether capture is on, which phones are set up, and when the certificate authority ends.

To turn it all off: npx nthcase-agent capture disable hands every phone back and deletes the certificate authority's key and every capture file on that machine. The certificate stays on each phone until you remove it there — on a Samsung it is listed in two places; remove it in both.

On a Samsung: Settings → Security and privacy → More security settings → View security certificates → User: delete "NthCase agent CA — (your machine's name)". Then Settings → Security and privacy → More security settings → User certificates: delete it there too.

On a Pixel and other Android phones: Settings → Security & privacy → More security & privacy → Encryption & credentials → User credentials: remove "NthCase agent CA — (your machine's name)".

The names differ a little between Android versions: searching Settings for "certificate" finds both screens.

What stays running on your phone

A run leaves WebDriverAgent running on the device — that is what makes the next run start in about a fifth of the time, and it is why your phone shows Automation Running after the test has finished. You should hear that from us rather than from your own status bar.

The agent stops it once nothing has used it for five minutes, and never while a session is attached. It asks WebDriverAgent to end itself through its own shutdown route over a temporary USB port forward: nothing is installed, no daemon is started, and no process is killed. The agent says which it will do when it starts, so nothing it does later is a surprise.

FlagWhat it does
--keep-wda-warmLeaves it running. The banner stays; the next run stays fast. For back-to-back suites.
--wda-idle-after 30sChanges the window. Ignored with --keep-wda-warm.

What a run on your own device costs

The base floor of 2 credits or the AI tokens it used, whichever is greater — and no device time, however long it runs. Your hardware is not ours to rent. The full arithmetic, including our own farm's tiers, is on /docs/pricing.

A lost connection does not make a run free. If the agent stops, the cable comes out or your machine goes offline while a run is on your device, the run is charged as if it had finished: the floor or its tokens. A discovery on your device that loses the connection, or fails after it has spent tokens, pays for what it used: at least 1 credit, never more than its quote. On one of our devices, a lost connection costs nothing.

A run your phone stops before its first step pays only the minimum. On a phone locked with a PIN, pattern or password the run stops before step 1, and it costs 1 credit, or its tokens if they cost more.

Running on your own iPhone

An iPhone needs one thing an Android phone does not: a WebDriverAgent signing identity that belongs to your Mac. Every iOS session builds and signs WebDriverAgent first, and the signature has to be yours — the platform holds only its own, and signing your device with our Apple team is not something this platform will do. So you tell the agent your Apple team, and it drives your phone with that.

bash
npx nthcase-agent connect --farm https://farm.nthcase.com --appium http://127.0.0.1:4723 \
  --ios-team-id <YOUR TEAM ID> --ios-wda-bundle-id com.<you>.WebDriverAgentRunner

A free Personal Team works. The one-time build that has to happen first is one command — it uses Appium's own WebDriverAgent project with the xcodebuild flags Appium does not pass, tells you exactly what it will do (including that Apple will register that phone against your developer account), prints the full command in case you would rather run it yourself, and waits for you to type yes:

bash
npx nthcase-agent setup-ios --ios-team-id <YOUR TEAM ID> --ios-wda-bundle-id com.<you>.WebDriverAgentRunner

A run cannot register your phone with your Apple team. A session that asks Appium to do it (appium:allowProvisioningDeviceRegistration) is refused before anything reaches Appium, and the reason is given. npx nthcase-agent setup-ios, at your machine, is where you say yes to it.

Your team id is not the code in your certificate's name. A signing certificate reads Apple Development: you@example.com (XXXXXXXXXX), and those ten characters are the certificate's, not your team's. Using them is the mistake that makes xcodebuild say it cannot find a profile for a bundle id whose profile is sitting on your disk. Run npx nthcase-agent doctor --ios-wda-bundle-id <id>: it reads your real team id off your own provisioning profiles and tells you what it is.

doctor is how you check the setup before you press anything. The iOS provisioning profile check says which bundle id it looked for, which team the profile belongs to, and when it expires; the iOS WebDriverAgent project check catches a target left on somebody else's team. Each one also says what it did not establish — reading a profile off a disk is not running a build.

Unlock the phone, and keep it unlocked for the first session. A locked iPhone refuses to mount the developer disk image, and the build stops there with nothing to do about it from here. Provisioning profiles expire, and some are short-lived — the iOS provisioning profile check prints the expiry date it read off your own disk, and that date is the one to go by. The first run after a profile lapses rebuilds WebDriverAgent and may ask you to trust the developer again on the device (Settings → General → VPN & Device Management).

What is not supported yet

Until your machine declares an identity, its iPhones are attached and not offered — they appear on the Devices screen with the reason, and they are not pickable for a run. That is deliberate: a phone we cannot drive must not be offered as one we can, and learning it after a run beats nothing at all but not by much. Android phones on the same machine are unaffected, and so are iOS simulators.

If a run does reach a device nothing may sign, it ends Error with the reason, which means it is billed as one: tokens only, and a refusal spends none.