Skip to main content
Five commands decide who the CLI is. login stores a credential, whoami reports the identity a command would run as right now, logout removes one, and profile ls / profile use list and switch between the credentials you have stored. A credential is a profile — an API key, the API URL it belongs to, and optionally a realtime (WebSocket) URL — kept in ~/.blobhub/credentials.yaml, written atomically at mode 0600 and refused on load if any group or other permission bit is set. The CLI never repairs the mode for you, and it never prints a key: not in command output, not in an error message, not in the --json payload.
That file is shared with blobhub-worker. One login on a machine serves both binaries, and both read the same default: and profiles: map. Three consequences worth knowing before you run the commands below: logout --all removes the worker’s credential too; profile use moves which profile a bare blobhub-worker start picks up; and any write to this file, by either binary — this CLI’s login, logout and profile use alike, the worker’s own login, or a one-time pre-profiles migration on the worker’s start — rewrites the whole file from what it read. From an older release of either binary, that rewrite silently drops realtime_url from every stored profile, not only the one being touched — worth knowing if the two binaries are not upgraded together on one machine. The worker also keeps a separate body of local state per profile, which is why it — unlike this CLI — cares which name you log in under.
In CI you need none of these commands. BLOBHUB_API_KEY resolves on its own, with no credentials file present at all and with no warning — see Configuration for the full precedence ladder. The other option is to script login itself: --api-key-stdin (or --api-key-env) with --no-input stores a validated profile with no terminal involved, which is worth the extra step when the automation needs a named, stored identity rather than a key that lives only in one process’s environment.

blobhub login

Authenticate and store the result as a named profile — interactively, or scripted with no terminal at all.

Synopsis

--api-key, --api-url and --realtime-url each take exactly one of their three sources — the literal flag, its -stdin sibling, or its -env <VAR> sibling — and the three are mutually exclusive: naming two for the same value, or claiming stdin with more than one -stdin flag anywhere on the line, is a usage error, exit 2, checked before anything is read. --api-key-env and its two siblings always take the variable’s name; none has a bare form that falls back to a fixed variable. Nothing in this workspace opens a WebSocket with the realtime URL yet — it is only stored and reported, so it is in place when something does.

What it does

  1. Resolves the API key, API URL, and realtime URL, each independently from its own three sources. A source that resolves to an empty value — an empty literal, an empty pipe, or a -env variable that is unset or blank — fails immediately, as INPUT_REQUIRED (or ENV_VAR_NOT_SET for the variable case).
  2. Prompts for whatever the API key and API URL still lack — but only when --no-input is absent, no -stdin flag was used anywhere on the line, and stdin is a real terminal. Off that path, an unclaimed API URL silently takes https://api.blobhub.io/v1 with no prompt and no error; an unclaimed API key is the one case with no such fallback (see step 4). The realtime URL is never prompted for, on any path — supply it with a flag or leave the profile without one.
  3. When prompting is allowed, asks for the API URL on stderr first, offering that same default; press Enter to take it. Then asks for the API key without echoing it, so it never appears on screen or in your shell history.
  4. Fails with INPUT_REQUIRED if the key is still unresolved here — no source claimed it and prompting wasn’t available — or if you were prompted for it and entered nothing. Either way, this happens before any network call.
  5. Validates the key against GET /v1/users/me. A rejection there is AUTH_INVALID.
  6. Writes the profile into ~/.blobhub/credentials.yaml at 0600 and prints the identity it resolved:
Under --json the payload carries profile, url, realtime_url, and user — the record GET /v1/users/me returned — plus the envelope’s schema_version. The key is not among them, here or anywhere else. realtime_url here always reports a concrete value, falling back the same way the flag does; the stored profile only gets a realtime_url entry when one was actually supplied — nothing defaults it in credentials.yaml itself. The first profile you store becomes the default. Later logins add or replace a profile but leave the default where it is; move it with blobhub profile use. Unlike blobhub-worker, login has no identity-change guard. Logging in again — under the same profile name or a new one — simply replaces or adds a profile, whoever the new key belongs to. A CLI switches identities by design; a worker bound to one identity for the life of a process does not. Because the store is shared, that has a consequence outside this binary: re-logging into a name a worker is pinned to is caught at its next start, as USER_IDENTITY_MISMATCH. Nothing is lost — that is the right place for it to surface, since the worker’s stored cursors belong to the old identity.

Errors

That last row is worth reading twice: CREDENTIALS_NOT_FOUND covers a file that is present but unusable, not only one that is absent.

Using a service-account key

login doesn’t distinguish a human’s key from a service account’s — store one the same way, under whatever --profile name fits: blobhub login --profile ci, pasting the account’s key at the prompt. The identity line it prints resolves to the account’s own name and id, and every command you later run under that profile — --profile ci, or BLOBHUB_PROFILE=ci — acts as the account, not as whoever minted the key. See blobhub whoami below for what else is different about a key scoped this way.

blobhub logout

Remove a stored profile, or every profile with --all.

Synopsis

What it does

Removes the named profile — or the default one, when you name none — and rewrites the credentials file. With --all, every profile goes and the default is cleared. One line is printed per profile removed:
Under --json the payload carries removed, the list of profile names that went — empty when there was nothing to remove. logout --all also removes any credential blobhub-worker was using, since there is one store. Its next start then fails CREDENTIALS_NOT_FOUND — for a reason recorded in this binary’s history, not its own. Removing the default profile leaves the installation with no default at all, and nothing chooses a new one: a later login sets the default only when no other profile exists. Until you run blobhub profile use, every command must name its profile with --profile or BLOBHUB_PROFILE, or supply a key some other way.

Errors

logout --all against no credentials file at all is not an error: nothing is removed, nothing is printed, and the exit code is 0.

blobhub whoami

Show the resolved identity, profile, and key scope.

Synopsis

What it does

Resolves a key, a URL and a realtime URL exactly as every other command does, calls GET /v1/users/me with them, then probes GET /v1/users/me/orgs to characterise the key’s scope, and prints five lines:
Under --json the payload carries profile (null when no profile was involved), url, realtime_url, user and scope, plus the envelope’s schema_version. This is the command to run when you are unsure which credential a command will pick up. Profile: (none) means no profile was involved at all — the key came from --api-key or BLOBHUB_API_KEY.

The scope line

A restricted key is a valid key. By the time the probe runs, GET /v1/users/me has already accepted it — the probe is enrichment, not authentication. restricted is the expected reading for exactly the org- and blob-scoped keys the platform recommends for automation, and such a key drives every command here except the admin-scoped reads. unknown says nothing about the key either. It means the probe itself failed — a network error, or a server error on that one route — so the CLI reports what it observed rather than guessing full. A service account’s key is a user-scoped key — the kind Create API Key mints when you target it directly — and it reports full here too, for the same reason a personal key does: the probe only needs the key to authenticate as a user, not to carry any particular role. Don’t read full as “more powerful” than restricted; it means “scoped to a user,” nothing about capability. What that scope still can’t do is change who can access what. Ten operations — minting and revoking API keys, storing and deleting credentials, granting and revoking memberships, creating and retiring accounts, minting an acting token, and changing an org’s or blob’s visibility — require a human behind the request, and a service account is never one. The same key that reports full here gets 403 from every one of them, regardless of the role it was minted with. Everything else is unaffected: blobhub workflow deploy, scheduler deploy, every push, pull and execute in this binary is ordinary product work and runs exactly as it does under a personal key. See The Access Perimeter for the full model and for impersonation, the way across it.

Errors

blobhub profile ls

List stored profiles, marking the default.

Synopsis

Takes no flags.

What it does

Reads the credentials file — no network call — and prints one line per profile in name order, with * against the default and a [realtime: …] suffix on any profile that has one stored:
Under --json the payload carries profiles — one entry per profile holding name, url, realtime_url (null when that profile never stored one) and a boolean default — and default, the default profile’s name or null. No key appears in either. No profiles, or no credentials file at all, prints nothing and exits 0.

Errors

blobhub profile use

Set the default profile.

Synopsis

What it does

Points the credentials file’s default at an already-stored profile, and prints:
Under --json the payload carries default, the profile name now set as the default. It stores nothing new: the profile must exist. This is how you recover a default after logout removed it, and how you switch between environments without re-entering a key. It also moves which profile a bare blobhub-worker start resolves — and with it which configuration, sessions and cursors that worker picks up. The worker announces the profile it resolved on its first log line so the change is visible rather than inferred, but if a worker runs on this machine, prefer naming its profile explicitly in its service definition.

Errors

See also

  • Worker authentication — the other half of the shared credential store.
  • Concepts — what is shared across the binary and what belongs to a blob domain.
  • Configuration — the credentials file these commands write, and the precedence ladder above it.
  • Service Accounts — what a service account is and how to create one.
  • Provisioning — worked recipes for CI, Docker, Kubernetes, systemd, and Ansible.
  • Create API Key — where an org- or blob-scoped key comes from.
  • Utilitiesdoctor diagnoses a credential these commands cannot explain.
  • Error codesAUTH_INVALID, PROFILE_NOT_FOUND and every other code above, with its remediation.