A command-line client for the DBOS Conductor API.
dbosctl talks to DBOS-managed Conductor, a self-hosted Conductor, or a
self-hosted Conductor with OpenID Connect (OIDC) auth — the target is selected
by a named profile.
Status: early. The command surface below is what ships today (login, identity, and app listing); workflow management lands in a later milestone.
Install script. Detects your platform, verifies the download against the
release checksums, and installs to the first writable of /usr/local/bin,
~/.local/bin, or the current directory:
curl -sSfL https://raw.githubusercontent.com/dbos-inc/dbos-ctl/main/install.sh | shSet VERSION=v0.1.0 to pin a release, or BIN_DIR=/somewhere to choose where it
lands. Releases also ship archives for linux, macOS, and Windows on amd64 and
arm64 if you would rather download one directly;
the binaries are statically linked, so they run anywhere, Alpine included.
With Go (1.24+), which builds from source:
go install github.com/dbos-inc/dbos-ctl/cmd/dbosctl@latestWith Nix:
nix run github:dbos-inc/dbos-ctl -- --help # run it without installing
nix profile install github:dbos-inc/dbos-ctlFor NixOS, add the flake as an input and use inputs.dbos-ctl.packages.${system}.default.
Intel Macs (x86_64-darwin) are not supported — nixpkgs dropped the platform in 26.11; use
the release binaries or go install there.
From a checkout:
make build # produces ./dbosctldbosctl version reports which of these you have: a release prints its tag, a
go install prints the module version, and a Nix or local build prints dev
with the commit it came from.
The binary is dbosctl, not dbos: the DBOS language SDKs ship their own
dbos entrypoints (the Python SDK installs a dbos console script), and two
different tools answering to one name on PATH is a silent, confusing failure.
The ctl suffix keeps this CLI unambiguous alongside any of them.
# DBOS-managed Conductor
dbosctl config set managed --managed
dbosctl login # opens the device-authorization flow
dbosctl whoami # confirm who you're logged in as
dbosctl app list
# A self-hosted Conductor with no auth
dbosctl config set local --auth none --url http://localhost:8090
dbosctl app list --profile local
# Registering an app
dbosctl app register <app-name> # must match app name in DBOS config
# Creating an API key
dbosctl api-key create <key-name> # typically provided to app via DBOS_CONDUCTOR_KEY env varConfiguration lives in config.yaml under your OS config dir
(~/.config/dbos/config.yaml on Linux, ~/Library/Application Support/dbos/ on
macOS). A profile is a named bundle of settings; config set creates or updates
one, touching only the fields you pass:
dbosctl config list # all profiles, marking the current one
dbosctl config show [profile] # one profile's settings
dbosctl config use <profile> # set the default profile
dbosctl config set <profile> ... # create or updateThere are three common shapes:
| Shape | How to create | Auth | Identity |
|---|---|---|---|
| DBOS-managed | config set x --managed |
Auth0 JSON Web Token (JWT) | real |
| Self-hosted + OIDC | config set x --url http://host:8090 --issuer <url> --client-id <id> [--audience <aud>] |
user JWT or dbos_ key |
real |
| Self-hosted, no auth | config set x --url http://host:8090 |
none | always local |
A profile must target either DBOS-managed Conductor (--managed) or a
self-hosted Conductor (--url); the two are mutually exclusive. --managed
points at cloud.dbos.dev and derives everything else (the /conductor URL,
bearer auth, and the Auth0 tenant) automatically. Passing
--issuer/--client-id implies bearer auth, so --auth is only needed for the
uncommon case of a self-hosted Conductor you
reach with a dbos_ API key but no OIDC login: --auth bearer. Because an API
key carries no user identity, give that profile an --org too.
dbosctl login # OIDC device flow against the profile's issuer; stores a token
dbosctl logout # forget the stored token for the current profilelogin runs the device-authorization flow:
it prints a URL and a code, you approve in a browser, and the token is stored in
credentials.json (mode 0600) next to config.yaml, keyed by profile. Tokens
are refreshed automatically on expiry when the issuer returns a refresh token.
Two ways to bypass the flow:
DBOS_TOKEN— a bearer token used as-is for one invocation.dbos_…API keys — set asDBOS_TOKEN(or stored); sent verbatim. These authenticate machine-to-machine calls (e.g.app list) but carry no user identity, sodbosctl whoamineeds a user login, not a key.
| Command | Description |
|---|---|
dbosctl login / dbosctl logout |
Acquire / discard credentials for the current profile |
dbosctl whoami |
Show the logged-in identity (local on a no-auth target) |
dbosctl app list |
List applications in the org |
dbosctl app get <name> |
Show one application's details |
dbosctl app versions <name> |
List an application's versions |
dbosctl app executors <name> |
List an application's connected executors |
dbosctl app metrics <name> |
List an application's metrics (--since, default 24h) |
dbosctl app register <name> |
Register an application |
dbosctl app update <name> |
Update tuning settings (e.g. --executor-timeout-secs, --private-mode) |
dbosctl app set-version <name> <version> |
Set the application's latest version |
dbosctl app delete <name> |
Delete an application (prompts to confirm; --force required when non-interactive) |
dbosctl workflow list |
List workflows, filterable (--status, --name, --since 1h, …); returns all matching by default, --limit/--offset to bound |
dbosctl workflow get <id> |
Show a workflow's details (app-scoped, needs --app) |
dbosctl workflow steps <id> |
List a workflow's steps |
dbosctl workflow events <id> |
List a workflow's events |
dbosctl workflow cancel|resume|delete <id>... |
Mutate one or more workflows (variadic; - reads IDs from stdin; --children on cancel/delete) |
dbosctl workflow fork <id> |
Fork a workflow into a new execution (prints the new ID; --start-step, --new-id) |
dbosctl queue list | get <name> |
Inspect queue definitions (app-scoped, needs --app) |
dbosctl schedule list | get <name> |
Inspect scheduled workflows |
dbosctl schedule pause | resume <name> |
Pause / resume a schedule |
dbosctl schedule trigger <name> |
Fire a schedule now (prints the started workflow ID) |
dbosctl schedule backfill <name> --since --until |
Replay a schedule over a window (prints the started workflow IDs) |
dbosctl api-key list |
List API keys (aliases: token, apikey) |
dbosctl api-key create <name> |
Create an API key — prints the secret once; scope with --app/--permission |
dbosctl api-key delete <name> |
Delete an API key |
dbosctl permission list |
List grantable permissions |
dbosctl config list | show | use | set |
Manage profiles |
dbosctl version (or --version) |
Print version information |
Each setting is resolved flag → environment → profile, so a flag always wins and the profile is the fallback:
| Setting | Flag | Env |
|---|---|---|
| Profile | --profile |
DBOS_PROFILE |
| Conductor URL | --url |
DBOS_URL |
| Organization | --org |
DBOS_ORG |
| Application | -a, --app |
DBOS_APP |
| Bearer token | — | DBOS_TOKEN |
| Output format | -o, --output |
— |
Flags are scoped to the command that uses them, so pass them after the
command name (dbosctl app list --org acme), and each command's --help lists
only the flags it honors.
Human-readable tables by default; -o json emits the raw API shape for
scripting (never truncated or reprojected):
dbosctl app list # aligned table
dbosctl app list -o json # raw JSON array
dbosctl whoami -o json # raw UserProfileCommands with a natural ID also accept -o ids (one ID per line), for piping —
a literal - reads IDs from stdin:
dbosctl workflow list -a myapp --status PENDING -o ids | dbosctl workflow cancel -a myapp -| Code | Meaning |
|---|---|
0 |
Success |
1 |
General error |
2 |
Usage error (bad flags/arguments) |
3 |
Authentication required (HTTP 401) — run dbosctl login |
4 |
Not found (HTTP 404) |
130 |
Interrupted (Ctrl-C) |
make generate # regenerate the API client from the vendored OpenAPI spec
make build # build ./dbosctl
make test # unit tests
make lint # golangci-lint
make snapshot # build all-platform artifacts without publishingThe generated client (internal/api) is committed; CI fails on spec drift
(make generate must be a no-op). Integration tests are tagged integration
and stand up real Conductor + Postgres in throwaway containers — see
make test-integration and .env.example for the required license key and
image/checkout settings.
git checkout main && git pull # release from origin/main
make lint && make test # CI reruns these, but fail locally first
make snapshot # optional: build all-platform artifacts, no publish
git tag -a v0.9.0 -m "v0.9.0"
git push origin v0.9.0