Environments
A named execution context binding one sandbox + a house-secret scoping policy into something a thread can reference, durable and inspectable within a house. threads.environment_id carries an FK with on delete set null, and the thread row keeps a snapshot of the environment — so later env edits or teardown don’t rewrite a thread’s history.
environments { id, house_id (FK cascade), author_id, name, sandbox_id (required at row level), secrets: jsonb "all" | {only: [names]} # which house secrets the box sees; default "all"}POST /api/environments with sandbox_id omitted auto-provisions a box on the env’s runtime. Daytona creates a fresh house-scoped sandbox via DAYTONA_API_KEY; delegate_task can run in that box or create one and attach it to the parent thread for later work. The secrets policy stores names at most, never values — see secrets for resolution. Sandbox pi defaults to DEFAULT_BOT_MODEL (llm-keys) — a house OPENROUTER_API_KEY reaches the box by default. Provider-specific keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, …) still work paired with a matching thread.config.model override.
| Endpoint | Auth | Notes |
|---|---|---|
GET /api/environments?house_id=<uuid> | house member | list |
GET /api/environments/<id> | house member | fetch one |
POST /api/environments | member-write | auto-provisions on the runtime (daytona default) when sandbox_id omitted |
PATCH /api/environments/<id> | member-write | partial update |
DELETE /api/environments/<id> | owner-only | soft delete |
Every house secret injects into the box by default (secrets: 'all'); a restricted env (secrets: { only: [...] }) injects that subset and nothing else. buildBoxEnv (packages/core/dispatch/sandbox-shared.ts) resolves names to values and the launch path exports them as env vars. A private repo needs the house connected to GitHub (Settings → Runtime → Connections, or arbe connection create github); there is no GitHub App yet.
A GitHub connection is the GITHUB_TOKEN house secret, and buildBoxEnv turns it into a signed-in box (packages/core/dispatch/connection-env.ts): it derives GH_TOKEN (for gh, which prefers it) and GIT_CONFIG_COUNT/GIT_CONFIG_KEY_0/GIT_CONFIG_VALUE_0 — an env-only credential helper (git >= 2.31) scoped to https://github.com, so the token is offered to that host and no other, and read from $GITHUB_TOKEN at call time rather than baked into a config string. The box also gets a git identity: GIT_AUTHOR_*/GIT_COMMITTER_* name the agent by handle with a non-routable <handle>@agents.arbe.invalid email, so commits show the agent as author and the token’s owner as pusher; the prompt asks the agent to add a Co-authored-by trailer for the human who set it to work. Nothing is persisted on the box, so rotating or removing the connection takes effect on the next run. Private git clone works in both run_command and delegate_task. A house that sets GH_TOKEN, a GIT_CONFIG_* or a GIT_AUTHOR_*/GIT_COMMITTER_* name itself keeps its own value.
CLI (all commands operate on the active house, set via arbe house select):
arbe env list [--json]arbe env view <name-or-id> # name (case-insensitive) or UUIDarbe env create <name> [--sandbox <id>] [--only-secret NAME] # default: all house secretsarbe env secrets <name|id> # show the policyarbe env secrets <name|id> --only NAME [--only NAME2] # restrict to a subset (replaces the set)arbe env secrets <name|id> --all # back to the defaultarbe env delete <name|id>--only-secret / --only are repeatable; omitting them means every house secret is available. Dispatch with --env: arbe --env work thread entries create <ref> "fix the bug" — --env is a root-level option that resolves the environment by name or ID and uses its sandbox_id, overriding any -s flag. Sandboxes are created once and reused across dispatches.
Stale thread sandbox pointers
A thread may keep reaching an old sandbox after its environment changes or is deleted. threads.environment_id is the current binding; threads.sandbox_id caches the resolved box. Compare that sandbox row’s environment_id with the thread’s. A mismatch confirms a stale pointer.
Clearing threads.sandbox_id makes the next turn resolve the current environment’s live sandbox. This is a direct production write: do it freely only in a test house; otherwise ask first. ensureThreadSandbox in packages/core/threads.ts owns re-resolution.
Code: packages/core/schemas/environment.ts (EnvironmentRowSchema), packages/core/environments.ts, apps/www/src/routes/api/environments/.
See daytona runtime, system/secrets, system/dispatch, runtime.