# Authentication & scopes

The SDK authenticates with a Maritime API key (`mk_…`). It lives on *your* server and never touches a browser.

## Get a key

Mint one from the dashboard (**Settings → API keys**) or with `maritime keys create`. Set it as `MARITIME_API_KEY` and both SDKs pick it up automatically.

*Screenshot: A default key is full-access; open Advanced to scope it down.*

## Scopes

Every key carries scopes. Hand a subsystem the *narrowest* key it needs. A leaked key can then only do what its scope allows. `manage` is a wildcard.

| Scope       | Grants                                                                                                                                       |
|-------------|------------------------------------------------------------------------------|
| `provision` | create agents                                                                                                                           |
| `deploy`    | start / stop / restart / sleep / chat                                                                                                 |
| `secrets`   | read / write env vars                                                                                                                 |
| `manage`    | everything, incl. delete + key management (wildcard)                                                                                   |
| `computers` | the Computers product (MCP and /api/v1/computers); a key with only this scope reaches nothing else, and manage or full access reach Computers too |
| `sandboxes` | Sandboxes (create, run, snapshots, secrets, sandbox webhooks); manage reaches them too. Starting a swarm needs provision               |

## Mint a scoped key

A background worker that only chats to existing agents needs `deploy`, not `provision`, `secrets`, or `manage`. Minting keys itself requires a `manage` key.

Without `scopes` a key gets `provision`, `deploy`, `secrets` and `manage`; `computers` and `sandboxes` are never added for you (`manage` already covers them). Pass `projectId` (`project_id` in Python) to tie a key to one project: it then works only on that project's front door and usage routes and is refused everywhere else. Use it for a per-tenant or partner-facing service.

### TypeScript

```typescript
const worker = await maritime.keys.create({
  name: 'chat-worker',
  scopes: ['deploy'],   // chat/start/stop only
})
// worker.rawKey is shown once. Hand it to the worker as MARITIME_API_KEY.

const tenant = await maritime.keys.create({ name: 'tenant-a', projectId: agent.projectId })
```

### Python

```python
worker = client.keys.create("chat-worker", scopes=["deploy"])
# worker["raw_key"] is shown once. Hand it to the worker as MARITIME_API_KEY.

tenant = client.keys.create("tenant-a", project_id=agent["projectId"])
```

## Per-sandbox keys

To let code inside or next to one sandbox drive only that sandbox, mint a per-sandbox key (`msk_…`) with `keys.create({ sandbox })`. It lasts one day by default (60 seconds to 30 days) and its secret is returned once.

```typescript
const key = await maritime.keys.create({ sandbox: sandbox.id, ttlSeconds: 3600 })
// key.secret is shown once.
```

```python
key = client.keys.create(sandbox=sandbox.id, ttl_seconds=3600)
# key.secret is shown once.
```

> **Note:** A key sent as a session token (dashboard) and an admin key are always full-access. Scope enforcement applies to user `mk_` keys, so restricted keys are genuinely restricted.
