Running Bullpen CLI headlessly (24/7 bots)¶
Bullpen CLI's auth flow is RFC 8628 device-auth — it requires an interactive browser on first login. For 24/7 bots that don't have a browser, the CLI supports a portable credential file workflow: log in once on a workstation, copy the encrypted credential file to your bot host, and run headless until the refresh token expires (~30 days).
This is the recommended path until a true server-side API-key auth surface is available.
What gets copied¶
~/.bullpen/credentials.json.enc — AES-256-GCM-encrypted. The encryption
key is derived from a stable embedded secret shared across official CLI
builds, the OS username, and the BULLPEN_HOME path.
For the file to decrypt on a different host, the username and home path components of the key derivation must match. Two knobs control this:
BULLPEN_USERNAME_OVERRIDE— overrides the OS username in the key derivation. Set this to a stable identifier (e.g.botops) on both the workstation and the bot host.BULLPEN_HOME— overrides the default~/.bullpendirectory. Set this to the same path on both hosts (e.g./var/lib/bullpen-bot).
Without these overrides, the credential file is bound to your workstation's OS user and home dir and will fail to decrypt on the bot host.
Setup¶
1. Workstation (interactive login)¶
export BULLPEN_USERNAME_OVERRIDE=botops
export BULLPEN_HOME=/tmp/bullpen-botops # any stable path
mkdir -p "$BULLPEN_HOME"
bullpen login # opens browser, completes device-auth
bullpen doctor auth # confirm Token + Refresh Token both Valid
2. Bot host (headless)¶
# Match the workstation's overrides exactly.
export BULLPEN_USERNAME_OVERRIDE=botops
export BULLPEN_HOME=/var/lib/bullpen-bot
mkdir -p "$BULLPEN_HOME"
# Copy the encrypted credential file (and the Turnkey keypair) over.
scp workstation:/tmp/bullpen-botops/credentials.json.enc "$BULLPEN_HOME/"
scp -r workstation:/tmp/bullpen-botops/keys "$BULLPEN_HOME/"
# Verify decryption + token health.
bullpen doctor auth
If doctor auth shows Token: Valid and Refresh Token: expires in N
days, you're set. The bot can run indefinitely until the refresh token
expires.
3. Refresh-token rotation¶
The bot's refresh token has a ~30-day TTL. Bullpen's CLI auto-rotates the
refresh token on every /api/auth/refresh call, so a bot that runs at
least one authenticated command every ~25 days stays online indefinitely
without a re-login.
Check days remaining at any time:
If the refresh token does expire, repeat the workstation step (run
bullpen login interactively) and re-copy the new credentials.json.enc
to the bot host.
Security tradeoffs¶
The portable credential file gives the bot host full account power — it can place trades, withdraw funds (within the existing CLI's restrictions), and access the same surface as the human operator.
- Lock down file permissions: the credential file is
0o600by default. Make sureBULLPEN_HOMEis on a filesystem only readable by the bot's OS user. - Don't share
BULLPEN_USERNAME_OVERRIDE-bound files between distinct bots. Each independent bot identity should run a separatebullpen logincycle with its own override identifier. - Don't commit the credential file to git. It's encrypted, but the embedded secret is shared across all CLI users — anyone who can also guess your username override + bullpen home can decrypt it.
- Rotate the override + re-login if you suspect the file has leaked.
Server-side API-key auth is planned so you can mint scoped, revocable credentials from a workstation and use them headlessly without copying any files. Until then, the portable credential workflow is the recommended path.
Troubleshooting¶
Bundle: NOT decryptable — keypair mismatch detected→ theBULLPEN_USERNAME_OVERRIDEorBULLPEN_HOMEdiffers between the workstation and the bot host. Double-check both env vars.Token: Expired (run: bullpen login)andRefresh Token: Expired→ the refresh token TTL elapsed. Re-run the workstation login + re-copy.bullpen doctor authreportsCredentials: Not found→ the credential file didn't make it across, orBULLPEN_HOMEis pointing somewhere else.
End.