# Fnox Development Guide
## Conventional Commits
Format: `<type>(<scope>): <description>` (lowercase, imperative mood)
**Types:** `feat`, `fix`, `refactor`, `docs`, `style`, `perf`, `test`, `chore`, `security`
**Scopes:** command names (`get`, `set`, `exec`, `list`, `provider`), provider names (`age`, `1password`, `bitwarden`, `bitwarden-sm`, `aws-kms`, `aws-sm`, `aws-ps`, `keychain`, `keepass`, `infisical`, `passwordstate`, `pass`, `proton-pass`), subsystems (`config`, `encryption`, `env`, `deps`)
Examples: `fix(aws-sm): handle pagination for large secret lists`, `feat(exec): add --no-inherit flag`
## Build & Test
```bash
mise run build # Build (debug mode, never use --release)
mise run test # Run all tests (cargo + bats)
mise run test:cargo # Cargo tests only
mise run test:bats # Bats tests only (depends on build)
mise run test:bats -- test/init.bats # Specific bats test file
mise run ci # Full CI: build + test + lint
mise run lint # Lint (hk)
mise run lint-fix # Auto-fix lint issues
```
**Provider test requirements** (all skip gracefully if credentials unavailable):
- 1Password: `OP_SERVICE_ACCOUNT_TOKEN` env var
- Bitwarden: `BW_SESSION` env var (use `source ./test/setup-bitwarden-test.sh` for local vaultwarden)
- Infisical: `INFISICAL_TOKEN` env var
- KeePass: `KEEPASS_PASSWORD` env var (self-contained, no external services)
- Passwordstate: `PASSWORDSTATE_BASE_URL`, `PASSWORDSTATE_API_KEY`, `PASSWORDSTATE_LIST_ID` env vars
## Code Style
- **Error handling:** `anyhow::Result` in commands, `thiserror`/`FnoxError` for domain errors
- **Logging:** `tracing` (not `println!`)
- **Naming:** modules `snake_case`, structs `PascalCase`, functions `snake_case`, constants `SCREAMING_SNAKE_CASE`, CLI args `kebab-case`
- **Async:** all commands and provider methods are async, `tokio::main` entry point
## Code Organization
```
src/commands/ # One file per command
src/providers/ # Implement Provider trait
src/encryption/ # Encryption methods
src/config.rs # Config parsing
src/env.rs # Centralized env var handling (LazyLock, FNOX_* prefix)
```
- Use `mod.rs` for module exports
- Import env vars via `use crate::env;` / `env::FNOX_*` — avoid direct `std::env::` calls
- CLI flags: `-P, --profile`, `-p, --provider`, `-d, --description`, `-k, --key-name`
## Environment Variables
- `FNOX_PROFILE` — profile to use (default: "default")
- `FNOX_CONFIG_DIR` — config directory (default: `~/.config/fnox`)
- `FNOX_AGE_KEY` — age encryption key
- `FNOX_PROMPT_AUTH` — enable/disable auth prompting in TTY (default: true)
## Config Structure
**Loading order** (later overrides earlier):
1. `~/.config/fnox/config.toml` (global)
2. Parent directory `fnox.toml` files (recursion, closer = higher priority)
3. `fnox.toml` (project)
4. `fnox.$FNOX_PROFILE.toml` (profile-specific, if not "default")
5. `fnox.local.toml` (local overrides, gitignored)
**Secret options:**
**Auth prompting:** on provider auth failure in TTY, fnox prompts to run the provider's auth command (e.g., `aws sso login`, `op signin`). Disable with `prompt_auth = false` in config or `FNOX_PROMPT_AUTH=false`.
## Provider Types
All providers follow the same pattern: config in `fnox.toml` stores references/names, actual secrets live in the provider. See `src/providers/` for implementations.
| Age | `age` | Encrypted in config | `age` crate |
| 1Password | `1password` | 1Password vault | `op` CLI |
| Bitwarden | `bitwarden` | Bitwarden vault | `bw` CLI |
| Bitwarden SM | `bitwarden-sm` | Bitwarden Secrets Manager | `bws` CLI |
| AWS KMS | `aws-kms` | Encrypted in config | `aws-sdk-kms` |
| AWS Secrets Manager | `aws-sm` | AWS SM | `aws-sdk-secretsmanager` |
| AWS Parameter Store | `aws-ps` | AWS SSM | `aws-sdk-ssm` |
| Keychain | `keychain` | OS keychain | `keyring` crate |
| KeePass | `keepass` | `.kdbx` file | `keepass-rs` crate |
| Infisical | `infisical` | Infisical | `infisical` CLI |
| Passwordstate | `passwordstate` | Passwordstate server | `reqwest` HTTP |
| password-store | `password-store` | GPG files | `pass` CLI |
| Proton Pass | `proton-pass` | Proton Pass vault | `pass-cli` CLI |
**Common provider config fields:** `type` (required), `prefix` (optional namespace), `region` (AWS providers). Most providers support `value` as item name, `item/field` for specific fields.
## GitHub Interactions
When posting comments on GitHub PRs or discussions, always include a note that the comment was AI-generated (e.g., "_This comment was generated by Claude Code._").