Skip to content

Feature Request: Environment-Specific Secrets in Parent Stacks #60

@alex-y-su

Description

@alex-y-su

Problem

Currently, secrets defined in .sc/stacks/<parent-stack>/secrets.yaml are available globally to all environments. This creates challenges:

  1. Security: Production secrets (API keys, passwords) shouldn't be accessible in dev/staging
  2. Isolation: Some secrets should only exist in specific environments
  3. Naming: Same secret name (e.g., DATABASE_PASSWORD) needs different values per environment while services expect a consistent name

Example of the Problem

# Current: .sc/stacks/devops/secrets.yaml
values:
  DATABASE_PASSWORD: "prod-password-123"      # Available everywhere - wrong for dev/staging
  STRIPE_API_KEY: "sk_live_xxx"               # Production key exposed to all envs
  SLACK_WEBHOOK: "https://hooks.slack.com/..." # Maybe OK to share

A developer deploying to staging inadvertently gets access to production secrets.

Proposed Solution

Add a secrets section to server.yaml that controls per-environment secret availability and mapping.

Syntax

# .sc/stacks/devops/server.yaml
schemaVersion: 1.0

secrets:
  inheritAll: false  # Default: true (backwards compatible)
  
  environments:
    staging:
      include:
        # null (~) means "use same key from secrets.yaml"
        SLACK_WEBHOOK: ~
        
        # Reference another key in secrets.yaml
        DATABASE_PASSWORD: "${secret:DATABASE_PASSWORD_STAGING}"
        STRIPE_API_KEY: "${secret:STRIPE_TEST_KEY}"
        
        # Literal value (no ${secret:} wrapper)
        LOG_LEVEL: "debug"
        ENABLE_PROFILING: "true"
    
    production:
      include:
        SLACK_WEBHOOK: ~
        DATABASE_PASSWORD: "${secret:DATABASE_PASSWORD_PROD}"
        STRIPE_API_KEY: "${secret:STRIPE_LIVE_KEY}"
        DATADOG_API_KEY: ~
        LOG_LEVEL: "warn"

resources:
  staging:
    template: stack-per-app
    # ...
  production:
    template: stack-per-app
    # ...

secrets.yaml (encrypted):

values:
  DATABASE_PASSWORD_PROD: "super-secret-prod-password"
  DATABASE_PASSWORD_STAGING: "staging-password-123"
  STRIPE_LIVE_KEY: "sk_live_xxx"
  STRIPE_TEST_KEY: "sk_test_yyy"
  SLACK_WEBHOOK: "https://hooks.slack.com/..."
  DATADOG_API_KEY: "dd-api-xxx"

Syntax Reference

Pattern Type Description
SECRET_NAME: ~ Reference Use value of SECRET_NAME from secrets.yaml
NAME: "${secret:KEY}" Mapped Reference Expose as NAME, fetch value from KEY in secrets.yaml
NAME: "value" Literal Expose as NAME with hardcoded value

Behavior

Setting Description
inheritAll: true (default) All secrets.yaml values available in all envs (current behavior)
inheritAll: false Only explicitly listed secrets available per environment
exclude: [names] Block specific secrets (only with inheritAll: true)

Example with Exclusions

secrets:
  inheritAll: true  # Start with all secrets
  
  environments:
    staging:
      exclude:
        - DATADOG_API_KEY      # Not needed in staging
        - STRIPE_LIVE_KEY      # Security: no prod keys in staging
      override:
        STRIPE_API_KEY: "${secret:STRIPE_TEST_KEY}"
    
    production:
      # Gets everything, no overrides needed

Service Usage (No Changes)

Client configurations remain unchanged:

# client.yaml
stacks:
  production:
    parent: company/devops
    secrets:
      DB_PASS: "${secret:DATABASE_PASSWORD}"  # Resolves to prod value
      STRIPE: "${secret:STRIPE_API_KEY}"      # Resolves to live key
# Same client.yaml deployed to staging
stacks:
  staging:
    parent: company/devops
    secrets:
      DB_PASS: "${secret:DATABASE_PASSWORD}"  # Resolves to staging value
      STRIPE: "${secret:STRIPE_API_KEY}"      # Resolves to test key

Validation

sc validate should:

  • Warn if a secret referenced in client.yaml isn't available for target environment
  • Error if ${secret:KEY} references a non-existent key in secrets.yaml
  • Warn about unused secrets in secrets.yaml

Migration

  • Backwards compatible: Without secrets section, all secrets available everywhere
  • Opt-in: Teams adopt incrementally
  • No client changes: Existing service configs work without modification

Benefits

  • Secret values stay encrypted in secrets.yaml
  • Mapping logic in version-controlled server.yaml
  • Supports both references and literals
  • No changes to service configurations
  • Clear separation of environments
  • Backwards compatible

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions