home / skills / jeremylongshore / claude-code-plugins-plus-skills / speak-multi-env-setup

This skill streamlines configuring Speak across development, staging, and production with per-env secrets, feature flags, and environment-aware defaults.

npx playbooks add skill jeremylongshore/claude-code-plugins-plus-skills --skill speak-multi-env-setup

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.6 KB
---
name: speak-multi-env-setup
description: |
  Configure Speak across development, staging, and production environments.
  Use when setting up multi-environment deployments, configuring per-environment secrets,
  or implementing environment-specific Speak configurations.
  Trigger with phrases like "speak environments", "speak staging",
  "speak dev prod", "speak environment setup", "speak config by env".
allowed-tools: Read, Write, Edit, Bash(aws:*), Bash(gcloud:*), Bash(vault:*)
version: 1.0.0
license: MIT
author: Jeremy Longshore <[email protected]>
---

# Speak Multi-Environment Setup

## Overview
Configure Speak across development, staging, and production environments for language learning applications.

## Prerequisites
- Separate Speak accounts or API keys per environment
- Secret management solution (Vault, AWS Secrets Manager, etc.)
- CI/CD pipeline with environment variables
- Environment detection in application

## Environment Strategy

| Environment | Purpose | API Keys | Data | Languages |
|-------------|---------|----------|------|-----------|
| Development | Local dev | Sandbox keys | Mock/test | All enabled |
| Staging | Pre-prod validation | Staging keys | Test data | All enabled |
| Production | Live users | Production keys | Real data | Enabled per plan |

## Configuration Structure

```
config/
├── speak/
│   ├── base.json           # Shared config
│   ├── development.json    # Dev overrides
│   ├── staging.json        # Staging overrides
│   └── production.json     # Prod overrides
```

### base.json
```json
{
  "timeout": 30000,
  "retries": 3,
  "cache": {
    "enabled": true,
    "ttlSeconds": 300
  },
  "audio": {
    "maxDuration": 60,
    "format": "wav",
    "sampleRate": 16000
  },
  "supportedLanguages": ["en", "es", "fr", "de", "ko", "ja", "zh-CN", "pt-BR"]
}
```

### development.json
```json
{
  "baseUrl": "https://api-sandbox.speak.com",
  "debug": true,
  "cache": {
    "enabled": false
  },
  "mockMode": true,
  "features": {
    "experimentalTutor": true,
    "betaSpeechEngine": true,
    "allLanguages": true
  }
}
```

### staging.json
```json
{
  "baseUrl": "https://api-staging.speak.com",
  "debug": false,
  "features": {
    "experimentalTutor": true,
    "betaSpeechEngine": false,
    "allLanguages": true
  },
  "rateLimits": {
    "lessonsPerHour": 100,
    "audioMinutesPerHour": 60
  }
}
```

### production.json
```json
{
  "baseUrl": "https://api.speak.com",
  "debug": false,
  "retries": 5,
  "features": {
    "experimentalTutor": false,
    "betaSpeechEngine": false,
    "allLanguages": false
  },
  "rateLimits": {
    "lessonsPerHour": 1000,
    "audioMinutesPerHour": 500
  }
}
```

## Environment Detection

```typescript
// src/speak/config.ts
import baseConfig from '../../config/speak/base.json';

type Environment = 'development' | 'staging' | 'production';

function detectEnvironment(): Environment {
  // Check multiple sources for environment
  const env = process.env.SPEAK_ENV ||
              process.env.NODE_ENV ||
              'development';

  const validEnvs: Environment[] = ['development', 'staging', 'production'];
  return validEnvs.includes(env as Environment)
    ? (env as Environment)
    : 'development';
}

export function getSpeakConfig(): SpeakConfig {
  const env = detectEnvironment();
  const envConfig = require(`../../config/speak/${env}.json`);

  return {
    ...baseConfig,
    ...envConfig,
    apiKey: process.env.SPEAK_API_KEY!,
    appId: process.env.SPEAK_APP_ID!,
    webhookSecret: process.env.SPEAK_WEBHOOK_SECRET,
    environment: env,
  };
}
```

## Secret Management by Environment

### Local Development
```bash
# .env.local (git-ignored)
SPEAK_ENV=development
SPEAK_API_KEY=sk_sandbox_***
SPEAK_APP_ID=app_dev_***
SPEAK_MOCK_MODE=true
```

### CI/CD (GitHub Actions)
```yaml
jobs:
  deploy:
    environment: ${{ matrix.environment }}
    env:
      SPEAK_ENV: ${{ matrix.environment }}
      SPEAK_API_KEY: ${{ secrets[format('SPEAK_API_KEY_{0}', matrix.environment)] }}
      SPEAK_APP_ID: ${{ secrets[format('SPEAK_APP_ID_{0}', matrix.environment)] }}
    strategy:
      matrix:
        environment: [staging, production]
```

### Production (Vault/Secrets Manager)
```bash
# AWS Secrets Manager
aws secretsmanager get-secret-value \
  --secret-id speak/production/credentials

# GCP Secret Manager
gcloud secrets versions access latest \
  --secret=speak-production-api-key

# HashiCorp Vault
vault kv get -field=api_key secret/speak/production
```

## Environment Isolation

### Prevent Production Operations in Non-Prod
```typescript
function guardProductionOperation(operation: string): void {
  const config = getSpeakConfig();

  if (config.environment !== 'production') {
    console.warn(`[speak] ${operation} blocked in ${config.environment}`);
    throw new Error(`${operation} only allowed in production`);
  }
}

// Usage
async function deleteUserData(userId: string): Promise<void> {
  guardProductionOperation('deleteUserData');
  await speakClient.users.delete(userId);
}
```

### Environment-Specific Lesson Restrictions
```typescript
function getAvailableLanguages(): string[] {
  const config = getSpeakConfig();

  if (config.features?.allLanguages) {
    return config.supportedLanguages;
  }

  // Production: Only enabled languages for user's plan
  return getUserPlanLanguages();
}

function canAccessFeature(feature: string): boolean {
  const config = getSpeakConfig();
  return config.features?.[feature] ?? false;
}
```

## Feature Flags by Environment

```typescript
interface FeatureFlags {
  experimentalTutor: boolean;
  betaSpeechEngine: boolean;
  newPronunciationModel: boolean;
  gamificationV2: boolean;
  offlineMode: boolean;
}

const featureFlags: Record<Environment, FeatureFlags> = {
  development: {
    experimentalTutor: true,
    betaSpeechEngine: true,
    newPronunciationModel: true,
    gamificationV2: true,
    offlineMode: true,
  },
  staging: {
    experimentalTutor: true,
    betaSpeechEngine: true,
    newPronunciationModel: true,
    gamificationV2: false,
    offlineMode: false,
  },
  production: {
    experimentalTutor: false,
    betaSpeechEngine: false,
    newPronunciationModel: false,
    gamificationV2: false,
    offlineMode: false,
  },
};

export function isFeatureEnabled(feature: keyof FeatureFlags): boolean {
  const env = detectEnvironment();
  return featureFlags[env][feature];
}
```

## Environment-Specific Audio Storage

```typescript
interface AudioStorageConfig {
  type: 'local' | 's3' | 'gcs';
  bucket?: string;
  encryption: boolean;
  retention: number; // days
}

const audioStorage: Record<Environment, AudioStorageConfig> = {
  development: {
    type: 'local',
    encryption: false,
    retention: 1,
  },
  staging: {
    type: 's3',
    bucket: 'speak-staging-audio',
    encryption: true,
    retention: 7,
  },
  production: {
    type: 's3',
    bucket: 'speak-production-audio',
    encryption: true,
    retention: 30,
  },
};
```

## Environment Health Checks

```typescript
async function verifyEnvironmentConfig(): Promise<EnvironmentCheck> {
  const config = getSpeakConfig();
  const checks = {
    apiKeyValid: false,
    canConnect: false,
    correctEnvironment: false,
    featuresAccessible: false,
  };

  try {
    // Verify API key
    const health = await speakClient.health.check();
    checks.apiKeyValid = true;
    checks.canConnect = health.healthy;

    // Verify we're hitting the right environment
    const expectedBase = config.baseUrl;
    checks.correctEnvironment = health.endpoint.includes(
      config.environment === 'production' ? 'api.speak.com' : `api-${config.environment}`
    );

    // Verify feature access
    if (config.features?.experimentalTutor) {
      checks.featuresAccessible = await canAccessExperimentalTutor();
    } else {
      checks.featuresAccessible = true;
    }
  } catch (error) {
    console.error('Environment verification failed:', error);
  }

  return {
    environment: config.environment,
    checks,
    allPassed: Object.values(checks).every(Boolean),
  };
}
```

## Output
- Multi-environment config structure
- Environment detection logic
- Secure secret management
- Production safeguards enabled
- Feature flags by environment

## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Wrong environment | Missing SPEAK_ENV | Set environment variable |
| Secret not found | Wrong secret path | Verify secret manager config |
| Config merge fails | Invalid JSON | Validate config files |
| Production guard triggered | Wrong environment | Check SPEAK_ENV value |
| Feature unavailable | Wrong environment | Verify feature flags |

## Examples

### Quick Environment Check
```typescript
const config = getSpeakConfig();
console.log(`Environment: ${config.environment}`);
console.log(`API Base: ${config.baseUrl}`);
console.log(`Mock Mode: ${config.mockMode ? 'ON' : 'OFF'}`);
console.log(`Features:`, config.features);
```

### Environment Switching Script
```bash
#!/bin/bash
# switch-speak-env.sh

case "$1" in
  dev)
    export SPEAK_ENV=development
    export SPEAK_API_KEY=$(vault kv get -field=api_key secret/speak/development)
    ;;
  staging)
    export SPEAK_ENV=staging
    export SPEAK_API_KEY=$(vault kv get -field=api_key secret/speak/staging)
    ;;
  prod)
    export SPEAK_ENV=production
    export SPEAK_API_KEY=$(vault kv get -field=api_key secret/speak/production)
    ;;
esac

echo "Switched to $SPEAK_ENV environment"
```

## Resources
- [Speak Environments Guide](https://developer.speak.com/docs/environments)
- [12-Factor App Config](https://12factor.net/config)
- [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/)

## Next Steps
For observability setup, see `speak-observability`.

Overview

This skill configures Speak across development, staging, and production environments for language learning applications. It provides a clear directory layout, environment detection, secret management patterns, and production safeguards. Use it to enforce per-environment feature flags, audio storage, and rate limits.

How this skill works

The skill loads a shared base config and merges an environment-specific override (development, staging, production). It detects the active environment from SPEAK_ENV, NODE_ENV, or defaults to development, then injects secrets from environment variables or a secrets manager. It also exposes guards, health checks, and feature-flag helpers to prevent unsafe operations outside production.

When to use it

  • Setting up new Speak deployments with separate dev/staging/prod configs
  • Managing per-environment API keys and secrets in CI/CD or Vault
  • Enabling or disabling experimental features based on environment
  • Implementing environment-specific audio storage and retention policies
  • Blocking destructive operations from non-production environments

Best practices

  • Keep base config for shared defaults and only override what differs per environment
  • Store API keys and sensitive values in a secret manager or CI secrets, not in repo
  • Use explicit environment detection (SPEAK_ENV) and default to development
  • Guard production-only operations with runtime checks to avoid data loss
  • Run environment health checks in CI to validate API keys, endpoints, and feature access

Example use cases

  • Local development: sandbox API, mock mode, and all languages enabled for testing
  • Staging: validate release with staging keys, test data, and limited rate limits
  • Production: use production keys, stricter retries, real data, and plan-limited languages
  • CI/CD matrix deploy: inject environment-specific secrets for staging and production
  • Automated health check: verify API connectivity, correct endpoint, and feature access before deploy

FAQ

How do I keep production keys secure in CI?

Store production keys in your CI provider secrets or a managed secrets store (Vault, AWS Secrets Manager) and map them into the job environment at deploy time.

What if the app reads the wrong environment?

Ensure SPEAK_ENV is set in your runtime or CI. Add health checks that verify the baseUrl matches the expected environment to catch misconfiguration early.