---
title: CLI
description: "raster — the SDK with a terminal in front of it."
---

`raster` is the public SDK with a terminal in front of it. There is no route here a customer's
own API key cannot take, and no credential in the process other than that key — which is what
makes the CLI a demonstration of the API rather than a privileged back door into it.

```bash
npm install -g @raster/cli
```

## Signing in

```bash
raster login
```

It asks for an API URL and an API key, with the key typed at a hidden prompt rather than passed
as a flag — a key on the command line is in the shell history, in the process table for as long
as the command runs, and in any CI log that echoes its commands.

The key is **proved before it is written**: storing an unusable credential turns the next
command's failure into a mystery two steps removed from its cause.

It is stored at `$XDG_CONFIG_HOME/raster/config.json` (falling back to `~/.config`), with the
file at mode `0600` and its directory at `0700`. A file rather than a shell profile, because a
key in a shell profile is a key in every process you ever start.

```bash
raster logout
```

That forgets the local credential. Revoke the key itself in the dashboard if it may have
leaked.

:::note
The CLI signs in with an API key, not an email and password. The API authenticates a browser
with a session cookie and refuses a cookie-authenticated write that arrives without an `Origin`
it names — a CLI has no origin to present, so it could sign in and then be unable to mint the
key it signed in to get. `--email` is refused with that explanation rather than half-working.
:::

## Commands

| Command                            | What it does                                    |
| ---------------------------------- | ----------------------------------------------- |
| `raster login`                     | store an API key for this machine               |
| `raster logout`                    | forget it                                       |
| `raster machine create`            | create a machine and wait for its desktop       |
| `raster machine list`              | every machine in the organization (alias: `ls`) |
| `raster machine open <machine>`    | the dashboard URL for its desktop               |
| `raster machine start <machine>`   |                                                 |
| `raster machine stop <machine>`    | keeps the disk                                  |
| `raster machine delete <machine>`  | destroys the disk (alias: `rm`)                 |
| `raster shell <machine>`           | an interactive shell inside it                  |
| `raster screenshot <machine>`      | write a PNG of its screen                       |
| `raster snapshot <machine>`        | capture its disk                                |
| `raster fork <machine>`            | a new machine from a copy of its disk           |
| `raster expose <machine> <port>`   | publish a port at its own https URL             |
| `raster template create <machine>` | save it as a starting point for others          |

`<machine>` is an id or a name.

## Options

| Flag              | Meaning                                         |
| ----------------- | ----------------------------------------------- |
| `--json`          | machine-readable output on stdout, nothing else |
| `--name <name>`   | names the thing being created                   |
| `--size <size>`   | `small`, `standard` or `large`                  |
| `--template <id>` | boot a new machine from a template              |
| `--public`        | expose the port with no credential at all       |
| `--out <path>`    | where a screenshot is written                   |
| `--no-wait`       | return before the machine is ready              |
| `--yes`           | do not ask before something destructive         |
| `--url <url>`     | the API URL, for `login`                        |

`--flag value` and `--flag=value` both work. Everything after `--` is positional whatever it
looks like, so a machine named `--json` is still addressable.

## Two output modes

The human mode is prose and may be reworded freely between releases. `--json` is a contract: it
goes to stdout alone, with every note and prompt on stderr, and it is what a script reads.

```bash
raster machine list --json | jq -r '.[] | .id'
```

## Exit codes

The CLI maps the API's error codes onto `sysexits`, so a shell script can branch without parsing
output:

| Code | When                                               |
| ---- | -------------------------------------------------- |
| `0`  | success                                            |
| `64` | `invalid_request` — EX_USAGE                       |
| `69` | `not_found` — EX_UNAVAILABLE                       |
| `77` | `unauthenticated`, `permission_denied` — EX_NOPERM |
| `1`  | everything else                                    |

## A few commands in detail

### `raster shell`

Puts your local terminal into raw mode so every keystroke, including `^C` and `^Z`, reaches the
remote pty rather than being handled locally — a shell where `^C` kills your connection instead
of the process you were running is not a shell. Raw mode is restored on every exit path,
including a crash.

### `raster machine open`

Prints the dashboard URL and does **not** launch a browser by default. A command that opens a
window is unusable over SSH and in CI, and guessing which of those you are in is how a CLI hangs
waiting on a browser that will never appear. Pass `--open` to ask for it explicitly.

Needs `RASTER_WEB_URL` pointing at your deployment's dashboard.

### `raster expose`

Prints the preview URL. When the preview is private — the default — it also prints the
credentialed link, which sets a cookie for that hostname and is the only time the secret is
shown.

## Environment

| Variable          | Meaning                           |
| ----------------- | --------------------------------- |
| `RASTER_API_KEY`  | overrides the stored credential   |
| `RASTER_BASE_URL` | overrides the stored API URL      |
| `RASTER_WEB_URL`  | the dashboard, for `machine open` |
| `XDG_CONFIG_HOME` | where the credential file lives   |

The environment wins over the stored config when it is set. That is how CI provides a key, and
how a machine with no home directory works at all.
