Connecting workers to RunnerQ Cloud with the conductor agent.
The conductor agent connects a worker to RunnerQ Cloud. It dials out over a WebSocket (no inbound port), describes the worker, and reports it on an interval and within about a second of a change.
import { startAgent } from "runnerq/conductor";
await worker.start();
const agent = startAgent(worker, {
url: "wss://cloud.runnerq.dev",
apiKey: process.env.RUNNERQ_CONDUCTOR_KEY!,
});
// ...
await agent.close(); // before stopping the worker: the Cloud records a clean shutdown
await worker.stop();- The worker shows in Fleet as an executor (
worker.id), with its host, queue, activity types, capacity, labels, what it's running and its counters. startAgentreturns at once; the agent reconnects with backoff when the connection drops.signalstops it likeclose().metadataOnly: truekeeps payloads, results, errors and event details from ever leaving the process, whatever the Cloud asks.- Requests beyond
maxConcurrentRequests(16) are refused rather than queued; each is bounded byrequestTimeoutMs(30s) or the Cloud's deadline.
When the worker's storage implements QueryStorage (from runnerq/storage;
PostgresStorage does), the agent also answers the Cloud's queries about activities,
steps, events and trees, and streams live events, across every queue in the database.
Without it, the agent serves only the executor. Payloads and results are shown as plain
JSON: a SuperJSON Date, Map or bigint appears as its JSON projection (an ISO
string, entry pairs, a decimal string).
Submissions, claims and successes store no event (the activity's own times record them).
While someone is watching the app in the Cloud, the agent announces them live instead: what
this worker claims and completes, and what its handlers spawn. A RunnerQClient in a
process without an agent announces nothing; its activities still appear once a worker
claims them. Announcements are best effort and never stored.
The agent is read-only unless you pass allowControl: true. Then, when the storage
implements CommandStorage (PostgresStorage does), the Cloud can cancel, retry, run
now, reschedule, reprioritize, delete and signal activities of the worker's queue.
Commands are idempotent by id (replayed for at least 24 hours) and work in metadata-only
mode too. A cancelled activity stops at once when it runs on this worker, or at its next
heartbeat elsewhere: its ctx.signal aborts with a claim_lost error, and it counts as a
lost claim, not a failure. The command ledger is the runnerq_commands table, which
PostgresStorage.initialize() adds to an existing database.