: void {
const env = process.env.NODE_ENV || 'development';
const keySource = env === 'production'
? process.env.SECRET_MANAGER_PROD_KEY
: process.env.SECRET_MANAGER_DEV_KEY;
if (!keySource) {
throw new Error(`Missing API key for environment: ${env}`);
}
this.validateKeyFormat(keySource, env);
this.keys.set(env, keySource);
}
private validateKeyFormat(key: string, env: string): void {
const expectedPrefix = env === 'production' ? 'fl_live_' : 'fl_test_';
if (!key.startsWith(expectedPrefix)) {
throw new Error(
`Key format mismatch. Expected prefix '${expectedPrefix}' for ${env}, ` +
`but received key starting with '${key.substring(0, 7)}...'`
);
}
}
public getActiveKey(): string {
const env = process.env.NODE_ENV || 'development';
const key = this.keys.get(env);
if (!key) {
throw new Error(No key loaded for current environment: ${env});
}
return key;
}
}
**Rationale:** This manager prevents cross-environment contamination by validating the key prefix against the current runtime environment. If a production key (`fl_live_`) is accidentally loaded in a development container, the application fails fast with a clear error, preventing accidental production usage.
#### 2. Observability and Rate Limit Interception
API providers typically return rate limit metadata in response headers. Ignoring these headers blinds the application to consumption anomalies. An interceptor should parse these headers and log usage metrics to detect sudden spikes indicative of credential theft.
```typescript
// src/api/SecureHttpClient.ts
import { SecretManager } from '../security/SecretManager';
export class SecureHttpClient {
private readonly baseUrl: string;
private readonly manager: SecretManager;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.manager = SecretManager.getInstance();
}
public async post(endpoint: string, payload: FormData): Promise<Response> {
const apiKey = this.manager.getActiveKey();
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`
},
body: payload
});
this.analyzeRateLimits(response);
return response;
}
private analyzeRateLimits(response: Response): void {
const limitHeader = response.headers.get('X-RateLimit-Limit');
const remainingHeader = response.headers.get('X-RateLimit-Remaining');
const resetHeader = response.headers.get('X-RateLimit-Reset');
if (limitHeader && remainingHeader) {
const limit = parseInt(limitHeader, 10);
const remaining = parseInt(remainingHeader, 10);
const usagePercent = ((limit - remaining) / limit) * 100;
if (usagePercent > 80) {
console.warn(
`API Usage Alert: ${usagePercent.toFixed(1)}% quota consumed. ` +
`Reset at: ${new Date(parseInt(resetHeader || '0', 10) * 1000).toISOString()}`
);
}
}
}
}
Rationale: This client wraps standard HTTP requests with automatic header analysis. By logging usage percentages, the system can trigger alerts when consumption deviates from baseline patterns. A sudden drop in remaining quota between requests is a strong indicator of unauthorized usage.
3. Rotation Protocol Implementation
Safe rotation requires a dual-key validation window. During rotation, the system must accept both the old and new keys to prevent service interruption. The architecture should support a configuration that allows multiple valid keys temporarily.
// src/security/RotationValidator.ts
export class RotationValidator {
private currentKey: string;
private previousKey: string | null;
constructor(currentKey: string, previousKey?: string) {
this.currentKey = currentKey;
this.previousKey = previousKey || null;
}
public isValid(key: string): boolean {
if (key === this.currentKey) return true;
if (this.previousKey && key === this.previousKey) return true;
return false;
}
public promoteNewKey(newKey: string): void {
this.previousKey = this.currentKey;
this.currentKey = newKey;
}
}
Rationale: This validator enables a grace period during rotation. When a new key is generated, it is injected as the currentKey, and the old key becomes the previousKey. Both are accepted until the rotation is confirmed successful across all services, after which the previous key is discarded.
Pitfall Guide
| Pitfall | Explanation | Fix |
|---|
| Git History Persistence | Deleting a secret in a new commit does not remove it from the repository's history. Bots index history, not just the latest state. | Add .env to .gitignore immediately. If a key is committed, rotate it regardless of deletion speed. Use pre-commit hooks to scan for secrets. |
| Cross-Environment Contamination | Using a production key in development or testing increases the risk of exposure through less secure channels and logs. | Enforce prefix validation (fl_live_ vs fl_test_). Fail fast if the key prefix does not match the runtime environment. |
| Silent Key Expiration | Keys may be revoked by the provider or expire without application awareness, causing sudden outages. | Implement health checks that periodically validate the key against a lightweight endpoint. Monitor for 401 Unauthorized responses. |
| Unbounded Consumption | Stolen keys used slowly may not trigger immediate alerts, leading to unexpected costs or quota exhaustion. | Parse and log X-RateLimit-Remaining headers. Set up alerts for usage spikes or rapid quota depletion. |
| Manual Rotation Downtime | Rotating keys without a dual-validation window causes service interruption as clients update at different rates. | Implement a rotation validator that accepts both old and new keys during the transition period. Update clients incrementally. |
| Chat/Email Distribution | Sharing keys via Slack, email, or unencrypted channels exposes them to unauthorized personnel and persistent logs. | Use secure secret management tools or vaults for distribution. Never transmit keys via plain text communication channels. |
| Ignoring Rate Limit Headers | Failing to read rate limit metadata removes the ability to detect anomalous usage patterns early. | Implement an HTTP interceptor that extracts X-RateLimit-* headers and logs consumption metrics for analysis. |
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Single Developer / Prototype | Environment Variables with .env.example | Low overhead; sufficient for non-critical workloads. | Minimal |
| Team Development / Staging | Segregated Keys + Prefix Validation | Prevents accidental production usage; isolates rate limits. | Low |
| Production / Enterprise | Secret Manager + Rotation Validator + Rate Limit Monitoring | Enforces zero-trust; enables safe rotation; detects compromise early. | Medium |
| High-Volume API Usage | Dedicated Key per Service + Quota Alerts | Prevents runaway scripts from exhausting shared quotas; granular monitoring. | Low |
Configuration Template
.env.example
# API Key Configuration
# Copy this file to .env and fill in the values.
# NEVER commit .env to version control.
# Production Key (Must start with fl_live_)
SECRET_MANAGER_PROD_KEY=your_production_key_here
# Development Key (Must start with fl_test_)
SECRET_MANAGER_DEV_KEY=your_development_key_here
# Application Environment
NODE_ENV=development
.gitignore Snippet
# Environment files
.env
.env.local
.env.production
.env.*.local
# IDE and OS files
.vscode/
.idea/
.DS_Store
Quick Start Guide
- Generate Keys: Create separate API keys for development and production via your provider's dashboard. Ensure the production key uses the
fl_live_ prefix and the development key uses fl_test_.
- Setup Environment: Create a
.env file in your project root. Add SECRET_MANAGER_PROD_KEY and SECRET_MANAGER_DEV_KEY with the respective values. Add .env to .gitignore.
- Integrate Manager: Import the
SecretManager class into your application entry point. Replace direct environment variable access with SecretManager.getInstance().getActiveKey().
- Verify Observability: Make a test request using the
SecureHttpClient. Check the logs to confirm that rate limit headers are being parsed and that usage percentages are reported.
- Validate Rotation: Generate a new key and test the
RotationValidator by configuring both the old and new keys. Confirm that requests succeed with either key, then promote the new key and discard the old one.