branch:
configuration.md
13348 bytesRaw
# Configuration

This guide covers everything you need to configure agents for local development and production deployment, including wrangler.jsonc setup, type generation, environment variables, and the Cloudflare dashboard.

## wrangler.jsonc

The `wrangler.jsonc` file configures your Cloudflare Worker and its bindings. Here's a complete example for an agents project:

```jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-agent-app",
  "main": "src/server.ts",
  "compatibility_date": "2025-01-01",
  "compatibility_flags": ["nodejs_compat"],

  // Static assets (optional)
  "assets": {
    "directory": "public",
    "binding": "ASSETS"
  },

  // Durable Object bindings for agents
  "durable_objects": {
    "bindings": [
      {
        "name": "MyAgent",
        "class_name": "MyAgent"
      },
      {
        "name": "ChatAgent",
        "class_name": "ChatAgent"
      }
    ]
  },

  // Required: Enable SQLite storage for agents
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["MyAgent", "ChatAgent"]
    }
  ],

  // AI binding (optional, for Workers AI)
  "ai": {
    "binding": "AI"
  }
}
```

### Key Fields

#### compatibility_flags

The `nodejs_compat` flag is **required** for agents:

```jsonc
"compatibility_flags": ["nodejs_compat"]
```

This enables Node.js compatibility mode, which agents depend on for crypto, streams, and other Node.js APIs.

#### durable_objects.bindings

Each agent class needs a binding:

```jsonc
"durable_objects": {
  "bindings": [
    {
      "name": "Counter",      // Property name on `env` (env.Counter)
      "class_name": "Counter" // Exported class name (must match exactly)
    }
  ]
}
```

| Field        | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| `name`       | The property name on `env`. Use this in code: `env.Counter` |
| `class_name` | Must match the exported class name exactly                  |

**When name and class_name differ:**

```jsonc
{
  "name": "COUNTER_DO", // env.COUNTER_DO
  "class_name": "CounterAgent" // export class CounterAgent
}
```

This is useful when you want environment variable-style naming (`COUNTER_DO`) but more descriptive class names (`CounterAgent`).

#### migrations

Migrations tell Cloudflare how to set up storage for your Durable Objects:

```jsonc
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["MyAgent"]
  }
]
```

| Field                | Description                                                   |
| -------------------- | ------------------------------------------------------------- |
| `tag`                | Version identifier (e.g., "v1", "v2"). Must be unique         |
| `new_sqlite_classes` | Agent classes that use SQLite storage (state persistence)     |
| `deleted_classes`    | Classes being removed                                         |
| `renamed_classes`    | Classes being renamed (see [Migrations](#migrations-1) below) |

#### assets

For serving static files (HTML, CSS, JS):

```jsonc
"assets": {
  "directory": "public",  // Folder containing static files
  "binding": "ASSETS"     // Optional: binding for programmatic access
}
```

With a binding, you can serve assets programmatically:

```typescript
export default {
  async fetch(request: Request, env: Env) {
    // static assets are served by the worker automatically by default

    // route the request to the appropriate agent
    const agentResponse = await routeAgentRequest(request, env);
    if (agentResponse) return agentResponse;

    // add your own routing logic here if you want to handle requests that are not for agents
    return new Response("Not found", { status: 404 });
  }
};
```

#### ai

For Workers AI integration:

```jsonc
"ai": {
  "binding": "AI",
  "remote": true  // Mandatory: use remote inference (for local dev)
}
```

Access in your agent:

```typescript
const response = await this.env.AI.run("@cf/moonshotai/kimi-k2.5", {
  prompt: "Hello!"
});
```

## Generating Types

Wrangler can generate TypeScript types for your bindings.

### Automatic Generation

Run the types command:

```bash
npx wrangler types
```

This creates or updates `worker-configuration.d.ts` with your `Env` type.

### Custom Output Path

Specify a custom path:

```bash
npx wrangler types env.d.ts
```

### Without Runtime Types

For cleaner output (recommended for agents):

```bash
npx wrangler types env.d.ts --include-runtime false
```

This generates just your bindings without Cloudflare runtime types.

### Example Generated Output

```typescript
// env.d.ts (generated)
declare namespace Cloudflare {
  interface Env {
    OPENAI_API_KEY: string;
    Counter: DurableObjectNamespace<import("./src/server").Counter>;
    ChatAgent: DurableObjectNamespace<import("./src/server").ChatAgent>;
  }
}
interface Env extends Cloudflare.Env {}
```

### Manual Type Definition

You can also define types manually:

```typescript
// env.d.ts
import type { Counter } from "./src/agents/counter";
import type { ChatAgent } from "./src/agents/chat";

interface Env {
  // Secrets
  OPENAI_API_KEY: string;
  WEBHOOK_SECRET: string;

  // Agent bindings
  Counter: DurableObjectNamespace<Counter>;
  ChatAgent: DurableObjectNamespace<ChatAgent>;

  // Other bindings
  AI: Ai;
  ASSETS: Fetcher;
  MY_KV: KVNamespace;
}
```

### Adding to package.json

Add a script for easy regeneration:

```json
{
  "scripts": {
    "types": "wrangler types env.d.ts --include-runtime false"
  }
}
```

## Environment Variables & Secrets

### Local Development (.env)

Create a `.env` file for local secrets (add to `.gitignore`):

```bash
# .env
OPENAI_API_KEY=sk-...
GITHUB_WEBHOOK_SECRET=whsec_...
DATABASE_URL=postgres://...
```

Access in your agent:

```typescript
class MyAgent extends Agent<Env> {
  async onStart() {
    const apiKey = process.env.OPENAI_API_KEY;
  }
}
```

### Production Secrets

Use `wrangler secret` for production:

```bash
# Add a secret
wrangler secret put OPENAI_API_KEY
# Enter value when prompted

# List secrets
wrangler secret list

# Delete a secret
wrangler secret delete OPENAI_API_KEY
```

### Non-Secret Variables

For non-sensitive configuration, use `vars` in wrangler.jsonc:

```jsonc
{
  "vars": {
    "API_BASE_URL": "https://api.example.com",
    "MAX_RETRIES": "3",
    "DEBUG_MODE": "false"
  }
}
```

Note: All values must be strings. Parse numbers/booleans in code:

```typescript
const maxRetries = parseInt(process.env.MAX_RETRIES, 10);
const debugMode = process.env.DEBUG_MODE === "true";
```

### Environment-Specific Variables

Use `[env.{name}]` sections for different environments (e.g. staging, production):

```jsonc
{
  "name": "my-agent",
  "vars": {
    "API_URL": "https://api.example.com"
  },

  "env": {
    "staging": {
      "vars": {
        "API_URL": "https://staging-api.example.com"
      }
    },
    "production": {
      "vars": {
        "API_URL": "https://api.example.com"
      }
    }
  }
}
```

Deploy to specific environment:

```bash
wrangler deploy --env staging
wrangler deploy --env production
```

## Local Development

### Starting the Dev Server

With Vite (recommended for full stack apps):

```bash
npx vite dev
```

Without Vite:

```bash
npx wrangler dev
```

### Local State Persistence

Durable Object state is persisted locally in `.wrangler/state/`:

```
.wrangler/
└── state/
    └── v3/
        └── d1/
            └── miniflare-D1DatabaseObject/
                └── ... (SQLite files)
```

### Clearing Local State

To reset all local Durable Object state:

```bash
rm -rf .wrangler/state
```

Or restart with fresh state:

```bash
npx wrangler dev --persist-to=""
```

### Inspecting Local SQLite

You can inspect agent state directly:

```bash
# Find the SQLite file
ls .wrangler/state/v3/d1/

# Open with sqlite3
sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite
```

## Dashboard Setup

### Automatic Resources

When you deploy, Cloudflare automatically creates:

- **Worker** - Your deployed code
- **Durable Object namespaces** - One per agent class
- **SQLite storage** - Attached to each namespace

### Viewing Durable Objects

1. Go to [dash.cloudflare.com](https://dash.cloudflare.com)
2. Select your account → Workers & Pages
3. Click your Worker
4. Go to **Durable Objects** tab

Here you can:

- See all Durable Object namespaces
- View individual object instances
- Inspect storage (keys and values)
- Delete objects

### Real-time Logs

View live logs from your agents:

```bash
npx wrangler tail
```

Or in the dashboard:

1. Go to your Worker
2. Click **Logs** tab
3. Enable real-time logs

Filter by:

- Status (success, error)
- Search text
- Sampling rate

### Analytics

The dashboard shows:

- Request count
- Error rate
- CPU time
- Duration percentiles
- Durable Object metrics

## Production Deployment

### Basic Deploy

```bash
npx wrangler deploy
```

This:

1. Bundles your code
2. Uploads to Cloudflare
3. Applies migrations
4. Makes it live on `*.workers.dev`

### Custom Domain

Add a route in wrangler.jsonc:

```jsonc
{
  "routes": [
    {
      "pattern": "agents.example.com/*",
      "zone_name": "example.com"
    }
  ]
}
```

Or use a custom domain (simpler):

```jsonc
{
  "routes": [
    {
      "pattern": "agents.example.com",
      "custom_domain": true
    }
  ]
}
```

### Preview Deployments

Deploy without affecting production:

```bash
npx wrangler deploy --dry-run    # See what would be uploaded
npx wrangler versions upload     # Upload new version
npx wrangler versions deploy     # Gradually roll out
```

### Rollbacks

Roll back to a previous version:

```bash
npx wrangler rollback
```

## Multi-Environment Setup

### Environment Configuration

Define environments in wrangler.jsonc:

```jsonc
{
  "name": "my-agent",
  "main": "src/server.ts",

  // Base configuration (shared)
  "compatibility_date": "2025-01-01",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],

  // Environment overrides
  "env": {
    "staging": {
      "name": "my-agent-staging",
      "vars": {
        "ENVIRONMENT": "staging"
      }
    },
    "production": {
      "name": "my-agent-production",
      "vars": {
        "ENVIRONMENT": "production"
      }
    }
  }
}
```

### Deploying to Environments

```bash
# Deploy to staging
npx wrangler deploy --env staging

# Deploy to production
npx wrangler deploy --env production

# Set secrets per environment
npx wrangler secret put OPENAI_API_KEY --env staging
npx wrangler secret put OPENAI_API_KEY --env production
```

### Separate Durable Objects

Each environment gets its own Durable Objects. Staging agents don't share state with production agents.

To explicitly separate:

```jsonc
{
  "env": {
    "staging": {
      "durable_objects": {
        "bindings": [
          {
            "name": "MyAgent",
            "class_name": "MyAgent",
            "script_name": "my-agent-staging" // Different namespace
          }
        ]
      }
    }
  }
}
```

## Migrations

Migrations manage Durable Object storage schema changes.

### Adding a New Agent

Add to `new_sqlite_classes` in a new migration:

```jsonc
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["ExistingAgent"]
  },
  {
    "tag": "v2",
    "new_sqlite_classes": ["NewAgent"]
  }
]
```

### Renaming an Agent Class

Use `renamed_classes`:

```jsonc
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["OldName"]
  },
  {
    "tag": "v2",
    "renamed_classes": [
      {
        "from": "OldName",
        "to": "NewName"
      }
    ]
  }
]
```

**Important:** Also update:

1. The class name in code
2. The `class_name` in bindings
3. Export statements

### Deleting an Agent Class

Use `deleted_classes`:

```jsonc
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["AgentToDelete", "AgentToKeep"]
  },
  {
    "tag": "v2",
    "deleted_classes": ["AgentToDelete"]
  }
]
```

**Warning:** This permanently deletes all data for that class.

### Migration Best Practices

1. **Never modify existing migrations** - Always add new ones
2. **Use sequential tags** - v1, v2, v3 (or use dates: 2025-01-15)
3. **Test locally first** - Migrations run on deploy
4. **Back up production data** - Before renaming or deleting

## Troubleshooting

### "No such Durable Object class"

The class isn't in migrations:

```jsonc
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["MissingClassName"]  // Add it here
  }
]
```

### "Cannot find module" in types

Regenerate types:

```bash
npx wrangler types env.d.ts --include-runtime false
```

### Secrets not loading locally

Check that `.env` exists and contains the variable:

```bash
cat .env
# Should show: MY_SECRET=value
```

### Migration tag conflict

Migration tags must be unique. If you see conflicts:

```jsonc
// Wrong - duplicate tags
"migrations": [
  { "tag": "v1", "new_sqlite_classes": ["A"] },
  { "tag": "v1", "new_sqlite_classes": ["B"] }  // Error!
]

// Correct - sequential tags
"migrations": [
  { "tag": "v1", "new_sqlite_classes": ["A"] },
  { "tag": "v2", "new_sqlite_classes": ["B"] }
]
```