# Gateway keys

> The keys that open Kuiper's models, browsers and sandboxes. Units on Kuiper get theirs from a variable; agents and scripts elsewhere use keys you create and revoke.

Web page: https://kuiper.sh/docs/gateway-keys/

## What a gateway key is

Kuiper's [model gateway](https://kuiper.sh/docs/models.md), [browsers](https://kuiper.sh/docs/browsers.md) and [sandboxes](https://kuiper.sh/docs/sandboxes.md) are served by the gateway, `gateway.kuiper.sh`. Every request to it carries a **gateway key**, `kgw_…`. There are two kinds:

- **Unit keys** belong to units on Kuiper. You never see or copy one. A variable references `${{models.API_KEY}}`, `${{browsers.API_KEY}}` or `${{sandboxes.API_KEY}}`, and each running copy of the unit gets its own key.
- **Your keys** are for agents and scripts that run elsewhere: your laptop, CI or another cloud. You create them in the dashboard or with the CLI.

[API tokens](https://kuiper.sh/docs/teams/#api-tokens) (`kpr_…`) are not gateway keys, and the gateway refuses them. A key that leaks from a browser script can never act as an admin token.

## Units on Kuiper

Name the variables your code reads and point them at the built-in `models`, `browsers` or `sandboxes` resource:

```sh
kuiper env set -p proxima -u agent \
  'STEEL_BASE_URL=${{browsers.BASE_URL}}' \
  'STEEL_CONNECT_URL=${{browsers.CONNECT_URL}}' \
  'STEEL_API_KEY=${{browsers.API_KEY}}' \
  'SANDBOX_URL=${{sandboxes.BASE_URL}}' \
  'SANDBOX_KEY=${{sandboxes.API_KEY}}' \
  'MODELS_BASE_URL=${{models.BASE_URL}}' \
  'MODELS_API_KEY=${{models.API_KEY}}'
kuiper restart -p proxima -u agent
```

| Output | Value |
| --- | --- |
| `models.BASE_URL` | `https://gateway.<region>.kuiper.sh/models/v1`: the base URL for Glove and OpenAI's SDK |
| `models.ANTHROPIC_BASE_URL` | `https://gateway.<region>.kuiper.sh/models`: the base URL for Anthropic's SDK |
| `models.GEMINI_BASE_URL` | `https://gateway.<region>.kuiper.sh/models`: the base URL for Google's Gen AI SDK |
| `models.API_KEY` | The unit's key (secret) |
| `browsers.BASE_URL` | `https://gateway.<region>.kuiper.sh/browsers`, the gateway in the unit's region |
| `browsers.CONNECT_URL` | `wss://gateway.<region>.kuiper.sh/browsers/connect` |
| `browsers.API_KEY` | The unit's key (secret) |
| `sandboxes.BASE_URL` | `https://gateway.<region>.kuiper.sh/sandboxes` |
| `sandboxes.API_KEY` | The unit's key (secret) |

A unit in no region gets `gateway.kuiper.sh` without the region.

- **No setup.** `models`, `browsers` and `sandboxes` exist as soon as a variable references them, like `secrets`. No unit, database, bucket or resource can be called `models`, `browsers` or `sandboxes`.
- **One key per running copy.** A redeploy, a move or a restart gets a new key. The old one stays valid while the old copy shuts down, so it can release its browsers and sandboxes, and is revoked once that copy has stopped. A unit that references several of these resources gets one key for all of them.
- **Secret.** A variable that references `API_KEY` is secret, so logs redact it. A deployment's record shows `${{browsers.BASE_URL}}` and the like, never the key.
- **Templates mix text and references**, as in `'STEEL_URL=${{browsers.BASE_URL}}/v1'`. See [Variables and secrets](https://kuiper.sh/docs/variables.md).

## Keys for use outside Kuiper

```sh
kuiper keys create laptop -p proxima --expires 30d     # printed once
kuiper keys list -p proxima                             # name, prefix, last used, status
kuiper keys revoke gk_… -p proxima                      # refused within seconds
```

In the dashboard, the project's **Settings**, then **Gateway keys**, does the same. A key is shown once and stored only as a hash. Owners and admins create and revoke keys; members see the list.

- `--services` chooses what a key opens: `browsers,sandboxes,models` (the default), or any of them, such as `--services models` for a key that only calls models. Keys made before the model gateway keep what they had.
- `--expires` takes `12h`, `30d` or `2w`. Without it, a key lasts until it is revoked.
- Each key has an ID, `gk_…`, which `revoke` and [budgets](https://kuiper.sh/docs/budgets.md) use. The key itself, `kgw_…`, is the secret.

Send a key in any of these headers:

- `Authorization: Bearer kgw_…`
- `x-api-key: kgw_…`
- `steel-api-key: kgw_…`, which is what Steel's SDKs send

To check a key:

```sh
curl -H "Authorization: Bearer $KEY" https://gateway.kuiper.sh/v1/whoami
```

It answers with the key's project, its unit for a unit key, its kind and its services, or 401.

## Revocation and outages

- A revoked key is refused by every gateway within seconds.
- A new key works at once.
- If Kuiper's control plane is briefly unavailable, the gateways keep serving the keys they know and keep a record of usage to send later. New keys can't be created until it is back.
