Skip to main content
The worker touches two directories. Credentials live in ~/.blobhub/, shared with blobhub-cli. Everything else lives under ~/.blobhub-worker/ (mode 0700), divided into one state tree per profile. YAML is used for every file a user might want to read or edit. This page covers the generic files. Per-section runtime state lives under a tree’s jobs/, in subdirectories owned by each section’s job_type and documented on that job type’s filesystem page.

The two trees

Nothing lives at the top level of ~/.blobhub-worker/. A tree is named after where its key came from — a stored profile, or a one-way digest of a directly supplied key — so two credentials can never share one. See Authentication and Profiles for why that is keyed on origin rather than on the resolved profile name. Run blobhub-worker whoami to print the tree a given invocation resolves to. For a key-derived tree you will need it: the digest is not guessable, and config.yaml has to be authored inside the tree before the first start.

File-by-file

~/.blobhub/credentials.yaml — the shared store

Written by blobhub-worker login and by blobhub login alike, mode 0600. A default: naming one profile, and a profiles: map:
The worker refuses to start if the file is readable by group or other (INSECURE_CREDENTIALS_PERMISSIONS) — it is never repaired for you. An absent file is not an error: a key supplied by --api-key or BLOBHUB_API_KEY needs no file at all.

identity.yaml — durable identity, per tree

The user this tree’s credential resolves to. Recorded on the tree’s first start if it is not there already, and compared on every start after; a mismatch is USER_IDENTITY_MISMATCH.
Absence stopped being an error in v0.5.0. Because the store is shared, blobhub login --profile ci can create a profile this worker has never seen — refusing its first start would tell you to run a login you had just run. IDENTITY_NOT_FOUND now means only that the file is present and unreadable.

instance.yaml — the lock, per tree

Atomically created at start with O_EXCL and removed on clean shutdown.
A stale file naming a dead PID is replaced on the next start. A live PID is checked further: if it belongs to a worker, start refuses with WORKER_ALREADY_RUNNING, naming the profile; if it is alive but belongs to some other program — the PID was recycled — the lock is reclaimed the same as a dead one, rather than blocking the tree forever. Because the lock lives inside the tree, two workers on different profiles never contend — that is the point of the layout. blobhub-worker instance ls|show|stop read this file (and the tree around it) to inventory and stop the workers on this machine — see blobhub-worker instance.

config.yaml — sections

User-edited, one per tree. Full reference — generic blocks plus the per-section session block and per-agent settings — is on the Configuration page. The shape of each section depends on its job_type. Because it lives inside the tree, --profile prod and --profile staging read different configurations and watch different sessions. INVALID_CONFIG names the full path it looked at, which is the quickest way to learn where a key-derived tree is.

logs/{instance_id}/worker.jsonl

Structured log, one JSON record per line. Captures preflight — including the worker_resolved record naming the profile and tree — section dispatch (attach / refuse), transient API errors, and shutdown. Per-section detail goes to a section-scoped log under that section’s jobs/ subtree. This file is written only when the worker runs with --tui — the TUI redirects the log stream to keep the terminal clean. In headless mode the same records go to stderr instead and no file is written. Log rotation is not implemented; the file grows for the life of the instance.

The jobs/ subtree

Each running section gets a directory under its tree’s jobs/. The naming convention and the contents are owned by the section’s job_type. Today the only job type is session_agent_harness, which lays out:
A job type’s subtree isn’t necessarily flat: session_agent_harness nests one directory per /-separated segment of a thread’s alias, so a section scoped to part of a session (session_prefix) ends up with directories several levels deep under threads/. Full reference: Session Agent Harness → Filesystem layout.

Migrating from a pre-profiles layout

Before v0.5.0 these files sat flat at the top of ~/.blobhub-worker/, with the credential in its own credentials.yaml there. The first start after upgrading moves them into profiles/default/ and the credential into the shared store, printing what it moved. See Authentication and Profiles for the two cases it refuses and the one that needs a follow-up.

See also