Plain Text
Store secrets as plain text (for default values only!).
Usage
Plain text is the default when no provider is specified:
toml
[secrets]
NODE_ENV = { default = "development" } # ← Plain text, safe for non-sensitive defaults
LOG_LEVEL = { default = "info" } # ← Plain text
API_TIMEOUT = { default = "30" } # ← Plain textWhen Plain Text is Appropriate
1. Non-Sensitive Defaults
toml
[secrets]
PORT = { default = "3000" }
HOST = { default = "localhost" }
NODE_ENV = { default = "development" }
LOG_LEVEL = { default = "info" }2. Public Configuration
toml
[secrets]
PUBLIC_API_URL = { default = "https://api.example.com" }
CDN_URL = { default = "https://cdn.example.com" }3. Development Fallbacks
toml
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted-production-db...", default = "postgresql://localhost/dev_db" } # ← Fallback for local devIf the encrypted value can't be decrypted (e.g., missing key), falls back to the plaintext default.
❌ When NOT to Use Plain Text
Never for Passwords
toml
# ❌ BAD - Never do this!
[secrets]
DATABASE_PASSWORD = { default = "super-secret-password" }
# ✅ GOOD - Use encryption
[secrets]
DATABASE_PASSWORD = { provider = "age", value = "encrypted..." }Never for API Keys
toml
# ❌ BAD
[secrets]
STRIPE_KEY = { default = "sk_live_abc123xyz789" }
# ✅ GOOD
[secrets]
STRIPE_KEY = { provider = "age", value = "encrypted..." }Never for Tokens
toml
# ❌ BAD
[secrets]
JWT_SECRET = { default = "my-secret-key" }
# ✅ GOOD
[secrets]
JWT_SECRET = { provider = "age", value = "encrypted..." }Mixing Plain and Encrypted
It's common to mix plain text defaults with encrypted values:
toml
[providers]
age = { type = "age", recipients = ["age1..."] }
[secrets]
DATABASE_PASSWORD = { provider = "age", value = "encrypted...", default = "dev-password" } # Encrypted sensitive values, fallback for local dev
DATABASE_HOST = { default = "localhost" } # Plain text non-sensitive defaults
DATABASE_PORT = { default = "5432" }
LOG_LEVEL = { default = "info" }Security Best Practices
- Never commit sensitive data as plain text
- Use encryption for anything that shouldn't be public
- Use plain text only for truly non-sensitive defaults
- Review
.gitignore- Ensure sensitive files aren't tracked - Use
fnox scanto detect secrets - Scans for accidentally committed secrets
Scan for Secrets
fnox can scan your codebase for accidentally committed secrets:
bash
# Scan for potential secrets
fnox scan
# Scan specific directory
fnox scan src/
# Scan and fix (interactive)
fnox scan --fixExamples
Safe Plain Text Usage
toml
# Application settings (non-sensitive)
[secrets]
APP_NAME = { default = "My Application" }
APP_VERSION = { default = "1.0.0" }
ENVIRONMENT = { default = "development" }
DEBUG_MODE = { default = "true" }
TIMEOUT_MS = { default = "5000" }
PUBLIC_SITE_URL = { default = "https://example.com" } # Public URLs
DOCS_URL = { default = "https://docs.example.com" }Mixed Usage (Plain + Encrypted)
toml
[providers]
age = { type = "age", recipients = ["age1..."] }
[secrets]
# Sensitive (encrypted)
DATABASE_URL = { provider = "age", value = "encrypted-connection-string..." }
API_KEY = { provider = "age", value = "encrypted-key..." }
# Non-sensitive (plain)
DATABASE_POOL_SIZE = { default = "10" }
CACHE_TTL_SECONDS = { default = "3600" }
FEATURE_FLAG_NEW_UI = { default = "false" }Remember
- ✅ Plain text is fine for public, non-sensitive configuration
- ✅ Use defaults for fallback values
- ❌ Never use plain text for passwords, keys, or tokens
- ✅ Use age or other providers for sensitive data
- ✅ Run
fnox scanto catch accidental secrets
Next Steps
- Age Encryption - Encrypt sensitive secrets
- Providers Overview - Choose the right provider
- Configuration Reference - Learn more about fnox.toml