Adds a new `foks` provider that stores secrets in a FOKS (https://foks.pub) end-to-end encrypted key-value store via the `foks` CLI. FOKS is open source and self-hostable; the same provider talks to the hosted service at foks.app and to self-hosted servers. [providers] foks = { type = "foks", prefix = "/fnox/" } # personal ops = { type = "foks", prefix = "/fnox/", team = "ops" } # team-shared [secrets] DATABASE_URL = { provider = "ops", value = "DATABASE_URL" } Config fields: prefix - KV path namespace (absolute; relative input is coerced with a leading slash since FOKS rejects relative paths) team - FOKS team to act on behalf of; passes through as --team home - custom FOKS home dir; passes through as --home, with FNOX_FOKS_HOME / FOKS_HOME as env fallbacks host - FOKS server hostname, used by the bot-token auto-login (FNOX_FOKS_HOST / FOKS_HOST also accepted) bot_token - FOKS bot token for non-interactive auth; almost always you want to leave this unset and pass it via the FOKS_BOT_TOKEN env var instead so it stays out of the committed config (FNOX_FOKS_BOT_TOKEN also accepted) Non-interactive auth (CI): When `bot_token` (or FOKS_BOT_TOKEN) is set together with `host`, the provider transparently runs `foks bot use --host <host>` on the first auth failure and retries the failing operation. CI workflows trigger this behavior with the bot token in the env; they don't need an explicit `foks bot use` step: env: FOKS_BOT_TOKEN: ${{ secrets.FOKS_BOT_TOKEN }} steps: - run: brew install foks - run: foks ctl start - run: fnox exec -- ./deploy.sh The auto-login is guarded by a one-shot mutex, so concurrent secret fetches don't dogpile and a bad token still surfaces as a clear ProviderAuthFailed rather than retrying forever. Error classification covers the FOKS stderr phrasings observed in end-to-end testing: - agent unreachable / no current user / no logged-in user / no active user / not logged in / no key material loaded / user is locked -> ProviderAuthFailed (drives the auto-login retry) - no rows in result set / not found / no such file or directory / no such key / does not exist (with a known secret_ref) -> ProviderSecretNotFound - missing CLI -> ProviderCliNotFound with `brew install foks` hint - everything else -> ProviderCliFailed Files: crates/fnox-core/providers/foks.toml - provider declaration (drives the build script -generated config / wizard / instantiation) crates/fnox-core/src/providers/foks.rs - implementation + tests crates/fnox-core/src/providers/mod.rs - module registration src/commands/provider/{mod.rs, add.rs} - ProviderType::Foks variant and `provider add foks` template docs/providers/foks.md - provider docs page docs/providers/overview.md docs/index.md - list entries docs/.vitepress/config.mjs - sidebar entry docs/cli/provider/add.md docs/cli/commands.json fnox.usage.kdl - CLI choices docs/public/schema.json - JSON-schema entry Test plan (all passing): - 21 unit tests for path building (absolute coercion of relative prefixes), common-args (team/home/empty-string handling), error classification (each of the auth / secret-not-found / cli-failed paths, including the "key material" phrasing), env-var resolution, and the auto-login skip-paths - All 163 fnox-core tests + 8 fnox tests pass - provider_add_types_match_provider_definitions stays green - cargo clippy --workspace --all-targets -- -D warnings clean - cargo fmt --check clean - End-to-end smoke-tested against foks.app: personal set/get, exec injection, batch fetch, missing-key error path, and the headline bot-token auto-login flow (locked-keys recovery via `foks bot use`) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>