fnox

repository·main·Indexed 24 days ago

https://github.com/jdx/fnox

A flexible secret management tool providing a unified interface for handling secrets across development, CI, and production environments. fnox supports encrypted secrets stored in git (using providers like age, aws-kms, azure-kms, or gcp-kms) and remote secrets fetched from cloud providers or password managers such as AWS Secrets Manager, 1Password, and HashiCorp Vault. It features profile-based configuration via fnox.toml, environment variable injection modes, and shell integration for automatic secret loading.

Tokens
86.8K
Snippets
245
Records
493
Agent score
83%

What's inside fnox

  1. What is fnox?

    main

    fnox is a secrets management tool designed to work with both encrypted secrets stored in git and remote cloud providers. It provides a unified interface to manage secrets across development, CI, and production environments.

    Key features include:

    • Hybrid Support: Use encrypted secrets in version control or references to remote cloud providers (AWS Secrets Manager, 1Password, etc.).
    • Multi-Environment Management: Use profiles within a single fnox.toml to manage different secrets for dev, staging, and production.
    • Flexible Storage: Store encrypted secrets in git using age, AWS KMS, Azure KMS, or GCP KMS.
    • Developer Experience: Supports offline workflows (with encrypted secrets), simple TOML configuration, and shell integration for automatic loading.
  2. Choose a secret storage provider

    main

    fnox supports several categories of providers depending on your security requirements, workflow, and environment. You can choose providers based on how they store data:

    • Encryption (secrets in git, encrypted): Stores encrypted ciphertext in your fnox.toml. Safe for version control. Examples: age, AWS KMS, Azure KMS, GCP KMS.
    • Cloud Secret Storage (remote, centralized): Stores secrets in a remote cloud service; fnox.toml only contains references to the secret names. Examples: AWS Parameter Store, AWS Secrets Manager, Azure Key Vault Secrets, GCP Secret Manager, Doppler, HashiCorp Vault.
    • Password Managers & Secret Services: Integrates with existing tools. Examples: 1Password, Bitwarden, Infisical, Proton Pass.
    • Local Storage: Stores secrets on your local machine. Examples: OS Keychain, KeePass, password-store, Plain (for non-sensitive defaults).
  3. Explore password-store ecosystem and alternatives

    main

    The password-store provider integrates with a wide ecosystem of third-party tools for managing your secrets. You can use GUI clients, mobile apps, or browser extensions to interact with the same underlying store used by fnox.

    Ecosystem Tools:

    • QtPass: Cross-platform GUI.
    • Android Password Store: Android application.
    • passff: Firefox extension.
    • browserpass: Browser extension.
    • gopass: A Go-based implementation with additional features.

    Alternative Providers in fnox: If password-store does not meet your needs, fnox also supports:

    • Age Encryption: A modern alternative to GPG.
    • OS Keychain: Uses OS-native storage.
    • 1Password: Integration with the commercial password manager.
  4. Use Azure App Configuration labels with fnox profiles

    main

    Since Azure App Configuration allows multiple values for a single key distinguished by a label, you can map these labels to fnox profiles. This allows you to use the same key name across different environments (e.g., dev vs prod) by switching profiles.

    [profiles.dev.providers]
    appconfig = { type = "azure-ac", endpoint = "https://myapp-config.azconfig.io", label = "dev" }
    
    [profiles.prod.providers]
    appconfig = { type = "azure-ac", endpoint = "https://myapp-config.azconfig.io", label = "prod" }
  5. Use `credential_command` for provider-scoped login

    main

    If you have multiple Vault/OpenBao providers that require different authentication methods, use the credential_command option. This allows you to define a specific login command for a named provider.

    fnox will set VAULT_ADDR and VAULT_NAMESPACE for the command based on the provider's configuration. The command's output (the token) is cached briefly for the current fnox process to avoid repeated logins when resolving multiple secrets from the same provider.

    [providers.vault_team_a]
    type = "vault"
    address = "https://vault.example.com"
    namespace = "team-a"
    path = "secret/team-a"
    credential_command = "vault login -method=oidc -token-only"
  6. Use FOKS teams for shared secrets

    main

    FOKS supports teams, which provide dedicated key-value namespaces for shared secrets. You can manage teams using the foks CLI and then configure fnox to use a specific team via the team key in the provider configuration.

    1. Create and manage teams via CLI

    foks team create my-team
    foks team add my-team alice

    2. Configure fnox to use a team

    In your fnox.toml, set the team option in the provider definition:

    [providers]
    ops = { type = "foks", prefix = "/fnox/", team = "ops" }
    
    [secrets]
    DATABASE_URL = { provider = "ops", value = "db/primary" }

    3. Mixing personal and team secrets

    You can define multiple provider instances to separate personal secrets from team-scoped secrets:

    [providers]
    me  = { type = "foks", prefix = "/fnox/" }
    ops = { type = "foks", prefix = "/fnox/", team = "ops" }
    
    [secrets]
    PERSONAL_TOKEN = { provider = "me",  value = "github-token" }
    DATABASE_URL   = { provider = "ops", value = "db/primary" }
  7. Access specific fields from Bitwarden Secrets

    main

    When referencing a secret in fnox.toml, you can specify which field of the Bitwarden secret to retrieve by appending the field name to the value string using a forward slash.

    Supported fields:

    • value (default): The actual secret content.
    • key: The secret's key name.
    • note: The secret's note.

    Example usage:

    [secrets]
    # Gets the secret value (default)
    MY_SECRET = { provider = "bws", value = "my-secret-name" }
    
    # Gets the secret's note
    MY_NOTE = { provider = "bws", value = "my-secret-name/note" }
    
    # Gets the secret's key name
    MY_KEY = { provider = "bws", value = "my-secret-name/key" }
    [secrets]
    MY_SECRET = { provider = "bws", value = "my-secret-name" }
    MY_NOTE = { provider = "bws", value = "my-secret-name/note" }
    MY_KEY = { provider = "bws", value = "my-secret-name/key" }
  8. Compare Hierarchy vs Imports in fnox

    main

    fnox provides two ways to compose configuration: Hierarchy and Imports.

    Hierarchy (Automatic)

    • Mechanism: Automatically walks up the directory tree.
    • Use Case: Best for monorepos and multi-service projects where configuration is tied to file location.
    • Behavior: Merges fnox.toml and fnox.local.toml files found in parent directories.

    Imports (Explicit)

    • Mechanism: Uses an import key within a fnox.toml file to pull in specific files.
    • Use Case: Best for cross-cutting concerns or shared secret bundles that aren't necessarily in a parent directory.
    • Syntax:
    # Explicit file imports
    import = ["./shared/secrets.toml", "./envs/dev.toml"]
  9. How hierarchical configuration works in fnox

    main

    fnox uses a hierarchical merging strategy to build configuration by walking up the directory tree from your current working directory. This allows monorepos and multi-service projects to share common secrets while allowing individual services to define their own or override parent values.

    Merge Order (Lowest to Highest Priority)

    When running fnox from a subdirectory, the configuration is merged in this specific order:

    1. ~/.config/fnox/config.toml (Global config)
    2. project/fnox.toml (Parent directory)
    3. project/fnox.local.toml (Parent local overrides)
    4. project/services/api/fnox.toml (Current directory)
    5. project/services/api/fnox.local.toml (Current local overrides)

    Key Rules

    • Precedence: Child configs override parent configs. Local configs (.local.toml) override main configs (fnox.toml) at the same level.
    • Global Config: The global config is always loaded as the base layer, even if you use -c/--config to point to a specific file or use root = true to stop directory recursion.
    • Stopping Recursion: Use root = true in a configuration file to prevent fnox from searching further up the parent directory tree.
  10. Understand the `if_missing` priority chain

    main

    When determining how to handle a missing secret, fnox evaluates configuration in the following order of priority (highest to lowest):

    1. CLI flag: --if-missing <mode>
    2. Environment variable: FNOX_IF_MISSING=<mode>
    3. Secret-level config: if_missing defined within the specific secret's TOML table.
    4. Top-level config: A global if_missing setting in the main configuration file.
    5. Base default environment variable: FNOX_IF_MISSING_DEFAULT=<mode>
    6. Default: warn (the fallback if nothing else is configured).
  11. How to use fnox to manage secrets

    main

    fnox uses a configuration file named fnox.toml to define secrets. This file can contain either the encrypted secret values themselves or references to secrets hosted in a cloud provider.

    You can consume these secrets in two primary ways:

    1. Command Execution: Use fnox exec -- <command> to run a specific command with the secrets from your fnox.toml loaded into its environment.
    2. Shell Integration: Enable shell integration to automatically load secrets into your shell environment whenever you cd into a directory containing a fnox.toml file.
  12. Understand the secret resolution order in fnox

    main

    When fnox attempts to resolve a secret, it follows a specific priority order. The first match found is used, and subsequent sources are ignored. The order is:

    1. Encrypted value: If a provider (like age) and an encrypted value are provided.
    2. Provider reference: If a provider (like aws) and a reference value (the secret name) are provided.
    3. Environment variable: If the secret name is already set in your current shell environment.
    4. Default value: If a default key is provided in the configuration as a fallback.