Runtime Commands
Runtime commands bind Caracal mandates to processes. They require caracal.toml and perform OAuth 2.0 token exchange directly with the STS. No CARACAL_ADMIN_TOKEN is needed — they use the application credentials from caracal.toml.
caracal run
Section titled “caracal run”Inject resource tokens into a subprocess’s environment and run it.
caracal run -- <command> [args...]The -- separator is optional but recommended when <command> accepts flags that would be parsed by caracal. All arguments after -- are passed verbatim to the subprocess.
What it does
Section titled “What it does”- Loads
caracal.toml. Exits with an error if the file is not found. - Checks MCP governance rules (see below).
- For each entry in
credentials[], exchanges the application’s client credentials for a resource token at the STS with a 60-minute TTL. - For each entry in
optional_credentials[], attempts the same exchange but logs a warning on failure rather than exiting. - Spawns the subprocess with the tokens injected as named environment variables.
- Forwards stdin, stdout, and stderr to the subprocess unchanged.
- Exits with the subprocess’s exit code.
Token exchange
Section titled “Token exchange”Each credential exchange posts to {zone_url}/oauth2/token using the zone_id, application_id, and app_client_secret from caracal.toml. The resulting token is a short-lived ES256-signed mandate. It is injected into the subprocess’s environment under the variable name specified by the credential’s env field.
Step-up challenges
Section titled “Step-up challenges”If the STS returns interaction_required, caracal run handles it automatically:
- Writes a JSON notice to stderr:
{"resource": "resource://my-api", "challenge_id": "chall_abc123", "reason": "step_up_required"}
- Polls
GET /step-up/{challengeId}every 2 seconds for up to 5 minutes. - Once the challenge is satisfied (e.g., the user completes an MFA prompt), retries the token exchange.
- If the 5-minute window expires without satisfaction, exits with code
1.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 | Subprocess exited successfully |
1 | Credential acquisition failed, or MCP governance blocked the command |
2 | Subprocess exited with a non-zero exit code |
127 | Failed to spawn the subprocess |
128 + N | Subprocess killed by signal N |
Configuration
Section titled “Configuration”Credentials are declared in caracal.toml. A minimal setup with one required token:
zone_url = "http://localhost:8080"zone_id = "zone_abc123"application_id = "app_def456"app_client_secret = "secret_xyz"
[[credentials]]env = "RESOURCE_TOKEN"resource = "resource://my-api"caracal run -- python3 agent.py# agent.py reads os.environ['RESOURCE_TOKEN']Multiple resources, with one optional:
[[credentials]]env = "PAYMENTS_TOKEN"resource = "resource://payments-api"
[[credentials]]env = "INVENTORY_TOKEN"resource = "resource://inventory-api"
[[optional_credentials]]env = "ANALYTICS_TOKEN"resource = "resource://analytics-api"on_failure = "warn"continue_on_failure:
continue_on_failure = trueWhen false (the default), if any required credential exchange fails the subprocess is not spawned and caracal run exits with code 1. When true, the failure is logged and the subprocess runs with the remaining tokens set. The environment variable for the failed credential is not set.
MCP governance
Section titled “MCP governance”caracal run detects when the subprocess command references an MCP server binary (mcp-server, fastmcp, @modelcontextprotocol). Configure governance behavior in caracal.toml:
[mcp_governance]mode = "block" # "block" or "log""block": writes a JSON notice to stderr and exits with code1before spawning the process."log": writes the notice and continues.
This lets you enforce that MCP servers only run through Caracal-governed wrappers.
caracal credential read
Section titled “caracal credential read”Print a short-lived token for a resource to stdout.
caracal credential read <resource>Exchanges the application credentials from caracal.toml for a 15-minute mandate scoped to <resource>. Outputs the token as a plain string to stdout.
| Argument | Description |
|---|---|
<resource> | Resource identifier, e.g., resource://my-api or resource_abc123 |
Use cases:
Testing an API endpoint manually:
TOKEN=$(caracal credential read resource://my-api)curl -H "Authorization: Bearer $TOKEN" http://my-api.internal/toolFetching a token in a script where 15 minutes is the right TTL (shorter than caracal run’s 60-minute tokens):
TOKEN=$(caracal credential read resource://my-api)# Use $TOKEN for one request or a short-lived operationStep-up:
caracal credential read does not handle step-up automatically. If the STS requires interaction, it exits immediately and writes a JSON error to stderr:
{"error": "interaction_required", "challenge_id": "chall_abc123"}Use caracal run when you need automatic step-up polling.