# Quickstart

> From a key to a running machine: log in, make a machine, run a command in it, copy a file in and out, pause it and delete it.

From a key to a machine you have used and put away, in about five minutes. You need a terminal and an API key.

## Get a key

For now, keys are made in Wheelmachine's console by your account's owner: ask for one that holds `cloud:*` to follow this page. A key looks like `ak_live_` and 40 letters and digits. Keep it like a password: anyone who has it can use your machines.

> **Coming:** A page where you make your own keys.

## Get wm

`wm` is the command line. For now it is built from its source, with Go 1.27 or later, which writes `wm` and `wm-mcp` (the server for agents):

Terminal:

```sh
$ cd frontend/cli
$ go build -o bin/ ./cmd/...
$ export PATH="$PWD/bin:$PATH"
```

## Log in

Terminal:

```sh
$ wm login
To sign in, open https://app.wheelmachine.ai/device and enter this code:

    BCDF-GHJK

Your browser is opening it. Sign in, type the code, then approve.
Waiting for you to approve (the code works until 09:10; Ctrl-C to stop)...
Logged in to account you@example.com.
Signed in. A key of your own, "wm on laptop" (key_3f9a1c2b4d5e6f70), is kept in /home/you/.config/wheelmachine/config.json, readable by you only.
```

You sign in on the page with GitHub or Google, and `wm` checks the key it is given with the API before it keeps it. On a computer with no browser, open the address on any other device; to keep a key you already have, use `wm login --key`. To use a key without keeping it, put it in `WHEELMACHINE_API_KEY` instead.

## Make a machine

Terminal:

```sh
$ wm machines create --name box
ID                            NAME  STATE    SIZE         LOCATION      AGE
m_01jb2xk3n4p5q6r7s8t9v0w1x2  box   running  2 CPU, 4 GB  Helsinki, FI  0s
```

It answers once the machine runs, usually in well under a second. This one has 2 CPUs and 4 GB of memory; `--cpus 4`, `8` or `16` give it more. From here on, the machine is named by its id or by its name.

## Run a command

Terminal:

```sh
$ wm exec box -- uname -s
Linux
$ wm sh box 'echo hello > note.txt && ls -la | wc -l'
4
```

`wm exec` runs a program with its words, without a shell; `wm sh` runs a line of shell, with pipes and redirections. Both print what the command wrote, and exit with its exit code. Commands start in the machine's work folder.

## Copy a file in and out

Terminal:

```sh
$ wm cp ./data.csv box:work/
$ wm exec box -- wc -l data.csv
1201 data.csv
$ wm cp box:work/note.txt .
```

In a machine, a path begins `work/` (the work folder, where commands start) or `home/` (its home). A path that ends in `/` is a folder, and the file keeps its name in it.

## Pause it

Terminal:

```sh
$ wm machines pause box
ID                            NAME  STATE   SIZE         LOCATION      AGE
m_01jb2xk3n4p5q6r7s8t9v0w1x2  box   paused  2 CPU, 4 GB  Helsinki, FI  3m
```

Paused, it keeps its memory and no running time is charged. A command wakes it, and so does `wm machines start box`; it carries on where it was. A machine also pauses by itself once it has been idle for five minutes: see [Sleep when idle](https://docs.wheelmachine.ai/machines.html#sleep-when-idle).

## Delete it

Terminal:

```sh
$ wm machines rm box
Delete m_01jb2xk3n4p5q6r7s8t9v0w1x2 and all its files? [y/N] y
m_01jb2xk3n4p5q6r7s8t9v0w1x2
```

Deleting takes its files with it, for good. In a script, add `--yes`.

## The same, in code

The libraries and the API do the same steps. Each reads the key from `WHEELMACHINE_API_KEY`.

Python:

```python
from pathlib import Path
from wheelmachine import Wheelmachine

client = Wheelmachine()  # reads WHEELMACHINE_API_KEY
machine = client.machines.create(name="box")
machine = client.machines.wait(machine.id, "running")  # at once when it already runs

result = client.machines.exec(machine.id, ["uname", "-s"])
print(result.exit_code, result.stdout)

client.machines.write_file(machine.id, "work/data.csv", Path("data.csv"))
client.machines.read_file(machine.id, "work/data.csv", to="data-back.csv")

client.machines.pause(machine.id)
client.machines.delete(machine.id)
```

TypeScript:

```ts
import { Wheelmachine } from "wheelmachine";

const client = new Wheelmachine(); // reads WHEELMACHINE_API_KEY
let machine = await client.machines.create({ name: "box" });
machine = await client.machines.wait(machine.id, "running"); // at once when it already runs

const result = await client.machines.exec(machine.id, ["uname", "-s"]);
console.log(result.exit_code, result.stdout);

await client.machines.writeFile(machine.id, "work/data.csv", { path: "data.csv" });
await client.machines.readFile(machine.id, "work/data.csv", { to: "data-back.csv" });

await client.machines.pause(machine.id);
await client.machines.delete(machine.id);
```

curl:

```sh
API=https://api.wheelmachine.ai
AUTH="Authorization: Bearer $WHEELMACHINE_API_KEY"

# make a machine, and keep its id (jq reads it from the answer)
ID=$(curl -s -X POST $API/v1/machines -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name": "box"}' | jq -r .id)

# run a command
curl -s -X POST $API/v1/machines/$ID/exec -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"command": ["uname", "-s"]}'

# copy a file in, and back out
curl -s -T data.csv "$API/v1/machines/$ID/files?path=work/data.csv" -H "$AUTH"
curl -s "$API/v1/machines/$ID/files?path=work/data.csv" -H "$AUTH" -o data-back.csv

# pause it, then delete it
curl -s -X POST $API/v1/machines/$ID/pause -H "$AUTH"
curl -s -X DELETE $API/v1/machines/$ID -H "$AUTH"
```

## Next

- [Machines](https://docs.wheelmachine.ai/machines.html): sizes, states, sleeping when idle, copies and snapshots.
- [Commands and files](https://docs.wheelmachine.ai/commands-and-files.html): long work, output, root, environment.
- [Keys and scopes](https://docs.wheelmachine.ai/keys.html): a key of its own for each program and each agent.
- [Agents](https://docs.wheelmachine.ai/agents.html): give an agent machines through `wm-mcp`.
