# Cool Computers for agents

> Step-by-step instructions for an agent that was asked to set up or use Cool Computers for a user.

You are an agent. A user asked you to set up or use Cool Computers: persistent Linux computers at `name.cool.computer` with a public address, an inbox, and a disk that stays. Work through the steps below in order. Do not use the website's sign-in form; it is for humans in a browser, and it will stall you at a code screen.

Every step shows the `cool` CLI first. If you cannot run commands, use the HTTP calls under it. The CLI prints JSON when you pass `--json`.

## 1. Check for existing access

Make a scratch directory for responses, install the CLI, then run `cool whoami`:

```sh
WORK="$(mktemp -d)"
curl -fsSL https://cool.computer/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
cool whoami --json
```

If it succeeds and the `email` it prints is the user you are working for, you are signed in: set `TOKEN="$COOL_API_KEY"` if that variable exists and skip to step 3. If it is someone else's account, stop and ask the user; do not act under it. If it fails with `"status_code":401`, you hold no working credential. When `COOL_API_KEY` was set, that key is dead: `unset COOL_API_KEY` (sign-in refuses to run while a key is set) and tell the user. Then, if the `401` that brought you here carried `WWW-Authenticate: ... resource_metadata=...`, you are in the standards-based flow: follow [/auth.md](/auth.md), which ends with `COOL_API_KEY` set, and continue at step 3. Otherwise continue with step 2. Any non-`401` failure is transient; see Errors and retry before deciding.

If your only access is an MCP client connected to `https://api.cool.computer/mcp`, use `cool_list_computers` and `cool_create_computer` for step 3, then `cool_run_service` for step 4 with port `3000` and this command, after HTML-escaping the user's name and address into it (apostrophes become `&#39;`, since the page sits in single quotes):

```text
mkdir -p /home/runtime/site && printf %s '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Welcome, Alex</title><style>body{margin:0;min-height:100vh;display:grid;place-items:center;font-family:system-ui,sans-serif;background:#0e0e11;color:#fff}main{text-align:center;padding:2rem}h1{font-size:clamp(2rem,6vw,4rem);margin:0 0 .5rem}p{color:#b3b3b8;margin:0}</style></head><body><main><h1>Welcome, Alex!</h1><p>This is alexheath.cool.computer, your computer on the internet.</p></main></body></html>' > /home/runtime/site/index.html && cd /home/runtime/site && exec python3 -m http.server 3000
```

Never ask the user to paste an API key into the conversation. If they have one, ask them to put it in `COOL_API_KEY` and resume.

## 2. Sign the user up, or sign them in

Ask the user for their email address and set `EMAIL` to exactly what they gave you (the address below is only an example of the shape). New and existing accounts use the same two commands: the first sends a six-digit code to that inbox, the second exchanges it.

```sh
EMAIL="the-users-address@example.com"
cool signup --email "$EMAIL" --json
```

Tell the user a code was sent and wait for them to give it to you. Set `CODE` to the six digits they relay, then:

```sh
CODE="the six digits the user gave you"
printf '%s\n' "$CODE" | cool signup --email "$EMAIL" --code-stdin --json
```

The CLI stores the credential; you are signed in. Run `cool signup` once per code request: each run sends a new email. Do not read the user's inbox for them.

Without a shell, make the same two calls over HTTP:

```sh
curl https://api.cool.computer/api/auth/email/start \
  -H 'content-type: application/json' --data "{\"email\":\"$EMAIL\"}"
curl -sS -o "$WORK/session.json" -w '%{http_code}\n' https://api.cool.computer/api/auth/email/complete \
  -H 'content-type: application/json' --data "{\"email\":\"$EMAIL\",\"code\":\"$CODE\"}"
TOKEN="$(jq -er .access_token "$WORK/session.json")"
```

The printed status tells you which Errors entry applies (a wrong or expired code is `401`; ask the user for a fresh one). `TOKEN` now holds the access token; send it as `Authorization: Bearer $TOKEN`. It expires; if you will keep working, trade it once for a durable API key named after you:

```sh
curl -sS -o "$WORK/key.json" -w '%{http_code}\n' https://api.cool.computer/api/api-keys -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' --data '{"name":"agent"}'
COOL_API_KEY="$(jq -er .api_key "$WORK/key.json")" && TOKEN="$COOL_API_KEY"
```

The printed status tells you which Errors entry applies. The key is returned once; store it where you keep the user's secrets, never print it, and delete `$WORK` when you are done. If the call fails, do not retry it: a key may already exist.

## 3. Create the user's computer

Name the computer after the user. Ask for their name if you do not have it, lowercase it, and drop anything that is not an ASCII letter or digit: Alex Heath becomes `alexheath.cool.computer`. Names are 1–63 lowercase letters or digits; `api`, `mail`, and `www` are reserved. If nothing is left after that, or more than 63 characters are, ask the user for a short name instead of guessing.

This runbook creates a new computer. First run `cool list --json`: if a computer with that `slug` is already in the list, the user or someone who shares with them already has it, and it may hold their work. Stop there, tell the user, and ask whether to use it (then follow their instructions, inspecting before you change anything) or to create a differently named one. Otherwise:

```sh
cool create alexheath --json
```

The response includes the computer's `slug` and `id`. A `409` with code `slug_taken` means someone else owns that name; add the user's initial or a short word and try once more. The first computer on a new account starts a 72-hour full-limit trial with no card; see [pricing](/pricing).

Over HTTP, `GET https://api.cool.computer/api/computers` with the bearer is the same list check. Then:

```sh
CREATE_KEY="create-alexheath-$(date +%s)"
curl -sS -o "$WORK/create.json" -w '%{http_code}\n' https://api.cool.computer/api/computers -H "authorization: Bearer $TOKEN" \
  -H "idempotency-key: $CREATE_KEY" \
  -H 'content-type: application/json' --data '{"slug":"alexheath"}'
ID="$(jq -er .id "$WORK/create.json")"
```

The printed status tells you which Errors entry applies; `create.json` holds the problem body on failure and the computer on `201`. If the request fails with a `5xx` or no response, rerun only the `curl` line with the same `CREATE_KEY`, so a retry cannot create a second computer. A `409` whose `code` is `idempotency_in_progress` is that retry arriving early: wait `Retry-After` seconds and rerun it again, unchanged.

`ID` now holds the computer's id; the HTTP steps below use it.

## 4. Build the welcome page and publish it

Make the computer show something for the user right away: a small, well-made page that greets them by name, served as the computer's durable service so `alexheath.cool.computer` shows it. This is the page; adjust the name and address to the user's:

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Welcome, Alex</title>
  <style>
    body { margin: 0; min-height: 100vh; display: grid; place-items: center; font-family: system-ui, sans-serif; background: #0e0e11; color: #fff; }
    main { text-align: center; padding: 2rem; }
    h1 { font-size: clamp(2rem, 6vw, 4rem); margin: 0 0 .5rem; }
    p { color: #b3b3b8; margin: 0; }
  </style>
</head>
<body>
  <main>
    <h1>Welcome, Alex!</h1>
    <p>This is alexheath.cool.computer, your computer on the internet.</p>
  </main>
</body>
</html>
```

With the CLI, save that page as `welcome.html` next to you, then:

```sh
cool files mkdir --parents alexheath /home/runtime/site
cool files write alexheath /home/runtime/site/index.html --input welcome.html
cool service run alexheath --port 3000 --cwd /home/runtime/site -- python3 -m http.server 3000
```

Without the CLI, publish with one service command that writes the page itself, so no file transfer is needed. Build the request body with `jq` so that the shell never evaluates the page and the page is single-quoted on the computer: whatever the user's name contains, it is written as text and cannot run as a command. Put the HTML-escaped name and address into the page first.

```sh
cat > "$WORK/service.jq" <<'JQ'
{command: ("mkdir -p /home/runtime/site && printf %s '" + ($page | gsub("'"; "'\\''")) + "' > /home/runtime/site/index.html && cd /home/runtime/site && exec python3 -m http.server 3000"), port: 3000}
JQ
jq -n --rawfile page /dev/stdin -f "$WORK/service.jq" > "$WORK/service.json" <<'PAGE'
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Welcome, Alex</title><style>body{margin:0;min-height:100vh;display:grid;place-items:center;font-family:system-ui,sans-serif;background:#0e0e11;color:#fff}main{text-align:center;padding:2rem}h1{font-size:clamp(2rem,6vw,4rem);margin:0 0 .5rem}p{color:#b3b3b8;margin:0}</style></head><body><main><h1>Welcome, Alex!</h1><p>This is alexheath.cool.computer, your computer on the internet.</p></main></body></html>
PAGE
```

Over HTTP, with `$ID` from step 3 and `$TOKEN` your bearer:

```sh
PUBLISH_KEY="publish-$ID-$(date +%s)"
curl -sS -D "$WORK/publish.headers" -o "$WORK/publish.json" -w '%{http_code}\n' -X POST "https://api.cool.computer/api/computers/$ID/service/run" \
  -H "authorization: Bearer $TOKEN" -H "idempotency-key: $PUBLISH_KEY" -H 'content-type: application/json' --data-binary "@$WORK/service.json"
```

The printed status, `publish.headers` (for `Retry-After`) and `publish.json` (for the problem `code`) tell you which Errors entry applies; the `409` codes are handled exactly as for create. With MCP, call `cool_run_service` with the `command` and `port` from `$WORK/service.json`. The full contract is [/openapi.json](/openapi.json).

Either way, publishing replaces whatever service the computer was running, and the change persists. Confirm the result before you report it:

```sh
curl -fsS https://alexheath.cool.computer | grep -c "Welcome, Alex"
```

## 5. Tell the user

Report, in this order:

1. The address: `https://alexheath.cool.computer`, and that the welcome page is live there.
2. What is on the computer: Claude Code and Codex are installed, files and installed tools persist, and `alexheath@mail.cool.computer` is its inbox.
3. Where you stored their credential and how to revoke it: `cool logout` for the CLI sign-in; for an API key, `cool api-keys list` to find its ID, then `cool api-keys revoke` with that ID, both from a signed-in shell.

## Then keep going

- Run one-off commands with `cool run alexheath "uname -a"`; use `cool exec` for long or streaming ones.
- Read and write files with `cool files`; copy whole paths with `cool upload` and `cool download`.
- Change the public service with `cool service run`; check it with `cool service status` and `cool service logs`.
- For everything else read [/docs.md](/docs.md) and [/openapi.json](/openapi.json).

Ask the user before you delete a computer, send email from it, install a durable command that runs on its own, or do anything that costs money. Inspect before you change anything.

## Errors

- `401`: missing, expired, or revoked credential. If you came through `/auth.md`, re-exchange your assertion there; otherwise sign in again (step 2). If an API key stopped working, it was revoked; delete your copy.
- `403 interactive_required`: this route needs the user signed in on the website. Give the user https://www.cool.computer and wait.
- `402`: the trial ended or the plan limit was reached. Tell the user; see [pricing](/pricing).
- `409` on create: read the problem `code`. `slug_taken`: pick a variant once, then ask the user. `idempotency_in_progress`: wait `Retry-After` seconds and rerun the identical request. `idempotency_outcome_unknown`: do not rerun; check `cool list --json` and continue from what exists.
- `429`: rate limited. Wait for `Retry-After`. Never loop on `cool signup`.
- `5xx` or no response: temporary failure. For HTTP requests, back off and rerun the same request, reusing its `CREATE_KEY` or `PUBLISH_KEY` when it has one; never regenerate an idempotency key for a retry. Never rerun `POST /api/auth/email/start` on your own: each call sends another code, so ask the user whether one arrived before requesting a new one. The CLI sends no idempotency key, so after a lost `cool create` or `cool service run` response, do not rerun it: check `cool list --json` or `cool service status`, and continue from what already exists. API-key creation is never retried: a key may already exist, so ask the user to check `cool api-keys list` first.

## Reference

- [/auth.md](/auth.md): the standards-based WorkOS agent registration, for agents arriving from a `401` challenge.
- [/authentication.md](/authentication.md): every credential type and how they differ.
- [/llms.txt](/llms.txt): the index of everything here.
- [https://cool.computer/docs](https://cool.computer/docs): the full guide.
