Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions apps/docs/content/zbox/access.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
---
title: Managing and accessing the workspace
description: Manage a Zerops Box workspace through its web console, its HTTP API, the zbox CLI or the zbox MCP server, and keep its API key safe.
---

Every workspace serves one HTTP API. The web console, the `zbox` CLI and `zbox mcp` are all clients of that API. You authorize them in one of two ways: through your Zerops login, or with the workspace API key.

| Interface | Use it for | Signed in with |
|---|---|---|
| [Web console](#web-console) | Working with sandboxes in the browser | Your Zerops login (from the Zerops GUI or the CLI), or the API key |
| [HTTP API](#http-api) | Your own code, services and CI | The API key |
| [zbox CLI](#zbox-cli) | Your terminal, scripts and agents | Your Zerops login, or the API key |
| [MCP server](#mcp-server) | AI agents that run code in throwaway sandboxes | Same as the CLI |

## API key

The **workspace API key** is the service environment variable `ZBOX_WORKSPACE_API_KEY`, which Zerops generates when it creates the workspace ([environment variables](/zbox/workspace-service#environment-variables)).

:::warning One key manages everything
The API key is the whole authority over the workspace. There are no scopes and no per-user keys. Anyone who holds it, and any sandbox you give it to, can create, read, change and terminate every sandbox, base, template, integration and webhook of the workspace. Keep it out of sandboxes unless you want them to manage the workspace. See [isolation and security](/zbox/isolation).
:::

You don't need the key when you are logged in to Zerops. The Zerops GUI and the CLI reach the workspace through Zerops, which adds the key on the way. When you need it, for a script, CI or another machine, print it:

```bash
zbox workspace key
```

The CLI reads the key from the service with your Zerops login. Because the key is a sensitive variable, the CLI may ask you to confirm the access in the browser first.

The API key never opens a sandbox port. Port links use one-time tickets instead ([access modes](/zbox/networking#access-modes)).

### Rotate the API key

1. Edit `ZBOX_WORKSPACE_API_KEY` of the workspace service in the Zerops GUI.
2. Reload the service. The reload applies the new key and keeps sandboxes running. Until then, only the old key works ([environment variables](/zbox/workspace-service#environment-variables)).
3. Update every script, CI secret and `zbox workspace use` that holds the old key.

After the reload, the old key is refused and browsers signed in to the console with it are signed out.

:::caution Logged-in access keeps the old key
The Zerops GUI and a logged-in CLI reach the workspace with an access that Zerops creates with the key of that moment. An access created before the reload keeps sending the old key until it expires, an hour after its last use, and the CLI fails with `Missing or invalid Workspace API key` (exit code 6). Reopen the console from the Zerops GUI, and fetch a new access for the CLI:

```bash
zbox workspace authorize <workspace-service-id>
```
:::

## Web console

Each workspace serves a web console at the root of its API URL. It covers sandboxes (terminal, files, policy, snapshots, ports, sessions, events, metrics), images, integrations, activity, and the workspace itself.

Open it in one of these ways:

- **From the Zerops GUI**: in the workspace service's left menu, open **Web UI** to see the console embedded in Zerops, or **Open web UI in new tab**. You are signed in through Zerops, with no key. Your [role in the project](/features/rbac) decides whether you can open it.
- **From the CLI**: `zbox workspace console` opens the console of the current workspace, signed in. For a workspace of your account it goes through Zerops. For one you added with `zbox workspace use`, it opens a one-time sign-in link that is valid for 60 seconds.
- **Directly**: open the API URL, for example `https://zbox-6da-8080.prg1.zerops.app`, and paste the API key into the sign-in form.

Signing in with the key exchanges it once for a session cookie, so the key is never stored in the browser. The session lasts 7 days. **Sign out** ends it in that browser.

:::caution The console needs HTTPS
The session cookie works only over HTTPS. Over the [Zerops VPN](/references/networking/vpn), the console at `http://<hostname>.zerops:8080` loads, but signing in fails without an error. Use the Zerops GUI or the public API URL for the console. The API itself works over the VPN with a bearer token.
:::

## HTTP API

The API URL is the Zerops subdomain of the workspace's port 8080, for example `https://zbox-6da-8080.prg1.zerops.app` ([subdomain access](/zbox/workspace-service#subdomain-access)). `zbox workspace` prints it on the `API` line. Other services in the project reach the API at `http://<hostname>:8080` ([project network](/zbox/workspace-service#project-network)). Every route is under `/api/rest/`.

Send the API key as a bearer token in the `Authorization` header, on every request including WebSocket upgrades. A key in the query string is refused. Only `GET /api/rest/ping` answers without the key.

```bash
export ZBOX_WORKSPACE_URL=https://zbox-6da-8080.prg1.zerops.app
export ZBOX_API_KEY=$(zbox workspace key)

# the workspace: capacity, usage, domains, policy
curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/workspace"
# live sandboxes, newest first
curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/sandbox-list"
```

The CLI, when logged in to Zerops, uses a different path: it calls the API through Zerops with a short-lived access token minted from your login, so no key is stored on your machine.

### OpenAPI document

Each workspace serves its API reference without the key:

- Swagger UI: `<API URL>/api/rest/swagger/`
- OpenAPI 3 document: `<API URL>/api/rest/swagger/openapi.yml`

The document describes the whole contract, including a few routes and fields that the beta does not act on yet. These docs describe only what works today. Generate a client from the document if you need one. There are no published SDKs.

### Operations

Requests that create, park, wake, fork, snapshot, restore or terminate sandboxes, pull or build bases, or build templates answer right away with an **operation** and do the work in the background ([which verbs are operations](/zbox/lifecycle#operations)). An operation has an `id`, a `kind`, a `status` (`PENDING`, `RUNNING`, `FINISHED` or `FAILED`), a `result` when it finished and an `error` when it failed.

To wait for one, long-poll it. `waitSeconds` holds the answer until the operation finishes or fails, for at most 30 seconds. If it is still running when the time is up, you get it as it is, so ask again:

```bash
curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/operation/<operation-id>?waitSeconds=30"
```

Creating a sandbox (`POST /api/rest/sandbox`) also takes `waitSeconds` in its body. The answer then carries the finished operation and the sandbox in its final state. `GET /api/rest/operation-list` lists recent operations, newest first.

The CLI waits for every operation by default. `--no-wait` prints the operation instead, `--timeout` stops waiting while the work goes on, and `zbox operation wait <id>` picks the wait up again.

### Idempotency keys

The requests that consume capacity require an `Idempotency-Key` header: create, fork, park, wake, snapshot, restore and terminate a sandbox, pull a base and create a template. Use a new unique value for each logical request, and reuse it only when you retry that same request.

- The same key with the same request within 24 hours replays the first answer. Nothing runs twice.
- The same key with a different request, or while the first request is still in progress, is refused with `409 idempotencyKeyConflict`.
- A request that failed releases its key, so you can retry with it.
- Keys are shared by the whole workspace, not per client.

```bash
curl -X POST "$ZBOX_WORKSPACE_URL/api/rest/sandbox" \
-H "Authorization: Bearer $ZBOX_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "app", "baseDigest": "<digest>", "networkMode": "PROJECT_INTERNET", "waitSeconds": 30}'
```

The CLI and the console send the header for you.

### Errors

Errors use the HTTP status code and one JSON shape. `code` is stable for your code to match on, and `meta[].metadata` lists problems by field:

```json
{"error": {"code": "invalidUserInput", "message": "Invalid user input", "meta": [{"code": "invalidUserInput", "error": "Invalid user input", "metadata": {"networkMode": ["field is required"]}}]}}
```

For example, a wrong or missing key answers `401 unauthorized`, an unknown sandbox `404 sandboxNotFound`, and a full workspace `409 workspaceFull` ([capacity](/zbox/workspace-settings#capacity)).

## zbox CLI

Install and log in as described in [getting started](/zbox/getting-started). Every command explains itself with `--help`, and `zbox --help` lists the commands by task. The rules below hold for all of them.

### Output and exit codes

On a terminal the CLI prints tables. When stdout is a pipe or a file, it prints tab-separated values with one header line. `--json` prints the API objects as they are, and `-q` (`--quiet`) prints only ids, one per line. Progress and hints go to stderr. Under `--json`, an error is one JSON object on stderr.

```bash
id=$(zbox new -q --template dev)
zbox get "$id" --json
```

| Exit code | Meaning |
|---|---|
| 0 | OK |
| 1 | Failed, for example a failed operation |
| 2 | Wrong usage: bad flags or arguments, a request the API refused as invalid, a missing `--yes` |
| 3 | Not found |
| 4 | Conflict: the sandbox's state does not allow it, a name is taken, an operation is in progress |
| 5 | Timed out waiting, the work may go on |
| 6 | Not authorized: no login, no workspace, a rejected key |
| 7 | Unavailable: the workspace is not reachable or is full, retry later |
| 130 | Interrupted |

`zbox exec` has exit codes of its own, see [run a command](/zbox/commands#run-a-command).

Commands that delete something (`remove`, `base remove`, `workspace teardown`, and so on) ask for confirmation on a terminal. Without a terminal they refuse with exit code 2 unless you pass `--yes` (`-y`).

### Several workspaces

The CLI remembers the workspaces you have used and keeps one as current. Commands work on the current one.

```bash
# the Box Workspace services of your Zerops account, and the ones stored here
zbox workspace list
# store a workspace of your account; it is reached through Zerops, no key is stored
zbox workspace authorize <service-id>
# store a workspace outside your account by its API URL and key
zbox workspace use https://box-1a2-8080.prg1.zerops.app --api-key <key>
# make another stored workspace the current one
zbox workspace switch <name>
# work on another stored workspace for one command only
zbox list --workspace <name>
# drop the current workspace from the CLI, nothing is deleted
zbox workspace forget
```

`zbox workspace use` stores the key in the CLI's configuration file. `ZBOX_WORKSPACE=<name>` in the environment does the same as `--workspace`.

### Scripts, CI and sandboxes

These environment variables override what the CLI has stored, for scripts and CI:

| Variable | What it does |
|---|---|
| `ZBOX_WORKSPACE_URL` and `ZBOX_API_KEY` | Work on the workspace at this API URL with this key, ignoring the stored ones. Set both or neither. |
| `ZBOX_TOKEN` | A Zerops [personal access token](/references/cli#personal-access-tokens) used instead of the stored login, for commands such as `zbox setup` and `zbox workspace teardown` |
| `ZBOX_CONFIG` | Path of the configuration file, `~/.config/zbox/config.json` by default |

The URL and key pair is the simplest way for CI, and for a sandbox that should manage its own workspace. A sandbox reaches its workspace's API only through the public API URL, so it needs a [network mode](/zbox/networking#egress) with internet access.

```bash
ZBOX_WORKSPACE_URL=https://zbox-6da-8080.prg1.zerops.app ZBOX_API_KEY=<key> zbox list
```

## MCP server

`zbox mcp` serves the current workspace as an MCP server on stdio, so an AI agent can run code in sandboxes. Register it in Claude Code:

```bash
claude mcp add zbox -- zbox mcp
# spawn the sandboxes from the template "dev"
claude mcp add zbox -- zbox mcp --template dev
```

Any MCP client that starts a stdio server can run the same command. It has two tools:

- `run_code` runs `code` in `python` (the default), `javascript` or `bash`, in `/workspace`, and answers its stdout, stderr and exit code. The image needs `python3`, `node` or `bash` for that. Without `sandbox_id`, every call creates a fresh sandbox named `mcp-<time>` and terminates it afterwards. With `keep: true` the sandbox stays and the answer carries its id, which later calls pass as `sandbox_id` to continue in it. `timeout_seconds` is at most 50.
- `kill_sandbox` terminates a sandbox kept by `run_code`.

Sandboxes come from `--template` or `--base`. Without either, they come from the first ready template, or from the only ready base when there is no template. Their network mode is `project_internet` unless you pass `--network`. The server uses the CLI's current workspace, or the one `--workspace` or the environment variables above name.

:::caution Kept sandboxes outlive the session
A sandbox kept with `keep: true` keeps running after the agent session ends and holds its share of the workspace until `kill_sandbox` or `zbox remove` terminates it. Check with `zbox list`.
:::
Loading
Loading