Docs/SDK and API/Go SDK/Providers & Auth

Providers & Auth

This page is the practical provider tutorial for Go developers embedding Seshat. Instead of listing knobs first, it starts from the real integration scenarios: local operator apps, multi-user backends, mixed provider stacks, and Codex-specific auth flows.

Public SDK Auth Contract

The exported Go SDK keeps the auth boundary intentionally simple. Your host app can pass credentials directly through ClientConfig.APIKey, use provider-specific values in ProviderConfig, or resolve credentials dynamically with CredentialResolver.

What changed recently

OAuth login is now exposed for Go hosts through sdk.NewOAuthDeviceFlow. That means a desktop app, CLI app, or internal operator tool can initiate login itself and then feed the resulting resolver back into the same runtime without inventing a separate auth bridge.

Choose The Right Scenario First

Single-user app

Best for a CLI app, a local desktop utility, or an internal operator tool where one human controls one runtime and simple env-var auth is acceptable.

Multi-user backend

Best for SaaS or internal platforms where credentials belong to each tenant or user and must be resolved dynamically per request.

Mixed capability stack

Best when reasoning, image generation, speech, and transcription should not all come from the same provider.

Codex-specific flow

Best when you want ChatGPT-backed Codex access inside your own app, including device flow login and downstream gRPC handoff.

Scenario 1: Single-User CLI App, Desktop Utility, Or Operator Tool

This is the simplest embedding path. Pick a model, inject one API key, and optionally override transport details through ProviderConfig. If your host product also uses the Seshat auth store, you can bootstrap login with the CLI and then let the runtime reuse the stored credentials.

Explicit provider config
client, err := sdk.NewClient(&sdk.ClientConfig{
    Model: sdk.ModelIdentifier{
        Provider: sdk.APIProviderAnthropic,
        Model:    "claude-sonnet-4-20250514",
    },
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
    ProviderConfig: &providers.Config{
        Provider: types.APIProviderAnthropic,
        APIKey:   os.Getenv("ANTHROPIC_API_KEY"),
        BaseURL:  "https://api.anthropic.com",
    },
})
Optional shared auth-store bootstrap
seshat login --provider openai

Scenario 2: Multi-User Backend Or SaaS Integration

In a backend, process-wide environment variables are usually the wrong boundary. The cleaner pattern is to let your app own secret storage, token exchange, or tenant settings, then resolve the provider credential at runtime through CredentialResolver.

ProviderAuth flowEnvironment variablesConfig pathNotes
anthropicAPI keyANTHROPIC_API_KEYClientConfig.APIKey or ProviderConfig.APIKeyDirect Claude path. Provider config also supports alias mapping and routing setup.
openaiAPI keyOPENAI_API_KEYClientConfig.APIKey or ProviderConfig.APIKeyAlso reused by current image, TTS, and STT capability providers when configured.
ollamaUsually noneOLLAMA_HOST, optional OLLAMA_API_KEYProviderConfig.BaseURL or detected local hostLocal daemon path. Runtime discovery checks OLLAMA_HOST and localhost defaults.
bedrockAWS credentialsAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGIONProviderConfig.RegionValidation explicitly requires AWS_REGION. Transport derives the rest from AWS auth state.
vertexGCP project + regionANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGIONProviderConfig.ProjectID, ProviderConfig.RegionClaude-on-GCP path. Validation fails until both project id and region are present.
foundryAPI key + Azure resourceANTHROPIC_FOUNDRY_API_KEY, ANTHROPIC_FOUNDRY_BASE_URL, ANTHROPIC_FOUNDRY_RESOURCEProviderConfig.APIKey, BaseURL, RegionUses Azure-style api-key auth plus the Foundry resource header when configured.
geminiAPI keyGOOGLE_API_KEY for auth resolution; GOOGLE_GEMINI_API_KEY appears in validation messagesClientConfig.APIKey or ProviderConfig.APIKeyThere is a current naming mismatch in code between auth resolution and validation messaging, worth normalizing later.
z-aix-api-keyZ_AI_API_KEYProviderConfig.APIKeyOpenAI-compatible body format, but auth headers are provider-specific.
openrouterAPI keyOPENROUTER_API_KEYProviderConfig.APIKeyUseful as a one-key routing layer across many vendors.
minimaxAPI keyMINIMAX_API_KEYProviderConfig.APIKeyDefault config maps MiniMax aliases and long-output models.
workers-aiAPI keyCLOUDFLARE_API_KEYProviderConfig.APIKeyModel ids are route-style Cloudflare identifiers.
mistralAPI keyMISTRAL_API_KEYProviderConfig.APIKeyOpenAI-compatible transport with Mistral-specific base URL.
codexOAuth device flow or CODEX_ACCESS_TOKEN / CODEX_API_KEY overrideCODEX_ACCESS_TOKEN, CODEX_API_KEY, optional OPENAI_CLIENT_ID, optional SESHAT_AUTH_PATHOAuth token persisted in ~/.seshat/auth.json or explicit ProviderConfig.APIKeySpecial login path. Runtime can resolve refreshed OAuth tokens from the local auth store or reuse an explicit bearer token.
deepseekAPI keyDEEPSEEK_API_KEYProviderConfig.APIKeyOpenAI-compatible direct provider with chat/reasoner aliases.
opencodeAPI keyOPENCODE_API_KEYProviderConfig.APIKeyGateway provider exposing curated Claude, Codex, and GLM options.
kimiAPI keyNo dedicated env mapping in providerEnvVar todayWould require explicit ProviderConfig/API key wiringProvider registry support exists, but the default config/auth wiring is still incomplete compared to the others.
Per-user credential injection
type VaultResolver struct {
    Keys map[string]string
}

func (r VaultResolver) ResolveAPIKey(ctx context.Context, provider string) (string, error) {
    if key, ok := r.Keys[provider]; ok {
        return key, nil
    }
    return "", nil
}

client, err := sdk.NewClient(&sdk.ClientConfig{
    Model: sdk.ModelIdentifier{
        Provider: sdk.APIProviderOpenAI,
        Model:    "gpt-5.5",
    },
    CredentialResolver: VaultResolver{
        Keys: map[string]string{
            "openai": os.Getenv("OPENAI_API_KEY"),
            "codex":  os.Getenv("CODEX_ACCESS_TOKEN"),
        },
    },
})
Gemini naming gap

The current code still has a naming mismatch around Gemini: auth resolution uses GOOGLE_API_KEY, while some validation messaging still mentions GOOGLE_GEMINI_API_KEY. The runtime works, but the naming should be normalized later.

Scenario 3: One Reasoning Model, Different Capability Providers

Seshat does not require one provider to own everything. The main reasoning model can come from one vendor while image generation, speech-to-text, and text-to-speech use completely different providers. This is the right shape when you want better cost control, better local quality on one modality, or a more sovereign deployment mix.

CapabilityCurrent provider optionsSDK config pathNotes
Core reasoningAnthropic, OpenAI, Ollama, Bedrock, Vertex, Foundry, Gemini, Z.ai, OpenRouter, MiniMax, Workers AI, Mistral, Codex, DeepSeek, OpenCode, KimiClientConfig.Model + ProviderConfigThis is the main session model path used by the mono-run loop.
Image generationOpenAI, GeminiClientConfig.ImageGenerationCurrent TUI capability picker exposes OpenAI and Gemini for generate_image.
Text to speechOpenAIClientConfig.TextToSpeechCurrent runtime wiring only initialises OpenAI for text_to_speech.
Speech to textOpenAIClientConfig.SpeechToTextCurrent runtime wiring only initialises OpenAI for speech_to_text.
Capability-specific providers
client, err := sdk.NewClient(&sdk.ClientConfig{
    Model: sdk.ModelIdentifier{
        Provider: sdk.APIProviderAnthropic,
        Model:    "claude-sonnet-4-20250514",
    },
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
    ImageGeneration: &sdk.ImageGenerationConfig{
        Provider: "openai",
        Model:    "gpt-image-1",
        APIKey:   os.Getenv("OPENAI_API_KEY"),
    },
    TextToSpeech: &sdk.TextToSpeechConfig{
        Provider: "openai",
        Model:    "gpt-4o-mini-tts",
        Voice:    "alloy",
        APIKey:   os.Getenv("OPENAI_API_KEY"),
    },
    SpeechToText: &sdk.SpeechToTextConfig{
        Provider: "openai",
        Model:    "gpt-4o-transcribe",
        APIKey:   os.Getenv("OPENAI_API_KEY"),
    },
})

Scenario 4: Codex Inside Your App Or Through gRPC

Codex is the one provider that needs extra explanation. It uses a ChatGPT-backed transport, a dedicated base URL, and an auth story that is different from the standard API-key-only path. For local products, the most ergonomic path is to launch the device flow directly from the SDK and then reuse the returned resolver.

OAuth device flow
flow, err := sdk.NewOAuthDeviceFlow(sdk.OAuthDeviceFlowConfig{
    Provider: string(sdk.APIProviderCodex),
    Persist:  true,
})
if err != nil {
    log.Fatal(err)
}

challenge, err := flow.Start(ctx)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Open %s and enter %s\n", challenge.VerificationURL, challenge.UserCode)

token, err := flow.Wait(ctx, challenge)
if err != nil {
    log.Fatal(err)
}

client, err := sdk.NewClient(&sdk.ClientConfig{
    Model: sdk.ModelIdentifier{
        Provider: sdk.APIProviderCodex,
        Model:    "gpt-5.3-codex",
    },
    CredentialResolver: flow.CredentialResolver(),
})
if err != nil {
    log.Fatal(err)
}

_ = token
Codex resolver wiring
client, err := sdk.NewClient(&sdk.ClientConfig{
    Model: sdk.ModelIdentifier{
        Provider: sdk.APIProviderCodex,
        Model:    "gpt-5.3-codex",
    },
    CredentialResolver: VaultResolver{
        Keys: map[string]string{
            "codex": os.Getenv("CODEX_ACCESS_TOKEN"),
        },
    },
    ProviderConfig: &providers.Config{
        Provider: types.APIProviderCodex,
        BaseURL:  "https://chatgpt.com/backend-api/codex",
    },
})
gRPC handoff

The gRPC server does not perform an interactive OAuth handshake itself. The intended pattern is: do the login in your client app, obtain a live bearer token, then pass that token explicitly in the request that targets the Codex model.

gRPC explicit token
resp, err := grpcClient.Query(ctx, &pb.QueryRequest{
    Prompt: "Review this repository and propose a refactor plan.",
    Model:  "codex:gpt-5.3-codex",
    ApiKey: accessToken,
    Stream: false,
})

Supported Provider Inventory

Once the scenario is clear, this table is the runtime-level source of truth for the provider ids, transports, endpoints, and auth modes currently understood by Seshat.

ProviderAuthTransportBase / EndpointNotes
anthropicAPI keyAnthropic Messages APIhttps://api.anthropic.comDirect Claude provider. Prompt caching and structured output are first-class here, and DefaultConfigs() ships Claude-oriented aliases like sonnet and haiku.
openaiAPI keyOpenAI-compatible /chat/completionshttps://api.openai.com/v1Primary GPT path. Also reused by the current image generation, text-to-speech, and speech-to-text capability providers.
ollamaNone by defaultLocal /api/chathttp://localhost:11434Local inference path. Models are discovered dynamically from the local daemon rather than from a static registry list.
bedrockAWS credentialsAnthropic-shaped Claude path via AWSRegion-driven integrationUsed for Claude through AWS. Runtime validation expects AWS region configuration before the provider can be used safely.
vertexGCP project + regionAnthropic-shaped Claude path via GCPProject and region derivedUsed for Claude on Google Cloud. Requires ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION.
foundryAPI key + resource headerAnthropic-shaped Claude path via Azurehttps://your-resource.services.ai.azure.com/anthropic/v1Azure-hosted Claude path. The runtime sets the Foundry resource header when configured.
geminiAPI keyGemini generateContent APIhttps://generativelanguage.googleapis.com/v1betaVery large context window path with audio-capable models. Current capability-provider options include Gemini for image generation.
z-aix-api-keyOpenAI-compatible body + custom auth headerhttps://api.z.ai/api/paas/v4GLM provider. The current client forces streaming for Z.ai requests and ships aliases such as glm-5 and glm-4.5.
openrouterAPI keyOpenAI-compatible /chat/completionshttps://openrouter.ai/api/v1Routing hub for multi-vendor models. Good fit when users want one key spanning Claude, GPT, DeepSeek, Qwen, or Llama families.
minimaxAPI keyMiniMax chatcompletion_v2 endpointhttps://api.minimax.chat/v1/text/chatcompletion_v2Long-output provider with strong multilingual and long-horizon positioning in the provider registry.
workers-aiAPI keyEdge chat route with provider-specific model pathinghttps://workers.ai/v1/chatCloudflare edge inference path. Model identifiers are route-style ids such as @cf/meta/llama-3.1-70b-instruct.
mistralAPI keyOpenAI-compatible /chat/completionshttps://api.mistral.ai/v1Direct Mistral provider with familiar OpenAI-compatible transport for the runtime.
codexOAuth device flowOpenAI Responses API /responseshttps://chatgpt.com/backend-api/codexSpecial path. Codex uses ChatGPT Pro OAuth, always streams, sends Codex-specific headers, and keeps a dedicated cookie jar for chatgpt.com / Cloudflare flows.
deepseekAPI keyOpenAI-compatible /chat/completionshttps://api.deepseek.com/v1Direct DeepSeek path with chat and reasoner aliases in DefaultConfigs().
opencodeAPI keyOpenAI-compatible /chat/completionshttps://opencode.ai/zen/v1Curated gateway exposing Claude, Codex, and GLM-style models behind one provider id.
kimiAPI keyOpenAI-compatible target expectedNo default base URL block in DefaultConfigs()Kimi exists in the provider registry and string resolver, but internal/providers/config.go does not currently ship a matching default config entry. Treat it as partially wired until that gap is closed.

Related Pages

Continue with Tools & Surface to shape what the authenticated runtime can actually do, or go back to Tools & Providers for the conceptual architecture behind the integration boundary.