Skip to main content

Provider keys

Provider credentials live server-side and rotate at runtime, without a redeploy.

Set a key with anyray-connect admin secret set provider_key <slug>, through your agent's admin MCP, on the Providers page, or with GET / PUT /admin/provider-keys. This runtime path wins over the ANYRAY_PROVIDER_KEY_* env vars, and keys stay server-side. With no key set, BYO clients must send both the provider credential and their Anyray client key. GET reads the shared store and answers 503 if it does not reply within 3 seconds, never an older copy. Retry it.

If this gateway cannot decrypt the stored keys (its ANYRAY_ADMIN_TOKEN differs from the one that sealed them), GET answers an empty config with unreadable: true, and so does /admin/provider-capabilities. A save there replaces every stored key, for every replica, so the write answers 409 provider_store_unreadable unless it carries ?replaceUnreadableStore=true. The Providers page warns and asks you to confirm first. Clears and per-key edits (field patches included) answer 503 provider_store_unreadable_edit, even with the confirm. After that answer the page asks for the same confirm on Save, which re-enters the provider's keys. Remove and Make default stay disabled until the keys can be read again. Restoring the token that sealed the keys keeps them all. An invalid patch answers 400; no_provider_named means every slug in the request is empty. Writes answer 503 if the store cannot be read or does not reply.

The gateway does not forward an Anyray client key (ark_…) as a provider credential. Outside pass-through, a request whose only credential is that key gets 424 no provider key configured for <provider> whenever the provider's auth mode would send it: Bedrock with x-anyray-aws-auth-type: apiKey, Vertex AI without a service account, Azure OpenAI on an API key or AD token, and Oracle without IAM signing. On /v1/realtime the gateway refuses the upgrade instead of dialing. Modes that sign or mint their own credential (AWS keys or a role, a service account, Entra, managed or workload identity, Oracle signing) ignore the client key and serve the request.

A new node-written outbound origin needs providerkeys:manage (providers:write for API keys), in addition to routing:write, even on a node without a provider. A new pairing of a credential with a node-written base URL (including its path) or custom_endpoints also needs that grant. A host injected from the node's own provider key is key-sourced and needs no extra grant. Copying that key's Base URL into routing is node-written, even when the URLs match. An inherited host is node-sourced for the child credential.

The comparison covers roots, groups and all branches, regardless of strategy. Credential selectors distinguish named pins, inline bags and unpinned resolution. Inline bags and headers use separate keyed HMACs. Every inline credential field counts, including beside a pin; changing or removing one needs a new grant. Header changes on node-sourced hosts need a new grant. The MACs stay inside the save comparison and need no private admin token. Save checks read provider keys from authority; an unavailable authority returns 503 provider_keys_authority_unavailable. Operators can reorder, move, duplicate or remove existing grants. A new grant without permission records the actor, code and route in the admin audit and returns 403 outbound_host_requires_providerkeys_manage.

Named provider keys​

A provider can hold several named keys. The default key serves every request that doesn't pin an id. On the Providers page, Add key takes a lowercase id (such as team-ml), an optional label, and the credential. On a provider with no credential yet, the first named key becomes the default.

PUT /admin/provider-keys returns {status, capabilities} with provider slugs and key ids only, never a credential. It patches:

PatchEffect
{ "keys": { "<id>": <credential> } }Adds or replaces that named key.
{ "keys": { "<id>": { "fields": { "<field>": "<value>" } } } }Changes only the named fields of an existing key. An omitted field keeps its stored value; null or "" removes one.
"" or null under an idRemoves that key.
"defaultKeyId": "<id>"Which key serves unpinned requests.
A bare credential for the providerReplaces the default key.
"" for the providerRemoves all of its keys.

The Providers page saves changed fields this way when capabilities.fieldPatches is true. Remove with "" or null. A key, bag field or field value that is only whitespace, and a bag with no non-empty value ({} included), are refused with 400 empty_provider_key and nothing is saved. Two field names that match once trimmed are refused with 400 too.

Pin a key from a routing target with provider_key_id (Routing). Each spend row records the key id that served it (providerKeyId). On the Routing page each target and lane has a Key column: a picker once the provider holds two or more keys, otherwise default key.