Skip to main content

Cloud credentials and OAuth

Kiln's cloud providers — Claude and Codex/GPT — need a credential to make a request. Historically that credential came from one place only: the ANTHROPIC_API_KEY / OPENAI_API_KEY environment variables, exported before launch. That works for a terminal-savvy developer and for CI, but it's a poor fit for an app you double-click: there's no env to export, and a key baked into the build is a leak waiting to happen.

So every cloud call now resolves its credential through one path, and a key can come from Settings as well as the environment.

How credentials resolve

The resolution order is:

  1. EnvironmentANTHROPIC_API_KEY / OPENAI_API_KEY. Highest priority, so nothing changes for existing shell-export and CI setups.
  2. Settings — a key entered in Settings ▸ Providers, stored in the macOS Keychain (never on disk in .kiln config or the app bundle).
  3. Otherwise the call fails with no available key.

A provider with a Settings key shows up everywhere Kiln consults for credentials: the assistant picker, the squad, ambient agents, every routed call.

Entering a key in Settings

Settings ▸ Providers ▸ Cloud API keys has a masked field per provider. Save writes to the Keychain; Clear deletes it. When the key is coming from the environment instead, the row says so and hides the field — the environment takes precedence.

Verify checks the key before you rely on it: it makes the cheapest authenticated request each provider offers (no tokens spent) and reports back. A successful response is "Key verified", a 401/403 is "Key rejected", anything else is a transport failure.

The terminal CLI agents are separate

The Claude Code and Codex CLI handoffs have their own login (claude /login, codex login) and keep their own credentials under ~/.claude / ~/.codex. A key entered in Kiln's Settings lives in the Keychain and is not injected into the terminal's environment, so a CLI handoff authenticates through the CLI's own login (or an exported env var), not through Kiln's Settings. That's deliberate: the CLIs' subscription logins are richer than a raw API key, and silently overriding them would be surprising.

OAuth for Anthropic

For Anthropic there's a real user-OAuth flow (the browser login Claude Code uses): authorization code + PKCE, yielding a short-lived access token and a refresh token. The token rides in the Authorization: Bearer header.

The OAuth flow stores tokens in the Keychain and handles refresh automatically when tokens near expiry.

OpenAI has no general user-OAuth for the Chat Completions endpoint, so the in-app provider stays API-key-only. ChatGPT OAuth, where you want it, lives in the Codex CLI, which manages its own login.