# Setup

## Why two tokens?

AI agents are powerful but they shouldn't hold the keys to the kingdom. Clawdflare splits Cloudflare access into two tiers:

| | Read token | Write token |
|---|---|---|
| **Stored** | Environment variable | Encrypted on disk (AES-256-GCM) |
| **Access** | AI agent uses freely | User enters PIN into native OS popup |
| **Visible to AI?** | Yes (env var) | Never — decrypted in-process, used once, discarded |
| **Permissions** | Zone: Read | Zone Settings: Edit |
| **Risk if leaked** | Someone can see your DNS records | Someone can change your SSL mode to "off" |

The read token lets the AI audit, inspect, and report. That's safe — it's read-only, and the whole point of an MCP is to let the AI see what's going on.

The write token is different. A misconfigured SSL mode, a purged cache at the wrong time, or a disabled HSTS header can take a site down or expose traffic. So write operations require you to physically authorize each one by entering a PIN into a native OS dialog that the AI cannot intercept, read, or replay.

### What the AI sees during a write operation

```
tool_call: clawdflare_fix(zone="example.com", dry_run=False)
→ [native OS dialog appears on screen, user enters PIN]
→ result: {"zone": "example.com", "fixed": ["ssl", "min_tls_version"], "errors": []}
```

The AI sees the result. It never sees the PIN, the decrypted token, or the HTTP Authorization header. The token exists in memory for the duration of the API call and is deleted immediately after.

### How the PIN dialog works

The PIN prompt runs as a **separate subprocess** on every platform. The AI agent's process never handles raw keystrokes — the PIN travels from the OS dialog's subprocess stdout directly into vault decryption code that never returns it to the MCP layer.

| Platform | Dialog used | How it runs |
|---|---|---|
| **macOS** | AppleScript (`osascript`) | Native "hidden answer" dialog via System Events |
| **Linux (GNOME)** | `zenity --password` | GTK password dialog, auto-detected |
| **Linux (KDE)** | `kdialog --password` | Qt password dialog, auto-detected |
| **Windows** | PowerShell `Get-Credential` | Native Windows credential dialog |
| **Fallback** | Python `tkinter` | Cross-platform, runs as subprocess with masked input |

On Linux, clawdflare checks for `zenity` first, then `kdialog`, then falls back to `tkinter`. No extra dependencies needed — at least one of these is available on any desktop Linux.

On headless/SSH environments where no GUI is available, write operations will fail with a clear error. This is by design — if there's no screen, there's no human to authorize.

### Threat model

This protects against:

- **Agent autonomy risk** — an AI deciding to "fix" something you didn't ask it to fix. The popup forces a human in the loop.
- **Prompt injection** — a malicious tool result or context can't trick the AI into using a write token it doesn't have.
- **Token leakage via logs** — the write token never appears in MCP tool results, CLI output, or agent conversation history.
- **Credential theft from env vars** — the write token isn't in your environment. It's encrypted at rest with a key only you know.

This does NOT protect against:
- Someone with physical access to your machine who also knows your PIN
- A compromised OS where the dialog subprocess itself is intercepted
- You entering the PIN when you shouldn't (read the dry-run output first)

## Step 1: Create Cloudflare API tokens

Go to [dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens) and create **two** tokens.

### Read-only token

- **Token name**: clawdflare-read
- **Permissions**: Zone → Zone → Read, Zone → Zone Settings → Read
- **Zone resources**: Include → All zones (or specific zones)

### Write token

- **Token name**: clawdflare-write
- **Permissions**: Zone → Zone Settings → Edit, Zone → Zone → Read
- **Zone resources**: Include → All zones (or specific zones)

Keep the write token — you'll need it in step 3. The read token goes into your environment.

## Step 2: Set the read-only token

### macOS / Linux

Add to your shell profile (`~/.zshrc`, `~/.bashrc`, etc.):

```bash
export CLOUDFLARE_API_TOKEN="your-read-only-token-here"
```

Reload your shell:

```bash
source ~/.zshrc
```

### Windows

```powershell
# User-level env var (persists across sessions)
[Environment]::SetEnvironmentVariable("CLOUDFLARE_API_TOKEN", "your-read-only-token-here", "User")
```

Or set it via System Settings → Environment Variables.

This is the token the AI uses for all read operations. It's intentionally limited — even if it leaks, no one can change anything.

## Step 3: Encrypt the write token

```bash
clawdflare setup-token
```

This will prompt you for:
1. Your Cloudflare **write** token (hidden input)
2. A **PIN** of your choosing (hidden input, minimum 4 characters)
3. PIN confirmation

The token is encrypted with AES-256-GCM using a key derived from your PIN (PBKDF2, 600k iterations). The encrypted vault is saved to `~/.config/clawdflare/vault.enc` (permissions set to `0600` on Unix).

**Choose a PIN you can type quickly.** You'll enter it into a popup every time you authorize a write operation. It doesn't need to be long — it's protecting a local file, not a remote service. 4-6 digits or a short word works fine.

## Step 4: Verify

```bash
# Read ops should work immediately
clawdflare zones
clawdflare audit example.com

# Write ops will pop up a PIN dialog
clawdflare fix example.com --apply
```

## Step 5: Add to Claude Code (optional)

Add clawdflare as an MCP server in your Claude Code settings (`~/.claude/settings.json`):

```json
{
  "mcpServers": {
    "clawdflare": {
      "command": "clawdflare",
      "args": ["serve"],
      "env": {
        "CLOUDFLARE_API_TOKEN": "your-read-only-token-here"
      }
    }
  }
}
```

Now the AI can audit your zones, check SSL settings, and list DNS records freely. When it needs to fix something, you'll see a native dialog asking for your PIN.

## Rotating tokens

### Rotate the read token
1. Create a new read-only token in Cloudflare
2. Update `CLOUDFLARE_API_TOKEN` in your shell profile (or Windows env var)
3. Revoke the old token in Cloudflare

### Rotate the write token
1. Create a new write token in Cloudflare
2. Run `clawdflare setup-token` (it will ask to overwrite the existing vault)
3. Revoke the old token in Cloudflare

### Change your PIN
Run `clawdflare setup-token` again. You'll need your write token handy — the old PIN can't be used to recover it (by design).

## Troubleshooting

**"CLOUDFLARE_API_TOKEN not set"** — your read token isn't in the environment. Check `echo $CLOUDFLARE_API_TOKEN` (Unix) or `$env:CLOUDFLARE_API_TOKEN` (PowerShell).

**"No encrypted token found"** — you haven't run `clawdflare setup-token` yet.

**"Wrong PIN or corrupted vault"** — you entered the wrong PIN. Try again. If you've forgotten it, run `clawdflare setup-token` to re-encrypt with a new PIN (you'll need the Cloudflare write token again).

**"Write operation cancelled by user"** — you clicked Cancel on the PIN dialog. That's the system working as intended.

**PIN dialog doesn't appear (macOS)** — make sure System Events has accessibility permissions. Check System Settings → Privacy & Security → Accessibility.

**PIN dialog doesn't appear (Linux)** — install `zenity` (`sudo apt install zenity`) or `kdialog` (`sudo apt install kdialog`). If neither is available, clawdflare falls back to `tkinter`, which requires a display (`$DISPLAY` or Wayland session).

**PIN dialog doesn't appear (Windows)** — PowerShell must be available (it is on all modern Windows). If `Get-Credential` is blocked by policy, clawdflare falls back to `tkinter`.
