Skip to main content
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

# 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:
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'
  }
});
The defineConfig() helper provides full TypeScript autocomplete and validation for your configuration.

Configuration Options

OptionDefaultDescription
environments['main']Environment tags that trigger deployments
workersAuto-generatedWorker name overrides per environment
versions.synctrueWhether 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:
# 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:
# 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:
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:

Validation Script

Create scripts/validate-refs.ts to ensure all component references exist before deployment:
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:
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

# 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:
# 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:
# 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

# 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

# See all versions
edgit tag list extraction

# Output:
# [email protected]
# [email protected]
# extraction@staging -> v1.0.0
# extraction@production -> v1.0.0

Troubleshooting

Problem: Component works locally but not in productionFix: Ensure the component is registered and synced
# 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
Problem: Components not updating in KVFix: Check KV namespace binding
# Verify KV namespace exists
wrangler kv:namespace list

# Check wrangler.toml has correct binding
# binding = "COMPONENTS_KV"
# id = "your-kv-id"
Problem: CI fails with “unregistered component reference”Fix: Register the component before referencing it
# List registered components
edgit components list

# Register missing component
edgit register path/to/component.yaml
edgit push --tags
Problem: Pushing environment tag doesn’t deployFix: Ensure tag format is correct and matches workflow triggers
# Correct format
edgit tag set extraction staging v1.0.0

# Push with --force for environment tags
edgit push --tags --force

Next Steps