Your machine, as more worker capacity.
A local worker node runs the same test-worker image the cluster runs, as Docker containers on a developer's desk. It leases the platform's jobs over HTTPS, runs them, and reports back exactly as a cloud worker does. The results land on the same dashboard, pull request and issue as every other run.

| A cloud worker | A local worker node |
|---|---|
| Runs on the platform's cluster and scales with the queue | Runs on a developer's machine, started and stopped by the developer |
| Takes every kind of job | Takes only the kinds its pools are responsible for and the project shares with nodes |
| Reads the platform's secret store | Never receives a platform secret |
| Clones with the project's stored credential | Clones with the developer's own GitHub token |
| Model calls bill to the platform's key | Model calls bill to the developer's own key |
| Hosts the app in a private cluster namespace | Hosts the app on the developer's own Docker, if the project allows it |
How it is built.
One HTTPS door, the worker gateway, stands between a node and the platform. Every call through it is checked against one question: does this node hold that job right now?
Developer machine
- qawa-node program with its status screen
- pool tests: containers for tests, explore, discovery, chat
- pool code: containers for code changes, patch repairs, videos
- the app under test, on the developer's Docker
- register, heartbeat
- lease a job, renew, finish
- record reads and writes, per-store policy
- git over HTTPS, held branches only
Platform
- worker gateway, authenticated with the node key
- the shared job queue that cloud workers lease from too
- the project's records, evidence and artifacts
- the internal git host that code changes push to
The gateway decides, not the node.
- Leases follow consent: a job is offered to a node only if the key is bound to its project, the project shares that kind with nodes, and the node's pools take it. Environment holds and per-project caps apply to nodes exactly as to cloud workers.
- Writes follow the lease: each store has its own policy. A node can write a job's results, its code change, its chat session or its patch revision only while it holds the job, and a nested run such as a code change's validation resolves to the job that owns it.
- Git is relayed, and guarded: git's own smart HTTP protocol passes through the gateway to the internal git host. A pre-receive guard refuses any push to a branch outside the runs the node holds.
- Artifacts are signed by the platform: screenshots, videos and logs upload through links the platform signs for that job, so a node never holds storage credentials.
- A dropped node costs a retry, not a result: when a node stops heartbeating its leases expire and the jobs return to the queue for any worker to take.

Setting one up.
-
An administrator mints a node key
A worker-scoped API key bound to one or more projects. The key can lease work from those projects and nothing else; widening the list means minting a new key.
-
The project owner opts in
On the project page, under Local worker nodes: allow developer machines, tick the kinds of work they may take, and choose whether they may host the app's isolated environment on their own Docker.
-
The developer runs init, build and up
The developer works from a checkout of the platform repository, with Docker running. init asks for the platform address, the key, a node name, the pools, the model provider and the developer's own tokens. build makes the test-worker image; up starts the containers and confirms the platform accepted the node.
-
The node appears on the Workers page
The dashboard lists it as node · name, with its host, its capacity, the kinds of work it takes and when it was last seen. From then on it leases jobs from the shared queue exactly as a cloud replica does.
$ npm run qawa-node -- init # platform, key, name, pools, provider, tokens
$ npm run qawa-node -- build # test-worker image from this checkout
$ npm run qawa-node -- up # start every pool's containers
$ npm run qawa-node # the status screen{ "remote_workers": {
"enabled": true,
"allow_isolated_env": true,
"kinds": ["test", "agent_task", "video"]
} }Pools: how many, doing what, with how much.
A node's containers run in pools. Each pool has a name, a container count, the kinds of work it takes, how many browser jobs and headless jobs each container runs at once, and a memory and CPU limit per container. Code changes build and boot whole apps, so that pool gets the memory; browser tests get more containers.
Presets cover the common shapes: tests, code, chat, vendor, and all. A pool changes from the command line or the status screen, and only the containers whose shape changed are recreated.
"pools": [
{ "name": "tests", "count": 2,
"responsibilities": ["test", "chat"],
"concurrency": 2, "agent_concurrency": 1,
"memory": "6g", "cpus": 3 },
{ "name": "code", "count": 1,
"responsibilities": ["agent_task", "vendor_task", "video"],
"concurrency": 1, "agent_concurrency": 2,
"memory": "10g", "cpus": 4 }
]
$ npm run qawa-node -- pool set tests count=3 cpus=1.5
$ npm run qawa-node -- pool add code big memory=16g
A terminal screen, not another tab.
The status screen is where a developer already works: the terminal. It shows whether the platform accepted the node, what each container is running, and how much CPU and memory each is using, including any app environment the node is hosting.
- Six screens: Overview, Pools, Workers, Projects, Logs and Settings, moved between with j and k or the number keys.
- Control without flags: U and D start and stop the node, + and - grow or shrink the selected pool, E toggles environment hosting, B rebuilds the image.
- Logs in place: a live stream from any container, with the worker's JSON lines printed readably.
- Closing it is safe: q closes the screen; the containers keep running until the developer stops them.
Why it helps a product team.
Faster loops on heavy apps
An app whose environment needs a monorepo build and many gigabytes of memory boots on a workstation that already has them. An explore sweep or a round of code-change fixes starts as soon as it is queued.
Capacity when the team needs it
A release week or an accessibility push adds machines to the fleet for as long as it lasts. Nobody has to resize the cluster, and the shared queue spreads the work across every worker.
Costs and credentials stay with the team
Model calls bill to the developer's key and clones use the developer's own repository access. The platform's secrets never leave the platform, so opting in is a decision the project owner can make alone.
Where it stops today
- A node needs Docker and a checkout of the platform repository; there is no packaged installer yet.
- A test that needs a platform secret the job record does not already carry stays on the cloud workers.
- Nodes read the dependency cache but never write it, so a node's first boot of a new lockfile installs from scratch.
- Platform administration, such as importing a project from another deployment, never runs on a node.
- A node's capacity is its machine's: pools share one Docker, and hosted app environments count against the same memory.
Questions people ask
Does a local node see the platform's secrets or the project's GitHub token?
No. A node never receives a platform secret. It clones with the developer's own GitHub token, bills model calls to the developer's own key, and pushes code changes only to the platform's internal repository, and only the branch of a run it holds.
What happens to a job if I close my laptop mid-run?
The node stops heartbeating and its lease expires. The job goes back to the queue and any worker, cloud or local, picks it up and runs it again from the start. Nothing is lost except the time the first attempt took.
Add your machine to the fleet.
Sign in, ask an administrator for a node key bound to your project, and run init, build and up. The node shows on the Workers page as soon as up confirms it.