# The CLI

`caas` is the caas.sh command line. Use it to run commands in new sandboxes, manage sandboxes and their snapshots, deploy MCP servers, push images, start workflows, connect credentials and manage billing from a terminal or a script. It calls the same API as the console, with an API key from your workspace.

## Install the CLI

The CLI needs Node 22 or later. Install the `@caascade/cli` package globally:

```sh
npm i -g @caascade/cli
```

> The package isn't on npm yet. Once it's published, this command installs it.

This installs the `caas` command. To list every command and option, run `caas help`, or `caas` on its own. Adding `--help` to any command does the same.

Some commands use other tools: `caas registry login` runs Docker, and `--from` uses Git.

## Connect the CLI to your workspace

The CLI doesn't sign in with your email and password. It uses an API key, which belongs to one workspace, together with that workspace's tenant ID.

### Create an API key

1. Sign in to the [console](https://console.caas.sh/) and open **Settings**.
2. Under **Workspace**, copy the **Tenant ID**.
3. Under **API keys**, enter a **Key name**, such as `my laptop`. If you're an owner and want to use `caas billing`, check **Billing access**.
4. Choose **Create key**, then copy the key. It starts with `ck_`, and the console shows it only once.

Owners and operators can create keys. Viewers can't.

### Configure the CLI

Save the tenant ID and key:

```sh
caas configure --tenant <tenant-id> --key <api-key>
```

The CLI saves them in `~/.config/caas/config.json`, or in `$XDG_CONFIG_HOME/caas/config.json` if `XDG_CONFIG_HOME` is set. Only you can read the file. Every later command uses what you saved. To check that everything works, list your sandboxes:

```sh
caas sandboxes list
```

Running `caas configure` again replaces the saved tenant ID and key.

### What a key can do

A new key can:

- list, create, start, stop and delete sandboxes, and run commands in them
- deploy MCP servers and manage their client tokens
- push and pull images in the registry
- start and control workflow runs
- read credentials, egress rules and the audit log

A key with **Billing access** can also read the balance and usage and add funds (`caas billing`).

A key can never do more than the member who created it. An operator's key can't use `caas billing`, and only an owner's key can connect or remove credentials or apps, or change egress rules. If the member who created a key leaves the workspace, the key stops working. To revoke a key, choose **Revoke** next to it under **Settings** → **API keys**.

## Choose a workspace

The CLI always works in one workspace: the one whose tenant ID you pass, with a key created in that workspace. A key from one workspace doesn't work in another.

To work in another workspace:

1. In the console, open the workspace card at the top of the sidebar and switch to that workspace.
2. Create an API key there, as in [Create an API key](#create-an-api-key), and copy that workspace's tenant ID.
3. Either save the new pair, which replaces the one you saved before:

   ```sh
   caas configure --tenant <other-tenant-id> --key <other-api-key>
   ```

   Or pass the pair to a single command and keep what you saved:

   ```sh
   caas sandboxes list --tenant <other-tenant-id> --key <other-api-key>
   ```

## Options for every command

| Option | What it does |
| --- | --- |
| `--tenant ID` | Use this workspace instead of the saved one. Pass `--key` with it. |
| `--key KEY` | Use this API key instead of the saved one. |
| `--api-url URL` | Use another API address. The default is `https://api.caas.sh`. It must use `https://`, except on `localhost`. `caas configure` saves it. |
| `--json` | Print the API's JSON response instead of the text output. |
| `--help` | Print every command and option. |

`caas run`, `caas mcp deploy`, `caas sandboxes exec`, `caas workflows run` and `caas billing add-funds` send their request with an idempotency key. If the CLI can't reach the API, the error names the key. Run the same command again with `--idempotency-key KEY` to retry without creating a second sandbox, command, run or payment.

### Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Success. |
| `1` | The request or the command failed. |
| `2` | The command line isn't valid, for example an unknown option, a missing value or a value out of range. Nothing was changed. |
| `3` | The API or the registry refused the key: it's revoked, from another workspace, or lacks the access the command needs. |

`caas run` and `caas sandboxes exec` exit with the command's own exit code when it's between 1 and 125, and with `1` for any other failure. Errors are printed to standard error as `caas: <message>`. When the workspace is out of funds, the error also links to the console page where an owner adds them.

## Sizes

`--size` sets a sandbox's vCPU, memory and disk. The default is `m`.

| Size | `--size` | vCPU | Memory | Disk |
| --- | --- | --- | --- | --- |
| XS | `xs` | 1/16 | 256 MiB | 2 GB |
| S | `s` | 1/4 | 1 GiB | 4 GB |
| M | `m` | 1/2 | 4 GiB | 8 GB |
| L | `l` | 1 | 6 GiB | 12 GB |
| XL | `xl` | 2 | 8 GiB | 16 GB |
| 2XL | `2xl` | 4 | 12 GiB | 20 GB |
| Custom | `custom` | 1 to 4 | At least 3 GiB per vCPU, at most 12 GiB | 2 to 20 GB |

For a custom size, add `--vcpu N --memory GIB --disk GB`, all whole numbers:

```sh
caas run --size custom --vcpu 2 --memory 6 --disk 12 -- node --version
```

A sandbox that runs your own image needs size M or larger. vCPU bills only while the sandbox computes; memory and disk bill while it runs. See [pricing](/pricing.html) for the rates.

## Run a command in a new sandbox

`caas run` creates a sandbox, runs one command in it, waits for the command to end, and prints its output:

```sh
caas run -- node --version
caas run --from . node task.js
caas run --name build-agent --size xl --from . node task.js
caas run --image worker:latest -- python task.py
caas run --from . --detach node long-task.js
caas run --ephemeral --egress-host api.example.com -- node --version
caas run --from . --expose 3000 -- node server.js
```

The command runs as an executable with its arguments, not through a shell. To use shell features, run a shell:

```sh
caas run --from . -- sh -c 'node build.js && node test.js'
```

Options can come anywhere before `--`, and everything after `--` is the command. Put `--` before a command whose arguments start with `--`.

Without `--image`, the sandbox uses the Default image: Alpine Linux with bash, coreutils, Node and Docker. It has no coding agents installed; install one in your command, or use your own image.

The command has a time limit of two hours, and runs once. When `caas run` is done, the sandbox keeps running until its idle stop. Delete it with `caas sandboxes delete` when you no longer need it.

| Option | What it does |
| --- | --- |
| `--from DIR` | Upload your code into `/workspace`. See [Upload your code](#upload-your-code). |
| `--image IMAGE` | Run in your own image from the registry. See [Push images to the registry](#push-images-to-the-registry). Can't be combined with `--from`. |
| `--name NAME` | Name the sandbox: up to 63 lowercase letters, digits and hyphens, starting and ending with a letter or digit, unique in the workspace. |
| `--size SIZE` | The sandbox's size. See [Sizes](#sizes). |
| `--egress-host HOST` | Allow outbound requests to this exact host. Repeat it for each host, up to 32. Other hosts are blocked unless you change the sandbox's [egress policy](#egress-policy). A command that starts with `npx` also gets `registry.npmjs.org`. |
| `--expose PORT` | Serve this port at the sandbox's own URL: 1024 to 65535, except 8788. See [Sandbox URLs](#sandbox-urls). |
| `--public` | With `--expose`: let anyone with the link open the URL, without an access token. |
| `--idle-stop MINUTES` | Stop the sandbox after this many minutes with no command, file transfer, MCP request or URL request: 5 to 240. The default is 15. |
| `--wake-on-request` | Let a command, a file transfer, an MCP request or a URL request start the sandbox again after you stopped it yourself. A sandbox that stopped while idle starts again on a request either way. |
| `--ephemeral` | Keep nothing between restarts: the sandbox starts fresh each time, and the sandbox takes no automatic or nightly snapshots. Can't be combined with `--from`. |
| `--detach` | Return as soon as the sandbox is created, without waiting for the command. |

With `--detach`, the CLI prints the new sandbox and the ID of its first command, `initial-agent-command`. Check on the command later:

```sh
caas sandboxes command <sandbox-id> initial-agent-command
```

### Upload your code

`--from DIR` uploads the committed Git tree of the repository at `DIR`, usually `.`, and unpacks it into `/workspace` when the sandbox first starts. You can use it with `caas run` and with `caas mcp deploy --stdio`.

- `DIR` must be the root of a Git repository with at least one commit.
- Commit your changes first. The CLI refuses a repository with uncommitted changes or untracked files. Files that Git ignores are never uploaded.
- The CLI refuses to upload a repository that tracks a sensitive-looking file: `.env` files, `.dev.vars`, `.npmrc`, `id_rsa`, `credentials`, or any `.pem` or `.key` file.
- The compressed upload can be at most 250 MiB.
- A sandbox started from an upload keeps its state, so you can't combine `--from` with `--ephemeral`. caas.sh keeps the upload for seven days; after that, your files live on in the sandbox's own snapshots.

## Manage sandboxes

`caas sandboxes` commands take the sandbox's ID, not its name. `caas sandboxes list` shows both.

### List and inspect

```sh
caas sandboxes list
caas sandboxes inspect <sandbox-id>
caas sandboxes events <sandbox-id>
```

| Command | What it does |
| --- | --- |
| `caas sandboxes list` | Lists every sandbox and MCP server that isn't deleted: its ID, name, kind (`agent` or `mcp`) and record state (`active` once it's created). `inspect` shows whether it's running. |
| `caas sandboxes inspect ID` | Shows the sandbox's details: its status, settings, size, URL, and usage, including what it costs per hour now and what it has cost today. |
| `caas sandboxes events ID` | Lists the sandbox's events, such as its starts and stops. |

### Start, stop and delete

```sh
caas sandboxes stop <sandbox-id>
caas sandboxes start <sandbox-id>
caas sandboxes delete <sandbox-id>
```

A sandbox that keeps state takes a snapshot as it stops, and starts again with its `/workspace`.

### Run commands in a sandbox

```sh
caas sandboxes exec <sandbox-id> -- node --version
caas sandboxes exec <sandbox-id> -- sh -c 'cd /workspace && ls -la'
caas sandboxes exec <sandbox-id> --timeout-ms 1800000 --detach -- node long-task.js
caas sandboxes command <sandbox-id> <command-id>
caas sandboxes cancel-command <sandbox-id> <command-id>
```

| Command | What it does |
| --- | --- |
| `caas sandboxes exec ID -- COMMAND [ARGS...]` | Runs a command in the sandbox, waits for it, prints its output and exits with its exit code. |
| `caas sandboxes command ID COMMAND_ID` | Shows a command's state, exit code and output. |
| `caas sandboxes cancel-command ID COMMAND_ID` | Stops a running command. |

`exec` takes these options before `--`:

| Option | What it does |
| --- | --- |
| `--timeout-ms MS` | The command's time limit in milliseconds: 1000 (1 second) to 7200000 (2 hours). The default is 600000 (10 minutes). |
| `--detach` | Print the command's ID and return without waiting. |

As with `caas run`, the command runs without a shell. A command sent to a sandbox that stopped while idle starts it again.

### Snapshots

A sandbox that keeps state takes a snapshot of `/workspace` every hour, when it stops, and every night, at 02:00 in the workspace's time zone unless you move it. You can also take your own.

```sh
caas sandboxes snapshot <sandbox-id> --name before-upgrade
caas sandboxes snapshots <sandbox-id>
caas sandboxes stop <sandbox-id>
caas sandboxes restore <sandbox-id> --snapshot <snapshot-id>
caas sandboxes delete-snapshot <sandbox-id> <snapshot-id>
caas sandboxes schedule <sandbox-id> --at 03:30 --time-zone Europe/Berlin
caas sandboxes schedule <sandbox-id> --off
```

| Command | What it does |
| --- | --- |
| `caas sandboxes snapshot ID [--name NAME]` | Takes a snapshot of a running sandbox. Without `--name`, it's named `manual`. Names follow the same rules as sandbox names. |
| `caas sandboxes snapshots ID` | Lists the snapshots: ID, name, kind, when each was taken, its size and which one is current. Then the bytes stored and the nightly schedule. |
| `caas sandboxes restore ID --snapshot SNAPSHOT_ID` | Restores a snapshot onto a stopped or failed sandbox and starts it. Stop the sandbox first. |
| `caas sandboxes delete-snapshot ID SNAPSHOT_ID` | Deletes a snapshot. |
| `caas sandboxes schedule ID --at HH:MM [--time-zone ZONE]` | Moves the nightly snapshot to this 24-hour time, in an IANA time zone such as `Europe/Berlin`. |
| `caas sandboxes schedule ID --off` | Turns the nightly snapshot off. |

Snapshots you take stay until you delete them. caas.sh keeps the newest 7 nightly snapshots. See [Sandboxes](/docs/sandboxes) for how snapshots work.

### Sandbox URLs

A sandbox can serve one HTTP port at its own address, `https://<label>.caa.sh`, where the label is an opaque name that reveals neither your workspace nor the sandbox's ID. The URL needs an access token unless you make it public.

```sh
caas sandboxes expose <sandbox-id> 8080
caas sandboxes url-token <sandbox-id> --ttl 600
caas sandboxes expose <sandbox-id> 8080 --public
caas sandboxes expose <sandbox-id> --off
```

| Command | What it does |
| --- | --- |
| `caas sandboxes expose ID PORT [--public]` | Serves `PORT` (1024 to 65535, except 8788) at the sandbox's URL, or changes the port. With `--public`, anyone with the link can open it. |
| `caas sandboxes expose ID --off` | Turns the URL off and revokes every access token for it. |
| `caas sandboxes url-token ID [--ttl SECONDS]` | Prints an access token for the URL, its expiry, and a `curl` command that uses it. `--ttl` is 60 to 86400 seconds; the default is 3600. |

Send the token as an `Authorization: Bearer` header. `caas sandboxes inspect` shows the URL. A request to the URL starts a sandbox that stopped while idle, and keeps it from stopping.

### Logs and outbound requests

```sh
caas sandboxes logs <sandbox-id>
caas sandboxes log-archives <sandbox-id>
caas sandboxes log-archive <sandbox-id> <archive-id> > runtime.jsonl
caas sandboxes egress <sandbox-id>
```

| Command | What it does |
| --- | --- |
| `caas sandboxes logs ID` | Prints the sandbox's recent runtime log. |
| `caas sandboxes log-archives ID [--cursor ARCHIVE_ID]` | Lists the log archives: each one's ID, time span, entries and size. Pass the printed `Next cursor` as `--cursor` for older ones. Archives are kept for 90 days. |
| `caas sandboxes log-archive ID ARCHIVE_ID` | Prints one archive as JSON Lines. |
| `caas sandboxes egress ID [--cursor N]` | Lists the outbound requests the sandbox tried to make, newest first. See below. |

Each line of `caas sandboxes egress` shows the time, method, host and path, whether the request was allowed or blocked, the rule that decided, the response status (`-` when the request failed), the bytes sent, and `key added` when a credential was added to it. Query strings, headers and bodies are never kept. caas.sh keeps the newest 1,000 allowed and 1,000 blocked requests for 7 days. Pass the printed `Next cursor` as `--cursor` for older ones.

## Deploy MCP servers

`caas mcp deploy` puts an MCP server online at its own endpoint, `https://mcp.caa.sh/<endpoint-id>`. Choose exactly one source:

```sh
caas mcp deploy --stdio "npx -y @modelcontextprotocol/server-filesystem /workspace"
caas mcp deploy --stdio "node server.js" --from .
caas mcp deploy --repo github.com/acme/server --ref main --command "npm start"
caas mcp deploy --image server:1.2.3
caas mcp deploy --image server:1.2.3 --transport http --port 3000 --command "node http.js"
```

| Option | What it does |
| --- | --- |
| `--stdio "COMMAND"` | Runs a server that speaks MCP over standard input and output. Add `--from DIR` to upload your code first. |
| `--repo URL` | Clones an HTTPS Git repository (`https://` is added if you leave it out) and runs `--command`, which defaults to `npm start`. |
| `--ref REF` | With `--repo`: the branch, tag or commit to deploy. |
| `--image IMAGE` | Runs your own image from the registry. Without `--command`, the image's own entrypoint and command run. |
| `--command "COMMAND"` | With `--repo` or `--image`: the command that starts the server. With `--image`, it replaces the image's entrypoint and command. |
| `--transport TRANSPORT` | With `--image`: `stdio` (the default) or `http`. |
| `--port PORT` | With `--transport http`: the port your server listens on, serving Streamable HTTP at `/mcp`. 1024 to 65535, except 8788. |

`--stdio` and `--command` take a quoted command line. The CLI splits it into words like a shell does, with quotes and backslashes, but doesn't run it through a shell. A command can have up to 32 words.

These options work as they do for `caas run`: `--name`, `--size` (with `--vcpu`, `--memory` and `--disk`), `--egress-host`, `--expose`, `--public`, `--idle-stop`, `--wake-on-request` and `--ephemeral`. A stdio server on your own image needs Node in the image. When a server from `--repo` or `--from` first starts and has a `package.json`, its production dependencies are installed.

The CLI prints the new server's details, including its endpoint (`endpointUrl`) and a first client token (`clientToken`) that can call every tool and doesn't expire. The token is shown only once. Clients send a token as an `Authorization: Bearer` header. A retry with the same `--idempotency-key` doesn't print the token again; create a new one with `caas mcp tokens create`.

### Deploy from a file

`caas mcp deploy -f FILE` (or `--file FILE`) deploys a server declared in a YAML or JSON file, the same format the console's **Deploy from a server file** page takes (under **MCP servers** → **Deploy server**):

```yaml
server: search-mcp
source:
  stdio: npx search-mcp
size: s
state:
  persistent: true
  snapshots: nightly at 02:00
idle: stop after 30m
tokens:
  - name: laptop-claude
    tools: [search.query, search.fetch]
  - name: ci-pipeline
    tools: [search.query]
    expires: 30d
egress: [api.search.example.com]
```

```sh
caas mcp deploy -f search-mcp.yaml
```

The CLI checks the whole file before it uploads or creates anything, creates the server and its tokens in one request, and prints the endpoint and each token's tools, expiry and secret once. The file declares the whole server, so only `--from` (with a `stdio` source and `persistent: true`), `--wake-on-request`, `--idempotency-key` and `--json` can be combined with `-f`. See [MCP servers](/docs/mcp) for every field.

### Client tokens

```sh
caas mcp tokens list <sandbox-id>
caas mcp tokens create <sandbox-id> --name "Claude Desktop"
caas mcp tokens create <sandbox-id> --name ci --expires-days 30 --allow-tool search.query
caas mcp tokens revoke <sandbox-id> <token-id>
```

| Command | What it does |
| --- | --- |
| `caas mcp tokens list ID` | Lists the server's client tokens: ID, name, whether each is active, revoked or expired, the tools it may call, and its calls and errors. |
| `caas mcp tokens create ID --name LABEL` | Creates a token and prints its secret once. |
| `caas mcp tokens revoke ID TOKEN_ID` | Revokes a token. The server keeps running. |

`create` takes these options:

| Option | What it does |
| --- | --- |
| `--name LABEL` | Required. A label for the client that uses the token. |
| `--expires-days DAYS` | When the token expires: 1 to 365 days. The default is 90. |
| `--allow-tool NAME` | Let the token call only this tool. Repeat it for each tool. Without it, the token can call every tool. |

## Push images to the registry

Your workspace has its own namespace in the caas.sh registry, `registry.caas.sh/<tenant-id>/`. Log Docker in with your saved key, then build and push a `linux/amd64` image:

```sh
caas registry login
docker build --platform linux/amd64 -t registry.caas.sh/<tenant-id>/server:1.2.3 .
docker push registry.caas.sh/<tenant-id>/server:1.2.3
caas mcp deploy --image server:1.2.3
```

`caas registry login` runs `docker login registry.caas.sh`, with your tenant ID as the username and your API key as the password. New keys can push and pull.

Wherever a command takes `--image`, you can write the image as `NAME[:TAG]` or `NAME@sha256:DIGEST`, optionally under `registry.caas.sh/` or `registry.caas.sh/<tenant-id>/`. The CLI looks a tag up in the registry (`latest` when you give none) and always deploys its digest. Images from other registries or other workspaces are refused: push them to your namespace first. See [Images and the registry](/docs/images).

## Run workflows

A workflow is a YAML or JSON file of steps. Each step runs in a sandbox of its own, and every finished step is a checkpoint you can resume from:

```yaml
workflow: hello
inputs:
  name: world
steps:
  - id: greet
    name: Write greeting
    run: echo "Hello, ${{ inputs.name }}" > greeting.txt
    outputs: [greeting.txt]
  - id: show
    name: Show greeting
    run: cat greeting.txt
    inputs: [greet]
```

```sh
caas workflows run hello.yaml --input name=caas --follow
caas workflows list
caas workflows show <run-id>
caas workflows pause <run-id>
caas workflows resume <run-id>
caas workflows resume <run-id> --from cp-1 --follow
caas workflows cancel <run-id>
```

| Command | What it does |
| --- | --- |
| `caas workflows run FILE` | Checks the file and its inputs, pins each image tag to its digest, starts the run, and prints its ID and a link to it in the console. |
| `caas workflows list` | Lists the runs: name and number, status, progress, cost and ID. |
| `caas workflows show RUN_ID` | Shows a run's steps: each one's state, machine and checkpoint, a failed step's exit code and log, and a running step's newest output. If the run can be resumed, it says how. |
| `caas workflows pause RUN_ID` | Lets the steps that are running finish, and starts nothing more. |
| `caas workflows resume RUN_ID` | Lifts a pause, or runs again the steps that didn't finish in a run that ended. |
| `caas workflows cancel RUN_ID` | Ends the run. |

| Option | What it does |
| --- | --- |
| `--input NAME=VALUE` | With `run`: sets one of the file's inputs. Repeat it for each input. Inputs the file declares without a default are required. |
| `--follow` | With `run`, `show` and `resume`: print each step as its state changes, and a running step's new output, until the run ends. The CLI exits with `1` unless the run succeeded. |
| `--from CHECKPOINT` | With `resume`: replay the run from that checkpoint, such as `cp-2`, with the same inputs. |

A workspace can have up to 10 runs queued, running or paused at once. A run's checkpoints are kept for 7 days after it ends; until then, you can resume it. See [Workflows](/docs/workflows) for the file format.

## Credentials and outbound access

caas.sh keeps your API keys and tokens out of your sandboxes. You connect each one as a credential of the workspace, and egress rules tell caas.sh which outbound requests to add it to. Inside a sandbox whose rules add it, the credential's name is a variable that holds only a placeholder.

Only an owner's key can connect, reconnect or remove credentials, add or remove apps, and add or remove egress rules. See [Credentials and outbound access](/docs/credentials) for how it all fits together.

### Credentials

```sh
caas credentials connect ANTHROPIC_API_KEY --api-key < anthropic-key.txt
caas credentials connect GITHUB_TOKEN --github
caas credentials connect CODEX --codex
caas credentials list
caas credentials reconnect ANTHROPIC_API_KEY < new-anthropic-key.txt
caas credentials remove ANTHROPIC_API_KEY
```

| Command | What it does |
| --- | --- |
| `caas credentials connect NAME METHOD` | Connects a credential. See the methods below. |
| `caas credentials list` | Lists the credentials: name, method, status, scopes and number of rules, and how to reconnect any that need it. |
| `caas credentials reconnect NAME` | For an API key, reads the new value from standard input. For a sign-in, starts a new one. |
| `caas credentials remove NAME` | Deletes the credential, its stored value and its egress rules. |

`NAME` is capital letters, digits and `_`, such as `GITHUB_TOKEN`, and can't start with `CAAS_`. `connect` takes exactly one method:

| Method | What it does |
| --- | --- |
| `--api-key` | Reads the key from standard input. The CLI never takes a key as an option. |
| `--github` | Signs in to GitHub through caas.sh's own GitHub App. |
| `--github-app APP` | Installs your own GitHub App, added with `caas credentials apps add`. |
| `--oauth APP` | Signs in through your own OAuth app, added with `caas credentials apps add`. Add `--scope SCOPE` for each scope to ask for; without it, the sign-in asks for every scope the app lists. |
| `--codex` | Connects a ChatGPT account for Codex: the CLI prints a code and OpenAI's page to enter it on. |

A sign-in prints a page to open. It returns to the console's Credentials page, which finishes it while you're signed in there as the owner who started it. The CLI then waits until the credential is ready; `--detach` returns at once instead, on `connect` and `reconnect`.

### Apps for sign-ins

Before you connect an `--oauth` or `--github-app` credential, add the app it signs in through. The app's secret is read from standard input:

```sh
caas credentials apps add google --type oauth2 --preset google \
  --client-id 1234.apps.googleusercontent.com \
  --scope https://www.googleapis.com/auth/drive.readonly < client-secret.txt
caas credentials connect GOOGLE_DRIVE --oauth google

caas credentials apps add acme-bot --type github-app --client-id Iv1.0123abcd --slug acme-bot < private-key.pem
caas credentials connect GITHUB_APP_TOKEN --github-app acme-bot

caas credentials apps list
caas credentials apps remove acme-bot
```

| Option | What it does |
| --- | --- |
| `--type TYPE` | `oauth2` or `github-app`. |
| `--preset PRESET` | With `oauth2`: `google`, `gitlab` or `linear`, which fills in the provider's endpoints. |
| `--authorize-url URL`, `--token-url URL` | With `oauth2` and no preset: the provider's endpoints, `https://` URLs on a public host. |
| `--token-auth METHOD` | With `oauth2` and no preset: how the token endpoint takes the client secret, `post` (the default) or `basic`. |
| `--client-id ID` | The app's client ID. For a GitHub App, its Client ID. |
| `--scope SCOPE` | With `oauth2`: each scope the app's credentials may ask for. At least one is required. |
| `--slug SLUG` | With `github-app`: the app's slug, as in `github.com/apps/<slug>`. |

The app ID is up to 32 lowercase letters, digits and hyphens. Set the app's redirect URI (for a GitHub App, its callback and setup URL) to `https://api.caas.sh/v1/credential-interactions/callback`.

### Egress rules

An egress rule adds a credential to HTTPS requests for a host, on every path:

```sh
caas egress rules add --preset anthropic --credential ANTHROPIC_API_KEY
caas egress rules add --preset github --credential GITHUB_TOKEN --sandbox <sandbox-id>
caas egress rules add --credential NPM_TOKEN --host registry.npmjs.org --header authorization --scheme bearer
caas egress rules list
caas egress rules remove <rule-id>
```

| Option | What it does |
| --- | --- |
| `--credential NAME` | Required. The credential to add. |
| `--preset PRESET` | `github` (`api.github.com` by bearer token, and `github.com` by basic auth for Git), or `anthropic`, `openai` or `google` (the provider's API key in the header its SDKs send). A model provider preset also gives the sandbox the provider's usual key variable, such as `ANTHROPIC_API_KEY`, holding a placeholder. |
| `--host HOST` | Without a preset: the host. `*.example.com` covers `example.com` and every name under it. |
| `--header NAME` | Without a preset: the request header to set, such as `authorization`. |
| `--scheme SCHEME` | Without a preset: `bearer`, `basic` or `raw` (the value as is). |
| `--username USER` | With `--scheme basic`: the username. Required for `basic`. |
| `--sandbox ID` | Apply the rule to one sandbox only. Without it, the rule applies to every sandbox. |

A sandbox can have up to 16 rules. A running sandbox picks up changed rules the next time it starts; a credential's new value applies at once. A rule only adds the credential: the sandbox's egress policy must still allow the host. A Codex credential isn't added by egress rules.

### Egress policy

Each sandbox has an ordered list of hosts it may and may not reach. The first match wins, and the list always ends by denying everything else. A new sandbox's policy allows its `--egress-host` hosts.

```sh
caas egress policy show <sandbox-id>
caas egress policy set <sandbox-id> --deny uploads.example.com --allow '*.example.com' --allow pypi.org
```

`caas egress policy set ID` replaces the policy with the `--allow HOST` and `--deny HOST` options, in the order you give them, and applies it to a running sandbox at once. Owners and operators can change it.

## Billing

`caas billing` needs a key with **Billing access**, which only an owner can create.

```sh
caas billing balance
caas billing add-funds 25
caas billing auto-recharge show
caas billing auto-recharge set --below 10 --recharge-to 50
caas billing auto-recharge set --off
caas billing usage
caas billing usage --month 2026-09
caas billing usage --csv > caas-usage.csv
```

| Command | What it does |
| --- | --- |
| `caas billing balance` | Prints the balance, the auto-recharge setting and recent top-ups with their receipts. |
| `caas billing add-funds AMOUNT` | Adds funds: $5 to $2,000, written as `25`, `25.50`, `$25.50` or `1,000`. It charges the card on file, or prints a Stripe Checkout link to pay and save a card. |
| `caas billing auto-recharge show` | Prints the auto-recharge setting. |
| `caas billing auto-recharge set` | Turns auto-recharge on, changing only the amounts you give. With `--off`, turns it off. |
| `caas billing usage` | Prints a month's spend: the total so far, the projection and today while the month is current, the amount for each meter, and the spend alert. |

| Option | What it does |
| --- | --- |
| `--below AMOUNT` | With `auto-recharge set`: recharge when the balance falls below this, $5 to $1,000. It's $10 until you change it. |
| `--recharge-to AMOUNT` | With `auto-recharge set`: charge the card back up to this, at least $5 above `--below` and at most $2,000. It's $50 until you change it. |
| `--off` | With `auto-recharge set`: turn auto-recharge off. You get an email when the balance falls below the amount instead. |
| `--month YYYY-MM` | With `usage`: the month to show. The default is this month. |
| `--csv` | With `usage`: print the month as CSV, one row per UTC day and meter, with the columns `day,meter,quantity,unit,rate_usd,amount_usd`. The amounts add up exactly to the month's total. Can't be combined with `--json`. |

Auto-recharge starts off, so adding funds never charges your card again on its own. Sandboxes run only while the balance is above $0. See [Billing and usage](/docs/billing).

## Workspace settings and the audit log

```sh
caas settings
caas settings --time-zone Europe/Berlin
caas audit
caas audit --action sandbox --since 2026-10-01
caas audit --action sandbox.stop --outcome failure --limit 20
```

`caas settings` prints the workspace's time zone, its one setting. `--time-zone ZONE` sets it, an IANA name such as `Europe/Berlin`; new sandboxes take their nightly snapshot at 02:00 in it.

`caas audit` lists who changed what in the workspace, newest first: the time, who did it, the action, its target, the outcome and the request ID. Records are kept for 365 days.

| Option | What it does |
| --- | --- |
| `--action ACTION` | An action, such as `sandbox.stop` or `api_key.create`, or a group: `sandbox`, `command`, `mcp_token`, `secret`, `egress`, `api_key`, `member`, `invite`, `tenant`, `billing` or `workflow`. |
| `--actor ID` | A user or API key ID, or `operator` or `system`. |
| `--target ID` | The ID of what was changed. |
| `--outcome OUTCOME` | `success` or `failure`. |
| `--since TIME`, `--until TIME` | ISO 8601 times, such as `2026-10-01` or `2026-10-01T12:00:00Z`. |
| `--limit N` | Records per page, 1 to 100. |
| `--cursor CURSOR` | The next page: the value the previous page printed. |

An API key never sees records about API keys, members or invites, and sees billing records only with **Billing access**. In what a key reads, users show as their user ID rather than their email.
