Start
How Troupe works
Troupe is one runtime that owns the devices, one contract that every client speaks, and tools built on that contract. This page walks through the parts in the order a test run meets them.
The parts
troupe test troupe studio your code (client) a coding agent
| | | |
+--------- REST and RPC -------------+-------- MCP --------+
|
the runtime, on 127.0.0.1
sessions, devices, the live stream, traces
| | |
ios-simulator android-emulator catalystNothing above the runtime talks to a device itself. The test framework, the studio, the client and an MCP agent all call the same procedures.
The runtime
The runtime is a daemon, one per user. troupe test, troupe studio, troupe install and troupe mcp start
it when none answers, and troupe runtime start --detach starts it by hand. It listens on 127.0.0.1 only, on
one port between 47000 and 47999, and writes its address and an owner token to runtime.json under
~/.troupe, where tools on the same machine find it. It exits by itself after 30 minutes with no session and no
device.
The runtime owns every device it starts. It names them troupe-…, keeps a registry of them, and never touches
a simulator or emulator it did not create. It also watches the host's memory and refuses a new device with
CAPACITY_UNAVAILABLE when there is no room for one.
When CI is set, or with --no-daemon, troupe test runs the same runtime inside its own process and shuts
its devices down at the end. See the runtime daemon.
Sessions
A session holds one device from the moment you ask for it until you release it or it expires.
sessions.createtakes a device spec (platform, and optionallydeviceType,os,localeand more) and apps to install. It returns at once with a session that isstarting, orreadywhen a warm device matched.sessions.waitwaits until the session isready: the device is booted, the driver has answered and the apps are installed.- Device calls name the session:
input.tap,ui.snapshot,media.screenshot,apps.launch. sessions.releaseends it. A healthy released device stays warm for five minutes, so the next session with the same spec starts on it without a boot.
A session also ends by itself. A test worker's session ends 30 seconds after its last heartbeat. Any other
session ends after 10 minutes without activity, or 60 minutes in total, unless you ask for other limits when you
create it. See sessions and sessions.create.
Platforms and drivers
A platform is a kind of device. Each one brings a way to start devices, a driver that reads the accessibility tree and performs actions, and, where it has one, a media engine for the live stream.
| Platform | Devices | Driver | Live stream |
|---|---|---|---|
ios-simulator | iOS Simulators, through simctl | An XCUITest runner, one per device | H.264, with live touch and keys |
android-emulator | Android Emulators, through Troupe's own adb client | An instrumentation driver on the device | H.264, with live touch and keys |
catalyst | Your Catalyst app as a Mac app in a phone-sized window | A host library linked into the app, driving it in process | none |
Each platform declares its capabilities: which app formats it installs, which input it takes, whether it can
rotate. The runtime checks them before it touches the device, so a call the platform cannot serve fails at once
with CAPABILITY_UNSUPPORTED. A test asks for a capability with
test.requires and is skipped where it is missing. The platform
pages list each platform's capabilities: iOS, Android,
Catalyst.
One contract, three surfaces
Every runtime operation is a procedure with an id such as sessions.create or input.tap, a typed input and
output, and a list of typed errors. There are 64 procedures in 14 groups: runtime, pairings, catalog,
sessions, apps, ui, input, system, media, logs, streams, traces, files and runs. The
runtime serves all of them the same way on three surfaces:
| Surface | Path | Used by |
|---|---|---|
| REST | /v1/<group>/<operation> with the operation in kebab case, such as POST /v1/input/tap or GET /v1/catalog/device-types | Anything that speaks HTTP, and the client |
| RPC | /rpc/v2/<group>/<operation> | oRPC clients |
| MCP | /mcp, or stdio through troupe mcp | Coding agents: 31 curated tools, named sessions_create, input_tap and so on |
The live video and input stream is a WebSocket on the same listener, at /v1/stream.
An error is the same on every surface: a code from one registry (LOCATOR_NOT_FOUND, APP_CRASHED,
DEVICE_LOST), a message and typed data. Each code is classed as the app's or test's fault, or as
infrastructure, which is how a test ends failed or errored. The
protocol reference lists every procedure and every error;
calling the API directly shows the requests.
Where the test framework fits
troupe test is a client of the runtime like any other.
- The main process loads
troupe.config.ts, asks the runtime what its platforms can do and how many devices it may run, and plans the tests. - It creates a session per worker, with your app as an install, and waits for it before any test starts. That time is outside every test timeout.
- Each worker is a child process that attaches to its session and sends a heartbeat every 10 seconds.
- A locator is data.
screen.getByRole('button', { name: 'Sign in' })is sent whole with the action and resolved on the device. - An action is one
inputcall. The runtime waits for the element to be actionable, acts, and answers. - An assertion polls
ui.queryuntil it holds or its timeout passes. - The trace is recorded by the runtime between
traces.startChunkandtraces.stopChunk, and joined with the runner's own steps into one zip.
See writing a test.
Where Troupe Cloud fits
Troupe Cloud serves the same contract at https://api.troupe.ali.ve, so the same client and the same MCP tools
work against it with an API key or a sign-in in place of the local owner token. A device call goes to a device
host that runs the same runtime. The live stream connects your browser to that host directly; video and input
do not pass through the API.
The cloud adds what a hosted service needs: organizations, projects, API keys, usage limits, and builds of your app from a repository or an uploaded archive. See Troupe Cloud.