Worker API
This API is used to instantiate DCP workers, which will perform compute tasks on the network in exchange for DCCs.
Record of Issue
Date |
Authors |
Rev |
Change |
|---|---|---|---|
Jul 29 2026 |
(docs) |
2.0.0 |
Documents |
May 20 2020 |
Ryan Rossiter |
1.0.0 |
Initial Worker API impl |
dcp/worker exports two classes: DistributiveWorker, the primary class described in this document, and Worker, a deprecated compatibility wrapper around it for existing code – see Worker API 1.0 compatibility, below. New code should use DistributiveWorker.
Glossary
Worker - The Worker is an entity representing a supervisor and any number of sandboxes. It connects to the distributed computer and performs work by assigning slices to sandboxes.
Supervisor - The supervisor is the entity responsible for managing sandboxes within the worker.
Sandbox - A sandbox is the environment in which a slice is computed. The supervisor instantiates and terminates these sandboxes, and assigns it work to be computed. A sandbox could be a web worker, a standalone V8 environment, or any other interface that implements the WebWorker spec.
Worker Class
The Worker class is an EventEmitter instance, and it emits events that can be listened to with the standard EventEmitter methods. See Events for the events it emits.
Constructor
new DistributiveWorker(config: object, SandboxConstructor?: constructor)
Neither argument is required to be present (the constructor tolerates config being omitted), but SandboxConstructor must be resolvable somehow: outside the browser, the constructor throws 'SandboxConstructor not specified for DistributiveWorker!' if it’s omitted and can’t be defaulted. In a browser, it defaults to ./evaluators’ BrowserEvaluator when omitted.
There is no identityKeystore constructor argument or option. The identity used to sign scheduler communications comes from the ambient identity (wallet.getId(), i.e. whatever dcp/identity currently considers the active/default identity for the process).
config: object (also available afterward as worker.config):
paymentAddress: Address: Address used for depositing DCCs in after a slice is computed. Coerced from a string automatically the first timeworker.configure()runs (called internally bystart()); throws if the result isn’t an instance ofwallet.Address. When omitted, resolved automatically via the worker-management backend (obtainUnclaimedEarningsPaymentAddress()).jobIds: string[] | false: When an Array, this worker will only compute slices for the listed jobs. Defaults tofalse(no restriction).computeGroups: object[]: Compute Groups this worker should join, in the same shape asjob.computeGroups(see: Compute API).leavePublicGroup: boolean: When true, this worker will not do work for the public Compute Group; it will only accept work from the groups listed incomputeGroups.maxSandboxes: number: Maximum number of sandboxes that can be working at one time. When omitted, defaults to a value derived from system memory (roughly one sandbox per 2.5 GB of RAM on Node.js, ornavigator.deviceMemory / 2.5in a browser that exposes it), falling back to1/2where that information isn’t available (seeDistributiveWorker.recommended.maxSandboxes, a static getter that computes this same value).utilization: { cpu: number, gpu: number }: Proportion (0< n <=1) ofcoresthis worker will actually use. Defaults to{ cpu: 1.0, gpu: 0.75 }.worker.effectiveCoresiscoresscaled by this value (see Properties).cores: { cpu: number, gpu: number }: How many CPU/GPU cores this machine has, for the purposes of scheduling.cpudefaults to a live-updating count derived fromconfig.hardware.vCores; setting it explicitly (including to0, which disables the worker for that resource type) stops it tracking hardware changes until unset again.gpudefaults to1.hardware: { vCores: number }: Override for the detected logical core count thatcores.cpu’s default is derived from.bannedGPUs: object[]: Regexp-keyed device descriptors (matching properties likedescription/device) identifying GPUs this worker should refuse to use, regardless ofcores.gpu/utilization.gpu.minimumWage: { CPU: number, GPU: number, in: number, out: number }: The minimum payout per slice the worker will accept from a job, expressed per unit of each resource:CPU: DCC per second of CPU time.GPU: DCC per second of GPU time.in: DCC per byte of inbound network traffic, that includes the bytes of slice input, arguments, work function, and modules.out: DCC per byte of outbound network traffic, that includes the bytes of console messages and results.
All four default to
0whenminimumWage(or the whole options object) is omitted.Note
This object is forwarded to the scheduler as-is (as part of the fetch-task request); dcp-client itself does not interpret its units, so the worth calculation happens scheduler-side.
schedulerConfig: object: A fragment merged overdcpConfig.scheduler(seeleafMerge) for this worker only; use{ location: url }to point this worker at a different scheduler than the rest of the application.trustScheduler: boolean: When true (the default), the scheduler is allowed to push configuration changes down to this worker.managed: boolean/unmanaged: boolean/claimedEarnings: boolean: Control the new worker-management integration (registration, run-info/task-info telemetry, unclaimed-earnings transfer) that now runs automatically duringstart()/stop()– see theclaim,metrics, anddrop-sliceevents, below. Settingconfig.unmanagedtruthy skips the management bookkeeping entirely during shutdown.allowConsoleAccess: boolean: When true, work functions running in this worker’s sandboxes may use the sandbox’s nativeconsoleobject directly, in addition to the usualconsoleevents.trustComputeGroupOrigins: boolean/allowOrigins: { any, fetchData, fetchWorkFunctions, fetchArguments, sendResults: string[] }: Control which origins this worker will fetch data, work functions, arguments, and modules from, and send results to.watchdogInterval: number | string: How often (acceptsms-style strings, e.g.'7s') the worker checks on its sandboxes’ health. Defaults todcpConfig.supervisor.tuning.watchdogIntervalor 7 seconds.sandboxOptions: object:SandboxConstructor: constructor: Constructor for the sandbox environment, it should implement the WebWorker API. When not provided in the browser, it will default to the globalWorkerconstructor.ignoreNoProgress: boolean = false: When true, the sandbox will ignore errors from the sandbox not firing progress events.
Worker API 1.0 compatibility
For existing code, new Worker(identityKeystore, config) is also available: it constructs a DistributiveWorker(config, config?.sandboxOptions?.SandboxConstructor) and returns a Proxy over it that also answers to a few legacy property names (workerOptions, workerId, paymentAddress, identityKeystore). New code should construct DistributiveWorker directly; using Worker logs a one-time console warning to that effect.
Note
The identityKeystore argument is vestigial: there is no supported way to run a worker under an identity other than the process’s own ambient one. If provided, it is compared (asynchronously, via wallet.getId()) against the ambient identity; a mismatch makes the proxy throw 'ad-hoc identity specification not supported in DCP Worker polyfill' on every subsequent property get/set.
Methods
worker.start(): Promise<void>
This method will start the worker. It will begin to fetch work from the supervisor and submit the computed results automatically. It does not throw if the worker is already started – it silently no-ops instead.
Immediately before starting, a cancelable beforeStart event is emitted (see Events, below); if any listener calls the provided callback, start() returns without starting the worker.
If the worker was disabled via Worker.disableWorker(), calling start() prompts the user (via confirmPrompt) to confirm they want to re-enable and start it, rather than starting immediately.
worker.stop(forceTerminate: boolean = false): Promise<void>
This method will stop the worker. If forceTerminate is true, the worker will terminate all working sandboxes without waiting for them to finish working; otherwise it waits for in-progress slices to complete first.
A cancelable stop event is emitted immediately (see Events, below); if any listener calls the provided callback, the stop is escalated to forceTerminate: true, regardless of what was originally requested. An end event fires once the worker (and schedMsg) have fully stopped.
Worker.disableWorker()
This static method sets a key in local storage (or the equivalent persistent store on Node) to disable the worker. The user will need to manually intervene (or call start(), which will prompt them) before the worker can be started again.
Other methods
The following methods exist and are used internally by the SchedMsg command handlers (below), but are otherwise lightly documented:
worker.pause()/worker.unpause(): While paused, the worker will not execute new slices, fetch new work, or return slices to the scheduler.worker.controlledStop(timeLimit: number = 86400 * 1000): Promise<void>: Callsstop(false), but forcesstop(true)aftertimeLimitmilliseconds if the graceful stop hasn’t completed by then.worker.returnSandbox(sandbox: Sandbox): Removes a sandbox from the supervisor and terminates it.worker.returnSlice(slice: Slice, reason: string = 'unknown'): Removes a slice from the supervisor and returns it to the scheduler.worker.ref()/worker.unref(): Control whether the worker holds the process/event-loop open while working (Node.js).
Properties
worker.working: boolean: This boolean indicates the current status of the worker. It should not be set manually.worker.schedMsg: SchedMsg: The internal schedMsg client instance. Custom behaviour for schedMsg commands can be provided on this object, see Overriding SchedMsg Handlers.worker.config: object: The same config object passed to the constructor (with defaults filled in); mutating it affects the running worker.worker.paymentAddress: Address: Convenience accessor forconfig.paymentAddress– prefer settingconfig.paymentAddressdirectly.worker.id: opaqueId(get/set): An opaque identifier for this worker. The getter is lazy: the first read (if nothing has been set yet) fetches a persisted id from local storage, generating and persisting a new one if none exists. Setting it directly (worker.id = '...') does not persist the value – it only affects the in-memory instance.Note
Through the deprecated
Worker1.0 proxy,worker.workerIdis effectively read-only: the getter forwards toid, but the setter does not, soworker.workerId = xhas no effect. Useworker.id = x, or constructDistributiveWorkerdirectly.worker.sandboxes: Sandbox[]/worker.workingSandboxes: Sandbox[]: All non-terminated sandboxes, and the subset of those currently executing a slice, respectively. These are the internalSandboxobjects, distinct from theSandboxHandleobjects handed tosandboxevent listeners – see Sandbox API, below.worker.slices: Slice[]/worker.queuedSlices: Slice[]/worker.workingSlices: Slice[]: All slices currently known to the worker, and the queued/working subsets thereof.worker.effectiveCores: { cpu: number, gpu: number }:config.cores, scaled byconfig.utilization.worker.originManager: OriginAccessManager: Enforces theallowOrigins/trustComputeGroupOriginsoptions.worker.supervisorVersion: string: Version string of the internal Supervisor implementation.
Note
There is no worker.supervisor property. The closest equivalent is worker.badSupervisorBackdoor, which the source explicitly documents as @deprecated - Please do not use this. The properties above cover the supported ways to interact with the supervisor from outside the Worker class.
Events
start: Emitted when the worker is started.beforeStart(cancel: () => void): Emitted immediately before starting; callcancel()to prevent the worker from starting.stop(escalate: () => void): Emitted immediately whenstop()is called; callescalate()to force an immediate stop regardless of theforceTerminateargument that was passed.end: Emitted once the worker has fully stopped (afterstop, onceschedMsgand the supervisor have both finished shutting down, and – unlessconfig.unmanagedis set – once the new worker-management bookkeeping for the run has been flushed).error: Emitted when an internal error occurs (including errors thrown bystart()/stop()and unhandledwarning/errorconditions from the supervisor).warning: Emitted for a non-fatal problem worth surfacing to the user. The callback argument is a message string.sandbox: Emitted when the worker instantiates a new sandbox. The argument provided to the callback is aSandboxHandle, not aSandboxinstance – see Sandbox API, below.connect/disconnect: Emitted when the worker’s scheduler connection connects/disconnects.beforeFetch(cancel: () => void): Emitted immediately before the worker requests slices from the scheduler; callcancel()to skip this fetch.fetch: Emitted once a fetch request to the scheduler completes, whether or not it returned any work (this includes fetches that found no slices available). The callback argument is either an object{ fetchStart, fetchEnd, fetchSize, jobs, fetchState }describing what was fetched, or, if the request itself failed, the Error instance.beforeResult(cancel: () => void, resultUrl: string | false, jobAddress: string, sliceNumber: number): Emitted immediately before the worker submits a completed slice’s result to the scheduler; callcancel()to abort the submission (the slice is returned to the scheduler instead).result: Emitted once a result submission completes. On success, the callback receives(resultUrl: string | false, payloadLength: number, jobAddress: string, sliceNumber: number); on failure (after retries are exhausted), it receives(error: Error, jobAddress: string, sliceNumber: number).payment: Emitted immediately after a successfulresult, once payment has been recorded. The callback receives(amount: string, paymentAddress: string, jobAddress: string, sliceNumber: number)– there is no single payload object, and noaccepted/reasonfields (a slice that wasn’t accepted doesn’t reach this event; seeresult, above).drop-slice: Emitted when a slice is returned/dropped outside the normal result-submission flow (e.g. the SIGHUP handler dropping all sandboxes). The callback receives{ jobId, sliceNumber, status }.metrics: Emitted alongside slice completion, carrying instrumentation data:{ jobId, sliceNumber, metrics, status }.notification: Emitted when the worker receives a job-level notification from the scheduler (the counterpart, at the worker/scheduler layer, of the Protocol API’sNotificationmessage – see Protocol API). The callback receives(eventName, resolveCallback); callresolveCallback()once you’ve finished handling it.claim: Emitted once, near the start ofstart(), after any previously-unclaimed earnings for this worker have been automatically transferred toconfig.paymentAddress(skipped whenconfig.claimedEarnings === falseorconfig.managed === false). The callback receives the transfer’s payload.
Note
fetch2 also exists, as a forward-looking variant of fetch (with jobs keyed differently); it is not yet part of the stable public contract.
Note
The claim/metrics/drop-slice/notification events reflect an automatic earnings-claim/registration/telemetry subsystem; treat the four event descriptions above as a starting point rather than an exhaustive contract.
Note
One known issue: a catch block in the supervisor’s sandbox-startup error handling (worker/supervisor2/index.js) does this.worker.emit(error) instead of this.worker.emit('error', error), so a worker.on('error', ...) listener won’t fire for that particular failure path.
Sandbox API
There are two distinct sandbox-related objects. worker.sandboxes/worker.workingSandboxes return Sandbox instances – the supervisor’s own internal objects. The sandbox event, however, hands listeners a lighter-weight SandboxHandle (exposing just id, public, jobAddress, and sliceNumber), and it’s the SandboxHandle that emits the per-slice lifecycle events most consumers actually want. Both are EventEmitters, but they emit different events, described separately below.
SandboxHandle Events
These are the events emitted on the SandboxHandle object provided by the worker’s sandbox event.
ready: Emitted when the sandbox is available and waiting to be assigned a slice (including when it becomes available again for reuse after finishing one).job: Emitted once the sandbox is associated with a specific job, before it starts on that job’s first slice. The callback argument is the job’sJobHandle.slice: Emitted when the sandbox begins working on a slice. The callback argument is the slice number (not a job description object).sliceEnd: Emitted when the sandbox finishes working on a slice (success or failure). The callback argument is the slice number.progress: Emitted when the slice reports progress. The callback argument is the progress value (a number between 0 and 100), orundefinedfor indeterminate/out-of-range progress reports.payment: Emitted alongside the worker’s ownpaymentevent (see Events, above), for the slice this sandbox most recently worked on. The callback receives(amount: string, paymentAddress: string, sliceNumber: number).metrics: Emitted alongside slice completion with per-sandbox instrumentation. The callback receives(sliceNumber: number, eventMeasurements: object).end: Emitted when the sandbox environment is terminated. The sandbox will not be used after this event is emitted.
Sandbox Events
These are emitted directly on the Sandbox instances found in worker.sandboxes/worker.workingSandboxes (not on their SandboxHandle):
start: Emitted when the sandbox begins working on a slice. The callback receives{ job, sandbox }, wherejobis the job’s public metadata (job.publicfrom the Compute API) andsandboxis this Sandbox instance.sandboxError: Emitted when the sandbox environment itself (as opposed to the work function running inside it) hits an error condition. The callback argument is the error.reject: Emitted when the sandbox is terminated while a slice was in progress. The callback argument is an Error describing the termination.
Overriding SchedMsg Handlers
The worker’s SchedMsg instance (worker.schedMsg) subscribes to global commands from the scheduler. It contains default handlers for cross-platform handling of commands, but they can be overriden for clients to provide their own behaviour.
The scheduler’s commands are propagated in the SchedMsg instance by means of it being an EventEmitter. Additional event listeners can be registered, and listeners will be executed in LIFO (last-in-first-out) order. This means that if you add your own listener, it will be run before the default one.
If a command listener returns false then it will cancel the execution of the remaining listeners. This is the recommended method of overriding the default listener:
const worker = new DistributiveWorker(config);
worker.schedMsg.on("restart", () => {
console.log("The scheduler asked this worker to restart.");
return false; // cancel the default behaviour
});
Note
registerHandler (used internally to wire up each command’s default listener) throws if called twice for the same command, so only commands with a default handler – listed below – can be overridden this way. Attempting to register the first listener for a command with no default (e.g. one made up for a custom application) works fine; there’s just nothing to cancel.
SchedMsg Commands
Every payload property below is named jobId; the corresponding handlers read/write worker.config.jobIds (see Constructor, above).
kill: This command instructs the worker to immediately stop working, and can optionally disable the worker to prevent restarting. The user will need to manually intervene to restart the worker.
Payload Object:temporary: boolean: When false, the worker will be disabled.
restart: Currently broken. The handler is a one-linethrow new Error('not supported'); a scheduler that sends this command today will cause the handler to throw synchronously out ofSchedMsg.onMessage().remove: This command instructs the worker to stop working on a specific job.
Payload Object:jobId: string: The id of the job to stop working on.
addPriorityJob: This command adds a job id toworker.config.jobIds, restricting (or further restricting) this worker to computing slices from the listed jobs.
Payload Object:jobId: string: The id of the job to add.immediate: boolean: When true, also immediately returns any in-progress slices/sandboxes not working on a priority job.
removePriorityJob: The inverse ofaddPriorityJob: removes a job id fromworker.config.jobIds.
Payload Object:jobId: string: The id of the job to remove.immediate: boolean: When true, also immediately returns any in-progress slices/sandboxes working on this job.
Note
There is no default handler for announce, reload, or openPopup – a worker receiving one of these commands just logs No SchedMsg handler registered for command '<name>' and otherwise ignores it. An application can register its own listener for any of these command names to give them real behaviour.