Skip to main content

Environment-Specific Secret Values

Use a single secret with different values for each environment. KnoxCall automatically uses the correct value based on the route’s active environment—no manual switching required.

The Problem

Traditional approach requires separate secrets per environment:
❌ Old Way: 3 Secrets
├─ stripe_production_key → sk_live_abc123
├─ stripe_staging_key    → sk_test_staging_456
└─ stripe_development_key → sk_test_dev_789

Route config must change per environment:
Production: {{secret:stripe_production_key}}
Staging:    {{secret:stripe_staging_key}}
Development: {{secret:stripe_development_key}}
Problems:
  • Accidental use of production keys in staging
  • Manual route config updates when switching environments
  • Difficult to audit which env uses which key
  • Secret sprawl (3x secrets)

The Solution

One secret, multiple environment-specific values:
✅ New Way: 1 Secret
stripe_api_key
  ├─ production:  sk_live_abc123
  ├─ staging:     sk_test_staging_456
  └─ development: sk_test_dev_789

Route config stays the same:
All environments: {{secret:stripe_api_key}}
Benefits:
  • ✅ Automatic environment-based resolution
  • ✅ Hard fail if environment value missing (prevents accidents)
  • ✅ Simplified route configuration
  • ✅ Clear audit trail per environment

How It Works

Step 1: Create Secret

Create a secret normally:
Name: stripe_api_key
Type: String
Leave the initial value empty—we’ll set environment-specific values next.

Step 2: Configure Environment Values

In the secret detail page:
  1. Select environment from dropdown (top right)
  2. Enter value for that environment
  3. Save
  4. Repeat for other environments
Example:
Environment: production
Value: sk_live_4eC39HqLyjWDarjtT1zdp7dc
[Save]

Environment: staging
Value: sk_test_staging_BQokikJOvBiI2HlWgH4olfQ2
[Save]

Environment: development
Value: sk_test_dev_4eC39HqLyjWDarjtT1zdp7dc
[Save]

Step 3: Reference in Route

Use the secret in your route config:
{
  "headers": {
    "Authorization": "Bearer {{secret:stripe_api_key}}"
  }
}

Step 4: Automatic Resolution

When a request comes in:
1. Client calls route
2. Route has active environment: "production"
3. KnoxCall looks up stripe_api_key environment config
4. Finds production value: sk_live_4eC39HqLyjWDarjtT1zdp7dc
5. Injects value into request
6. Proxies to Stripe API ✅
If route is in staging environment:
Same route, staging environment:
→ Uses sk_test_staging_BQokikJOvBiI2HlWgH4olfQ2 automatically

Hard Fail Behavior

Safety feature: If a secret lacks a value for the requested environment, the request fails immediately.
Scenario:
- Secret: stripe_api_key
- Configured environments: production, staging
- Route environment: development ❌

Result:
HTTP 500 Internal Server Error
{
  "error": "Secret 'stripe_api_key' not configured for environment 'development'"
}
Why no fallback?
  • Prevents accidental production key usage in dev/staging
  • Forces explicit configuration
  • Makes missing config obvious during testing
  • Complies with principle of least privilege
Solution: Add the missing environment value or change route’s active environment.

Supported Secret Types

Environment-specific values work with all secret types:

String Secrets

API Key: stripe_api_key
  ├─ production:  sk_live_abc123
  ├─ staging:     sk_test_staging_456
  └─ development: sk_test_dev_789

OAuth2 Secrets

OAuth Client: google_oauth
  ├─ production:
  │    client_id: prod-client-id.apps.googleusercontent.com
  │    client_secret: GOCSPX-prod-abc123

  ├─ staging:
  │    client_id: staging-client-id.apps.googleusercontent.com
  │    client_secret: GOCSPX-staging-def456

  └─ development:
       client_id: dev-client-id.apps.googleusercontent.com
       client_secret: GOCSPX-dev-ghi789

Certificate Secrets (mTLS)

Client Certificate: bank_mtls_cert
  ├─ production:
  │    cert: -----BEGIN CERTIFICATE----- (prod cert)
  │    key: -----BEGIN PRIVATE KEY----- (prod key)

  ├─ staging:
  │    cert: -----BEGIN CERTIFICATE----- (staging cert)
  │    key: -----BEGIN PRIVATE KEY----- (staging key)

  └─ development:
       cert: -----BEGIN CERTIFICATE----- (dev cert)
       key: -----BEGIN PRIVATE KEY----- (dev key)

Advanced Usage

Partial Environment Coverage

You can configure only the environments you need:
Secret: internal_api_key

Configured:
  ✅ production
  ✅ staging
  ❌ development (not configured)

Routes in development environment:
  → Will fail (hard fail)
  → Forces you to add dev value or use different secret

Duplicating Environments

Clone an existing environment’s value:
  1. Go to secret detail page
  2. Select source environment (e.g., “production”)
  3. Click Duplicate to another environment
  4. Choose target environment (e.g., “staging”)
  5. Modify the duplicated value if needed
  6. Save
Use case: Copy production config to staging, then change specific values.

Deleting Environment Values

Protected deletion: Cannot delete an environment value if any route is actively using it in that environment.
Scenario:
- Secret: stripe_api_key has production value
- Route "Stripe Charges" is active in production environment
- Try to delete production value

Result:
❌ Error: Cannot delete - 1 route using this secret in production
   Routes: Stripe Charges (route_abc123)
Solution:
  1. Switch routes to different environment, OR
  2. Update routes to use different secret, OR
  3. Delete the routes first

Viewing All Environments

In the secret detail page, see all configured environments at a glance:
Secret: stripe_api_key

Environment Status:
  ✅ production    (configured, 4 routes using)
  ✅ staging       (configured, 2 routes using)
  ✅ development   (configured, 0 routes using)
  ⚠️  testing      (not configured)

API Reference

Get Secret Environments

GET /admin/secrets/{secret_id}/environments

Response:
{
  "environments": [
    {
      "environment_name": "production",
      "created_at": "2025-01-15T10:00:00Z",
      "updated_at": "2025-01-20T14:30:00Z"
    },
    {
      "environment_name": "staging",
      "created_at": "2025-01-15T10:05:00Z",
      "updated_at": "2025-01-15T10:05:00Z"
    }
  ]
}
Note: Values are never returned via API for security.

Create/Update Environment Value

PUT /admin/secrets/{secret_id}/environments/{environment_name}

Body (String secret):
{
  "value": "sk_test_abc123"
}

Body (OAuth2 secret):
{
  "oauth2_config": {
    "client_id": "your-client-id",
    "client_secret": "your-client-secret",
    "auth_url": "https://provider.com/oauth/authorize",
    "token_url": "https://provider.com/oauth/token",
    "scopes": ["read", "write"]
  }
}

Body (Certificate secret):
{
  "certificate_pem": "-----BEGIN CERTIFICATE-----...",
  "private_key_pem": "-----BEGIN PRIVATE KEY-----...",
  "passphrase": "optional-key-passphrase"
}

Delete Environment Value

DELETE /admin/secrets/{secret_id}/environments/{environment_name}

Response:
{
  "deleted": true
}

Or if in use:
{
  "error": "Cannot delete - routes using this environment",
  "routes_using": ["route_abc123", "route_xyz789"]
}

Duplicate Environment

POST /admin/secrets/{secret_id}/environments/{source_env}/duplicate

Body:
{
  "target_environment": "development"
}

Response:
{
  "duplicated": true,
  "source": "staging",
  "target": "development"
}

Migration Guide

From Naming Convention to Environment Configs

Before:
Secrets:
  - stripe_production_key
  - stripe_staging_key
  - stripe_development_key

Route config (production):
{
  "Authorization": "Bearer {{secret:stripe_production_key}}"
}
After:
Secret: stripe_api_key
  ├─ production:  (value from stripe_production_key)
  ├─ staging:     (value from stripe_staging_key)
  └─ development: (value from stripe_development_key)

Route config (all environments):
{
  "Authorization": "Bearer {{secret:stripe_api_key}}"
}
Steps:
  1. Create new secret: stripe_api_key (no initial value)
  2. Copy values:
    • Environment: production → Paste value from stripe_production_key
    • Environment: staging → Paste value from stripe_staging_key
    • Environment: development → Paste value from stripe_development_key
  3. Update routes:
    • Change {{secret:stripe_production_key}} to {{secret:stripe_api_key}}
    • Remove environment-specific references
  4. Test in staging first
  5. Deploy to production
  6. Delete old secrets after 48 hours

Best Practices

✅ Do This

  1. Use environment configs for new secrets
    • Simpler, safer, better audit trail
  2. Configure all environments you use
    • Don’t leave gaps (hard fail protects you)
  3. Test in staging first
    • Verify correct values loaded per environment
  4. Use descriptive names
    • stripe_api_key not secret_42
    • Easier to manage across environments
  5. Document your environments
    • Keep a list of which environments exist
    • Standard: production, staging, development

❌ Avoid This

  1. Don’t mix approaches for same service
    • Either use env configs OR naming convention
    • Don’t use both for Stripe (confusing)
  2. Don’t delete environment values carelessly
    • Check if routes are using them first
    • System prevents deletion, but verify manually
  3. Don’t share values across environments
    • Never use production keys in staging
    • Even if system allows it, don’t do it
  4. Don’t skip testing after migration
    • Always test in dev/staging first
    • Verify hard fail works as expected

Troubleshooting

”Secret not configured for environment”

Problem:
Route fails with:
"Secret 'api_key' not configured for environment 'staging'"
Solutions:
  1. Add the missing environment value
  2. Change route’s active environment
  3. Use a different secret that has the environment

Can’t delete environment value

Problem:
Cannot delete - 3 routes using this secret in production
Solution:
-- Find routes using this secret
SELECT r.name, r.id
FROM routes r
JOIN route_environment_configs rec ON r.id = rec.route_id
WHERE rec.environment_name = 'production'
AND rec.active = true;

-- Then either:
1. Switch those routes to different environment
2. Update routes to use different secret
3. Accept that you can't delete (it's in use!)

Environment dropdown not showing

Problem: Can’t see environment selector in secret detail page Possible causes:
  1. Secret type doesn’t support environments (shouldn’t happen - all types do)
  2. Browser cache issue - hard refresh (Ctrl+F5)
  3. Old UI version - ensure admin-ui is up to date
Solution:
cd admin-ui && npm run build
pm2 restart knoxcall

Wrong value being used

Problem: Route in staging is using production value Debug steps:
  1. Check route’s active environment (top right dropdown)
  2. Verify secret has value for that environment
  3. Check route config uses correct secret name
  4. Test with a different route
Common cause: Route’s active environment is set to “production” even though you think it’s staging.

Security Considerations

Encryption

All environment values are encrypted individually:
Secret: stripe_api_key

Each environment value:
├─ Encrypted with unique data encryption key (DEK)
├─ DEK encrypted with master key (KEK)
├─ Stored: ciphertext + IV + auth tag
└─ Decrypted only when route resolves secret

Audit Trail

Every environment value change is logged:
SELECT
  secret_id,
  environment_name,
  operation, -- 'create', 'update', 'delete'
  performed_by,
  performed_at
FROM secret_environment_audit_log
WHERE secret_id = 'sec_abc123';

Access Control

  • Only tenant admins can view/edit secret environment configs
  • API requires authentication via JWT
  • No way to retrieve values via API (write-only)
  • Environment values never logged or transmitted in plaintext

Compliance

Environment-specific secrets help with:
  • PCI DSS: Separate production and test credentials
  • SOC 2: Audit trail of secret changes per environment
  • GDPR: Environment isolation for data protection
  • HIPAA: Segregation of production and non-production

Next Steps

Creating Secrets

Learn how to create secrets in KnoxCall

Using Secrets in Routes

Reference secrets in route configurations

Environment Basics

Understand KnoxCall environments

Secret Rotation

Best practices for rotating secrets

📊 Guide Info

  • Level: Intermediate
  • Time: 10 minutes
  • Prerequisites: Basic understanding of secrets and environments

🏷️ Tags

secrets, environments, configuration, security, credentials