Encrypted Credentials Management
Overview
The @outputai/credentials package provides encrypted secrets management for Output SDK workflows. It replaces process.env patterns with a structured, encrypted YAML-based system that supports scoped credentials with deep merging.
When to Use This Skill
- •Adding API keys or tokens to a workflow
- •Migrating from
process.envto encrypted credentials - •Setting up per-workflow or per-environment secrets
- •Debugging missing credential errors (
MissingCredentialError,MissingKeyError) - •Configuring custom credential providers (Vault, AWS Secrets Manager)
Library API
Import
import { credentials } from '@outputai/credentials';
credentials.get(path, defaultValue?)
Safe read with optional default. Never throws.
// Returns value or undefined
const region = credentials.get('aws.region');
// Returns value or default
const region = credentials.get('aws.region', 'us-east-1');
credentials.require(path)
Strict read. Throws MissingCredentialError if not found.
const apiKey = credentials.require('anthropic.api_key');
Error Types
import { MissingCredentialError, MissingKeyError } from '@outputai/credentials';
| Error | Thrown When | Fix |
|---|---|---|
MissingCredentialError | credentials.require() path not found | Add the credential via output credentials edit |
MissingKeyError | No decryption key available | Set OUTPUT_CREDENTIALS_KEY env var or create .key file |
CLI Commands
# Initialize credentials (generates key + encrypted YAML template) output credentials init # Global output credentials init -e production # Environment-specific output credentials init -w payment_processing # Workflow-specific # Edit credentials (decrypts, opens $EDITOR, re-encrypts on save) output credentials edit # Global output credentials edit -e production # Environment output credentials edit -w payment_processing # Workflow # Show decrypted credentials (debugging) output credentials show # Global output credentials show -e development # Environment # Get single credential value output credentials get anthropic.api_key # Global output credentials get stripe.key -w payment_processing # Workflow
Flags:
- •
-e/--environment: Target environment (production, development) - •
-w/--workflow: Target a specific workflow - •
-f/--force: Overwrite existing credentials (init only) - •Note:
-eand-ware mutually exclusive
Three-Tier Scope System
1. Global Credentials
config/credentials.yml.enc # Encrypted YAML config/credentials.key # Decryption key (DO NOT COMMIT)
Key env var: OUTPUT_CREDENTIALS_KEY
2. Environment-Specific Credentials
config/credentials/production.yml.enc config/credentials/production.key
Key env var: OUTPUT_CREDENTIALS_KEY_PRODUCTION
3. Per-Workflow Credentials
src/workflows/{name}/credentials.yml.enc
src/workflows/{name}/credentials.key
Key env var: OUTPUT_CREDENTIALS_KEY_{WORKFLOW_NAME} (uppercased)
Key Resolution Chain
For each scope, the key is resolved in order:
- •Environment variable (
OUTPUT_CREDENTIALS_KEY,OUTPUT_CREDENTIALS_KEY_{ENV}, orOUTPUT_CREDENTIALS_KEY_{WORKFLOW}) - •Key file on disk (e.g.,
config/credentials.key) - •Throws
MissingKeyErrorif neither found
Workflow credentials fall back to the global key if no workflow-specific key exists.
Credential Merging
When a workflow has its own credentials, they deep-merge over global credentials. Workflow values win at the same path:
# Global (config/credentials.yml.enc) anthropic: api_key: sk-ant-global aws: region: us-east-1 # Workflow (src/workflows/my_workflow/credentials.yml.enc) anthropic: api_key: sk-ant-workflow-specific stripe: secret_key: sk_live_workflow # Merged result at runtime: # anthropic.api_key -> sk-ant-workflow-specific (overridden by workflow) # aws.region -> us-east-1 (from global) # stripe.secret_key -> sk_live_workflow (added by workflow)
Migration from process.env
Before (old pattern)
import { httpClient } from '@outputai/http';
const API_KEY = process.env.SERVICE_API_KEY || '';
const client = httpClient({
prefixUrl: 'https://api.service.com',
headers: { Authorization: `Bearer ${API_KEY}` }
});
After (credentials pattern)
import { httpClient } from '@outputai/http';
import { credentials } from '@outputai/credentials';
const apiKey = credentials.require('service.api_key');
const client = httpClient({
prefixUrl: 'https://api.service.com',
headers: { Authorization: `Bearer ${apiKey}` }
});
Migration Steps
- •Run
output credentials initto create the encrypted file and key - •Run
output credentials editto add your secrets - •Replace
process.env.Xreads withcredentials.require('x')orcredentials.get('x', default) - •Remove environment variables from
.envfiles - •Add
*.keyto.gitignore
Custom Providers
Replace the default encrypted YAML backend with Vault, AWS Secrets Manager, etc.:
import { setProvider } from '@outputai/credentials';
setProvider({
loadGlobal: ({ environment }) => {
return fetchFromVault(`credentials/${environment || 'default'}`);
},
loadForWorkflow: ({ workflowName, environment }) => {
return fetchFromVault(`workflows/${workflowName}`) ?? null;
}
});
Provider Interface
interface CredentialsProvider {
loadGlobal(context: { environment: string | undefined }): Record<string, unknown>;
loadForWorkflow(context: {
workflowName: string;
workflowDir: string | undefined;
environment?: string | undefined;
}): Record<string, unknown> | null;
}
Security Considerations
- •Never commit
.keyfiles - Add*.keyto.gitignore - •Safe to commit
.yml.encfiles - Cannot be read without the key - •Key file permissions - Created with mode
0o600(owner-only read/write) - •Temp file cleanup - Plaintext overwritten with null bytes before deletion during
edit - •Use env vars in CI/CD - Set
OUTPUT_CREDENTIALS_KEYin your pipeline - •Encryption - AES-256-GCM with unique random nonce per encryption
Verification Checklist
- •
credentialsimported from@outputai/credentials - •
credentials.require()used for mandatory secrets (notprocess.env) - •
credentials.get()used with default for optional values - •
*.keylisted in.gitignore - • Credentials initialized via
output credentials init - • Secrets added via
output credentials edit
Related Skills
- •
output-dev-http-client-create- Creating HTTP clients that use credentials - •
output-dev-step-function- Using credentials in step functions - •
output-error-http-client- Troubleshooting HTTP client issues