Files
automaker/docs/UNIFIED_API_KEY_PROFILES.md
Stefan de Vogelaere b88c940a36 feat: unify Claude API key and profile system with flexible key sourcing
- Add ApiKeySource type ('inline' | 'env' | 'credentials') to ClaudeApiProfile
- Allow profiles to source API keys from credentials.json or environment variables
- Add provider templates: OpenRouter, MiniMax, MiniMax (China)
- Auto-migrate existing users with Anthropic key to "Direct Anthropic" profile
- Update all API call sites to pass credentials for key resolution
- Add API key source selector to profile creation UI
- Increment settings version to 5 for migration support

This allows users to:
- Share a single API key across multiple profile configurations
- Use environment variables for CI/CD deployments
- Easily switch between providers without re-entering keys
2026-01-19 17:28:28 +01:00

13 KiB
Raw Permalink Blame History

Unified Claude API Key and Profile System

This document describes the implementation of a unified API key sourcing system for Claude API profiles, allowing flexible configuration of how API keys are resolved.

Problem Statement

Previously, Automaker had two separate systems for configuring Claude API access:

  1. API Keys section (credentials.json): Stored Anthropic API key, used when no profile was active
  2. API Profiles section (settings.json): Stored alternative endpoint configs (e.g., z.AI GLM) with their own inline API keys

This created several issues:

  • Users configured Anthropic key in one place, but alternative endpoints in another
  • No way to create a "Direct Anthropic" profile that reused the stored credentials
  • Environment variable detection didn't integrate with the profile system
  • Duplicated API key entry when users wanted the same key for multiple configurations

Solution Overview

The solution introduces a flexible apiKeySource field on Claude API profiles that determines where the API key is resolved from:

Source Description
inline API key stored directly in the profile (legacy behavior, default)
env Uses ANTHROPIC_API_KEY environment variable
credentials Uses the Anthropic key from Settings → API Keys

This allows:

  • A single API key to be shared across multiple profile configurations
  • "Direct Anthropic" profile that references saved credentials
  • Environment variable support for CI/CD and containerized deployments
  • Backwards compatibility with existing inline key profiles

Implementation Details

Type Changes

New Type: ApiKeySource

// libs/types/src/settings.ts
export type ApiKeySource = 'inline' | 'env' | 'credentials';

Updated Interface: ClaudeApiProfile

export interface ClaudeApiProfile {
  id: string;
  name: string;
  baseUrl: string;

  // NEW: API key sourcing strategy (default: 'inline' for backwards compat)
  apiKeySource?: ApiKeySource;

  // Now optional - only required when apiKeySource = 'inline'
  apiKey?: string;

  // Existing fields unchanged...
  useAuthToken?: boolean;
  timeoutMs?: number;
  modelMappings?: { haiku?: string; sonnet?: string; opus?: string };
  disableNonessentialTraffic?: boolean;
}

Updated Interface: ClaudeApiProfileTemplate

export interface ClaudeApiProfileTemplate {
  name: string;
  baseUrl: string;
  defaultApiKeySource?: ApiKeySource; // NEW: Suggested source for this template
  useAuthToken: boolean;
  // ... other fields
}

Provider Templates

The following provider templates are available:

Direct Anthropic

{
  name: 'Direct Anthropic',
  baseUrl: 'https://api.anthropic.com',
  defaultApiKeySource: 'credentials',
  useAuthToken: false,
  description: 'Standard Anthropic API with your API key',
  apiKeyUrl: 'https://console.anthropic.com/settings/keys',
}

OpenRouter

Access Claude and 300+ other models through OpenRouter's unified API.

{
  name: 'OpenRouter',
  baseUrl: 'https://openrouter.ai/api',
  defaultApiKeySource: 'inline',
  useAuthToken: true,
  description: 'Access Claude and 300+ models via OpenRouter',
  apiKeyUrl: 'https://openrouter.ai/keys',
}

Notes:

  • Uses ANTHROPIC_AUTH_TOKEN with your OpenRouter API key
  • No model mappings by default - OpenRouter auto-maps Anthropic models
  • Can customize model mappings to use any OpenRouter-supported model (e.g., openai/gpt-5.1-codex-max)

z.AI GLM

{
  name: 'z.AI GLM',
  baseUrl: 'https://api.z.ai/api/anthropic',
  defaultApiKeySource: 'inline',
  useAuthToken: true,
  timeoutMs: 3000000,
  modelMappings: {
    haiku: 'GLM-4.5-Air',
    sonnet: 'GLM-4.7',
    opus: 'GLM-4.7',
  },
  disableNonessentialTraffic: true,
  description: '3× usage at fraction of cost via GLM Coding Plan',
  apiKeyUrl: 'https://z.ai/manage-apikey/apikey-list',
}

MiniMax

MiniMax M2.1 coding model with extended context support.

{
  name: 'MiniMax',
  baseUrl: 'https://api.minimax.io/anthropic',
  defaultApiKeySource: 'inline',
  useAuthToken: true,
  timeoutMs: 3000000,
  modelMappings: {
    haiku: 'MiniMax-M2.1',
    sonnet: 'MiniMax-M2.1',
    opus: 'MiniMax-M2.1',
  },
  disableNonessentialTraffic: true,
  description: 'MiniMax M2.1 coding model with extended context',
  apiKeyUrl: 'https://platform.minimax.io/user-center/basic-information/interface-key',
}

MiniMax (China)

Same as MiniMax but using the China-region endpoint.

{
  name: 'MiniMax (China)',
  baseUrl: 'https://api.minimaxi.com/anthropic',
  defaultApiKeySource: 'inline',
  useAuthToken: true,
  timeoutMs: 3000000,
  modelMappings: {
    haiku: 'MiniMax-M2.1',
    sonnet: 'MiniMax-M2.1',
    opus: 'MiniMax-M2.1',
  },
  disableNonessentialTraffic: true,
  description: 'MiniMax M2.1 for users in China',
  apiKeyUrl: 'https://platform.minimaxi.com/user-center/basic-information/interface-key',
}

Server-Side Changes

1. Environment Building (claude-provider.ts)

The buildEnv() function now resolves API keys based on the apiKeySource:

function buildEnv(
  profile?: ClaudeApiProfile,
  credentials?: Credentials // NEW parameter
): Record<string, string | undefined> {
  if (profile) {
    // Resolve API key based on source strategy
    let apiKey: string | undefined;
    const source = profile.apiKeySource ?? 'inline';

    switch (source) {
      case 'inline':
        apiKey = profile.apiKey;
        break;
      case 'env':
        apiKey = process.env.ANTHROPIC_API_KEY;
        break;
      case 'credentials':
        apiKey = credentials?.apiKeys?.anthropic;
        break;
    }

    // ... rest of profile-based env building
  }
  // ... no-profile fallback
}

2. Settings Helper (settings-helpers.ts)

The getActiveClaudeApiProfile() function now returns both profile and credentials:

export interface ActiveClaudeApiProfileResult {
  profile: ClaudeApiProfile | undefined;
  credentials: Credentials | undefined;
}

export async function getActiveClaudeApiProfile(
  settingsService?: SettingsService | null,
  logPrefix = '[SettingsHelper]'
): Promise<ActiveClaudeApiProfileResult> {
  // Returns both profile and credentials for API key resolution
}

3. Auto-Migration (settings-service.ts)

A v4→v5 migration automatically creates a "Direct Anthropic" profile for existing users:

// Migration v4 -> v5: Auto-create "Direct Anthropic" profile
if (storedVersion < 5) {
  const credentials = await this.getCredentials();
  const hasAnthropicKey = !!credentials.apiKeys?.anthropic;
  const hasNoProfiles = !result.claudeApiProfiles?.length;
  const hasNoActiveProfile = !result.activeClaudeApiProfileId;

  if (hasAnthropicKey && hasNoProfiles && hasNoActiveProfile) {
    // Create "Direct Anthropic" profile with apiKeySource: 'credentials'
    // and set it as active
  }
}

4. Updated Call Sites

All files that call getActiveClaudeApiProfile() were updated to:

  1. Destructure both profile and credentials from the result
  2. Pass credentials to the provider via ExecuteOptions

Files updated:

  • apps/server/src/services/agent-service.ts
  • apps/server/src/services/auto-mode-service.ts (2 locations)
  • apps/server/src/services/ideation-service.ts (2 locations)
  • apps/server/src/providers/simple-query-service.ts
  • apps/server/src/routes/enhance-prompt/routes/enhance.ts
  • apps/server/src/routes/context/routes/describe-file.ts
  • apps/server/src/routes/context/routes/describe-image.ts
  • apps/server/src/routes/github/routes/validate-issue.ts
  • apps/server/src/routes/worktree/routes/generate-commit-message.ts
  • apps/server/src/routes/features/routes/generate-title.ts
  • apps/server/src/routes/backlog-plan/generate-plan.ts
  • apps/server/src/routes/app-spec/sync-spec.ts
  • apps/server/src/routes/app-spec/generate-features-from-spec.ts
  • apps/server/src/routes/app-spec/generate-spec.ts
  • apps/server/src/routes/suggestions/generate-suggestions.ts

UI Changes

1. Profile Form (api-profiles-section.tsx)

Added an API Key Source selector dropdown:

<Select
  value={formData.apiKeySource}
  onValueChange={(value: ApiKeySource) => setFormData({ ...formData, apiKeySource: value })}
>
  <SelectContent>
    <SelectItem value="credentials">Use saved API key (from Settings  API Keys)</SelectItem>
    <SelectItem value="env">Use environment variable (ANTHROPIC_API_KEY)</SelectItem>
    <SelectItem value="inline">Enter key for this profile only</SelectItem>
  </SelectContent>
</Select>

The API Key input field is now conditionally rendered only when apiKeySource === 'inline'.

2. API Keys Section (api-keys-section.tsx)

Added an informational note:

API Keys saved here can be used by API Profiles with "credentials" as the API key source. This lets you share a single key across multiple profile configurations without re-entering it.

User Flows

New User Flow

  1. Go to Settings → API Keys
  2. Enter Anthropic API key and save
  3. Go to Settings → Providers → Claude
  4. Create new profile from "Direct Anthropic" template
  5. API Key Source defaults to "credentials" - no need to re-enter key
  6. Save profile and set as active

Existing User Migration

When an existing user with an Anthropic API key (but no profiles) loads settings:

  1. System detects v4→v5 migration needed
  2. Automatically creates "Direct Anthropic" profile with apiKeySource: 'credentials'
  3. Sets new profile as active
  4. User's existing workflow continues to work seamlessly

Environment Variable Flow

For CI/CD or containerized deployments:

  1. Set ANTHROPIC_API_KEY in environment
  2. Create profile with apiKeySource: 'env'
  3. Profile will use the environment variable at runtime

Backwards Compatibility

  • Profiles without apiKeySource field default to 'inline'
  • Existing profiles with inline apiKey continue to work unchanged
  • No changes to the credentials file format
  • Settings version bumped from 4 to 5 (migration is additive)

Files Changed

File Changes
libs/types/src/settings.ts Added ApiKeySource type, updated ClaudeApiProfile, added Direct Anthropic template
libs/types/src/provider.ts Added credentials field to ExecuteOptions
libs/types/src/index.ts Exported ApiKeySource type
apps/server/src/providers/claude-provider.ts Updated buildEnv() to resolve keys from different sources
apps/server/src/lib/settings-helpers.ts Updated return type to include credentials
apps/server/src/services/settings-service.ts Added v4→v5 auto-migration
apps/server/src/providers/simple-query-service.ts Added credentials passthrough
apps/server/src/services/*.ts Updated to pass credentials
apps/server/src/routes/**/*.ts Updated to pass credentials (15 files)
apps/ui/src/.../api-profiles-section.tsx Added API Key Source selector
apps/ui/src/.../api-keys-section.tsx Added profile usage note

Testing

To verify the implementation:

  1. New user flow: Create "Direct Anthropic" profile, select credentials source, enter key in API Keys section → verify it works
  2. Existing user migration: User with credentials.json key sees auto-created "Direct Anthropic" profile
  3. Env var support: Create profile with env source, set ANTHROPIC_API_KEY → verify it works
  4. z.AI GLM unchanged: Existing profiles with inline keys continue working
  5. Backwards compat: Profiles without apiKeySource field default to inline
# Build and run
npm run build:packages
npm run dev:web

# Run server tests
npm run test:server

Future Enhancements

Potential future improvements:

  1. UI indicators: Show whether credentials/env key is configured when selecting those sources
  2. Validation: Warn if selected source has no key configured
  3. Per-provider credentials: Support different credential keys for different providers
  4. Key rotation: Support for rotating keys without updating profiles