Nyzhi supports two ways to authenticate with LLM providers: API keys (via environment variables or config) and OAuth (browser-based login). You can connect multiple accounts per provider and benefit from automatic rate-limit rotation when you hit usage caps.
For which providers are available and how to configure them, see Providers and Configuration.
Credential Resolution Order
When Nyzhi needs credentials for a provider, it checks these sources in order:
- Config API key —
api_keyfield in[provider.<name>]section of config.toml - Environment variable — provider-specific env var (e.g.,
OPENAI_API_KEY) - Token store — stored OAuth/API tokens in
auth.json- Async resolution: refresh expired tokens first
- Sync resolution: use non-expired bearer tokens only
- Error — if none found, returns a clear error with a hint about the expected env var and OAuth availability
API Keys
The simplest way to authenticate. Set the provider’s environment variable:
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export GEMINI_API_KEY="AI..."
Or store it in config:
[provider.openai]
api_key = "sk-..."
Or store it in the token store:
nyz login openai
# Choose "API Key" when prompted, then paste your key
Environment Variable Names
Each built-in provider has a default env var. Custom providers can specify their own via env_var in config:
| Provider | Env Var |
|---|---|
| openai | OPENAI_API_KEY |
| anthropic | ANTHROPIC_API_KEY |
| gemini | GEMINI_API_KEY |
| openrouter | OPENROUTER_API_KEY |
| deepseek | DEEPSEEK_API_KEY |
| groq | GROQ_API_KEY |
| kimi | KIMI_API_KEY |
| minimax | MINIMAX_API_KEY |
| glm | GLM_API_KEY |
OAuth Login
For providers that support it, OAuth gives you a bearer token without needing a raw API key.
nyz login gemini # Google PKCE flow
nyz login openai # OpenAI device code flow
nyz login anthropic # Anthropic PKCE flow
nyz login chatgpt # ChatGPT device code flow (for Codex)
Google / Gemini (PKCE)
- Nyzhi starts a local HTTP server on a random port.
- Opens your browser to Google’s OAuth consent screen.
- After you grant access, Google redirects to the local server with an authorization code.
- Nyzhi exchanges the code for access and refresh tokens using PKCE.
- Tokens are stored in
auth.jsonwith a label likeaccount-1.
Scope: https://www.googleapis.com/auth/generative-language.retriever
OpenAI (Device Code)
- Nyzhi requests a device code from OpenAI’s device authorization endpoint.
- Prints a verification URL and user code for you to enter in your browser.
- Polls until you complete authorization.
- Exchanges the device code for access and refresh tokens.
- Tokens are stored for provider
"openai".
Anthropic (PKCE)
Same PKCE flow as Google, with Anthropic’s OAuth endpoints.
Scope: user:inference
ChatGPT (Device Code)
Same device code flow as OpenAI, stored under provider "chatgpt". Used for ChatGPT Plus/Pro access (Codex-style).
Token Storage
Tokens are stored in ~/.local/share/nyzhi/auth.json. The format supports multiple accounts per provider:
{
"openai": [
{
"label": "account-1",
"token": {
"access_token": "...",
"refresh_token": "...",
"expires_at": "2025-03-01T00:00:00Z",
"provider": "openai"
},
"active": true,
"rate_limited_until": null
}
]
}
Legacy Migration
Nyzhi previously stored tokens in the OS keyring. On first run, any existing keyring tokens are migrated to auth.json.
Multi-Account Support
You can store multiple accounts per provider:
nyz login openai # stores as first account
nyz login openai # stores as second account (with label)
Rate-Limit Rotation
When a provider returns HTTP 429 (rate limited):
- The current account is marked
rate_limited_untilfor the specified wait period. - Nyzhi switches to the next non-rate-limited account.
- If all accounts are rate-limited, it falls back to exponential backoff.
This happens automatically — you only see a brief Retrying... message.
Account Management
nyz whoami # show auth status for all providers
nyz logout <provider> # remove all tokens for a provider
Token Refresh
OAuth tokens expire. Nyzhi refreshes them automatically:
- Google: Uses the refresh token to get a new access token from Google’s token endpoint.
- OpenAI: Uses the refresh token with OpenAI’s token endpoint.
- Anthropic: Uses the refresh token with Anthropic’s token endpoint.
A token is considered expired when expires_at - 60 seconds <= now (60-second buffer to avoid edge cases).
Refresh only happens in async code paths (the TUI and nyz run). The sync path used by some CLI commands skips refresh and uses the stored token as-is.
Auth Status
Check your authentication status:
nyz whoami
Output shows each provider’s status:
env— using an API key from environment variableconnected— using a stored OAuth tokennot connected— no credentials found
Inside the TUI, /login shows the same information.
Plugin Auth (Experimental)
A plugin interface exists for custom auth providers, supporting registration of providers that define their own OAuth flows, token refresh logic, and authorization callbacks. This is currently unused but designed for future extension.