Workers
Introduction
Section titled “Introduction”A memory can choose a model that only a machine of yours can reach, such as a model served by Ollama on your laptop. Brain does not connect to that machine. A small program there, the worker, asks Brain for model calls, runs them on the local model and sends the answers back. The machine needs no open port and Brain holds no key for it.
Brain still runs every job. Only the model call moves. For how one source travels, see How a worker runs your own models.
Declare the model
Section titled “Declare the model”Add the model to the file named by BRAIN_MODELS_FILE with "runs": "worker". Brain keeps no model, provider or key for it, so the entry takes no other field except id, label and, for an embedding model, dimensions.
{ "chat": [{ "id": "local-qwen", "label": "Qwen 3 on my Mac", "runs": "worker" }], "embeddings": [{ "id": "local-embed", "label": "Nomic on my Mac", "runs": "worker", "dimensions": 768 }]}Set dimensions so Brain checks the width of every vector the worker returns. In Settings, a memory chooses the model like any other. While no worker serves it, the list marks it no worker connected.
Create a worker token
Section titled “Create a worker token”A workspace admin opens Workers in the web app, names the token and chooses the memories it serves: every memory, or a list. The secret starts with pbw_ and is shown once. Brain keeps only its hash.
A worker token does one thing: it takes model calls of the memories it serves and answers them. It reads no memory, and every other route refuses it. An agent token (pb_) or a signed-in session is refused on the worker routes in turn. Revoking the token stops the worker at its next request.
Run the worker
Section titled “Run the worker”The worker is the package @pilotoo/brain-worker, with the command pilotoo-brain-worker. A file names Brain and the models the machine serves, and the token stays in the environment.
{ "brainUrl": "https://brain.example.com", "tokenEnv": "BRAIN_WORKER_TOKEN", "concurrency": 1, "models": [ { "id": "local-qwen", "kind": "chat", "runner": "ollama", "model": "qwen3.5:9b", "numCtx": 16384 }, { "id": "local-embed", "kind": "embedding", "model": "nomic-embed-text", "baseUrl": "http://127.0.0.1:11434/v1", "dimensions": 768 } ]}pnpm --filter @pilotoo/brain-worker buildBRAIN_WORKER_TOKEN=pbw_... node packages/brain-worker/dist/main.js worker.jsonEach id is the id of a model in BRAIN_MODELS_FILE. The worker asks only for the ids it serves. On SIGTERM or SIGINT it finishes the call in hand and stops.
The worker file
Section titled “The worker file”| Field | Default | Meaning |
|---|---|---|
brainUrl |
required | The address of Brain. It must be https://; http:// is accepted only for localhost, 127.0.0.1 and [::1], because the token travels in every request. |
tokenEnv |
BRAIN_WORKER_TOKEN |
The environment variable that holds the token. The file never does. |
concurrency |
1 |
Calls answered at once. |
models[].id |
required | The id of the model in BRAIN_MODELS_FILE. |
models[].kind |
required | chat or embedding. |
runner |
required for chat | ollama or openai-compatible. |
model |
required | The model’s name on that server. |
baseUrl |
Ollama: http://127.0.0.1:11434. Others: required |
The server’s address. An embedding model uses an OpenAI-compatible endpoint, such as Ollama’s /v1. |
apiKeyEnv |
none | The name of an environment variable that holds a key, when the server wants one. |
numCtx, numPredict, think |
Ollama’s | ollama only: the context window, the longest reply, and whether the model thinks first. |
maxTokens |
the server’s | openai-compatible only. |
timeoutSeconds |
300 |
The longest a chat call may run on this machine, at most 1800: Brain holds a call for at most 30 minutes from its first claim. |
dimensions |
none | Embedding only: the width the server’s vectors must have. |
When no worker answers
Section titled “When no worker answers”A call waits for a worker to claim it. The job keeps its own lease alive while it waits.
| What happens | Result |
|---|---|
| No worker claims the call within 2 minutes. | The call fails and the job goes back to the queue, to try again after a short wait. |
| A worker claims the call and stops, so it stops extending its claim. | The claim runs out, the call fails and the job goes back to the queue. |
| The worker reports an error, for example the model’s reply has the wrong shape. | The call fails the same way. |
| The job fails on its last attempt. | The document fails with No worker answered for model <id>. |
Someone asks a memory whose answer model runs on a worker. |
The question waits the same way, and fails with the same reason. |
| A result arrives for a claim that was lost. | Brain refuses it and changes nothing. |
Two workers that serve the same model share its calls: each call goes to one of them.
Brain checks a worker’s answer exactly as it checks the answer of a hosted model. The worker runs the same code Brain runs for a hosted model, on its own model.
Embedding on a worker
Section titled “Embedding on a worker”Passages added while no worker answers are stored without vectors and counted as pending. Start Embed again in the memory’s settings once a worker is back, and Brain embeds them in batches. See Models.
See which models are served
Section titled “See which models are served”GET /api/models marks a model that runs on a worker with "runs": "worker". It is available while a worker that serves it was seen in the last minute. Availability is only a display: a job still queues and waits for a worker, whatever the mark says.
The Workers page lists each token with the memories it serves, the models its worker declared at its last request and when it was last seen.
Options
Section titled “Options”| Setting | Default | Meaning |
|---|---|---|
"runs": "worker" |
server |
On an entry of BRAIN_MODELS_FILE: only a worker answers it. |
BRAIN_WORKER_TOKEN |
none | On the worker’s machine: the token. The variable’s name is tokenEnv in the worker file. |
Next steps
Section titled “Next steps”- How a worker runs your own models for the step-by-step picture.
- Models for the file that declares them.
- HTTP API for the routes of the worker and of its tokens.