# Configuration Reference
Complete reference for the `fnox.toml` configuration file.
## JSON Schema
A JSON Schema is available for IDE autocompletion and validation:
```
https://fnox.jdx.dev/schema.json
```
### Editor Setup
**VS Code** with [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml):
```toml
#:schema https://fnox.jdx.dev/schema.json
[providers]
age = { type = "age", recipients = ["age1..."] }
```
**JetBrains IDEs**: Add the schema URL in Settings > Languages & Frameworks > Schemas and DTDs > JSON Schema Mappings.
## File Location
fnox looks for configuration files in this order (lowest to highest priority):
1. **Global config**: `~/.config/fnox/config.toml` (or `$FNOX_CONFIG_DIR/config.toml`)
2. `fnox.toml` in parent directories (hierarchical search)
3. `fnox.toml` in current directory
4. `fnox.$FNOX_PROFILE.toml` alongside each `fnox.toml` (profile-specific)
5. `fnox.local.toml` alongside each `fnox.toml` (for local overrides)
6. Path specified via `-c, --config` flag
### Global Configuration
The global config file stores machine-wide secrets and providers that apply to all projects:
```bash
# Initialize global config
fnox init --global
# Add secrets to global config
fnox set MY_TOKEN "secret-value" --global
# Add providers to global config
fnox provider add aws aws-sm --global
```
**Location**: `~/.config/fnox/config.toml` (customizable via `FNOX_CONFIG_DIR`)
**Use cases**:
- Personal API tokens used across multiple projects
- Machine-specific credentials
- Default providers available everywhere
## Basic Structure
```toml
# Top-level settings
if_missing = "warn" # Global default for missing secrets
import = ["./shared/secrets.toml"] # Import other configs
# Provider definitions
[providers]
PROVIDER_NAME = { type = "PROVIDER_TYPE" } # ... provider-specific config ...
# Secret definitions
[secrets]
SECRET_NAME = { provider = "PROVIDER_NAME", value = "...", default = "...", if_missing = "error", description = "..." }
# Profile definitions
[profiles.PROFILE_NAME]
# ... same structure as top-level ...
```
## Top-Level Settings
### `if_missing`
Global default behavior when secrets cannot be resolved.
```toml
if_missing = "error" # or "warn", "ignore"
```
**Values:**
- `"error"` - Fail if secret is missing
- `"warn"` - Print warning and continue (default)
- `"ignore"` - Silently skip missing secrets
**Priority:** Lowest (overridden by secret-level, env vars, CLI flags).
### `imports`
List of config files to import.
```toml
import = ["./shared/base.toml", "./envs/dev.toml"]
```
**Usage:**
- Paths relative to current config file
- Imported files merged into current config
- Later imports override earlier ones
## Provider Configuration
```toml
[providers.PROVIDER_NAME]
type = "PROVIDER_TYPE"
# ... provider-specific fields ...
```
### `auth_command`
Override the authentication command for a specific provider instance. When provider authentication fails in a TTY, fnox prompts to run this command. By default, each provider type has a built-in auth command (e.g., `bw login` for Bitwarden, `op signin` for 1Password).
```toml
[providers]
# Use rbw instead of the default bw CLI
rbw = { type = "bitwarden", backend = "rbw", auth_command = "rbw unlock" }
# Use a custom AWS SSO profile
aws = { type = "aws-sm", region = "us-east-1", auth_command = "aws sso login --profile myprofile" }
# Disable auth prompting for this provider
vault = { type = "vault", address = "https://vault.example.com", auth_command = "" }
```
Setting `auth_command = ""` disables the auth prompt for that provider instance.
### Common Provider Types
#### Age Encryption
```toml
[providers.age]
type = "age"
recipients = [
"age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p",
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQs..."
]
```
#### AWS Secrets Manager
```toml
[providers]
aws = { type = "aws-sm", region = "us-east-1", prefix = "myapp/" } # prefix is optional
```
#### AWS KMS
```toml
[providers]
kms = { type = "aws-kms", key_id = "arn:aws:kms:us-east-1:123456789012:key/...", region = "us-east-1" }
```
#### Azure Key Vault Secrets
```toml
[providers]
azure = { type = "azure-sm", vault_url = "https://myapp-vault.vault.azure.net/", prefix = "myapp/" } # prefix is optional
```
#### Azure Key Vault Keys
```toml
[providers]
azurekms = { type = "azure-kms", vault_url = "https://myapp-vault.vault.azure.net/", key_name = "encryption-key" }
```
#### GCP Secret Manager
```toml
[providers]
gcp = { type = "gcp-sm", project = "my-project-id", prefix = "myapp/" } # prefix is optional
```
#### GCP Cloud KMS
```toml
[providers.gcpkms]
type = "gcp-kms"
project = "my-project-id"
location = "us-central1"
keyring = "fnox-keyring"
key = "fnox-key"
```
#### 1Password
```toml
[providers]
onepass = { type = "1password", vault = "Development", account = "my.1password.com" } # account is optional
```
#### Bitwarden
```toml
[providers]
bitwarden = { type = "bitwarden", collection = "collection-id", organization_id = "org-id" } # both optional
```
#### HashiCorp Vault
```toml
[providers]
vault = { type = "vault", address = "https://vault.example.com:8200", path = "secret/myapp", token = "hvs.CAESIJ..." } # token optional, can use VAULT_TOKEN env var
```
#### OS Keychain
```toml
[providers]
keychain = { type = "keychain", service = "fnox", prefix = "myapp/" } # prefix is optional
```
## Secret Configuration
```toml
[secrets]
SECRET_NAME = { provider = "PROVIDER_NAME", value = "...", default = "...", if_missing = "error", description = "..." }
```
### Fields
#### `provider`
Provider to use for this secret.
```toml
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted..." }
```
**Required:** Unless using only `default` (plain text).
#### `value`
Provider-specific value:
- **Encryption providers** (age, aws-kms, etc.): Encrypted ciphertext
- **Remote providers** (aws-sm, 1password, etc.): Secret name/reference
```toml
[secrets]
# Encrypted ciphertext (age)
DATABASE_URL = { provider = "age", value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC..." }
# Remote reference (AWS)
DATABASE_URL = { provider = "aws", value = "database-url" } # Secret name in AWS Secrets Manager
```
#### `default`
Fallback value if secret cannot be resolved.
```toml
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted...", default = "postgresql://localhost/dev" } # Fallback for local dev
```
**Use for:**
- Non-sensitive defaults
- Local development fallbacks
- Optional configuration
#### `if_missing`
Behavior when secret cannot be resolved.
```toml
[secrets]
DATABASE_URL = { provider = "aws", value = "database-url", if_missing = "error" } # Fail if missing (critical secret)
ANALYTICS_KEY = { provider = "aws", value = "analytics-key", if_missing = "ignore" } # Silently skip if missing (optional)
```
**Values:** `"error"`, `"warn"`, `"ignore"`
**Priority:** Overrides top-level `if_missing`, but overridden by env vars and CLI flags.
#### `description`
Human-readable description.
```toml
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted...", description = "Production database connection string" }
```
## Profile Configuration
Profiles allow environment-specific configuration:
```toml
# Default profile (no prefix)
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted-dev..." }
# Production profile
[profiles.production]
[profiles.production.providers]
aws = { type = "aws-sm", region = "us-east-1" }
[profiles.production.secrets]
DATABASE_URL = { provider = "aws", value = "database-url" }
```
### Profile Structure
```toml
[profiles.PROFILE_NAME]
if_missing = "error" # Profile-specific default
[profiles.PROFILE_NAME.providers]
PROVIDER_NAME = { type = "PROVIDER_TYPE" } # ... provider config ...
[profiles.PROFILE_NAME.secrets]
SECRET_NAME = { provider = "PROVIDER_NAME", value = "..." } # ... secret config ...
```
### Profile Inheritance
Profiles inherit top-level secrets and providers:
```toml
# Top-level (inherited by all profiles)
[secrets]
LOG_LEVEL = { default = "info" }
DATABASE_URL = { provider = "age", value = "encrypted-dev..." }
# Production profile
[profiles.production.secrets]
DATABASE_URL = { provider = "aws", value = "prod-db" } # Overrides top-level DATABASE_URL
# Inherits LOG_LEVEL="info" from top-level
```
You can disable this merge behavior at runtime:
```bash
fnox exec --profile production --no-defaults -- ./deploy.sh
```
With `--no-defaults`, only `[profiles.<name>.secrets]` are used for the selected profile.
## Complete Example
```toml
# Global settings
if_missing = "warn"
import = ["./shared/common.toml"]
# Providers
[providers]
age = { type = "age", recipients = ["age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"] }
aws = { type = "aws-sm", region = "us-east-1", prefix = "myapp/" }
# Default profile secrets
[secrets]
DATABASE_URL = { provider = "age", value = "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdC...", default = "postgresql://localhost/dev", description = "Database connection string" }
JWT_SECRET = { provider = "age", value = "encrypted...", if_missing = "error" }
LOG_LEVEL = { default = "info" }
# Production profile
[profiles.production]
if_missing = "error"
[profiles.production.providers]
aws = { type = "aws-sm", region = "us-east-1", prefix = "myapp-prod/" }
[profiles.production.secrets]
DATABASE_URL = { provider = "aws", value = "database-url", description = "Production database" }
JWT_SECRET = { provider = "aws", value = "jwt-secret" }
# Inherits LOG_LEVEL from top-level
```
## Local Overrides
Create `fnox.local.toml` alongside `fnox.toml` for local overrides:
```toml
# fnox.local.toml (gitignored)
[secrets]
DATABASE_URL = { default = "postgresql://localhost/mylocal" } # Override for local development
DEBUG_MODE = { default = "true" }
```
**Important:** Add to `.gitignore`:
```gitignore
fnox.local.toml
```
## Profile-Specific Config Files
You can create environment-specific config files that load based on the `FNOX_PROFILE` environment variable:
```bash
# Directory structure
project/
├── fnox.toml # Base config
├── fnox.production.toml # Production overrides
├── fnox.staging.toml # Staging overrides
├── fnox.development.toml # Development overrides
└── fnox.local.toml # Local overrides (gitignored)
```
Example usage:
```bash
# Use default config (fnox.toml only)
fnox exec -- npm start
# Use production config (fnox.toml + fnox.production.toml)
FNOX_PROFILE=production fnox exec -- ./deploy.sh
# Use staging config (fnox.toml + fnox.staging.toml)
FNOX_PROFILE=staging fnox exec -- ./deploy.sh
```
**Key differences:**
- `fnox.$FNOX_PROFILE.toml` files are **committed to git** (environment-specific, but shared with team)
- `fnox.local.toml` is **gitignored** (machine-specific, personal overrides)
- Profile-specific files work with the default profile's secrets, not `[profiles.xxx]` sections
- `fnox.default.toml` is **not loaded** (use `fnox.toml` instead)
## Hierarchical Configuration
fnox searches parent directories for `fnox.toml` files:
```
project/
├── fnox.toml # Root config
└── services/
└── api/
└── fnox.toml # API config (inherits from root)
```
Merge order (lowest to highest priority):
1. **Global config** (`~/.config/fnox/config.toml`)
2. Root `fnox.toml`
3. Root `fnox.$FNOX_PROFILE.toml` (if `FNOX_PROFILE` is set and not "default")
4. Root `fnox.local.toml`
5. Child `fnox.toml`
6. Child `fnox.$FNOX_PROFILE.toml` (if `FNOX_PROFILE` is set and not "default")
7. Child `fnox.local.toml`
**Note**: Global config is always loaded, even when `root = true` stops parent directory recursion.
## Next Steps
- [CLI Reference](/cli/) - All available commands
- [Environment Variables](/reference/environment) - Environment variable reference
- [Providers Overview](/providers/overview) - Available providers