Skip to content

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:

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).

env

Default injection mode for all secrets in the config. Secrets that don't set their own env inherit this value.

toml
env = "exec"  # or true, false

Values:

  • true - Inject into the shell (via shell integration / fnox export) and fnox exec subprocesses (default)
  • "exec" - Only inject into fnox exec subprocesses; never the interactive shell
  • false - Never inject; secrets are only accessible via fnox 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:

toml
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 this

Note 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.

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

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.

toml
# project/fnox.toml
import = ["./shared/secrets.toml"] # project/shared/secrets.toml

[providers.keepass]
type = "keepass"
database = "./secrets.kdbx" # project/secrets.kdbx

Paths 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.

toml
[daemon]
enabled = true
idle_timeout = "8h"

Fields:

  • enabled - Enable daemon-backed resolution. Defaults to false.
  • 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

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.

daemon_cache

Disable daemon cache reuse for all secrets that use this provider.

toml
[providers.op]
type = "1password"
vault = "Engineering"
daemon_cache = false

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

daemon_cache

Disable daemon cache reuse for this secret.

toml
[secrets]
PAYMENT_API_KEY = { provider = "op", value = "Payments/api-key", daemon_cache = false }

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.

env

Where the secret is injected as an environment variable.

toml
[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 only

Values:

  • true - Injected by shell integration and fnox exec (default)
  • "exec" - Only injected into fnox exec subprocesses, never the interactive shell
  • false - Never injected as an env var; retrieve explicitly with fnox 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.

toml
[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).

toml
[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.

toml
[secrets]
USERNAME = { provider = "pass", value = "master", line = 2 }

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

MIT LicenseCopyright © 2026jdx.dev