# 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
