Run an Agent with caracal run
caracal run exchanges the current application’s ambient mandate for a per-resource per-call token, injects it into the subprocess environment as RESOURCE_TOKEN, and runs the specified command. The subprocess receives a valid 60-minute mandate without needing to implement the token exchange itself. If the exchange triggers a step-up challenge, caracal run polls until the challenge is satisfied or times out.
Prerequisites
Section titled “Prerequisites”caracalCLI installed and the stack running.- An application registered in the zone with a
client_secret. - The application has a grant for the target resource.
Basic usage
Section titled “Basic usage”caracal run -- my-script.shThe double dash (--) separates caracal run options from the subprocess command. The subprocess receives RESOURCE_TOKEN in its environment.
Required environment variables
Section titled “Required environment variables”| Variable | Description |
|---|---|
CARACAL_ZONE_ID | Zone to authenticate against |
CARACAL_APPLICATION_ID | The application performing the exchange |
CARACAL_CLIENT_SECRET | Application credential |
CARACAL_RESOURCE | Resource identifier to request a token for (e.g., resource://payments) |
Set them in your shell or .env:
export CARACAL_ZONE_ID=my-zoneexport CARACAL_APPLICATION_ID=app-abc123export CARACAL_CLIENT_SECRET=secret-xyzexport CARACAL_RESOURCE=resource://inventory
caracal run -- python process_inventory.pyRESOURCE_TOKEN in the subprocess
Section titled “RESOURCE_TOKEN in the subprocess”Inside the subprocess, read RESOURCE_TOKEN and use it as a bearer token:
#!/bin/bashcurl -H "Authorization: Bearer $RESOURCE_TOKEN" \ http://inventory:8080/itemsimport os, httpx
token = os.environ['RESOURCE_TOKEN']response = httpx.get('http://inventory:8080/items', headers={'Authorization': f'Bearer {token}'})The injected token is a per-call mandate — an ES256-signed JWT with a 60-minute TTL scoped to the specified resource. It is not the ambient mandate.
Request specific scopes
Section titled “Request specific scopes”By default, caracal run requests all scopes the application’s grant covers. To request a subset:
CARACAL_SCOPES="inventory:read" caracal run -- python read_only_script.pyOne-shot credential without running a subprocess
Section titled “One-shot credential without running a subprocess”caracal credential read prints a short-lived (15-minute) token for a resource to stdout. Use it in scripts that manage token injection themselves:
TOKEN=$(caracal credential read resource://payments)curl -H "Authorization: Bearer $TOKEN" http://payments:8080/statusStep-up challenge handling
Section titled “Step-up challenge handling”If the active policy requires a step-up challenge for the target resource, caracal run receives an interaction_required response from the STS. It then:
- Prints the challenge ID and type to stderr.
- Polls the STS every 2 seconds, waiting up to 300 seconds for an external system to satisfy the challenge.
- If satisfied, retries the token exchange with the
challenge_secretand injects the resulting token. - If the timeout expires, exits with a non-zero status.
$ caracal run -- python submit_transfer.pyStep-up challenge required (mfa).Challenge ID: chall-abc123Waiting for challenge to be satisfied (max 300s)...[poll] 2s elapsed[poll] 4s elapsedChallenge satisfied. Token exchange complete.The challenge must be satisfied by an external process — for example, the user completing an MFA flow in your portal. See Step-Up Re-Authentication for the satisfaction API.
Multiple resources
Section titled “Multiple resources”caracal run supports a single resource per invocation. For workflows that need tokens for multiple resources, run separate caracal credential read calls and inject them manually:
TOKEN_A=$(caracal credential read resource://payments)TOKEN_B=$(caracal credential read resource://reporting)
PAYMENT_TOKEN=$TOKEN_A REPORT_TOKEN=$TOKEN_B \ python generate_report.pyExit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 | Subprocess exited with code 0 |
| Non-zero | Subprocess exit code, or token exchange failed before subprocess started |
Token exchange failures (grant missing, policy deny, step-up timeout) print the error to stderr and exit with a non-zero code before the subprocess starts.
What to read next
Section titled “What to read next”- Integrate the TypeScript SDK — for services that need to manage tokens programmatically
- Step-Up Re-Authentication — satisfy step-up challenges from application code
- Issue Grants and Invitations — ensure the application has the right grant before running