# Practical Configuration and Credential Handling for Indie Developer Tools

> Practical configuration and credential-handling patterns for solo maintainers of CLIs, SDKs, and small developer tools.

Source: https://indiefounder.me/devtools/practical-configuration-for-indie-devtools/  
Published: 2026-11-05

Developer tools fail in production for two common reasons that have nothing to do with the core logic: the user cannot find where to set a required value, or a credential ends up in a log, a shell history, or a public repository. As a solo maintainer you cannot offer a full configuration management product. You need the smallest set of conventions that make the tool usable, debuggable, and safe by default.

This guide covers the practical patterns that work for early CLIs, SDKs, and small libraries. It is not a design for a multi-tenant config service.

## Start with the fewest places a user must look

Every additional configuration surface multiplies support questions. Prefer this order of precedence (highest first):

1. Explicit command-line flag or function argument (for one-off overrides and scripts).
2. Environment variable (for CI, containers, and secrets).
3. Local config file (for persistent personal or project settings).
4. Sensible defaults baked into the binary or library.

Document the order once in the README and stick to it. Users should be able to predict which value wins without reading source code.

Illustrative example only: a CLI that talks to an API might accept `--api-key`, fall back to `MYTOOL_API_KEY`, then look for `api_key` in `~/.config/mytool/config.toml`, and finally refuse to run if none is present. The error message lists the three places it checked.

## Config files: location, format, and ownership

Use the platform conventions so users already know where to look:

- Unix-like systems: XDG Base Directory (`$XDG_CONFIG_HOME` or `~/.config/mytool/`).
- macOS: the same XDG path is usually fine; some tools also check `~/Library/Application Support/`.
- Windows: `%APPDATA%\mytool\` or the equivalent.

Choose a human-readable format. TOML or a simple YAML subset is easier to edit than JSON for most users. Avoid custom binary formats or encrypted blobs that cannot be inspected when something breaks.

Rules that reduce pain:

- Never write secrets into a world-readable file. Default file permissions should be user-only (0600 on Unix).
- Support a project-local config (for example `.mytool.toml` in the current directory) that overrides the global one. This lets teams share non-secret settings in version control while keeping credentials out.
- Ignore unknown keys with a warning rather than failing. This lets you add options later without breaking existing files.
- Provide a `config init` or `config path` command that creates the file with comments and prints the exact location.

Do not auto-create config files on first run unless the user has explicitly opted into persistence. Silent writes surprise people and complicate uninstall.

## Credentials: keep them out of the wrong places

Credentials (API keys, tokens, passwords, private keys) deserve stricter handling than ordinary settings.

Practical defaults:

- Prefer environment variables or the system keychain / credential helper over plain config files.
- If a config file must hold a secret, make the file permission restrictive and document that it should never be committed.
- Never accept a secret as a positional argument that will appear in `ps` output or shell history. Use a flag that can be passed via environment or a prompt that does not echo.
- Support a `--password-stdin` or equivalent pattern so CI systems can pipe the value without putting it on the command line.
- When the tool stores a long-lived token after an OAuth flow, put it in the platform credential store if one is available, or in a dedicated file with tight permissions. Document how to revoke or delete it.

Illustrative flow: the user runs `mytool auth login`. The tool opens a browser, receives a short-lived code, exchanges it for a token, and stores the token via the OS keychain. Subsequent commands read from the keychain. A `mytool auth logout` command deletes the entry. No token ever appears in a config file that might be copied.

For libraries and SDKs the rule is stricter: never read credentials from a global config file on the user's behalf. Accept them as explicit arguments or from an environment variable the calling application already controls. Libraries that silently phone home or load secrets from disk create security reviews that block adoption.

## Environment variables and CI

Environment variables are the most reliable channel for automation. Keep the names predictable and prefixed (`MYTOOL_API_KEY`, `MYTOOL_CONFIG_PATH`). Support both the specific variable and a generic one if your ecosystem already has a convention (for example `DATABASE_URL` style).

In documentation, show the CI snippet that sets the variable without printing it. Mention that many CI systems mask values that match certain patterns, but the tool itself should never log the raw secret even if the mask fails.

A useful extra: accept a path to a file that contains the secret (`MYTOOL_API_KEY_FILE=/run/secrets/api_key`). This pattern works well with Docker secrets and Kubernetes and keeps the value out of the process environment list.

## Validation and clear failure messages

When a required value is missing or malformed, fail immediately with a message that lists:

- What was expected.
- Every place the tool looked.
- The exact command or config change that would fix it.

Avoid generic "invalid configuration" errors. They generate support tickets that you then have to debug by asking the user to paste files.

Validate early, before any network call or long-running work. If the API key is empty, do not start an upload and fail halfway.

## What not to do

- Do not invent a new configuration language or nested include system. Users will not learn it.
- Do not store credentials in the same file as non-secret settings if you can avoid it. The mixed file is the one people accidentally commit.
- Do not require a running daemon or cloud account just to load local settings.
- Do not log the full resolved configuration by default. Offer a `config dump` or `--debug` flag that redacts known secret keys.
- Do not change the precedence order between releases without a major version bump and a clear migration note.

## Practical checklist before you ship

- [ ] Precedence order (flag > env > file > default) is documented and implemented consistently.
- [ ] Config file location follows platform conventions and is printable via a command.
- [ ] Secrets are never written to world-readable files or command-line history by default.
- [ ] A project-local config file is supported and can be safely committed (no secrets).
- [ ] Missing required values produce an error that names every lookup location.
- [ ] `--debug` or equivalent redacts credential fields.
- [ ] Logout / uninstall path exists for any stored tokens.
- [ ] README contains a minimal working example for both interactive and CI use.
- [ ] Library consumers can supply credentials explicitly without the library reading global state.

## Common pitfalls

- Writing the API key into `~/.mytool/config` with 0644 permissions and then wondering why it appeared in a shared screenshot.
- Accepting `--token=secret` as the primary interface so every shell history and process list contains the value.
- Auto-creating a config file on first run that the user never asked for and cannot find later.
- Treating environment variables and config files as interchangeable without documenting which wins.
- Logging the entire config object at info level during normal operation.
- Making the library reach into the user's home directory for credentials; this breaks in restricted environments and during security reviews.

## When the simple approach is no longer enough

Revisit the design when:

- Teams need shared, centrally managed configuration that must stay in sync across many machines.
- You must support fine-grained per-project overrides that conflict with each other in complex monorepos.
- Compliance requirements demand audit logs of every credential access or rotation.
- The number of settings has grown so large that a single flat file is no longer readable.

Until then, one well-documented precedence order, platform-standard file locations, environment variables for secrets, and error messages that tell the user exactly where to look will cover the majority of early developer-tool use cases. The configuration system should disappear for people who only need the defaults and should be obvious for people who need to change something.

Clear configuration is part of the product surface. When users can set a value in the place they already expect and the tool fails loudly with the right hint, you spend less time answering the same support question and more time on the actual tool.
