> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ensemble.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Your First Deployment

> From local development to global edge deployment with tag-based versioning

You've built your ensemble. Now let's put it in production where it matters.

## Prerequisites

Before deploying:

* Wrangler installed and authenticated (`ensemble wrangler login` or `wrangler login`)
* Working ensemble running locally (`ensemble wrangler dev --local-protocol http`)
* `wrangler.toml` configured
* Edgit initialized (`edgit init`)

## Quick Start

```bash theme={null}
# Build and deploy
pnpm run build
pnpm run deploy
```

That's it. Your ensemble is live globally.

Output:

```
Total Upload: 45.23 KiB / gzip: 12.45 KiB
Uploaded my-conductor-app (1.2 sec)
Published my-conductor-app (0.5 sec)
  https://my-conductor-app.your-subdomain.workers.dev
Current Deployment ID: abc123-def456-ghi789
```

**You're live.** Your ensemble is running in 300+ cities worldwide with sub-50ms cold starts.

## Deployment Configuration

Create a `conductor.config.ts` in your project root for type-safe deployment settings:

```typescript theme={null}
import { defineConfig } from '@ensemble-edge/conductor';

export default defineConfig({
  // Environment tags that trigger deployments
  // 'main' is always implicit (maps to HEAD of main branch)
  environments: ['staging', 'production'],

  // Optional: Custom worker names per environment
  workers: {
    main: 'my-app-dev',
    staging: 'my-app-staging',
    production: 'my-app'
  },

  // Version tag sync to KV
  versions: {
    sync: true,           // Sync version tags to KV
    retention: 'last-10'  // Keep 10 most recent versions per component
  },

  // KV namespace for components
  kv: {
    namespace: 'COMPONENTS_KV'
  }
});
```

<Tip>
  The `defineConfig()` helper provides full TypeScript autocomplete and validation for your configuration.
</Tip>

### Configuration Options

| Option               | Default           | Description                                                                            |
| -------------------- | ----------------- | -------------------------------------------------------------------------------------- |
| `environments`       | `['main']`        | Environment tags that trigger deployments                                              |
| `workers`            | Auto-generated    | Worker name overrides per environment                                                  |
| `versions.sync`      | `true`            | Whether to sync version tags to KV                                                     |
| `versions.retention` | `'last-10'`       | How many versions to keep (`'all'`, `'last-5'`, `'last-10'`, `'last-20'`, `'last-50'`) |
| `kv.namespace`       | `'COMPONENTS_KV'` | KV binding name in wrangler.toml                                                       |

## Tag-Based Deployment

Conductor uses **Git tags** for deployment, integrated with Edgit's component versioning system:

```bash theme={null}
# 1. Create a version tag for your component
edgit tag create my-prompt v1.0.0
edgit push --tags

# 2. Deploy to staging (mutable environment tag)
edgit tag set my-prompt staging v1.0.0
edgit push --tags --force

# 3. After testing, promote to production
edgit tag set my-prompt production v1.0.0
edgit push --tags --force
```

**Version tags** (`v1.0.0`) are immutable snapshots.
**Environment tags** (`staging`, `production`) are mutable pointers that can be moved between versions.

### Why Tags?

* **Instant rollbacks** - Just move the environment tag back
* **Audit trail** - Git history shows who deployed what, when
* **Hot-swappable components** - Prompts and schemas update without rebuilds
* **Multi-environment** - Different versions in staging vs production

## CI/CD with GitHub Actions

Copy the workflow templates to your project:

```bash theme={null}
# From the conductor catalog
cp -r node_modules/@ensemble-edge/conductor/catalog/cloud/cloudflare/templates/.github .
cp -r node_modules/@ensemble-edge/conductor/catalog/cloud/cloudflare/templates/scripts .
```

Or create `.github/workflows/conductor.yaml`:

```yaml theme={null}
name: Conductor Deployment

on:
  push:
    branches: [main]
    tags:
      - 'components/**'
      - 'logic/**'
  pull_request:
    branches: [main]

env:
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
  CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run tests
        run: pnpm test

      - name: Validate component references
        run: npx tsx scripts/validate-refs.ts

  deploy-main:
    needs: validate
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm run build

      - name: Deploy to main
        run: wrangler deploy

      - name: Sync components to KV
        run: |
          # Sync component content for @main tags
          edgit sync --env main --kv COMPONENTS_KV

  deploy-tag:
    needs: validate
    if: startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Parse tag
        id: tag
        run: |
          TAG=${GITHUB_REF#refs/tags/}
          # Extract prefix/type/name/slot from tag
          PREFIX=$(echo $TAG | cut -d'/' -f1)
          TYPE=$(echo $TAG | cut -d'/' -f2)
          NAME=$(echo $TAG | cut -d'/' -f3)
          SLOT=$(echo $TAG | cut -d'/' -f4)
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          echo "prefix=$PREFIX" >> $GITHUB_OUTPUT
          echo "type=$TYPE" >> $GITHUB_OUTPUT
          echo "name=$NAME" >> $GITHUB_OUTPUT
          echo "slot=$SLOT" >> $GITHUB_OUTPUT

      - name: Build and deploy
        if: steps.tag.outputs.slot == 'staging' || steps.tag.outputs.slot == 'production'
        run: |
          pnpm run build
          wrangler deploy --env ${{ steps.tag.outputs.slot }}

      - name: Sync component to KV
        run: |
          edgit sync --tag ${{ steps.tag.outputs.tag }} --kv COMPONENTS_KV
```

### Required Secrets

Add these to your repository secrets:

* `CLOUDFLARE_API_TOKEN`: Create at [https://dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens)
* `CLOUDFLARE_ACCOUNT_ID`: Find in Cloudflare dashboard

## Validation Script

Create `scripts/validate-refs.ts` to ensure all component references exist before deployment:

```typescript theme={null}
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';

async function validateRefs() {
  // Get registered components
  const result = execSync('npx edgit components list --format json', {
    encoding: 'utf-8'
  });
  const components = JSON.parse(result);
  const componentNames = new Set(components.map((c: any) => c.name));

  // Find all YAML files
  const yamlFiles = await glob('**/*.yaml', {
    ignore: ['node_modules/**', 'dist/**']
  });

  let errors = 0;

  for (const file of yamlFiles) {
    const content = fs.readFileSync(file, 'utf-8');

    // Find component references like prompts/extraction@staging
    const refs = content.match(/[a-z]+\/[a-z-]+@[a-z0-9.]+/g) || [];

    for (const ref of refs) {
      const [, name] = ref.match(/([a-z-]+)@/) || [];
      if (name && !componentNames.has(name)) {
        console.error(`ERROR: ${file} references unregistered component: ${ref}`);
        errors++;
      }
    }
  }

  if (errors > 0) {
    console.error(`\n${errors} unregistered component reference(s) found`);
    process.exit(1);
  }

  console.log('All component references are valid');
}

validateRefs();
```

## Environment Setup

### wrangler.toml

Configure environments in your `wrangler.toml`:

```toml theme={null}
name = "my-conductor-app"
main = "dist/index.js"
compatibility_date = "2024-01-01"

[ai]
binding = "AI"

[[kv_namespaces]]
binding = "COMPONENTS_KV"
id = "your-kv-id"

# Staging environment
[env.staging]
name = "my-conductor-app-staging"

[[env.staging.kv_namespaces]]
binding = "COMPONENTS_KV"
id = "your-staging-kv-id"

# Production environment
[env.production]
name = "my-conductor-app-production"

[[env.production.kv_namespaces]]
binding = "COMPONENTS_KV"
id = "your-production-kv-id"
```

### Create KV Namespaces

```bash theme={null}
# Create KV namespaces for each environment
wrangler kv:namespace create COMPONENTS_KV
wrangler kv:namespace create COMPONENTS_KV --env staging
wrangler kv:namespace create COMPONENTS_KV --env production
```

Add the IDs to your `wrangler.toml`.

## Component Sync to KV

When tags are pushed, the workflow syncs component content to Cloudflare KV:

```bash theme={null}
# Manual sync for testing
edgit sync --tag components/prompts/extraction/staging --kv COMPONENTS_KV
```

This enables **hot-swappable components** - update a prompt without rebuilding your Worker:

```yaml theme={null}
# In your ensemble, reference by environment tag
agents:
  - name: analyzer
    operation: think
    prompt: prompts/extraction@staging  # Loaded from KV at runtime
```

## Deployment Workflow

### Complete Flow

```
1. Develop locally
   └─> pnpm run dev

2. Create version tag
   └─> edgit tag create extraction v1.0.0
   └─> edgit push --tags

3. Deploy to staging
   └─> edgit tag set extraction staging v1.0.0
   └─> edgit push --tags --force
   └─> GitHub Actions deploys to staging worker
   └─> Components synced to staging KV

4. Test in staging
   └─> curl https://my-app-staging.workers.dev/api/v1/execute/...

5. Promote to production
   └─> edgit tag set extraction production v1.0.0
   └─> edgit push --tags --force
   └─> GitHub Actions deploys to production worker
   └─> Components synced to production KV

6. Rollback if needed
   └─> edgit tag set extraction production v0.9.0
   └─> edgit push --tags --force
   └─> Instant rollback (just moves the tag)
```

### Tag Format

Tags follow the 4-level hierarchy: `{prefix}/{type}/{name}/{slot}`

* **prefix**: `components` (hot-swappable) or `logic` (requires rebuild)
* **type**: `prompts`, `agents`, `schemas`, etc.
* **name**: Component name
* **slot**: Version (`v1.0.0`) or environment (`staging`, `production`)

Examples:

```
components/prompts/extraction/v1.0.0      # Version tag (immutable)
components/prompts/extraction/staging     # Environment tag (mutable)
logic/agents/analyzer/production          # Logic requires rebuild
```

## Rollback

### Instant Rollback

```bash theme={null}
# Something's wrong? Roll back instantly
edgit tag set extraction production v0.9.0
edgit push --tags --force

# Done in under 50ms (just updates KV)
```

### View History

```bash theme={null}
# See all versions
edgit tag list extraction

# Output:
# extraction@v0.9.0
# extraction@v1.0.0
# extraction@staging -> v1.0.0
# extraction@production -> v1.0.0
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Component not found in production">
    **Problem**: Component works locally but not in production

    **Fix**: Ensure the component is registered and synced

    ```bash theme={null}
    # Register component
    edgit register path/to/component.yaml

    # Create and push tags
    edgit tag create my-component v1.0.0
    edgit tag set my-component production v1.0.0
    edgit push --tags --force
    ```
  </Accordion>

  <Accordion title="KV sync not working">
    **Problem**: Components not updating in KV

    **Fix**: Check KV namespace binding

    ```bash theme={null}
    # Verify KV namespace exists
    wrangler kv:namespace list

    # Check wrangler.toml has correct binding
    # binding = "COMPONENTS_KV"
    # id = "your-kv-id"
    ```
  </Accordion>

  <Accordion title="Validation script failing">
    **Problem**: CI fails with "unregistered component reference"

    **Fix**: Register the component before referencing it

    ```bash theme={null}
    # List registered components
    edgit components list

    # Register missing component
    edgit register path/to/component.yaml
    edgit push --tags
    ```
  </Accordion>

  <Accordion title="Environment tag not triggering deploy">
    **Problem**: Pushing environment tag doesn't deploy

    **Fix**: Ensure tag format is correct and matches workflow triggers

    ```bash theme={null}
    # Correct format
    edgit tag set extraction staging v1.0.0

    # Push with --force for environment tags
    edgit push --tags --force
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Deployment Strategies" icon="rocket" href="/edgit/guides/deployment-strategies">
    Progressive rollouts, canaries, blue-green
  </Card>

  <Card title="CI/CD Integration" icon="infinity" href="/edgit/getting-started/cicd-integration">
    Advanced automation with Edgit
  </Card>

  <Card title="Rollback & Time Travel" icon="clock-rotate-left" href="/edgit/guides/rollback-time-travel">
    Emergency rollbacks and debugging
  </Card>

  <Card title="Configuring Cloudflare" icon="cloud" href="/conductor/getting-started/configuring-cloudflare">
    Complete Cloudflare setup guide
  </Card>
</CardGroup>
