# Cool Computers auth.md

You are an agent. This file is the standards-based WorkOS registration path for `https://api.cool.computer`. If you did not arrive here from a `401` challenge carrying `resource_metadata`, stop and use https://www.cool.computer/agents.md instead: an email code, no browser claim. If you did, follow the steps below in order and do not skip ahead; they end with a credential, and https://www.cool.computer/agents.md step 3 takes it from there.

## 1. Check whether you already have access

Stop at the first one that is true, and do not register:

1. `COOL_API_KEY` is set in your environment. Use it as the bearer token.
2. The `cool` CLI is installed and `cool whoami` succeeds. Use the CLI.
3. You are an MCP client and `https://api.cool.computer/mcp` is already connected. Use its tools.

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. Values for this service

| Name | Value |
| --- | --- |
| Issuer (`ISSUER`) | `https://worldly-butterfly-14.authkit.app` |
| Resource (`RESOURCE`) | `https://api.cool.computer` |
| Scope granted after the claim | `cool-computers:operate` |
| API base | `https://api.cool.computer/api` |
| API contract | https://api.cool.computer/openapi.json |

Cool Computers supports one registration method, `service_auth`: you register with the user's email, and the user approves you once in a browser. Anonymous registration and provider identity assertions are not supported.

## 3. Register and get an access token

The exact commands are in the WorkOS section at the end of this file. The shape is:

1. **Ask for the email first.** Use the email of the user's Cool Computers account, or the email they want a new account under. Do not guess. Only that account can complete the claim, and a wrong guess wastes the attempt.
2. Register (`service_auth`) and start a claim attempt. You get a `verification_uri`.
3. **Give the user that link and wait.** They sign in there, or choose Sign up if they are new, and the page shows them a short code. They must do this themselves: do not open the link in an automated browser, read their inbox, or complete sign-in for them. The link lasts five minutes and only two can be pending at once, so send it when the user is ready.
4. The user tells you the code. Complete the claim with it, then store the returned identity as the WorkOS section describes.
5. Exchange the assertion for an access token with `RESOURCE="https://api.cool.computer"`. This is required: a token exchanged without it is rejected with `401`.

## 4. Create an API key, once

The access token lasts five minutes. Use it once to create a durable API key, then use that key for everything else:

```bash
COOL_API_KEY="$(curl -sS https://api.cool.computer/api/api-keys \
  -H "Authorization: Bearer $ACCESS_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"<your agent name>"}' | jq -er '.api_key // empty')" || echo "key creation failed; see Errors"
```

The secret is returned exactly once, so do not retry this request automatically. Store the key where you store other secrets for this user (an OS keychain, or the `COOL_API_KEY` entry of a `.env` file the user has told you to use). Never print it, log it, commit it, or echo it back.

An access token can create a key but cannot list or revoke keys. The user does that with the `cool` CLI: `cool login`, then `cool api-keys`.

## 5. Continue in the runbook

You now hold `COOL_API_KEY`. Run `export COOL_API_KEY` and `TOKEN="$COOL_API_KEY"`, then go to https://www.cool.computer/agents.md step 3 to create the user's computer. Do not create one from here. A new user's account is created by your first authenticated call.

## Errors

| Response | Meaning | What to do |
| --- | --- | --- |
| `401` with `WWW-Authenticate` | Missing, expired, or wrong-audience credential. | Re-exchange the assertion with `RESOURCE` set. If the API key stopped working, it was revoked: drop it and register again. |
| `403 insufficient_scope` | The token lacks `cool-computers:operate`. | The claim did not finish. Complete step 3. |
| `403 interactive_required` | This route needs the user signed in. | Give the user https://www.cool.computer and wait. |
| `402` | The trial ended or the plan limit is reached. | Tell the user; see https://www.cool.computer/pricing.md. |
| `429` | Rate limited. | Wait for `Retry-After`. Do not loop. |
| `5xx`, or this file returns `502` | Temporary failure. | Back off and retry the same request. The exception is key creation in step 4: a key may already exist, so do not retry. Tell the user, and ask them to check `cool api-keys` before you create another. |

## Revocation

Revoke your API key with `POST https://api.cool.computer/api/auth/logout` using the key as the bearer, then delete your stored copy. The user can revoke your API key at any time with `cool api-keys`; you will see that as a `401`.

## Reference

- Other credential types and the CLI sign-in: https://www.cool.computer/authentication.md
- Agent index: https://www.cool.computer/llms.txt
- Developer resources: https://www.cool.computer/docs.md
- Pricing and limits: https://www.cool.computer/pricing.md

---

# auth.md — agent registration

You are an agent. This service allows you to register for an identity and exchange it for credentials.

Run these commands in order. **Don't print secret values and don't write your own scripts** — the commands below persist secrets and surface only non-secret fields. Commands are macOS (`curl`, `security`, `jq`); adapt as needed for Linux (`secret-tool`, reads stdin) or Windows (PowerShell `Set-Secret`/`Get-Secret`). Only `security` takes the secret on argv — `secret-tool`/`Set-Secret` don't, so don't carry its "rotate if exposed" caveat to them.

Set once:

```bash
ISSUER="https://worldly-butterfly-14.authkit.app"
SVC="${ISSUER#https://}"        # keychain service label (issuer host)
EMAIL="<user-email>"           # login_hint, and keychain account for email-based methods
```

## 1. Check for an existing registration — reuse before creating a new one

```bash
if   security find-generic-password -s "$SVC" -a "$EMAIL"  -w >/dev/null 2>&1; then ACCT="$EMAIL"
fi
# ACCT set -> skip to step 4 (Exchange). ACCT unset -> continue to step 2.
```

## 2. Register — pick the method that fits; capture into `$REG`, never print it

```bash
# service_auth — you have the user's email (claim ceremony required):
REG="$(curl -sS "$ISSUER/agent/identity" -H 'Content-Type: application/json' \
  -d "{\"type\":\"service_auth\",\"login_hint\":\"$EMAIL\"}")"
ACCT="$EMAIL"
# errors: invalid_request -> fix the body; invalid_login_hint -> fix EMAIL; service_auth_registration_disabled -> method not enabled for this environment.
```

**Don't persist `$REG` yet** — the service_auth response holds only a short-lived `claim.token`. Keep it in memory; persist after the claim verifies (step 3).

## 3. Claim ceremony — service_auth (required)

Mint an attempt and give the user its link. They sign in there and the page shows them a code; once they read it back to you, complete the claim with it.

```bash
CLAIM_TOKEN="$(printf '%s' "$REG" | jq -r .claim.token)"
ATT="$(curl -sS "$ISSUER/agent/identity/claim" -H 'Content-Type: application/json' \
  -d "{\"type\":\"service_auth\",\"claim_token\":\"$CLAIM_TOKEN\",\"login_hint\":\"$EMAIL\"}")"
# errors: invalid_claim_token -> restart at step 2; invalid_login_hint -> fix EMAIL; claim_expired | claim_revoked | already_claimed -> restart at step 2; auth_method_disabled -> method was disabled; too_many_attempts -> wait for a pending attempt to expire.

# give the user this link (non-secret); the code is shown to them on the page, not here:
printf '%s' "$ATT" | jq '{verification_uri:.attempt.verification_uri}'

# the user reads the code off that page and gives it to you; submit it with the claim token:
USER_CODE="<code the user read off the claim page>"
VER="$(curl -sS "$ISSUER/agent/identity/claim/complete" -H 'Content-Type: application/json' \
  -d "{\"claim_token\":\"$CLAIM_TOKEN\",\"user_code\":\"$USER_CODE\"}")"

# success returns the verified identity once — persist it; an error returns {code,message}:
printf '%s' "$VER" | jq -e .identity.assertion >/dev/null && security add-generic-password -U -s "$SVC" -a "$ACCT" -w "$VER"
# errors: claim_not_confirmed -> user hasn't finished on the page yet, wait and retry; invalid_user_code -> wrong code, ask the user again; user_code_expired -> re-run this step for a fresh link; claim_expired | already_claimed -> restart at step 2; claim_denied -> user denied the claim, restart at step 2; auth_method_disabled -> method was disabled; organization_selection_required | stale_organization_selection -> user must re-select an organization on the claim page.
```

## 4. Exchange the assertion for an access token

```bash
ASSERTION="$(security find-generic-password -s "$SVC" -a "$ACCT" -w | jq -r .identity.assertion)"
RESOURCE="<resource-uri>"   # optional — binds the token's aud to a resource this service recognizes; omit both resource lines for the default audience
CRED="$(curl -sS "$ISSUER/oauth2/token" -H 'Content-Type: application/x-www-form-urlencoded' \
  -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
  --data-urlencode "assertion=$ASSERTION" \
  --data-urlencode "resource=$RESOURCE")"
ACCESS_TOKEN="$(printf '%s' "$CRED" | jq -r .access_token)"
# errors: invalid_request -> assertion could not be decoded; invalid_grant -> assertion expired/revoked, restart at step 2; invalid_target -> resource URI not recognized; unsupported_grant_type -> use the grant above.
```

Send it as `Authorization: Bearer $ACCESS_TOKEN`. **Don't persist the access token** — it's short-lived (~5 minutes). When it expires, re-run this step from the stored assertion; when the assertion itself expires, refresh (step 5).

## 5. Refresh — when the stored assertion nears expiry

```bash
RT="$(security find-generic-password -s "$SVC" -a "$ACCT" -w | jq -r .identity.refresh_token.value)"
REG="$(curl -sS "$ISSUER/agent/identity" -H 'Content-Type: application/json' \
  -d "{\"type\":\"refresh\",\"refresh_token\":\"$RT\"}")"
security add-generic-password -U -s "$SVC" -a "$ACCT" -w "$REG"   # rotates the refresh token; overwrite
# errors: invalid_refresh_token -> restart at step 2.
```

Then re-run step 4 with the fresh assertion.

## Any request

`5xx` -> back off and retry the same request. `rate_limit_exceeded` -> wait `retry_after` seconds. A `4xx` not listed above -> fix per the response body; don't replay.
