blobhub-worker start loads the configuration, runs preflight checks, and runs each configured
section. The per-section behavior — what the worker does once it’s attached — is determined by
the section’s job_type.
Synopsis
The profile decides more than the credential: each one owns its own
config.yaml, cursors, logs and lock, so
several workers run side by side on one machine. See Authentication and Profiles.
In headless mode (the default) the worker writes structured JSON log lines to stderr, which
makes it suitable for running under systemd, a container supervisor, or tmux. With --tui, those
same JSON lines are redirected to a file at {state tree}/logs/{instance_id}/worker.jsonl
(append mode, line-buffered) so the dashboard has the terminal to itself; stderr is silenced while
the dashboard runs. Log rotation is not implemented in v1.
Preflight
Before any section is contacted,start runs the following steps in this order. A failure here exits the
process with a clear code; nothing on the server is touched.
- Migration, if a pre-profiles install is found — it operates on fixed locations that exist before any profile is chosen, and may create the very credentials file step 2 then reads. See Upgrading.
- Credential resolution: profile name, then key, then URL, each
--flag> environment > stored — elsePROFILE_NOT_FOUND,CREDENTIALS_NOT_FOUNDorINSECURE_CREDENTIALS_PERMISSIONS. - State-tree selection, from where the key came from. Everything after this resolves inside that tree.
The tree is announced on the first log line (
worker_resolved) — the way to see that ablobhub profile useelsewhere has moved which profile a barestartpicks up. config.yamlloaded and validated from that tree — elseINVALID_CONFIG, which names the full path it looked at. Each section’sjob_typeis dispatched to the corresponding job-type validator, which may surface additional codes (e.g.UNKNOWN_JOB_TYPE, or job-type-specific section errors).- Identity:
GET /v1/users/meresolves the key’s user. If the tree has noidentity.yamlyet it is recorded now; otherwise the server’suser_idmust match the recorded one — elseAUTH_INVALID/USER_IDENTITY_MISMATCH. - Lock acquired at
{state tree}/instance.yaml(atomicO_EXCL; a stale file with a dead PID is replaced) — elseWORKER_ALREADY_RUNNING, naming the profile. A worker on a different profile does not collide.
config.yaml schema and
Reference for process-level remediation.
Per-section behavior
For each configured section the worker dispatches to the section’sjob_type. The runtime
behavior — what gets attached, what gets polled, what gets posted — is documented on the
corresponding job-type page:
session_agent_harness→ Overview.
Shutdown
SIGINT (Ctrl-C) and SIGTERM trigger a graceful shutdown:
- Stop accepting new events.
- Ask each running section to drain — the per-job-type teardown is documented on the corresponding job-type page.
- Remove the tree’s
instance.yamland exit 0.
SIGKILL skips the graceful path; the next start on that profile will replace the stale instance.yaml
and run each job type’s recovery routine.
In --tui mode, a SIGINT/SIGTERM delivered from outside the process — for example, from
blobhub-worker instance stop — now drains exactly as headless does: it waits
for the in-flight run to finish however long that takes, with no cap of its own. Previously a --tui
worker took the default disposition on those signals instead: no drain, no lock release, an agent turn
cut mid-flight.
Pressing q at the keyboard is a different path with a different need: it drains too, but bounded to 10
seconds before cancelling the in-flight run, since someone at the terminal wants it back promptly. Reach
for instance stop rather than q when you want to stop a --tui worker without risking an in-flight
turn.
Exit codes
start exits non-zero on any preflight failure, on any unhandled fatal error, and on user-requested
shutdown after surfacing the cause. Diagnostic detail is emitted to the structured log stream
(stderr by default; the worker.jsonl file under --tui) as a worker_exit JSON record.
Full code catalog: Reference.

