# CLI

`raw` is one static binary for macOS, Linux and Windows. It does in one command what a person does in the console, and
it answers either in words or, with `--json`, in the API's own JSON. It keeps itself current: once a day it looks for
a newer release and swaps itself in the background, so the next run is the new one.

## Install

```sh
curl -fsSL https://raw.computer/install.sh | sh
```

The script picks the build for your OS and architecture (macOS or Linux, amd64 or arm64), checks its sha256 against the
release, and installs it as `~/.local/bin/raw`, a directory you own, so the CLI can update itself there. Set
`RAW_INSTALL_DIR` to install elsewhere. On Windows, download
`https://raw.computer/cli/<version>/raw-windows-amd64.exe`, where `<version>` is the one
[`/cli/latest.json`](https://raw.computer/cli/latest.json) names.

```sh
raw version
```

```text
raw 0.1.0 (2c99fd7ffe65) darwin/arm64
```

## Sign in

```sh
raw login
```

`raw login` prints a code and opens the console's **Sign in the raw CLI** page (pass `--no-browser` to only print it).
Signed in to the console, check that the page shows the code your terminal does, and choose **Approve**. The CLI gets a
**personal token** (`rc_pat_…`) and keeps it in `~/.config/raw/config.json`, readable only by you (mode `0600`).

```text
To sign in, open this page and confirm the code WDJB-MJHT:

  https://raw.computer/app/device?code=WDJB-MJHT

Waiting for approval (the code expires in 10 minutes)…
Signed in as ada <ada@example.com>.
Acting in default · Default (org ada) as admin; change it with: raw workspaces use <code>
```

A personal token acts as you, in whichever of your workspaces a command names, with your relation there and never
more. It never acts at org level, so it cannot rename an org or change its money. Each sign-in is listed in the console
under **API keys › Signed-in CLIs**, where you can sign one out. `raw logout` revokes the stored token and forgets it.

In CI and other places with no browser, set an environment variable instead of signing in:

| Variable        | Holds                                                                  |
| --------------- | ---------------------------------------------------------------------- |
| `RAW_TOKEN`     | a personal token (`rc_pat_…`); name its workspace with `RAW_WORKSPACE` |
| `RAW_API_KEY`   | an API key of one workspace (`rc_live_…`, from **API keys**)           |
| `RAW_WORKSPACE` | the workspace to act in: its id (`ws_…`) or its code                   |

`RAW_TOKEN` wins over `RAW_API_KEY`, and both win over the stored token. A token from the environment acts in the
workspace the environment names, never in one chosen for the stored token.

## Workspaces

Every command takes `--workspace <ws>` (an id, `ws_…`, or a code such as `ci`). Without it, a command acts in the
workspace chosen last:

```sh
raw workspaces list
raw workspaces use ci
```

```text
   CODE     NAME     ORG  RELATION  ID
*  default  Default  ada  admin     ws_01m6xxgde9f2c9q3y3gn9e8zdp
   ci       CI       ada  admin     ws_01m6xxh0yq5b8c0k4m2v6t9rza
```

`raw login` chooses one for you: the one chosen before, else one you administer. An API key acts in its own workspace
only.

## Commands

| Command                                      | What it does                                          | With `--json`, prints            |
| -------------------------------------------- | ----------------------------------------------------- | -------------------------------- |
| `raw login [--no-browser]`                   | sign in through the browser                           | `{ "object": "login", … }`       |
| `raw logout`                                 | revoke the stored token and forget it                 | `{ "object": "logout", … }`      |
| `raw whoami`                                 | the person, token, workspace and org in use           | `GET /v1/me`                     |
| `raw workspaces list`                        | your workspaces; `*` marks the one in use             | `GET /v1/workspaces`             |
| `raw workspaces use <ws>`                    | choose the workspace commands act in                  | the workspace                    |
| `raw workspaces rename <name>`               | rename the workspace (an admin; its code stays)       | `POST /v1/workspaces/{id}`       |
| `raw workspaces set-residency <where>`       | `any`, `eu`, or `default` (the org's), an admin       | `POST /v1/workspaces/{id}`       |
| `raw keys list`                              | the workspace's API keys you may see                  | `GET /v1/api_keys`               |
| `raw keys create <name> [--relation viewer]` | an API key; its secret is shown once                  | `POST /v1/api_keys`              |
| `raw keys revoke <key>`                      | revoke an API key                                     | `DELETE /v1/api_keys/{id}`       |
| `raw members list`                           | the workspace's members, the org's owners among them  | `GET /v1/members`                |
| `raw members invite <email> [--relation]`    | mail an invitation (`developer` by default), an admin | `POST /v1/invitations`           |
| `raw members set <member> <relation>`        | make `viewer`, `developer` or `admin` their relation  | `POST /v1/members/{id}`          |
| `raw members remove <member>`                | remove them from the workspace, an admin              | `DELETE /v1/members/{id}`        |
| `raw invitations list`                       | the invitations nobody accepted yet, an admin         | `GET /v1/invitations`            |
| `raw invitations withdraw <invitation>`      | withdraw one; its link stops working                  | `DELETE /v1/invitations/{id}`    |
| `raw sandboxes create [--class] [--size]`    | a sandbox (`std`, `4x8` by default)                   | `POST /v1/sandboxes`             |
| `raw sandboxes list`                         | the workspace's sandboxes, newest first               | `GET /v1/sandboxes`              |
| `raw sandboxes get <sbx>`                    | one sandbox                                           | `GET /v1/sandboxes/{id}`         |
| `raw sandboxes stop <sbx>` / `start <sbx>`   | stop it (only its disk is billed), start it again     | `POST …/stop`, `POST …/start`    |
| `raw sandboxes fork <sbx>`                   | a new sandbox from its disk and memory now            | `POST …/fork`                    |
| `raw sandboxes delete <sbx>`                 | delete it and its disk                                | `DELETE /v1/sandboxes/{id}`      |
| `raw snapshots list [--sandbox sbx]`         | the workspace's snapshots, newest first               | `GET /v1/snapshots`              |
| `raw snapshots get <snp>`                    | one snapshot                                          | `GET /v1/snapshots/{id}`         |
| `raw snapshots create <sbx> [--name]`        | snapshot a sandbox's disk, and memory if it runs      | `POST /v1/snapshots`             |
| `raw snapshots delete <snp>`                 | delete a snapshot, freeing its unique bytes           | `DELETE /v1/snapshots/{id}`      |
| `raw templates list`                         | the templates to start from: the workspace's and ours | `GET /v1/templates`              |
| `raw templates get <tpl>`                    | one template                                          | `GET /v1/templates/{id}`         |
| `raw usage [--from day] [--to day]`          | usage per day, class and size                         | `GET /v1/usage`                  |
| `raw wallet`                                 | the org's balance and entries (owners and billing)    | `GET /v1/wallet`                 |
| `raw budget`                                 | what the workspace may spend; whether a start can pay | `GET /v1/workspaces/{id}/budget` |
| `raw api <METHOD> <path> [--data json]`      | any endpoint, as the CLI's token                      | the body the API answered        |
| `raw mcp`                                    | the commands as MCP tools, for an agent's harness     | MCP on stdin and stdout          |
| `raw agent setup [--remove]`                 | skill and `raw mcp` into Claude Code, Codex, Cursor   | `{ "object": "agent_setup", … }` |
| `raw version`                                | this build, and a newer release if one is out         | `{ "object": "version", … }`     |
| `raw update`                                 | update now (it happens by itself once a day)          | `{ "object": "update", … }`      |

Every command has `--help`, and `raw help <command>` is the same. Singular names work too (`raw sandbox ls`).

A `<member>` is an email or an id (`usr_…`), and an `<invitation>` an email or an id (`inv_…`), as the lists show them.
What only an admin does needs the token of `raw login` as an admin of the workspace (or an owner of its org): an API
key is never an admin, so the CLI with `RAW_API_KEY` is refused with `raw login` as its next step. What acts at org
level (an org's owners and name, a new workspace, the monthly caps, accepting an invitation) is the console's
([API › Only in the console](/docs/api#only-in-the-console)).

### Examples

```sh
raw keys create ci --relation developer        # the secret is printed once; store it
raw sandboxes create --class std --size 4x8 --name build-42
raw sandboxes create --size 8x16 --on-full fail # fail at once instead of queueing when cells are full
raw sandboxes create --template tpl_01m6xxj9a2b3c4d5e6f7g8h9jk  # start from a template (raw templates list)
raw snapshots list --sandbox sbx_ny1_01m6xxh3k2q8v7c4t9r5wdz6ya
raw members invite ada@example.com --relation admin
raw members set ada@example.com developer
raw invitations withdraw ada@example.com
raw workspaces set-residency eu
raw usage --from 2026-11-01 --to 2026-11-08
raw api GET /v1/me
raw api POST /v1/api_keys --data '{"name":"deploy","relation":"viewer"}'
echo '{"name":"from-stdin"}' | raw api POST /v1/api_keys --data -
```

Every create sends an `Idempotency-Key`, and a request that got no answer is sent again with the same key, so a
dropped connection never makes two of anything. Pass `--idempotency-key <key>` to `create`, `stop`, `start`, `fork`,
`raw snapshots create`, `members invite` and the other commands that send a `POST` to keep a retry across runs safe as
well: the same command run again is answered as the first was
([API › Idempotency](/docs/api#idempotency)).

## Output

Without `--json`, a command writes what it did in a few lines, or a table. With `--json`, **stdout holds exactly one
JSON document**: the API's answer as it came (for `raw sandboxes create`, the answer of `POST /v1/sandboxes`), or the
error, in the API's [error shape](/docs/errors). Anything meant for a person (the sign-in code, notes) goes to stderr.

```sh
raw sandboxes list --json | jq -r '.data[].id'
```

The exit status says how it went: `0` done, `1` refused or failed, `2` a command line that cannot run (an unknown
command or flag, a missing argument).

## Errors

An error says what happened, then its next step, as the API gives it, with the command to run where one helps:

```text
$ raw sandboxes create --class std --size 4x8
raw: No cell serves std yet: raw.computer is in early access. [capacity_unavailable]
  next: Read the early-access note; sandbox creates open when the first cell does.
  see:  https://raw.computer/docs/limits#early-access
  docs: https://raw.computer/docs/errors#capacity_unavailable
```

That is every sandbox create today: during early access no cell is open to customers ([Limits](/docs/limits#early-access)).
The CLI says so plainly and exits `1`; it never pretends a machine started.

The API's codes are on the [Errors](/docs/errors) page. A few the CLI makes itself, in the same shape:

| Code              | When                                                                          |
| ----------------- | ----------------------------------------------------------------------------- |
| `unauthorized`    | no token: not signed in, and `RAW_TOKEN` and `RAW_API_KEY` are not set        |
| `invalid_request` | a command line that cannot run (exit `2`)                                     |
| `unreachable`     | the API did not answer (the network, or a wrong `RAW_API_URL`)                |
| `interrupted`     | stopped (Ctrl-C) before the API answered                                      |
| `local_error`     | something on this machine: a file it cannot write, a build that cannot update |

## Updates

Once a day, a run of `raw` starts a copy of itself in the background that reads
[`/cli/latest.json`](https://raw.computer/cli/latest.json), downloads the build for its OS and architecture when it is
newer, checks its sha256, and renames it over the binary. The run that started it does not wait, and the next run is
the new version. `raw update` does the same now. Nothing is replaced unless the whole file arrived and its sha256 is
the release's.

The first run of a release whose skill changed writes the new skill where `raw agent setup` put the old one
([Agents](/docs/agents#the-skill)).

It does not update itself in CI (where `CI` is set: install the newest with the script instead), in a development
build, or when `RAW_NO_UPDATE=1` is set. If the binary is in a directory you cannot write, `raw update` says so;
install it again with the script.

## For agents

`raw agent setup` writes the [raw-computer skill](/docs/agents#the-skill) and the MCP server `raw mcp` into Claude Code,
Codex and Cursor, so an agent there has the commands as tools and knows how to use them ([Agents](/docs/agents)).
Calling the CLI itself:

- Use `--json` and branch on the exit status and `error.code`, never on a message.
- Name the workspace with `--workspace` (or `RAW_WORKSPACE`) so a command never depends on a choice stored earlier.
- `raw api` reaches any endpoint of the [API](/docs/api) with the CLI's token and version.
- Every error's `next_step.cli`, when there is one, is a `raw` command that helps.

## Environment

| Variable          | What it does                                                        |
| ----------------- | ------------------------------------------------------------------- |
| `RAW_TOKEN`       | a personal token to use instead of the stored one                   |
| `RAW_API_KEY`     | an API key to use instead of the stored token                       |
| `RAW_WORKSPACE`   | the workspace to act in (id or code)                                |
| `RAW_API_URL`     | the API's origin (default `https://raw.computer`), or `--api <url>` |
| `RAW_CONFIG_DIR`  | where the CLI keeps its files (default `~/.config/raw`)             |
| `RAW_NO_UPDATE`   | `1`: never update by itself                                         |
| `RAW_INSTALL_DIR` | for `install.sh`: where to install (default `~/.local/bin`)         |
