# Cloudflare Workers

Sync KeyEnv secrets to Cloudflare Workers.

Source: https://keyenv.dev/docs/guides/cloudflare-workers/

Sync secrets from KeyEnv to your Cloudflare Workers.

## Prerequisites

- Cloudflare account with Workers enabled
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) installed
- KeyEnv service token with read access

## Quick Sync

```bash
export KEYENV_TOKEN=your-service-token

# Export and bulk upload secrets
keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq '[.secrets[] | {name: .key, text: .value, type: "secret_text"}]' | \
  wrangler secret:bulk
```

## Sync Script

Create a reusable sync script:

```bash
#!/bin/bash
# sync-to-cloudflare.sh

set -euo pipefail

: "${KEYENV_TOKEN:?KEYENV_TOKEN is required}"

PROJECT_ID="${KEYENV_PROJECT:-YOUR_PROJECT_ID}"
ENVIRONMENT="${KEYENV_ENV:-production}"

echo "Syncing KeyEnv ($ENVIRONMENT) to Cloudflare Workers..."

# Export and transform for Cloudflare
keyenv export -p "$PROJECT_ID" -e "$ENVIRONMENT" --format json | \
  jq '[.secrets[] | {name: .key, text: .value, type: "secret_text"}]' | \
  wrangler secret:bulk

echo "Secrets synced to Cloudflare Workers!"
```

## CI/CD Integration

### GitHub Actions

```yaml
name: Deploy Worker

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install KeyEnv CLI
        run: curl -fsSL https://keyenv.dev/install.sh | bash

      - name: Sync secrets
        run: |
          export PATH="$HOME/.keyenv/bin:$PATH"
          keyenv export -p ${{ vars.KEYENV_PROJECT }} -e production --format json | \
            jq '[.secrets[] | {name: .key, text: .value, type: "secret_text"}]' | \
            npx wrangler secret:bulk
        env:
          KEYENV_TOKEN: ${{ secrets.KEYENV_TOKEN }}
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

      - name: Deploy Worker
        run: npx wrangler deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

## Single Secret Sync

Sync individual secrets:

```bash
# Get a specific secret
value=$(keyenv get -p YOUR_PROJECT_ID -e production DATABASE_URL)

# Set in Cloudflare
echo "$value" | wrangler secret put DATABASE_URL
```

## Multiple Workers

Sync to multiple Workers:

```bash
#!/bin/bash

WORKERS=("api-worker" "auth-worker" "queue-worker")

for worker in "${WORKERS[@]}"; do
  echo "Syncing to $worker..."

  keyenv export -p YOUR_PROJECT_ID -e production --format json | \
    jq '[.secrets[] | {name: .key, text: .value, type: "secret_text"}]' | \
    wrangler secret:bulk --name "$worker"
done
```

## Selective Sync

Sync only specific secrets:

```bash
# Only sync secrets that don't contain "LOCAL" in the key
keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq '[.secrets[] | select(.key | contains("LOCAL") | not) | {name: .key, text: .value, type: "secret_text"}]' | \
  wrangler secret:bulk
```

## wrangler.toml Configuration

Non-sensitive config goes in `wrangler.toml`:

```toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[vars]
ENVIRONMENT = "production"
LOG_LEVEL = "info"
PUBLIC_API_URL = "https://api.example.com"

# Secrets are managed via wrangler secret (synced from KeyEnv)
# DATABASE_URL, API_KEY, etc.
```

## Using Secrets in Workers

Access secrets in your Worker code:

```typescript
// src/index.ts
export interface Env {
  DATABASE_URL: string;
  API_KEY: string;
  ENVIRONMENT: string;  // From wrangler.toml [vars]
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Access secrets
    const dbUrl = env.DATABASE_URL;
    const apiKey = env.API_KEY;

    return new Response('OK');
  },
};
```

## Durable Objects

Secrets are available to Durable Objects too:

```typescript
export class MyDurableObject {
  constructor(private state: DurableObjectState, private env: Env) {}

  async fetch(request: Request): Promise<Response> {
    // Access secrets via this.env
    const apiKey = this.env.API_KEY;
    return new Response('OK');
  }
}
```

## Workers KV

For frequently accessed secrets, consider caching in KV:

```typescript
async function getSecret(env: Env, key: string): Promise<string | null> {
  // Try KV cache first
  let value = await env.SECRETS_KV.get(key);

  if (!value) {
    // Fall back to env binding
    value = env[key as keyof Env] as string;
    // Cache for 1 hour
    await env.SECRETS_KV.put(key, value, { expirationTtl: 3600 });
  }

  return value;
}
```

## List Current Secrets

View secrets currently set in your Worker:

```bash
wrangler secret list
```

## Delete Secrets

Remove a secret:

```bash
wrangler secret delete SECRET_NAME
```

Remove all and re-sync:

```bash
# List and delete all
for secret in $(wrangler secret list | tail -n +3 | awk '{print $1}'); do
  wrangler secret delete "$secret" --force
done

# Re-sync from KeyEnv
keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq '[.secrets[] | {name: .key, text: .value, type: "secret_text"}]' | \
  wrangler secret:bulk
```

## Pages Functions

For Cloudflare Pages with Functions, set secrets via the dashboard or API:

```bash
# Using Pages API
secrets=$(keyenv export -p YOUR_PROJECT_ID -e production --format json)

echo "$secrets" | jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
  curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/$PROJECT_NAME" \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"deployment_configs\":{\"production\":{\"env_vars\":{\"$key\":{\"value\":\"$value\",\"type\":\"secret_text\"}}}}}"
done
```

## Best Practices

1. **Use wrangler.toml for non-secrets** - Version control public config
2. **Automate syncing** - Run sync in CI/CD before deployments
3. **Use secret bindings** - Access via `env.SECRET_NAME`, not `process.env`
4. **Separate by environment** - Use different Workers for staging/production

## Troubleshooting

### Wrangler Not Logged In

```bash
wrangler login
# or use API token
export CLOUDFLARE_API_TOKEN=your-token
```

### Secret Not Available

Ensure you've deployed after adding secrets:

```bash
wrangler deploy
```

### Type Errors

Update your `Env` interface to include all secrets:

```typescript
export interface Env {
  // Add all your secrets here
  DATABASE_URL: string;
  API_KEY: string;
}
```
