RKAT_ANTHROPIC_API_KEY, RKAT_OPENAI_API_KEY,
RKAT_GEMINI_API_KEY, or the matching provider-native names) - set them and
rkat run works. The powerful path is realm-scoped bindings:
declare a realm in your config, register global credentials via rkat auth login or binding-scoped credentials through REST/RPC, and pass --auth-binding <realm>:<binding> for a session or structural auth_binding on a mob-member
spawn.
rkat auth login provisions the reserved global realm in the home-rooted doc
at ~/.rkat/config.toml. Once configured, global is the implicit tail of a
workspace realm’s chain, so a single sign-in is inherited by every workspace
realm (credential reads inherit down the chain). For a binding owned by a
specific realm such as
prod:openai, manage that realm’s config entries directly; credential writes
are strict-owner and must explicitly target the realm that defines the binding.
A child-addressed inherited write is rejected with the owner identity. See
Realm inheritance.What this guide is for
Use this guide when you need more than the env-var fast path:- explicit realm-scoped bindings
- OAuth or cloud-IAM-backed auth
- hot-swapping provider identity
- per-mob-member credential overrides
ProviderRuntimeRegistry. The environment
fallback is a synthesized binding with a typed
origin = "synthetic_env_default" and CredentialSourceSpec::Env. It is
ephemeral, is not a durable realm, and cannot participate in inheritance.
The fast path: env vars
RKAT_* variants take precedence over provider-native names.
If both public OpenAI and unprefixed Azure OpenAI env vars are present, the
public OpenAI env default wins. Use
RKAT_AZURE_OPENAI_API_KEY plus
RKAT_AZURE_OPENAI_ENDPOINT, or an explicit realm binding, to make Azure
OpenAI the env default in that case.
Azure image generation can additionally use
RKAT_AZURE_OPENAI_IMAGE_GENERATION_DEPLOYMENT and
RKAT_AZURE_OPENAI_IMAGE_GENERATION_API_VERSION (or their unprefixed
equivalents).
The powerful path: realms + bindings
A realm is a named collection of backend profiles, auth profiles, and provider bindings. It lets you:- Run multiple sessions against different accounts or tenants
- Mix OAuth (Claude.ai Pro/Max), api_key (keyed by CI), and cloud IAM (Bedrock/Vertex/Foundry) in one process
- Hot-swap a session’s active binding mid-conversation
- Give different mob members different credentials
Realm shape
openai) with an Azure-specific
backend/auth pair:
Log in
rkat auth login openai provisions the ChatGPT OAuth backend.
The non-interactive API-key example provisions the public OpenAI API backend.
They can expose different model sets because they use different account
systems.
GitHub Copilot uses device authorization and provisions three routes over one
shared credential account:
global:copilot_openai,
global:copilot_anthropic, and global:copilot_gemini. They remain ordinary
OpenAI, Anthropic, and Gemini provider identities; credential_account = "github_copilot" only makes their GitHub token, derived Copilot token cache,
AuthMachine lifecycle, status, and logout shared. Meerkat queries the signed-in
account’s /models endpoint and rejects a known-unavailable model/dialect
before dispatch. If discovery is temporarily unavailable, auth still succeeds
and the request is attempted normally. A known-unavailable error includes the
compatible model IDs observed for that account; IDs outside Meerkat’s built-in
catalog also require a normal [models.<id>] configuration entry.
rkat auth login persists credentials and the matching [realm.global]
binding section into the home-rooted global realm doc (~/.rkat/config.toml),
so the sign-in is inherited by every workspace realm via the chain tail. To use
a binding owned by another realm (for example prod:openai), declare that
binding in the owning realm’s config and use an external source such as the env
profiles above, or complete login through REST/RPC with explicit realm_id,
binding_id, and profile_id. CLI login never populates a prod managed-store
profile; credential writes are strict-owner.
Use the binding
Subcommands
1
Login and profile management
2
Status and refresh
rkat auth refresh is a no-op for api_key / azure_api_key / static_bearer
auth methods (nothing to refresh). For OAuth-backed methods it
exchanges the persisted refresh token for a fresh access token and
writes the new bundle back to the TokenStore.3
Delete
Release auth smoke
make e2e-auth is the CI-safe auth smoke lane. It drives Meerkat’s
browser OAuth plumbing against a local OAuth fixture, verifies token-store
and AuthMachine lifecycle behavior across login, status, refresh, restart,
resolve, and logout, and skips optional live-provider canaries when their
seeded credentials are absent.
Real third-party browser login pages remain a manual pre-release check.
Do not automate provider-hosted OpenAI, Anthropic, or Google login pages in
CI; those pages are intentionally subject to MFA, CAPTCHA, account policy,
and provider UI changes.
Manual browser-login release checklist:
- Start login for the target provider.
- Confirm the system browser opens the provider-hosted login or consent page.
- Complete the callback and confirm Meerkat reports the login complete.
- Check auth status and confirm the target binding is
valid. - Run one tiny call through that binding.
- Logout and confirm auth status is cleared and the credential is gone.
Auth methods
Each provider’s runtime accepts a matrix of(backend_kind, auth_method) pairs. The table below summarizes what’s wired today.
External commands (
CredentialSourceSpec::Command) and file
descriptors (CredentialSourceSpec::FileDescriptor) let host
applications inject tokens without going through OAuth.
Credential sources
Thesource field on an auth profile declares where credentials
come from.
FileDescriptor is a host-integration seam rather than a plain standalone resolver path. It requires a host-scoped reader/injection path, so treat it as an advanced embedding surface rather than a generic CLI config trick.Hot-Swap Between Turns
When a session is created with--auth-binding prod:default, its
persisted SessionMetadata.auth_binding carries that binding through
resume and hot-swap. Changing the model/provider on an RPC
turn/start request keeps the binding by default; pass an explicit
auth_binding on the turn request to scope the swap to a different
realm or binding.
ProviderRuntimeRegistry::resolve with the new
binding. It does not silently replace an explicit configured binding with the
environment fallback.
Per-mob-member override
The mob runtime does not promote ambient credentials into member authority. Live model-backed spawn paths that require credentials must receive an explicit binding, either from the compiled member declaration/overlay or asauth_binding on the host-side spawn request:
auth_binding object only; the colon-joined realm:binding[:profile]
form is a CLI input convenience and is converted before it crosses the
wire boundary.
Native desktop hosts
Native applications should useHostAuthService for the complete interactive
transaction instead of recreating the CLI or RPC flow. The service owns strict
binding-owner resolution, PKCE and one-time state, exchange, coordinated
TokenStore persistence, AuthMachine lifecycle publication, rollback, status,
and logout. The application owns only its loopback listener, browser launch,
and UI.
Open one persistence capability and share that exact capability between
AgentFactory and HostAuthService:
MeerkatMachine alive for as long as the factory and auth service;
the snippet returns it in Arc, while a real host normally stores all three in
one application state object. A TokenStore open
failure is returned as a fault and is never collapsed into
interactive_login_required.
Managed-store resolution also requires per-session AuthMachine bindings. Do
not use the factory’s standalone build mode for a runtime-backed desktop
session:
native-keyring feature and
replace default_auto() with default_keyring_auto(). That profile preserves
an existing CLI file credential as the per-key authority, but a brand-new key
is written to the OS keyring. Use the same keyring-aware backend in every
process that must see desktop-first credentials; the stock CLI intentionally
uses the file profile to avoid surprise OS keychain prompts.
The browser flow is split for native UI:
- Bind the loopback listener and choose its redirect URI.
- Call
login_start(config, target, redirect_uri), then open the returnedauthorize_url. - Pass the callback’s
stateandcodetologin_complete. - Use
statusandlogoutfor secret-free UI projections and sign-out.
Refresh coordination
Refresh semantics are owned by theAuthMachine DSL (one instance
per <realm>:<binding>). On resolve, the runtime:
- Checks
expires_at. If present and within the refresh window (AUTH_LEASE_TTL_REFRESH_WINDOW_SECS, 60 seconds before expiry), marks the leaseExpiringvia the DSL. - Under
Expiring, the next resolve triggersBeginRefresh. In-process dedup ensures only one refresh HTTP call per binding even under concurrent resolves; cross-process dedup via filesystem lockfile whenrefresh-file-lockis enabled. - On successful refresh, fires
CompleteRefreshand updatesexpires_at. On failure,RefreshFailedTransient(retry) orRefreshFailedPermanent(routes toReauthRequired+ emits an[AUTH_REAUTH_REQUIRED]system notice on the session).
specs/machines/auth/.
Audit logging
Every accepted lifecycle transition emits a structured tracing event:target = "meerkat::auth::audit"- Fields:
binding_key,action,from_phase,to_phase
create_profile, delete_profile, login_oauth_complete,
login_device_complete, logout. Filter any tracing::Subscriber
on the meerkat::auth::audit target to build a persistent audit log.
Feature flags
Themeerkat-providers crate gates optional auth methods behind
features so you only pay for what you use:
The shipped
rkat CLI enables Anthropic, OpenAI, Gemini, OAuth,
native-keyring, and refresh-file locking in its dependency graph, while its
stock TokenStore profile remains file-backed. Bedrock, Vertex, and Foundry
transport features live on the corresponding provider crates and must be
selected deliberately in a custom build.
See also
- Hooks - observe auth events or gate turns on credential state
- Mobs - per-member
auth_bindingoverrides - Machine Authority - rationale behind single-owner runtime state
