Skip to main content
datumctl is built to run unattended. Any CI system — GitLab CI, CircleCI, Jenkins, Buildkite, Argo, a cron job, or a container entrypoint — can authenticate as a service account, run commands, and branch on machine-readable results without a human at the keyboard.
On GitHub Actions, you rarely need to wire this up by hand. The setup-datumctl-action installs the CLI and authenticates a service account in one step. This page is for every other pipeline, and for understanding what that action does under the hood.

Authenticate as a service account

Interactive browser login is the default, and it is the wrong choice for a pipeline — there is no browser to open and no prompt a build agent can answer. Instead, authenticate with a service account credentials file using datumctl login --credentials:
datumctl login --credentials ./datum-credentials.json
This performs a non-interactive login: datumctl signs a JWT with the service account’s private key, exchanges it for an access token, and stores the resulting session so subsequent commands are authenticated. No browser, no device code, no --no-browser device flow.
Create the service account and download its credentials JSON before you can log in this way. See Service accounts for creating an account, choosing a Datum-managed key, and downloading the one-time credentials file. Treat that file like a password — inject it from your CI secret store, never commit it.
For a self-hosted or otherwise custom environment, point login at that environment’s auth host:
datumctl login --credentials ./datum-credentials.json \
  --hostname auth.example.com
1

Store the credentials JSON as a secret

Save the full contents of the service account credentials file as a CI secret (for example DATUM_SA_CREDENTIALS). Do not inline it into a script or commit it to the repository.
2

Materialize it to a file at runtime

datumctl login --credentials takes a path, so write the secret to a file in the job’s workspace first. Prefer a path the runner cleans up automatically.
printf '%s' "$DATUM_SA_CREDENTIALS" > "$RUNNER_TEMP/datum-credentials.json"
datumctl login --credentials "$RUNNER_TEMP/datum-credentials.json"
3

Run your datumctl commands

Once logged in, the session is cached and later steps in the same job are authenticated.
datumctl get projects --organization "$DATUM_ORG" -o json

A generic pipeline step

The same three moves work in any CI system. A GitLab CI job, for example:
deploy:
  image: alpine:latest
  script:
    - printf '%s' "$DATUM_SA_CREDENTIALS" > /tmp/datum-credentials.json
    - datumctl login --credentials /tmp/datum-credentials.json
    - datumctl apply -f ./project.yaml --organization "$DATUM_ORG"
Base64-encode the secret if your CI strips newlines or mangles multi-line values — the credentials JSON contains a PEM private key with embedded newlines. Decode it back to a file before calling datumctl login. (The GitHub Action accepts raw JSON or base64 for exactly this reason.)

Use auth get-token as a credential helper

When another tool in your pipeline needs a raw bearer token to call the Datum API directly, datumctl auth get-token prints one for the active session on stdout:
TOKEN=$(datumctl auth get-token)
curl -H "Authorization: Bearer $TOKEN" https://api.datum.net/...
datumctl handles refresh for you: if the stored token has expired, get-token uses the stored refresh material to obtain a new one before printing, so the value you capture is always fresh. This makes it a natural credential helper for scripts and HTTP clients that live alongside datumctl in a job.
Most commands never need this — datumctl authenticates its own requests automatically. Reach for auth get-token only when a separate process needs the token. For its Kubernetes ExecCredential output mode, see using datumctl with kubectl.

Get machine-readable failures with —error-format=json

By default datumctl prints errors as human-readable text on stderr. In a pipeline you want to branch on failures programmatically, so pass the global --error-format=json flag to get a single-line JSON envelope instead:
datumctl get projects --organization does-not-exist --error-format json
{
  "error": {
    "message": "the organization \"does-not-exist\" was not found",
    "hint": "Run 'datumctl get organizations' to see organizations you can access.",
    "retryable": false
  }
}
--error-format is a persistent flag available on every command, and it only shapes errors on stderr — successful output on stdout is still controlled by -o. Read the retryable field to decide whether to back off and retry, and code/hint to decide what to do next. For the full envelope schema and the allowed values (human, json, yaml), see Output formats & scripting.

Branch on exit codes

datumctl follows conventional exit-code semantics, so a pipeline can gate on $?:
Exit codeMeaning
0Success.
1A general error — unknown command, invalid flag value, authentication failure, or a failed API request.
>1A lower-level fatal error from the underlying request machinery.
datumctl diff is the exception: it exits 0 when there are no differences, 1 when there are, and >1 on error — which makes it a useful gate before a write.
datumctl diff -f ./project.yaml --organization "$DATUM_ORG" \
  && datumctl apply -f ./project.yaml --organization "$DATUM_ORG"
datumctl delete runs without a confirmation prompt. In automation, gate destructive operations behind an explicit review step or a --dry-run=client preview. See Output formats & scripting for the full scripting checklist, including pinning --organization/--project on every call.

Credential storage on headless runners

On a workstation, datumctl stores your session in the operating system keyring (Keychain, Secret Service, and so on). CI runners and containers usually have no system keyring available, so datumctl transparently falls back to an on-disk file at:
~/.datumctl/credentials.json
The file is written with 0600 permissions but is not encrypted, so on a shared or long-lived runner you should treat it as sensitive. When the fallback engages, datumctl login prints a one-time warning to stderr, for example:
warning: system keyring unavailable (...); falling back to insecure file storage at /home/runner/.datumctl/credentials.json
This is expected and harmless on an ephemeral runner. To keep it clean:
  • Prefer ephemeral runners that are destroyed after the job, so the credentials file never outlives the build.
  • On a persistent or shared runner, log out at the end of the job so the file does not linger:
    datumctl logout --all
    
  • Point $HOME at a job-scoped, cleaned-up directory if your runner reuses home directories across builds, so each job gets its own credential file.
Last modified on July 2, 2026