How DeepSeek-Reasonix Manages API Keys and Secrets via api_key_env Without Storing in Config Files

DeepSeek-Reasonix stores only the environment variable name in JSON/TOML configuration files while persisting actual API secrets to a global .env file, using upsertDotEnv and removeDotEnv to ensure credentials never appear in project-level version control.

DeepSeek-Reasonix implements a secure credential management pattern that keeps sensitive API keys out of configuration files. Instead of embedding secret values directly in JSON or TOML configs, the application uses the apiKeyEnv field to reference environment variables, delegating actual secret storage to a user-controlled global .env store managed by desktop/dotenv.go. This architecture ensures that the system can manage API keys and secrets via api_key_env without exposing them in repository-tracked configuration files.

The Architecture Behind apiKeyEnv Security

Environment Variables vs. Config Files

Traditional approaches often embed secrets directly in configuration files, creating security risks when those files are committed to version control. DeepSeek-Reasonix solves this by storing only the environment variable name (e.g., DEEPSEEK_API_KEY) in the APIKeyEnv field defined in desktop/settings_app.go, while the actual value lives in a global credential store outside the project directory.

Separation of Concerns

The architecture enforces a strict separation between configuration and credentials:

  • Configuration files contain the apiKeyEnv string identifying which environment variable to use for each provider.
  • Global .env store contains the actual KEY=value pairs managed through upsertDotEnv and removeDotEnv functions in desktop/dotenv.go.

Writing and Removing Secrets via the Backend

When users save or clear provider keys through the Settings UI, the backend delegates to dedicated functions in desktop/settings_app.go that interface with the global .env store without modifying the project's configuration files.

Setting a Provider Key

The SetProviderKey function validates the environment variable name and persists the secret via upsertDotEnv at line 815:

func (a *App) SetProviderKey(apiKeyEnv, value string) (string, error) {
    apiKeyEnv = strings.TrimSpace(apiKeyEnv)
    if apiKeyEnv == "" {
        return "", fmt.Errorf("apiKeyEnv cannot be empty")
    }
    if err := upsertDotEnv(apiKeyEnv, value); err != nil {
        return "", err
    }
    return a.saveProviderCredential(apiKeyEnv, value)
}

Clearing a Provider Key

The ClearProviderKey function removes the entry using removeDotEnv (implemented at line 1588 in desktop/settings_app.go):

func (a *App) ClearProviderKey(apiKeyEnv string) error {
    apiKeyEnv = strings.TrimSpace(apiKeyEnv)
    if apiKeyEnv == "" {
        return fmt.Errorf("apiKeyEnv cannot be empty")
    }
    return removeDotEnv(apiKeyEnv)
}

Dotenv Implementation

The desktop/dotenv.go file implements upsertDotEnv to write KEY=value pairs to the global store and removeDotEnv to delete entries, ensuring the secret never touches the project's configuration files or appears in settings.json.

Runtime Resolution and Shadow Detection

At runtime, DeepSeek-Reasonix resolves credentials using os.LookupEnv and config.ResolveCredentialForRoot. The system checks whether the environment variable referenced by apiKeyEnv contains a value without exposing the secret through the configuration layer.

Shadow Warning

The implementation includes a safety mechanism around line 821 in desktop/settings_app.go called providerCredentialShadowWarning. This emits a warning if a system-level environment variable could shadow the value stored in the global .env file, preventing confusion about which credential is actually active when both sources define the same variable name.

Frontend State Management

The frontend in desktop/frontend/src/lib/providerModels.ts mirrors this design pattern without accessing actual secret values. Each provider object exposes apiKeyEnv and a boolean keySet property that indicates whether the environment variable currently resolves to a non-empty value.

Detecting Key Requirements

export function providerRequiresKey(provider: {
    requiresKey?: boolean;
    apiKeyEnv?: string;
}): boolean {
    return Boolean((provider.apiKeyEnv ?? "").trim());
}

UI Interaction

The Settings panel in desktop/frontend/src/components/SettingsPanel.tsx calls SetProviderKey and ClearProviderKey through the Wails bridge defined in desktop/frontend/src/lib/bridge.ts. The UI never receives the raw secret value—only the environment variable name—maintaining security across the frontend-backend boundary while allowing users to see which keys are configured via the keySet boolean.

Summary

  • DeepSeek-Reasonix stores only environment variable names in configuration files via the apiKeyEnv field, ensuring actual secrets never appear in JSON or TOML configs.
  • Secrets are persisted to a global .env file using upsertDotEnv in desktop/dotenv.go and removed via removeDotEnv when clearing keys.
  • The SetProviderKey and ClearProviderKey functions in desktop/settings_app.go validate input by trimming whitespace and checking for empty strings before delegating to the dotenv store.
  • Runtime resolution uses os.LookupEnv with shadow detection via providerCredentialShadowWarning to alert users of conflicting environment variables that might override stored values.
  • The frontend tracks key status through keySet booleans in providerModels.ts without accessing secret values, ensuring credentials remain confined to the backend store.

Frequently Asked Questions

What happens if an environment variable is set both in the system and the global .env file?

DeepSeek-Reasonix emits a providerCredentialShadowWarning to alert users when a system-level environment variable might override the value stored in the global .env file. This prevents confusion about which credential is actually being used for API authentication, as os.LookupEnv typically prioritizes system-level variables over file-based entries.

Is the apiKeyEnv pattern secure for production environments?

Yes, this pattern enhances security by ensuring API keys never appear in version-controlled configuration files. By storing only the variable name in project configs and the actual value in a user-specific global .env store managed by desktop/dotenv.go, DeepSeek-Reasonix prevents accidental credential exposure through Git repositories while maintaining flexibility for different deployment environments.

How does the frontend determine if a key is set without accessing the secret value?

The frontend checks the keySet boolean property on provider objects defined in desktop/frontend/src/lib/providerModels.ts. This property reflects whether apiKeyEnv resolves to a non-empty value at runtime through the Wails bridge, allowing the UI to display key status indicators without ever receiving the actual secret from SetProviderKey calls.

Where does DeepSeek-Reasonix store the global .env file?

The global .env file location is managed by desktop/dotenv.go, which implements the upsertDotEnv and removeDotEnv functions. This file exists outside the project directory in a user-specific global location (typically in the user's home directory or application data folder), ensuring that secrets persist across application restarts while remaining separate from repository-tracked configuration files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →