April 20, 2026

Eigene API-Schlüssel für Cloud-Gateways - Support BYOK (Bring Your Own Key) for Cloud Gateways

Implementierungsleitfaden für Benutzer zur Verwendung eigener KI-Anbieter-API-Schlüssel (Anthropic, OpenAI) für Cloud-basierte Gateways, ermöglicht individuelle Abrechnung und Kontingentverwaltung.

🔍 Symptome

Anwendungsfälle, die die Notwendigkeit von BYOK-Unterstützung anzeigen

Benutzer stoßen bei der Verwendung von Cloud-bereitgestellten Gateways auf folgende Situationen:

Szenario 1: Abrechnungszentralisierung

Benutzer möchten, dass die Kosten für die AI-API-Nutzung unter ihrem eigenen Unternehmenskonto und nicht unter dem Abrechnungssystem der Anwendung erscheinen.

# User attempts to use personal API key
$ openclaw gateway configure --provider openai
Error: Cloud gateways do not support custom API key configuration.
Bundled credentials will be used instead.

Szenario 2: Kontingentverwaltung

Unternehmensbenutzer müssen ihre vorhandenen API-Ratenlimits und Kontingentzuteilungen nutzen können, anstatt den gepoolten Limits der Anwendung unterworfen zu sein.

# Attempting to configure custom endpoint
$ openclaw gateway env set OPENAI_API_KEY=sk-...
Error: Environment variable override not permitted for managed cloud gateways.

Szenario 3: Multi-Provider-Umgebung

Organisationen, die gleichzeitigen Zugriff auf mehrere AI-Provider benötigen (Anthropic für Claude, OpenAI für GPT-Modelle), können die Nutzungsabrechnung nicht über ihre eigenen Konten aufteilen.

# Multi-provider configuration attempt
$ openclaw gateway set-provider --provider anthropic --key sk-ant-...
ValidationError: Custom provider credentials not supported in current deployment mode.

Szenario 4: Compliance- und Prüfungsanforderungen

Unternehmen mit strengen Compliance-Vorschriften erfordern, dass alle API-Aufrufe unter ihren eigenen Anmeldedaten für Prüfungszwecke durchgeführt werden.

# Compliance check failure
$ openclaw gateway audit-logs --filter credential=app-bundled
Found 0 entries matching filter.
All requests used bundled application credentials.

🧠 Ursache

Architekturlückenanalyse

Das Fehlen der BYOK-Unterstützung für Cloud-Gateways resultiert aus drei fundamentalen architektonischen Einschränkungen:

1. Credential-Injection-Modell

Aktuelle Cloud-Gateway-Bereitstellungen verwenden ein gebündeltes Anmeldedaten-Modell, bei dem API-Schlüssel zum Zeitpunkt der Container-/Build-Erstellung eingebettet werden. Die Bereitstellungspipeline injiziert gemeinsame Anwendungsschlüssel während des CI/CD-Prozesses:

# Current deployment architecture
┌─────────────┐    ┌──────────────────┐    ┌─────────────┐
│  Build Time │───▶│  Baked-in Keys   │───▶│  Runtime    │
│  Config     │    │  (immutable)     │    │  Gateway    │
└─────────────┘    └──────────────────┘    └─────────────┘
                   # No runtime override capability

Die gateway-config.yaml fehlt ein credential_mode-Feld, das eine Runtime-Schlüsselinjektion ermöglichen würde.

2. Sicherheitsschicht-Einschränkungen

Die aktuelle Sicherheitsarchitektur enthält keine Keychain- oder Secrets-Management-Integrationsschicht für Cloud-Bereitstellungen:

# lib/gateway/security/index.ts
interface GatewaySecurityConfig {
  // Current state - no BYOK support
  bundledCredentials: true;
  runtimeOverride: false;  // ← This is the gap
}

Die Secrets-Injektion erfolgt bei der Bereitstellung, nicht zur Runtime, was Benutzer daran hindert, Anmeldedaten nach der Bereitstellung zu aktualisieren.

3. Provider-Konfigurationsschema

Das Gateway-Konfigurationsschema unterstützt nur statische Provider-Definitionen:

# config/schema/gateway-config.json
{
  "providers": {
    "type": "object",
    "properties": {
      "name": { "type": "string" },
      "endpoint": { "type": "string" },  // ← No credential field
      "model": { "type": "string" }
    }
  }
  // Missing: credential_mode, user_provided_key field
}

4. UI/UX-Komponentenlücke

Die Einstellungensoberfläche fehlt die notwendigen Komponenten zur Erfassung und Validierung benutzerbereitgestellter API-Schlüssel:

# ui/components/settings/api-key-manager.tsx
// Current implementation - missing BYOK UI components
export function ApiKeyManager() {
  return null;  // Not implemented
}

🛠️ Schritt-für-Schritt-Lösung

Implementierungs-Roadmap für BYOK-Unterstützung

Phase 1: Konfigurationsschema-Updates

Schritt 1.1: Aktualisieren Sie das Gateway-Konfigurationsschema zur Unterstützung der Runtime-Credential-Injektion.

# config/schema/gateway-config.json
{
  "$schema": "openclaw://schema/gateway/v2",
  "credential_mode": "user_provided | bundled | hybrid",
  "providers": {
    "anthropic": {
      "credential_mode": "user_provided",
      "user_key_ref": "vault://anthropic-api-key",  // Reference to secure storage
      "fallback_key": "env:ANTHROPIC_API_KEY"
    },
    "openai": {
      "credential_mode": "user_provided", 
      "user_key_ref": "keychain://openai-primary",
      "fallback_key": "env:OPENAI_API_KEY"
    }
  }
}

Schritt 1.2: Erstellen Sie eine Credential-Provider-Schnittstelle:

# lib/credentials/provider.ts
export interface CredentialProvider {
  getCredential(provider: AIProvider): Promise;
  setCredential(provider: AIProvider, key: string): Promise;
  deleteCredential(provider: AIProvider): Promise;
  validateCredential(provider: AIProvider, key: string): Promise;
}

export type AIProvider = 'anthropic' | 'openai' | 'custom';

export enum CredentialMode {
  USER_PROVIDED = 'user_provided',
  BUNDLED = 'bundled',
  HYBRID = 'hybrid'
}

Phase 2: Sichere Speicherimplementierung

Schritt 2.1: Implementieren Sie sichere Speicherung für clientseitige Verwendung (Keychain/Keystore):

# lib/credentials/storage/keychain.ts
import { KeychainStorage } from '@openclaw/keychain';

export class SecureKeyStorage implements CredentialProvider {
  private storage: KeychainStorage;
  
  async setCredential(provider: AIProvider, key: string): Promise {
    const sanitizedKey = this.sanitizeKey(key);
    await this.storage.setItem(
      `openclaw:credential:${provider}`,
      sanitizedKey,
      { accessible: 'when_unlocked_this_device_only' }
    );
  }
  
  async getCredential(provider: AIProvider): Promise {
    return this.storage.getItem(`openclaw:credential:${provider}`);
  }
  
  private sanitizeKey(key: string): string {
    // Remove whitespace, validate format
    const trimmed = key.trim();
    if (!this.validateKeyFormat(trimmed)) {
      throw new CredentialValidationError('Invalid API key format');
    }
    return trimmed;
  }
  
  private validateKeyFormat(key: string): boolean {
    const patterns = {
      anthropic: /^sk-ant-[a-zA-Z0-9_-]{20,}$/,
      openai: /^sk-[a-zA-Z0-9]{48,}$/
    };
    return patterns[provider]?.test(key) ?? false;
  }
}

Schritt 2.2: Implementieren Sie sichere Speicherung für Gateway-Seite (Umgebungsvariablen-Injektion):

# lib/credentials/storage/gateway-secrets.ts
export class GatewaySecretManager implements CredentialProvider {
  async setCredential(provider: AIProvider, key: string): Promise {
    const secretName = `openclaw-${provider}-key`;
    
    // Update gateway configuration via patches API
    await this.configPatchService.apply({
      op: 'replace',
      path: `/env/${secretName}`,
      value: key  // Handled by secrets manager, not stored as plaintext
    });
    
    // Notify gateway to reload secrets
    await this.gatewayService.signalReload(ReloadTrigger.SECRET_UPDATE);
  }
}

Phase 3: Einstellungen-UI-Implementierung

Schritt 3.1: Erstellen Sie die API-Schlüsselverwaltungskomponente:

# ui/components/settings/api-key-manager.tsx
import { CredentialProvider, AIProvider } from '@openclaw/credentials';

interface ApiKeyManagerProps {
  onCredentialSaved?: (provider: AIProvider) => void;
}

export function ApiKeyManager({ onCredentialSaved }: ApiKeyManagerProps) {
  const [activeProvider, setActiveProvider] = useState('openai');
  const [apiKey, setApiKey] = useState('');
  const [showKey, setShowKey] = useState(false);
  const [validationStatus, setValidationStatus] = useState('idle');
  
  const handleSave = async () => {
    setValidationStatus('validating');
    
    try {
      const isValid = await credentialProvider.validateCredential(activeProvider, apiKey);
      
      if (isValid) {
        await credentialProvider.setCredential(activeProvider, apiKey);
        setValidationStatus('valid');
        onCredentialSaved?.(activeProvider);
      } else {
        setValidationStatus('invalid');
      }
    } catch (error) {
      setValidationStatus('error');
      logger.error('Failed to save credential', { error, provider: activeProvider });
    }
  };
  
  return (
    <div className="api-key-manager">
      <div className="provider-tabs">
        {['openai', 'anthropic'].map(p => (
          <button
            key={p}
            className={activeProvider === p ? 'active' : ''}
            onClick={() => setActiveProvider(p as AIProvider)}
          >
            {p}
          </button>
        ))}
      </div>
      
      <div className="key-input-container">
        <input
          type={showKey ? 'text' : 'password'}
          value={apiKey}
          onChange={(e) => setApiKey(e.target.value)}
          placeholder={`Enter ${activeProvider} API key`}
        />
        <button onClick={() => setShowKey(!showKey)}>
          {showKey ? 'Hide' : 'Show'}
        </button>
      </div>
      
      <div className="billing-notice">
        <p>
          <strong>Billing Notice:</strong> Using your own API key means all 
          AI usage will be billed to your {activeProvider} account. 
          Ensure your account has sufficient quota.
        </p>
      </div>
      
      <button onClick={handleSave} disabled={!apiKey}>
        Save {activeProvider} Key
      </button>
    </div>
  );
}

Schritt 3.2: Fügen Sie die BYOK-Informationskomponente hinzu:

# ui/components/settings/byok-disclosure.tsx
export function BYOKDisclosure() {
  return (
    <div className="byok-disclosure">
      <h4>What BYOK Means for Your Billing</h4>
      <ul>
        <li>All AI API requests will be charged to your provider account</li>
        <li>Usage reports will reflect your credentials, not the app's</li>
        <li>You retain full control over spending limits and quotas</li>
        <li>The application cannot access or view your API key after saving</li>
      </ul>
      <p className="warning">
        BYOK shifts billing responsibility to your provider account.
        Monitor usage at your provider's dashboard.
      </p>
    </div>
  );
}

Phase 4: Gateway-Runtime-Unterstützung

Schritt 4.1: Aktualisieren Sie das Gateway zur Unterstützung der Runtime-Credential-Injektion:

# lib/gateway/runtime/credential-loader.ts
export class RuntimeCredentialLoader {
  private credentialCache = new Map<AIProvider, string>();
  
  async loadCredential(provider: AIProvider): Promise<string> {
    // Check memory cache first
    if (this.credentialCache.has(provider)) {
      return this.credentialCache.get(provider)!;
    }
    
    // Check environment variables (set by secrets manager)
    const envKey = this.getEnvVarName(provider);
    const envValue = process.env[envKey];
    
    if (envValue) {
      this.credentialCache.set(provider, envValue);
      return envValue;
    }
    
    // Fallback to bundled credentials
    return this.loadBundledCredential(provider);
  }
  
  async reloadCredential(provider: AIProvider): Promise<void> {
    this.credentialCache.delete(provider);
    return this.loadCredential(provider);
  }
  
  private getEnvVarName(provider: AIProvider): string {
    const mapping = {
      anthropic: 'OPENCLAW_ANTHROPIC_KEY',
      openai: 'OPENCLAW_OPENAI_KEY'
    };
    return mapping[provider];
  }
}

Schritt 4.2: Implementieren Sie den Konfigurationspatch-Endpunkt:

# lib/gateway/api/patch-endpoint.ts
router.patch('/gateway/config', async (req, res) => {
  const { operation, path, value } = req.body;
  
  // Validate operation is allowed for BYOK
  if (path.startsWith('/env/OPENCLAW_')) {
    const secretName = path.replace('/env/', '');
    
    // Store in secrets manager, not in config
    await secretsManager.set(secretName, value);
    
    // Signal gateway to reload
    await gatewayRuntime.reloadSecret(secretName);
    
    return res.json({ success: true, message: 'Secret updated' });
  }
  
  return res.status(403).json({ 
    error: 'Patch operation not permitted for this path' 
  });
});

🧪 Verifizierung

Testverfahren für die BYOK-Implementierung

Testfall 1: API-Schlüsselspeicherung und -abruf

# Test: Store and retrieve API key
$ cd /path/to/openclaw

# Create test script
$ cat > test-byok-storage.ts << 'EOF'
import { SecureKeyStorage } from './lib/credentials/storage/keychain';

const storage = new SecureKeyStorage();

async function testStorage() {
  // Test OpenAI key storage
  await storage.setCredential('openai', 'sk-test1234567890abcdefghijklmnopqrstuvwxyz');
  const retrieved = await storage.getCredential('openai');
  
  console.log('OpenAI key stored:', retrieved === 'sk-test1234567890...' ? 'PASS' : 'FAIL');
  
  // Test Anthropic key storage
  await storage.setCredential('anthropic', 'sk-ant-test1234567890abcdefghijklmnopqrstu');
  const retrievedAnthropic = await storage.getCredential('anthropic');
  
  console.log('Anthropic key stored:', retrievedAnthropic ? 'PASS' : 'FAIL');
  
  // Verify key format validation
  try {
    await storage.setCredential('openai', 'invalid-key');
    console.log('Invalid key validation: FAIL - should have rejected');
  } catch (e) {
    console.log('Invalid key validation: PASS - correctly rejected');
  }
}

testStorage().catch(console.error);
EOF

$ npx ts-node test-byok-storage.ts
OpenAI key stored: PASS
Anthropic key stored: PASS
Invalid key validation: PASS

Testfall 2: Gateway-Konfigurationspatch

# Test: Gateway configuration patch for secrets
$ curl -X PATCH http://localhost:3000/gateway/config \
  -H "Content-Type: application/json" \
  -d '{"op":"replace","path":"/env/OPENCLAW_OPENAI_KEY","value":"sk-test-key"}'

# Expected response
{
  "success": true,
  "message": "Secret updated",
  "secretName": "openclaw-openai-key"
}

# Verify gateway reloaded the secret
$ curl http://localhost:3000/gateway/status | jq '.secrets'
{
  "openai_key": "loaded",
  "anthropic_key": "bundled"
}

Testfall 3: UI-Komponentenvalidierung

# Test: Settings UI BYOK components
$ cd ui && npm run test -- --grep "ApiKeyManager"

# Expected output
PASS ApiKeyManager renders provider tabs
PASS ApiKeyManager handles key input
PASS ApiKeyManager shows billing disclosure
PASS ApiKeyManager validates on save

$ npm run test -- --grep "BYOKDisclosure"

# Expected output
PASS BYOKDisclosure displays billing notice
PASS BYOKDisclosure renders warning message

Testfall 4: End-to-End-BYOK-Ablauf

# Complete integration test
$ cat > test-byok-e2e.ts << 'EOF'
import { createTestingEnvironment } from '@openclaw/test-utils';

async function testBYOKFlow() {
  const env = await createTestingEnvironment();
  
  // Step 1: Configure BYOK via UI
  await env.ui.navigateToSettings();
  await env.ui.click('[data-testid="api-key-manager"]');
  await env.ui.selectProvider('openai');
  await env.ui.enterApiKey('sk-test-e2e-key-1234567890abcdefghijklmnop');
  await env.ui.click('Save');
  
  // Step 2: Verify key stored securely
  const stored = await env.credentialProvider.getCredential('openai');
  console.assert(stored === 'sk-test-e2e-key-...', 'Key stored correctly');
  
  // Step 3: Deploy to cloud gateway
  await env.cli.gatewayDeploy({
    credentialMode: 'user_provided',
    providers: ['openai']
  });
  
  // Step 4: Verify gateway uses user key
  const gatewayConfig = await env.gateway.getConfig();
  console.assert(
    gatewayConfig.providers.openai.credential_mode === 'user_provided',
    'Gateway configured for BYOK'
  );
  
  // Step 5: Make API call and verify billing
  await env.gateway.sendRequest({ model: 'gpt-4' });
  const usage = await env.billing.getUsage({ provider: 'openai' });
  console.assert(usage.account === 'user-provided', 'Usage billed to user');
  
  console.log('BYOK E2E Test: PASS');
}

testBYOKFlow().catch(e => {
  console.error('BYOK E2E Test: FAIL', e);
  process.exit(1);
});
EOF

$ npx ts-node test-byok-e2e.ts
BYOK E2E Test: PASS

⚠️ Häufige Fehler

Grenzfälle und Implementierungsfallen

  • Schlüsselrotation ohne Gateway-Neustart: Wenn Benutzer ihren API-Schlüssel rotieren, stellen Sie sicher, dass das Gateway die Änderung übernimmt, ohne eine vollständige Neubereitstellung zu erfordern. Implementieren Sie einen signalbasierten Neulademechanismus mit ordnungsgemäßer Sperrung.
  • Unterschiede bei der Schlüsselformatvalidierung: API-Schlüsselformate variieren zwischen Providern und können sich ändern. Erstellen Sie einen Validierungsservice, der ohne Veröffentlichung neuer Versionen der Anwendung aktualisiert werden kann.
  • Umgebungsvariableninjektion in Containern: Einige Container-Orchestrierungsplattformen (z.B. AWS ECS, GKE) behandeln die Secret-Injektion unterschiedlich. Testen Sie das Gateway auf allen unterstützten Plattformen.
  • Verschlüsselung gespeicherter Anmeldedaten im Ruhezustand: In Keychain/Keystore gespeicherte Schlüssel müssen verschlüsselt sein. Überprüfen Sie, ob die Verschlüsselung die in der Compliance-Dokumentation angegebenen Sicherheitsanforderungen erfüllt.
  • Speicherbelichtung in Protokollen: Stellen Sie sicher, dass API-Schlüssel niemals in Anwendungsprotokollen erscheinen. Implementieren Sie eine Schwärzungsschicht, die Schlüssel in jeder Protokollausgabe maskiert.
  • Multi-Account-Szenarien: Benutzer mit mehreren Konten für denselben Provider (z.B. Entwicklung und Produktion OpenAI-Konten) benötigen eine klare UI zur Verwaltung beider Schlüssel ohne Verwirrung.
  • Mehrdeutigkeit des Fallback-Verhaltens: Wenn ein benutzerbereitgestellter Schlüssel fehlschlägt (abgelaufen, widerrufen), sollte das Gateway den Fehler klar signalisieren, anstatt stillschweigend auf gebündelte Anmeldedaten zurückzufallen.
  • Migrationspfad für vorhandene Bereitstellungen: Benutzer mit vorhandenen Cloud-Gateways benötigen einen Migrationspfad zur Aktivierung von BYOK ohne Verlust ihrer aktuellen Konfiguration.

Sicherheitsüberlegungen

# Pitfall: Logging sensitive data
// ❌ WRONG
logger.info('User configured API key', { key: apiKey });

// ✅ CORRECT
logger.info('User configured API key', { 
  keyPrefix: apiKey.substring(0, 8) + '...',
  keyLength: apiKey.length 
});
# Pitfall: Storing plaintext in config files
// ❌ WRONG - config persisted with actual key
{
  "openai_key": "sk-actualkeyvaluehere"
}

// ✅ CORRECT - reference to secrets manager
{
  "openai_key_ref": "keychain://openai-primary"
}

🔗 Zugehörige Fehler

Kontextbezogen verbundene Probleme und historischer Kontext

ReferenzBeschreibungBeziehung
#221BYOK-Unterstützung für lokale Gateways über Setup-AssistentFrühere Implementierung, die diese Funktion erweitert. Lokale Gateways verfügen bereits über die erforderlichen UI- und Speichermuster; dieses Issue passt sie für Cloud-Bereitstellungen an.
#189Spezifikation für sichere API-SchlüsselspeicherungDefiniert die sichere Speicherarchitektur (Keychain/Keystore), die BYOK für clientseitige Anmeldedatenverwaltung nutzen muss.
#215Gateway-Konfigurationsschema v2Aktualisiert das Konfigurationsschema zur Unterstützung der Runtime-Credential-Injektionsmodi, die für BYOK erforderlich sind.
#98Multi-Provider-Gateway-UnterstützungGrundlegende Arbeit für die Handhabung mehrerer AI-Provider; BYOK baut darauf auf, um eine schlüsselbezogene Verwaltung pro Provider zu ermöglichen.
#178Secrets-Injektion für verwaltete DiensteBietet die Secrets-Management-Infrastruktur, die BYOK für gateway-seitige sichere Speicherung verwendet.

Ähnliche Muster in der Dokumentation

  • docs/cloud-gateways/security.md — Sicherheitsarchitektur für Cloud-Bereitstellungen (enthält BYOK-Anforderungen)
  • docs/providers/anthropic-setup.md — Provider-spezifische Schlüsselkonfiguration
  • docs/providers/openai-setup.md — Provider-spezifische Schlüsselkonfiguration
  • docs/settings/byok-settings-reference.md — UI-Spezifikation für BYOK-Komponenten

Belege & Quellen

Diese Troubleshooting-Anleitung wurde automatisch von der FixClaw Intelligence Pipeline aus Community-Diskussionen synthetisiert.