# Runtime

**The model in one line:** secrets live on the house; environments live on the house, bind the secrets they need, and target one sandbox; sandboxes run code on [daytona](./sandbox-daytona.md) by default.

Three CLI command groups manage arbe's runtime layer: sandboxes run code, environments configure them, and secrets store credentials. For the underlying concepts, see [daytona runtime](./sandbox-daytona.md), [system/environments](./environments.md), and [system/secrets](./secrets.md).


## What it takes to dispatch

Dispatch runs in-process — see [dispatch](./dispatch.md). An env-bound thread is a normal thread whose bots get a sandbox to reach via the `run_command` tool; selection and authorship are identical to a local thread. Three things must line up for it to round-trip:

- the worker has `OPENROUTER_API_KEY` (the default in-process pi-ai key);
- the house has the provider key required by the env-bound model (for example, `openrouter/...` needs `OPENROUTER_API_KEY`);
- the environment names a runtime and the worker can reach it: daytona (the default) provisions on demand via `DAYTONA_API_KEY`; the legacy sprite runtime instead needs the house's `SPRITES_TOKEN` ([secrets](./secrets.md)).

The sandbox is the bot's hands, reached per tool call. Envs stay useful even with one sandbox: they're the seam for named profiles — different repos or secret sets per branch — and for splitting one sandbox across configurations.


## Sandboxes

A sandbox is a remote machine + working tree where a bot's hands run. Daytona is the runtime; you rarely provision one by hand — creating an environment auto-provisions a daytona box (`DAYTONA_API_KEY`), and `delegate_task` spins a fresh box per coding run. See [daytona runtime](./sandbox-daytona.md) for how a run mirrors into its thread and resumes.

`arbe sandbox` inspects and manages machines. The CRUD verbs take `--runtime daytona` to act on daytona machines (identity is the daytona `sandbox.id`):

```sh
arbe sandbox list --runtime daytona            # house's live daytona machines
arbe sandbox <id> --runtime daytona            # detailed view
arbe sandbox stop <id> --runtime daytona       # stop (start to resume)
arbe sandbox start <id> --runtime daytona
arbe sandbox destroy <id> --runtime daytona    # delete
```

The CLI reads the house's `DAYTONA_API_KEY` server-side, so no local key is needed for any verb. Bare `arbe sandbox` (no `--runtime`) targets the legacy sprite runtime and its forensics (`setup`, `ping`, `diagnose`, `env`) — see [system/sandbox-sprite](./sandbox-sprite.md). `arbe env diagnose <env>` is the one-stop dispatch preflight: it probes the env's sandbox plus static secret checks.

All commands accept `--json` for machine-readable output.


## Environments

Environments link a sandbox to a house with configuration — which repo to clone and which secrets to inject. A sandbox is created once and reused across runs.

```sh
arbe env list                                              # list all in the active house
arbe env view staging                                      # detailed view with secret bindings
arbe env create staging --sandbox my-sandbox --repo https://github.com/org/repo
arbe env create openrouter --sandbox openrouter --secret OPENROUTER_API_KEY
arbe env bind-secret staging GITHUB_TOKEN                  # add or upgrade a binding
arbe env bind-secret staging NICE_TO_HAVE_KEY --optional   # warn instead of fail
arbe env unbind-secret staging GITHUB_TOKEN                # remove a binding
arbe env delete staging                                    # remove it
```

When creating an environment, `--sandbox` is required. If you pass `--repo`, it's also added to the allowed repos list. Secret bindings can be set inline at creation with `--secret NAME` (required) or `--optional-secret NAME` (warn-only) — both repeatable — or managed afterward with `bind-secret` / `unbind-secret`. Bindings store names; values resolve at dispatch — see [secrets](./secrets.md).

Aliases: `ls`/`l` for list, `rm`/`del` for delete.


## Secrets

Secrets are house-scoped encrypted values. They get injected into sandboxes at runtime through environment secret bindings.

```sh
arbe secret list                                    # see all in the active house
arbe secret view OPENAI_API_KEY                     # metadata, timestamps, audience
echo "sk-..." | arbe secret set OPENAI_API_KEY      # create (or rotate if it exists)
arbe secret delete OPENAI_API_KEY                   # remove it
```

Values are always read from stdin, never passed as arguments — so they don't end up in shell history. If a secret with the same name already exists, `set` rotates it rather than creating a duplicate.

Options on `set`:
- `--audience environment|capability` controls scope (default: `environment`)
- `--shared` makes the secret available to other agents in the same scope

Aliases: `ls`/`l` for list, `rm`/`del` for delete. All commands accept `--json`.


## Typical setup workflow

Starting from nothing, here's the sequence to get ready for dispatching work:

```sh
# 1. Store credentials (values come from stdin)
printf '%s' "$OPENROUTER_API_KEY" | arbe secret set OPENROUTER_API_KEY --shared
printf '%s' "$GITHUB_TOKEN"       | arbe secret set GITHUB_TOKEN       --shared

# 2. Create the environment — daytona auto-provisions the sandbox
arbe env create prod --repo https://github.com/org/repo \
  --secret OPENROUTER_API_KEY --secret GITHUB_TOKEN

# 3. Dispatch work
arbe env use prod                                      # set active env once
THREAD_ID=$(arbe thread create <parent-ref>)           # active env fills in --env
arbe thread entries create "$THREAD_ID" "implement the login page"
```

`arbe thread create --env prod <parent>` is the explicit form; without `--env` it falls back to the active env. The leading entry kicks off dispatch — tail with `arbe thread entries read "$THREAD_ID"` (exits on dispatch terminals, non-zero on failed).

On www at `/threads/new`, step 2 collapses to "+ Create env": the server auto-provisions the daytona box when `sandbox_id` is omitted, given `DAYTONA_API_KEY` is configured.

Day-to-day usage is `arbe thread create` + `arbe thread entries create`, plus `arbe sandbox list --runtime daytona` to check live machines.

Each bot replies with its own model ref (`agent.model`, default `openrouter/anthropic/claude-haiku-4.5`). A thread-level model override can pin a conversation when needed; otherwise each agent keeps its assigned model. `OPENROUTER_API_KEY` is the base credential for both local pi and in-thread bot replies.
