Skip to content

Providers and login

Status: source candidate for one maia login covering worker accounts and model providers, vendor executor accounts (Connections) for Codex CLI and Claude Code, Node account selection by usage, Connection grants and pools (S2), and Desktop Realtime voice through Vercel AI Gateway. Planned: the decision layer (S8, R10a), central model and effort selection (R10b) and the Realtime voice provider contract (R7). Provider capabilities change; an adapter is re-tested before it is enabled or released.

Outcome

One human sign-in feeds three separate runtime ports:

maia login
  |-- worker accounts (Connections, owned by the Node)
  |     -> AgentExecutor: Codex CLI (codex app-server), Claude Code (Agent SDK)
  |-- model providers ($MAIA_HOME/auth.json, one credential per provider)
        -> ModelProvider: inference for the Maia agent (Pi provider catalog)
        -> RealtimeVoice: Desktop voice through Vercel AI Gateway
  • A model provider supplies inference to the native Maia agent.
  • An executor runs a complete vendor or hosted agent behind the common Session interface. A hosted coding agent is always an executor, never a model provider: its lifecycle, rules, tools and authority differ.
  • A Realtime voice adapter carries speech for the exact selected Session; it is neither an inference session nor an executor.

The Providers domain owns adapter semantics, sign-in and refresh policy, capability discovery, lifecycle mapping and error normalization. Maia Node owns the durable Connection records, revisions, events, leases and receipts. Provider code reaches those records only through registered Node operations. Shared contracts are in Maia normative contracts; secret storage, identity and incident behavior are in Maia operations.

One sign-in

maia login is the one sign-in entry point (decision); the exact grammar is in the CLI reference. maia connection login start asks the Node to sign one existing account in again, and maia agent login still opens Pi's interactive /login, which writes the same auth.json without Maia's key checks.

  • Worker account (maia login codex or maia login claude). The Node stores a Connection record without any token. At a terminal the CLI runs the vendor's own sign-in (codex login, claude auth login) for the account's root; without a terminal the Node starts a Codex device-code sign-in, and Claude Code needs a terminal. The secret stays in the vendor CLI's config root: <node state>/accounts/<connection-id> (mode 0700), or the user's normal vendor login for a system-default account. Sessions set or unset CODEX_HOME or CLAUDE_CONFIG_DIR explicitly.
  • Installed CLIs (maia setup). maia connection detect reads each installed Claude Code and Codex CLI on the Node: path, version, and its own --version and status output (claude auth status --json, codex login status) run with a Session's variables and no vendor root or key, 3 seconds at most; for Codex, two codex debug prompt-input renders in temporary homes check that the worker's prompt keys still work. A custom endpoint counts as signed in: Claude's env.ANTHROPIC_BASE_URL with an apiKeyHelper in settings.json, or a Codex model_provider with a base_url and an auth command or env_key. Maia reads those keys and the endpoint host (and a safe URL for the gateway login hint when Claude is skipped), never a credential file, the helper's output or another value. maia setup previews and confirms a system-default account and an approved executable for each usable CLI (decision).
  • Gateway (maia login gateway --name <name> --base-url <url> --api-key-stdin). A gateway Connection is an AI gateway's base URL and API key that Claude Code and Codex Sessions can use instead of a vendor account; the gateway keeps its own accounts, models and quotas. The Node stores the key in an owner-only file in the Connection's root and hands it to a worker in one environment variable. API-key gateways such as Vercel AI Gateway are the default; a subscription proxy such as a subscription-backed model proxy can breach provider terms, so the owner decides per gateway.
  • Model provider (maia login <model-provider>). Pi's own subscription or API-key sign-in stores one credential per provider in $MAIA_HOME/auth.json (default ~/.maia/auth.json); a new sign-in replaces the old one. Users: the Maia agent, Node provider pools (OAuth only) and Desktop voice (the vercel-ai-gateway key).

Rules that hold today:

  • Maia never reads, stores or proxies a vendor credential (auth.json, .credentials.json or a Keychain entry of Codex or Claude Code).
  • Maia never writes Pi's ~/.pi or a vendor's ambient auth.json.
  • API keys come from a hidden prompt or --api-key-stdin, never from argv. An empty, multi-line or oversized key is refused. A credential Maia's own parser cannot read is removed again. Errors show Maia's message, never the provider's raw error.
  • The Node reads auth.json only as an owner-only regular file (mode 0600, no symbolic link, one hard link). It projects OAuth providers as provider pools (maia provider pool list) and leases one credential to a managed Pi worker; API keys are not leased. The packaged worker keeps the lease in memory. On macOS the Pi RPC path instead writes the leased access and refresh token to an owner-only per-Session auth.json under the Node state directory, and nothing removes that copy yet.
  • Model providers belong to the Maia agent, so --profile does not apply to them.

Minimal public vocabulary

  • login acquires credentials, connection manages safe account metadata, and model reads catalog data; their fields do not overlap. There are no provider command trees such as cursor, codex-pool or gateway; vendor names and gateway appear only as arguments (login codex, login gateway, integration claude-code).

  • Source candidates today: maia login, maia setup, the maia connection family (create, detect, list, show, login start, enable, disable, capacity set, usage show, models list), maia provider pool list (provider pools) and maia integration for Codex and Claude Code (approval of the exact vendor executable).

  • Target vocabulary without a roadmap slice:

    maia logout <connection-id> --expected-revision <n> [--command-id <id>]
    maia model list [--connection <id>] [--capability <model-inference|realtime-voice>] [--limit <1..100>] [--after <cursor>]
    maia model refresh <connection-id> [--command-id <id>]
    maia model show <connection-id> <model-id>
    

Worker accounts (Connections)

A Connection is one named vendor account or AI gateway (maia.connection/v1): executor kind (gateway for a gateway), config root, state, slot cap, limitedUntil, the last vendor-reported usage snapshot and revision. Records are disabled, never deleted; disabling is how a gateway is removed. Owners:

Secrets

Credentials must never appear in:

  • .maia files, AGENTS.md, Agent definitions, Routines or Skills;
  • SQLite domain rows, events, receipts, logs, error messages or artifacts;
  • prompts, context manifests, transcripts, A2A messages or MCP context;
  • command arguments, a managed worker's environment, stdout, crash diagnostics or core dumps.

Today the only managed store is a Source candidate: gateway keys on a macOS profile that opted in (D-139). A gateway key still enters a Claude Code or Codex worker's environment at launch, the one place this list does not hold for it. Model-provider credentials are plain JSON in the owner-only auth.json (plus the per-Session Pi RPC copy above), and worker-account credentials stay in vendor custody. Desktop voice also accepts AI_GATEWAY_API_KEY from the Desktop process environment and reads auth.json without the Node's file checks.

The managed SecretStore target is in operations (decision). Provider adapters see a secret only as an opaque handle that has no JSON encoder; display identifiers are HMAC-derived and rotate with the credential.

Model catalog and capability evidence

Planned with R10. A provider catalog is discovery data, not proof that Maia can use a capability safely.

  • Store exact provider model IDs; never invent aliases. A catalog snapshot records its Connection and adapter revision, provider ETag or digest, fetch and expiry times, completeness, and each model's lifecycle, region, modalities, limits and capabilities. Its TTL is one hour; a failed refresh keeps the old snapshot visible but ineligible for new admission.
  • Deprecation or removal never changes a running activation; it blocks a new one with a typed replacement diagnostic when the provider publishes one.
  • Capability evidence records, per Connection, adapter version and model: inference, function calling, parallel tools, structured output, streaming, realtime audio, cancellation, usage reporting, data residency and provider retention, each verified, observed-only or unsupported, plus token and tool-schema limits and the probe-suite digest.
  • Deterministic probes cover streaming, schema fidelity, malformed tool arguments, cancellation, usage, error redaction, artifact limits and data policy. Evidence expires after seven days, on any Connection, adapter or model-policy revision, and on a failed probe. The Control Node signs it; configuration text cannot self-attest a capability.
  • Eligibility is the intersection of catalog, fresh evidence, Project and operator policy, and the requested operation. Unknown or expired evidence is display-only and fails closed for tool use, external execution and voice.

Semantic evaluation provider

Planned (R10a, S8). Semantic evaluation uses the Node-owned evaluation adapter, not a voice capability or a second coordinator. Candidate routes are Jev through Vercel AI Gateway or a Codex/ChatGPT-account Session.

  • An explicit model and Connection prevent ambient provider selection or silent substitution.
  • Export policy binds the owner, source permissions, permitted data, retention, budget and cancellation.
  • Shared infrastructure never shares model conversations, credentials or cross-owner result caches.
  • Raw uncertainty and unknown usage pass through the adapter unchanged.
  • A new dependency, a Gateway key and paid calls each need separate approval.

Central model and effort selection

Planned (R10b). One Node/Swarm-owned policy prepares coordinator and execution worker model calls:

  1. Filter the admitted catalog by capability, owner and export policy, Connection health, explicit human pins and remaining budget.
  2. Jev may rank eligible model and effort candidates from the request, a bounded context manifest, complexity and latency/cost policy.
  3. Node validates the result and freezes the exact Connection, model and version, supported effort, context digest and budget before the call. Unsupported effort is rejected, never mapped to another setting.

Workers do not own routing, provider selection or evaluator clients. Coordinators may propose setup changes but cannot bypass admission or change a call in flight. Record selection provenance, policy, rubric and source versions, uncertainty and observed usage; an estimated cost is not usage. Reuse a decision only while all its scope and freshness keys stay valid. A required dependency outage follows AI dependency outage and manual recovery, never an undocumented fallback model. Provider quotas are ceilings, not a Maia concurrency guarantee; Node also bounds queueing, calls and spend.

Provider attempts, errors and usage

Planned for native Maia agent inference (R10, T1).

  • Every provider or executor call records an attempt before dispatch: attempt, operation, activation and effect IDs, Connection and revision, adapter and version, requested and resolved model, provider request ID, reservation, input digest, attempt number, state (planned, dispatched, streaming, succeeded, failed, outcome-unknown, reconciling), first output and terminal times.

  • Errors normalize to stable codes:

    authentication_failed | authorization_failed | invalid_request
    model_unavailable | capability_unavailable | rate_limited | quota_exhausted
    budget_exhausted | timeout_before_dispatch | timeout_after_dispatch
    transport_failed | provider_internal | content_rejected | cancelled
    outcome_unknown | protocol_violation
    

    An error carries a safe message, retryability, retryAfterMs (bounded to 1 second through 1 hour), the provider's safe request ID, the attempt ID and a redacted evidence digest. Raw bodies, headers, tokens, account IDs and stack traces stay inside the adapter; mapping snapshots are fuzzed for secret leaks.

  • Price cards are immutable and use integer micros. Admission reserves against one price-card digest. Provider usage may raise a settlement but never lowers an observed counter. Missing or late usage follows the budget reservation contract.

  • Adapters publish only through the Node observation plane. Attempt state, fallback, rate limits, terminal outcome, usage and spend are authoritative; text and audio progress are bounded diagnostic chunks. One upstream stream serves an attempt regardless of subscriber count; provider cursors never replace Maia cursors. Account IDs are never metric labels.

Selection and fallback

Executor accounts use account selection. For model calls, today maia agent --model <provider/model> overrides the agent.model default in $MAIA_HOME/config.toml. The planned order (R10) is:

  1. explicit CLI model or credential override;
  2. Routine override allowed by Project policy;
  3. Agent definition;
  4. Project default;
  5. user default;
  6. Maia default, only when it resolves to one unambiguous enabled credential.

Ambiguity fails with the candidate IDs; there is no "first account found". Fallback is off by default (decision). When policy enables it, it lists exact credentials and failure classes:

  • a verified pre-dispatch failure may select the next allowed credential;
  • a verified no-output transport failure may retry as a new attempt in the same operation lineage;
  • after any token, tool call, external effect or outcome-unknown, fallback is closed until reconciliation;
  • authorization, invalid request, policy denial and budget exhaustion never fall through. A rate limit falls through only to a credential the policy names, and only when the remaining reservation covers its price card.

Each fallback attempt keeps the activation, operation, command, input and reservation lineage. Backoff is full jitter from 1 second, capped at 30 seconds and three attempts.

Realtime voice provider

Voice is optional input and output for the exact selected Session (decision). Capture, focus and the confirmation screen belong to Desktop and mobile clients; routing and confirmation rules belong to Swarm voice orchestration. This section owns Connection, token, transport, event and data-policy boundaries.

Current source candidate

  • Desktop Rust reads the Gateway key from AI_GATEWAY_API_KEY in the Desktop process or, on Unix, from the vercel-ai-gateway entry of $MAIA_HOME/auth.json, and requests a 60-second client token for openai/gpt-realtime-2 from the fixed Gateway HTTPS endpoint.
  • The renderer receives only that token, the pinned Gateway WSS URL, the expiry and an empty tool list, and connects through the AI SDK 7 experimental Realtime API (ai, @ai-sdk/gateway). The maia tool and prompt come from Maia Node through the SDK.
  • Maia stores no audio. The transcript stays in client memory unless a confirmed voice relay sends it to a Session as a turn.

Target contract (R7)

R7 moves token minting behind Node operations:

  • Data policy. Before minting, Node resolves the allowed provider and region, consent version, provider-retention mode, transcript policy (ephemeral or redacted-opt-in), redaction policy digest, forbidden content classes, allowed voice actions (ask, status, preview, confirm), concurrency and maximum Session duration. Raw audio retention is always false. Only the policy digest and consent metadata persist. A Connection that cannot attest the policy cannot mint.

  • Token. Minting binds the actor, device and client Session, target Maia Session, Connection and model, origin and audience, tool manifest, data policy, TTL and voiceSessionId. The token never appears in a public DTO, event, log, URL or deep link; it is rate-limited, revocable and cannot override model or tools. Reconnect mints a new token and invalidates pending confirmations.

  • Transport. voice.session.start returns a single-use ticket (expiry at most 60 seconds) and the selected transport: WebRTC (DTLS-SRTP) preferred, WebSocket (TLS, origin allowlist, 16 KiB control frames, bounded audio frames) as fallback. The provider token travels once over the authenticated broker channel. One device holds at most two voice Sessions, one actor at most four.

  • Events. Normalized kinds are audio.started, audio.stopped, transcript.partial, transcript.corrected, response.delta, tool.requested, tool.result, interrupted, error and closed. Text is at most 16 KiB, safe messages at most 2 KiB, and audio metadata carries no audio bytes. Node validates schemas, deduplicates provider IDs, orders by one voice sequence and links tool results to receipt IDs. A sequence gap forces reconciliation; malformed events never reach the target Session; a transcript correction never reuses an earlier confirmation.

  • Lifecycle.

    requested -> authorized -> token-minted -> connecting -> active
    connecting|active -> reconnecting -> token-minted|closed
    active -> stopping -> closed
    any non-terminal state -> failed|revoked|expired
    

    closed, failed, revoked and expired end the provider transport only; they never change the bound Maia Session.

  • Scope. Voice for a coordinator inherits that coordinator's discovery ceiling, not the human's portfolio (ownership). Switching target rebuilds the provider conversation and fences late audio and tool results. Context already sent to a provider cannot be recalled.

  • Transcripts. Persistence is an opt-in grant per actor, device and consent version, with purpose, expiry and withdrawal. Redaction runs before storage; a redaction failure keeps the transcript ephemeral and marks evidence degraded. Withdrawal stops new persistence and schedules eligible content for purge.

Candidates are OpenAI Realtime directly or OpenAI and xAI realtime models through Vercel AI Gateway. Each must pass Maia's tests for schema fidelity, tool-call correlation, interruption, error mapping, latency and confirmation replay before it is selectable. The target fixes no realtime model ID; today's Desktop slice pins openai/gpt-realtime-2.

Adapter conformance

  • Ports are capability interfaces, not SDK re-exports; provider types stay inside the adapter.
  • An adapter manifest pins package and API versions, the Node runtime range, auth methods, operations and capabilities, schema digest, known limits and the rollback version. Build and startup refuse a mismatched package version; any adapter or dependency change invalidates capability evidence.
  • CI runs deterministic fake-provider suites for every lifecycle and error state plus a replay corpus of sanitized real protocol shapes. Opt-in live smoke tests cover sign-in, one bounded inference or Session, cancellation, usage, cursor recovery and sign-out without keeping provider content; voice adds reconnect, barge-in, tool correlation and token revocation. Live evidence is redacted and expires after 30 days.
  • Credential scans cover CLI JSON, logs, events, receipts, transcripts, context manifests and errors. No output includes a token, refresh token, API key, authorization header, secret path or raw provider response.

Imported from Maia OS docs at 3dc3e07