Real-World Example
Let's build a complete setup for a typical web application with development, staging, and production environments.
The Scenario
You're building an API that needs:
- Database URL
- API keys (Stripe, SendGrid)
- JWT secret
- External service URLs
Requirements:
- Development: Secrets in git (encrypted) so team can clone and run
- Staging: Secrets in git (encrypted) with staging values
- Production: Secrets in AWS Secrets Manager (never in git)
Step 1: Initialize
cd my-api
fnox init
git initStep 2: Set Up Age Encryption (for Dev/Staging)
# Generate age key
age-keygen -o ~/.config/fnox/age.txt
# Get your public key
grep "public key:" ~/.config/fnox/age.txt
# Output: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# For teams: collect everyone's public keys and add them allAdd to fnox.toml:
# Shared age provider for dev and staging
[providers.age]
type = "age"
recipients = [
"age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p", # alice
"age1pr3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqabc123", # bob
"age1zr3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqdxf456", # ci
]Set decryption key:
# Add to ~/.bashrc or ~/.zshrc
export FNOX_AGE_KEY=$(cat ~/.config/fnox/age.txt | grep "AGE-SECRET-KEY")Step 3: Add Development Secrets
# Encrypt development secrets
fnox set DATABASE_URL "postgresql://localhost/mydb" --provider age
fnox set JWT_SECRET "$(openssl rand -hex 32)" --provider age
fnox set STRIPE_KEY "sk_test_abc123" --provider age
fnox set SENDGRID_KEY "SG.test123" --provider ageYour fnox.toml now contains encrypted secrets:
[providers.age]
type = "age"
recipients = ["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"]
[secrets.DATABASE_URL]
provider = "age"
value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC..."
[secrets.JWT_SECRET]
provider = "age"
value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC..."
[secrets.STRIPE_KEY]
provider = "age"
value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC..."
[secrets.SENDGRID_KEY]
provider = "age"
value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC..."Commit this! It's encrypted and safe.
git add fnox.toml
git commit -m "Add encrypted development secrets"Step 4: Add Staging Profile
Add staging secrets (also encrypted):
# Switch to staging profile
fnox set DATABASE_URL "postgresql://staging.db.example.com/mydb" \
--provider age \
--profile staging
fnox set JWT_SECRET "$(openssl rand -hex 32)" \
--provider age \
--profile staging
fnox set STRIPE_KEY "sk_test_staging_xyz" \
--provider age \
--profile staging
fnox set SENDGRID_KEY "SG.staging456" \
--provider age \
--profile stagingYour fnox.toml now has a staging profile:
[providers.age]
type = "age"
recipients = ["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"]
# Development (default profile)
[secrets.DATABASE_URL]
provider = "age"
value = "..."
[secrets.JWT_SECRET]
provider = "age"
value = "..."
# ... other dev secrets ...
# Staging profile
[profiles.staging.secrets.DATABASE_URL]
provider = "age"
value = "..."
[profiles.staging.secrets.JWT_SECRET]
provider = "age"
value = "..."
# ... other staging secrets ...Step 5: Add Production Profile (AWS Secrets Manager)
Add production configuration (secrets stored in AWS):
# Add to fnox.toml
[profiles.production.providers.aws]
type = "aws-sm"
region = "us-east-1"
prefix = "myapi/"
[profiles.production.secrets.DATABASE_URL]
provider = "aws"
value = "database-url"
if_missing = "error" # Critical secret
[profiles.production.secrets.JWT_SECRET]
provider = "aws"
value = "jwt-secret"
if_missing = "error"
[profiles.production.secrets.STRIPE_KEY]
provider = "aws"
value = "stripe-key"
if_missing = "error"
[profiles.production.secrets.SENDGRID_KEY]
provider = "aws"
value = "sendgrid-key"
if_missing = "error"Create secrets in AWS:
aws secretsmanager create-secret \
--name "myapi/database-url" \
--secret-string "postgresql://prod.rds.amazonaws.com/mydb"
aws secretsmanager create-secret \
--name "myapi/jwt-secret" \
--secret-string "$(openssl rand -base64 64)"
aws secretsmanager create-secret \
--name "myapi/stripe-key" \
--secret-string "sk_live_REAL_KEY_HERE"
aws secretsmanager create-secret \
--name "myapi/sendgrid-key" \
--secret-string "SG.REAL_KEY_HERE"Commit the production references:
git add fnox.toml
git commit -m "Add production profile (AWS Secrets Manager)"Step 6: Local Overrides
Create .gitignore:
cat > .gitignore << 'EOF'
fnox.local.toml
.env
EOFEach developer can create personal overrides:
# fnox.local.toml (not committed)
[secrets.DATABASE_URL]
default = "postgresql://localhost/alice_db" # Personal DB
[secrets.DEBUG_MODE]
default = "true" # Enable debuggingStep 7: Use It
Development
# Enable shell integration
eval "$(fnox activate bash)"
echo 'eval "$(fnox activate bash)"' >> ~/.bashrc
# Navigate to project (secrets auto-load)
cd my-api
# fnox: +4 DATABASE_URL, JWT_SECRET, STRIPE_KEY, SENDGRID_KEY
# Run the app
npm run devOr explicitly:
fnox exec -- npm run devStaging
# Deploy to staging
fnox exec --profile staging -- ./deploy.sh
# Or set profile for session
export FNOX_PROFILE=staging
fnox exec -- ./deploy.shProduction
# Ensure AWS credentials are set
export AWS_REGION=us-east-1
# Deploy to production
fnox exec --profile production -- ./deploy.shStep 8: CI/CD Setup
GitHub Actions
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v3 # Installs fnox via mise
# Decrypt dev secrets for testing
- name: Setup fnox
env:
FNOX_AGE_KEY: ${{ secrets.FNOX_AGE_KEY }}
run: |
# Use the CI age key to decrypt secrets
echo "FNOX_AGE_KEY is already set from secrets"
- name: Run tests
run: |
fnox exec -- npm test
deploy-staging:
if: github.ref == 'refs/heads/develop'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v3
- name: Deploy to staging
env:
FNOX_AGE_KEY: ${{ secrets.FNOX_AGE_KEY }}
run: |
fnox exec --profile staging -- ./deploy.sh
deploy-production:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v3
- name: Deploy to production
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: us-east-1
FNOX_IF_MISSING: error # Fail if any secret is missing
run: |
fnox exec --profile production -- ./deploy.shSet GitHub Secrets
- Go to your repo → Settings → Secrets → Actions
- Add
FNOX_AGE_KEY:bash# Copy the CI age secret key (from the CI recipient's age.txt) cat ~/.config/fnox/ci-age.txt | grep "AGE-SECRET-KEY" - Add
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYfor production
Step 9: Team Onboarding
New team member joins:
# 1. Clone the repo
git clone https://github.com/myorg/my-api
cd my-api
# 2. Install fnox (via mise)
mise install
# 3. Generate age key
age-keygen -o ~/.config/fnox/age.txt
# 4. Share public key with team
grep "public key:" ~/.config/fnox/age.txt
# Send to team lead to add to fnox.toml recipients
# 5. Set decryption key
echo 'export FNOX_AGE_KEY=$(cat ~/.config/fnox/age.txt | grep "AGE-SECRET-KEY")' >> ~/.bashrc
source ~/.bashrc
# 6. Enable shell integration
echo 'eval "$(fnox activate bash)"' >> ~/.bashrc
# 7. Team lead updates fnox.toml with new recipient
# Then re-encrypts all secrets:
fnox set DATABASE_URL "$(fnox get DATABASE_URL)" --provider age
# ... repeat for all secrets
# 8. New team member pulls and runs
git pull
cd my-api
# fnox: +4 DATABASE_URL, JWT_SECRET, STRIPE_KEY, SENDGRID_KEY
npm run dev # Just works!What We Achieved
✅ Dev secrets encrypted in git → Team can clone and run immediately ✅ Staging secrets encrypted in git → Safe deployment ✅ Prod secrets in AWS → Never in git, centrally managed ✅ Shell integration → Secrets auto-load on cd ✅ Local overrides → Each developer can customize ✅ CI/CD ready → Tests run with dev secrets, deploys use appropriate profiles ✅ Team-friendly → Simple onboarding, no manual secret sharing
File Structure
my-api/
├── .gitignore # fnox.local.toml, .env
├── fnox.toml # Committed (encrypted dev/staging, AWS refs for prod)
├── fnox.local.toml # Gitignored (personal overrides)
├── package.json
├── src/
└── .github/
└── workflows/
└── ci.yml # CI/CD with fnoxNext Steps
- Providers - Explore other providers
- Shell Integration - Advanced shell setup
- Hierarchical Config - Organize larger projects