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.jsonEditor Setup
VS Code with Even Better 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):
- Global config:
~/.config/fnox/config.toml(or$FNOX_CONFIG_DIR/config.toml) fnox.tomlin parent directories (hierarchical search)fnox.tomlin current directoryfnox.$FNOX_PROFILE.tomlalongside eachfnox.toml(profile-specific)fnox.local.tomlalongside eachfnox.toml(for local overrides)- Path specified via
-c, --configflag
Global Configuration
The global config file stores machine-wide secrets and providers that apply to all projects:
# 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 --globalLocation: ~/.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
# 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.
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).
env
Default injection mode for all secrets in the config. Secrets that don't set their own env inherit this value.
env = "exec" # or true, falseValues:
true- Inject into the shell (via shell integration /fnox export) andfnox execsubprocesses (default)"exec"- Only inject intofnox execsubprocesses; never the interactive shellfalse- Never inject; secrets are only accessible viafnox get
Setting env = "exec" at the top level keeps every secret out of the interactive shell by default — useful when AI coding agents or other tools run in your shell and would otherwise inherit all injected secrets. Applications still receive secrets when launched through fnox exec -- <command>, and individual secrets can opt back in with env = true:
env = "exec" # nothing enters the interactive shell...
[secrets]
DATABASE_URL = { provider = "age", value = "..." } # exec-only (inherited)
HOMEBREW_GITHUB_API_TOKEN = { provider = "age", value = "...", env = true } # ...except thisNote that this limits ambient exposure: processes in your shell no longer see secret values in their environment. Anyone who can run commands in your shell can still invoke fnox get or fnox exec themselves — for a hard boundary, combine this with the MCP server allowlist and OS-level sandboxing.
imports
List of config files to import.
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
Path Values
Paths declared in config files are resolved relative to the config file that declares them. This applies to imports and provider filesystem paths such as age.key_file, keepass.database, keepass.keyfile, password-store.store_dir, and foks.home.
# project/fnox.toml
import = ["./shared/secrets.toml"] # project/shared/secrets.toml
[providers.keepass]
type = "keepass"
database = "./secrets.kdbx" # project/secrets.kdbxPaths beginning with ~ expand to your home directory. Absolute paths are used unchanged. CLI path arguments remain relative to the current working directory, and environment variable paths keep their existing environment-specific behavior.
daemon
Enable memory-only daemon caching for supported read commands.
[daemon]
enabled = true
idle_timeout = "8h"Fields:
enabled- Enable daemon-backed resolution. Defaults tofalse.idle_timeout- How long the daemon stays alive while idle. Defaults to"8h". Supports values such as"30m","8h", or"1d".
See Per-User Daemon.
Provider Configuration
[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).
[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.
daemon_cache
Disable daemon cache reuse for all secrets that use this provider.
[providers.op]
type = "1password"
vault = "Engineering"
daemon_cache = falseCommon Provider Types
Age Encryption
[providers.age]
type = "age"
recipients = [
"age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p",
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQs..."
]AWS Secrets Manager
[providers]
aws = { type = "aws-sm", region = "us-east-1", prefix = "myapp/" } # prefix is optionalAWS KMS
[providers]
kms = { type = "aws-kms", key_id = "arn:aws:kms:us-east-1:123456789012:key/...", region = "us-east-1" }Azure Key Vault Secrets
[providers]
azure = { type = "azure-sm", vault_url = "https://myapp-vault.vault.azure.net/", prefix = "myapp/" } # prefix is optionalAzure Key Vault Keys
[providers]
azurekms = { type = "azure-kms", vault_url = "https://myapp-vault.vault.azure.net/", key_name = "encryption-key" }GCP Secret Manager
[providers]
gcp = { type = "gcp-sm", project = "my-project-id", prefix = "myapp/" } # prefix is optionalGCP Cloud KMS
[providers.gcpkms]
type = "gcp-kms"
project = "my-project-id"
location = "us-central1"
keyring = "fnox-keyring"
key = "fnox-key"1Password
[providers]
onepass = { type = "1password", vault = "Development", account = "my.1password.com" } # account is optionalBitwarden
[providers]
bitwarden = { type = "bitwarden", collection = "collection-id", organization_id = "org-id" } # both optionalHashiCorp Vault
[providers]
vault = { type = "vault", address = "https://vault.example.com:8200", path = "secret/myapp", token = "hvs.CAESIJ..." } # token optional, can use VAULT_TOKEN env varOS Keychain
[providers]
keychain = { type = "keychain", service = "fnox", prefix = "myapp/" } # prefix is optionalSecret Configuration
[secrets]
SECRET_NAME = { provider = "PROVIDER_NAME", value = "...", default = "...", if_missing = "error", description = "..." }Fields
provider
Provider to use for this secret.
[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
[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 Managerdaemon_cache
Disable daemon cache reuse for this secret.
[secrets]
PAYMENT_API_KEY = { provider = "op", value = "Payments/api-key", daemon_cache = false }default
Fallback value if secret cannot be resolved.
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted...", default = "postgresql://localhost/dev" } # Fallback for local devUse for:
- Non-sensitive defaults
- Local development fallbacks
- Optional configuration
if_missing
Behavior when secret cannot be resolved.
[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.
env
Where the secret is injected as an environment variable.
[secrets]
GITHUB_TOKEN = { provider = "age", value = "..." } # true (default): shell + fnox exec
DATABASE_URL = { provider = "age", value = "...", env = "exec" } # only fnox exec subprocesses
SIGNING_KEY = { provider = "age", value = "...", env = false } # never injected; fnox get onlyValues:
true- Injected by shell integration andfnox exec(default)"exec"- Only injected intofnox execsubprocesses, never the interactive shellfalse- Never injected as an env var; retrieve explicitly withfnox get
Priority: Overrides the top-level env default.
fnox export follows shell semantics: env = "exec" and env = false secrets are excluded unless --all is passed.
as_file
Write the secret to an ephemeral temp file and set the env var to the file path instead of the value.
[secrets]
GOOGLE_APPLICATION_CREDENTIALS = { provider = "op", value = "GCP Service Account/key file", as_file = true }json_path
Extract a field from a JSON secret value (dot notation for nesting).
[secrets]
DB_PASSWORD = { provider = "aws", value = "db-credentials", json_path = "credentials.password" }line
Extract the Nth line (1-indexed) from a multi-line secret value. Useful for providers that pack several related values into one entry. Mutually exclusive with json_path.
[secrets]
USERNAME = { provider = "pass", value = "master", line = 2 }description
Human-readable description.
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted...", description = "Production database connection string" }Profile Configuration
Profiles allow environment-specific configuration:
# 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
[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:
# 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-levelYou can disable this merge behavior at runtime:
fnox exec --profile production --no-defaults -- ./deploy.shWith --no-defaults, only [profiles.<name>.secrets] are used for the selected profile.
Complete Example
# 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-levelLocal Overrides
Create fnox.local.toml alongside fnox.toml for local overrides:
# fnox.local.toml (gitignored)
[secrets]
DATABASE_URL = { default = "postgresql://localhost/mylocal" } # Override for local development
DEBUG_MODE = { default = "true" }Important: Add to .gitignore:
fnox.local.tomlProfile-Specific Config Files
You can create environment-specific config files that load based on the FNOX_PROFILE environment variable:
# 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:
# 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.shKey differences:
fnox.$FNOX_PROFILE.tomlfiles are committed to git (environment-specific, but shared with team)fnox.local.tomlis gitignored (machine-specific, personal overrides)- Profile-specific files work with the default profile's secrets, not
[profiles.xxx]sections fnox.default.tomlis not loaded (usefnox.tomlinstead)
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):
- Global config (
~/.config/fnox/config.toml) - Root
fnox.toml - Root
fnox.$FNOX_PROFILE.toml(ifFNOX_PROFILEis set and not "default") - Root
fnox.local.toml - Child
fnox.toml - Child
fnox.$FNOX_PROFILE.toml(ifFNOX_PROFILEis set and not "default") - Child
fnox.local.toml
Note: Global config is always loaded, even when root = true stops parent directory recursion.
Next Steps
- CLI Reference - All available commands
- Environment Variables - Environment variable reference
- Providers Overview - Available providers