Vaults
Per-Workspace credential storage that sessions use to authenticate to MCP servers in Orca Agent Engine.
A Vault in Orca Agent Engine holds the credentials sessions need to authenticate to MCP servers at runtime. A vault is the container; individual credentials inside the vault carry the secret material - bearer tokens, OAuth access and refresh tokens, or environment-variable secrets. Sessions reference vaults by ID, and the runtime injects the right credential into each MCP request without ever returning the secret to the agent or your application.
Vaults are Workspace-scoped. A vault is visible only to sessions in the Workspace where it was created. Do not store secrets meant for a different Workspace; create a fresh vault there instead.
Get your registry endpoint
Registry endpoint
Examples on this page target your registry endpoint - the deployment host root, with no path
suffix. For CLI, set ORCA_REGISTRY_URL and exactly one of ORCA_ACCESS_TOKEN (Bearer) or
ORCA_API_KEY (x-api-key). For TypeScript SDK, set ORCA_BASE_URL / ORCA_API_KEY (Bearer).
To find the endpoint, see Connect to the registry.
Create a vault
Create a vault by POSTing a CreateVaultRequest to /v1/vaults. display_name is required and holds 1-255 characters; metadata accepts up to 16 string -> string pairs.
vault=$(ork agent vaults create \
--display-name "support-mcp-creds" \
-o json)
VAULT_ID=$(jq -r '.id' <<< "$vault")A successful create returns 200 OK with the new vault record:
{
"id": "vlt_01H8...",
"type": "vault",
"display_name": "support-mcp-creds",
"metadata": { "team": "support" },
"archived_at": null,
"created_at": "2026-05-11T17:24:08Z",
"updated_at": "2026-05-11T17:24:08Z"
}The vault is empty until you add a credential.
Add a credential
Add a credential by POSTing a CreateCredentialRequest to /v1/vaults/{vaultId}/credentials. The request carries an optional display_name, optional metadata, and a required auth object. The auth.type field selects one of three credential types:
auth.type | What it stores | Use it for |
|---|---|---|
static_bearer | A long-lived bearer token bound to one MCP server URL. | MCP servers that authenticate with a fixed API token. |
mcp_oauth | An OAuth access token, its expiry, and an optional refresh configuration. | MCP servers behind OAuth, where the token must be refreshed over time. |
environment_variable | Stored and served by the registry, but not injected. No runtime delivers it to a sandbox yet. | Not usable today - see the note below. |
static_bearer
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | static_bearer. |
token | string | Yes | Bearer token. Write-only - never returned. |
mcp_server_url | string | Yes | Absolute URL of the MCP server this token authenticates to. |
curl -fsS "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/credentials" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "tickets-mcp",
"auth": {
"type": "static_bearer",
"mcp_server_url": "https://mcp.example.com/tickets",
"token": "<server-token>"
}
}'mcp_oauth
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | mcp_oauth. |
access_token | string | Yes | OAuth access token. Write-only - never returned. |
mcp_server_url | string | Yes | Absolute URL of the MCP server the token is valid for. |
expires_at | string | No | RFC 3339 expiry of the access token. |
refresh | object | No | Refresh configuration. Omit it for a token you refresh yourself. |
refresh.refresh_token | string | Yes (within refresh) | Refresh token. Write-only. |
refresh.token_endpoint | string | Yes (within refresh) | Token endpoint the runtime calls to refresh. |
refresh.client_id | string | Yes (within refresh) | OAuth client ID. |
refresh.token_endpoint_auth.type | enum | Yes (within refresh) | none, client_secret_basic, or client_secret_post. Both client_secret_* variants also require client_secret (write-only). |
refresh.resource | string | No | Resource indicator sent with the refresh request. |
refresh.scope | string | No | Scope requested on refresh. |
curl -fsS "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/credentials" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "github-mcp",
"auth": {
"type": "mcp_oauth",
"mcp_server_url": "https://mcp.example.com/github",
"access_token": "<oauth-access-token>",
"expires_at": "2026-05-12T17:24:08Z",
"refresh": {
"refresh_token": "<oauth-refresh-token>",
"token_endpoint": "https://auth.example.com/oauth/token",
"client_id": "<oauth-client-id>",
"token_endpoint_auth": {
"type": "client_secret_post",
"client_secret": "<oauth-client-secret>"
}
}
}
}'environment_variable
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | environment_variable. |
secret_name | string | Yes | Environment variable name, 1-255 characters. Unique within the vault. |
secret_value | string | Yes | Secret value. Write-only - never returned. |
networking | object | Yes | Egress policy. Either { "type": "unrestricted" } or { "type": "limited", "allowed_hosts": [...] } with at most 16 hosts. Each host is a bare hostname, an IPv4 address, or a *. wildcard. |
injection_location | object | No | Where the secret is injected: { "header": true }, { "body": true }, or both. At least one must be enabled. |
Nothing injects these yet. The registry validates, stores, and serves an environment_variable
credential, but no runtime delivers it. The only consumer of a session's prepared vault credentials
skips every credential that is not bound to an MCP server, and a sandbox's environment is built
solely from gateway and model variables - secret_name is never read. A session referencing such a
credential starts normally and the variable is simply absent.
static_bearer and mcp_oauth credentials do work; they are MCP-server bound.
curl -fsS "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/credentials" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "analytics-api-key",
"auth": {
"type": "environment_variable",
"secret_name": "ANALYTICS_API_KEY",
"secret_value": "<secret>",
"networking": {
"type": "limited",
"allowed_hosts": ["api.analytics.example.com", "*.cdn.example.com"]
},
"injection_location": { "header": true }
}
}'networking is stored but not enforced in this build. The registry validates the block and echoes it back on the credential record, and no runtime reads it, so limited restricts nothing and allowed_hosts blocks nothing. Do not use it as a security boundary. An environment's networking block behaves the same way.
Create a credential from the CLI or the SDK by passing the same auth object. $VAULT_ID / vaultId is the vault you created above; each example captures the new credential's id as $CREDENTIAL_ID / credentialId:
credential=$(ork agent vaults credentials create \
--vault "$VAULT_ID" \
--display-name tickets-mcp \
--auth-json '{"type":"static_bearer","mcp_server_url":"https://mcp.example.com/tickets","token":"<server-token>"}' \
-o json)
CREDENTIAL_ID=$(jq -r '.id' <<< "$credential")Secret fields - token, access_token, refresh_token, client_secret, and secret_value - are write-only. The credential record the registry returns echoes the non-secret parts of auth (such as mcp_server_url, expires_at, secret_name, and networking) and omits every secret. Save the secrets yourself if you need them again.
Constraints
- A vault holds at most 20 active credentials. Exceeding that returns
409withcredential limit exceeded. static_bearerandmcp_oauthcredentials are unique permcp_server_urlwithin a vault;environment_variablecredentials are unique persecret_name. A duplicate returns409.- URL matching is normalized: the runtime lowercases the scheme and host and strips default ports and trailing slashes before comparing a credential to an
mcp_serversentry. - Credentials are accessible only when the vault is referenced by a session. The runtime fetches the credential at the gateway, scopes it to the MCP request, and never returns the secret to your application or to agent code.
Authorize an MCP server from the CLI
The CLI can create an mcp_oauth credential without making you copy access and refresh tokens.
It discovers the authorization server, uses a public client with PKCE, opens a browser, and submits
the resulting tokens directly to the vault:
ork agent vaults credentials create \
--vault "$VAULT_ID" \
--mcp-server-url "https://mcp.example.com/mcp" \
-o jsonPass --oauth-issuer if discovery advertises multiple issuers, or --oauth-client-id if the
server requires a pre-registered client. For SSH, use a forwarded fixed --callback-address port
with --no-browser. The command rejects combining --mcp-server-url with --auth-json. See the
CLI reference for all OAuth flags.
Reference the vault at session creation
Pass the vault ID in the vault_ids array when creating a session. The runtime injects the vault's credentials into MCP requests the session makes. $AGENT_ID and $ENVIRONMENT_ID identify the agent to run and the environment to run it in.
curl -fsS "$ORCA_REGISTRY_URL/v1/sessions" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent": "'"$AGENT_ID"'",
"environment_id": "'"$ENVIRONMENT_ID"'",
"vault_ids": ["'"$VAULT_ID"'"]
}'You can reference multiple vaults to authenticate to multiple MCP servers. A vault only has to belong to the same Workspace and not be archived; there is no provider-matching requirement.
Rotate a credential
Rotate in place by sending an update. Update uses POST on the credential path and takes the same auth.type you created the credential with, plus only the fields you want to replace.
ork agent vaults credentials update $CREDENTIAL_ID \
--vault $VAULT_ID \
--auth-json '{"type":"static_bearer","token":"<new-server-token>"}'Update rotates the secret only. mcp_server_url and secret_name are set at create time and cannot change - to point a credential at a different server, archive or delete it and create a new one. For mcp_oauth, update accepts access_token, expires_at, and the refresh block; for environment_variable, it accepts secret_value, networking, and injection_location.
Sessions already running with the old credential keep using it until they reconnect to the MCP server. New sessions pick up the rotated credential immediately.
A 409 with credential refresh in progress means a token refresh already holds a lease on the credential and your rotation would race it. Wait for the refresh to land and retry.
Validate an MCP OAuth credential
Validation is the diagnostic to reach for when an OAuth-protected MCP server starts rejecting an agent's calls. If the credential carries a refresh token, the registry refreshes first and stores the rotated tokens - OAuth servers commonly rotate the refresh token on use, so exercising the grant without persisting the result would break the credential. It then sends an MCP initialize probe with the current access token and reports both outcomes without exposing any token.
The operation applies only to mcp_oauth credentials: any other type returns 400 with credential is not mcp_oauth, and an archived credential returns 404 because its secrets have already been purged.
ork agent vaults credentials validate $CREDENTIAL_ID \
--vault $VAULT_IDThe response reports what the probe and the refresh attempt each returned:
{
"type": "vault_credential_validation",
"credential_id": "vcrd_01H8...",
"vault_id": "vlt_01H8...",
"validated_at": "2026-05-11T17:24:08Z",
"has_refresh_token": true,
"status": "valid",
"mcp_probe": {
"method": "initialize",
"http_response": {
"status_code": 200,
"content_type": "application/json",
"body": "{\"result\":{...}}",
"body_truncated": false
}
},
"refresh": {
"status": "succeeded",
"http_response": {
"status_code": 200,
"content_type": "application/json",
"body": "{\"access_token\":\"***\"}",
"body_truncated": false
}
}
}| Field | Values | Meaning |
|---|---|---|
status | valid, invalid, unknown | Overall verdict. |
has_refresh_token | boolean | Whether the credential carries a refresh token at all. |
mcp_probe.http_response | object or null | Raw response to the initialize probe. null when the request never completed. |
refresh.status | succeeded, connect_error, failed, no_refresh_token | Result of the refresh attempt. no_refresh_token means there was nothing to refresh. |
status is derived in this order, so a broken refresh surfaces even when the old access token still works:
refresh.statusisfailed- the verdict isinvalid.refresh.statusisconnect_error- the verdict isunknown.- The probe returned
2xx- the verdict isvalid. - The probe returned
401or403- the verdict isinvalid. - Anything else - the verdict is
unknown.
Because validation persists rotated tokens, it can lose a race: a 409 with credential was rotated concurrently means another writer rotated the credential first, and the call is safe to retry. That is a different conflict from the credential refresh in progress an update returns.
Other operations
List vaults
ork agent vaults listList accepts limit, page, and include_archived. Pass include_archived=true to surface archived vaults.
List credentials in a vault
ork agent vaults credentials list --vault $VAULT_IDUpdate vault metadata
Update uses POST on the vault path with partial-update semantics: omitted fields are left unchanged, and setting a metadata key to null removes it.
curl -fsS -X POST "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "display_name": "support-mcp-creds (prod)" }'Archive or delete
Archive marks the vault read-only, hides it from default list results, and purges the secret payloads of its credentials. Delete removes the vault and all its credentials from the registry and returns a vault_deleted tombstone. Credentials have the same two operations on their own path.
# Archive the vault
curl -fsS -X POST "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/archive" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN"
# Delete the vault
curl -fsS -X DELETE "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN"
# Archive a single credential
curl -fsS -X POST "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/credentials/$CREDENTIAL_ID/archive" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN"
# Delete a single credential
curl -fsS -X DELETE "$ORCA_REGISTRY_URL/v1/vaults/$VAULT_ID/credentials/$CREDENTIAL_ID" \
-H "Authorization: Bearer $ORCA_ACCESS_TOKEN"Credential refresh
The registry refreshes mcp_oauth credentials that carry a refresh block, and persists the rotated access and refresh tokens when the grant succeeds. static_bearer and environment_variable credentials never refresh - rotate them yourself with update.
To check that an OAuth credential's token and refresh configuration still work, run validation. Webhooks for credential lifecycle events are not exposed today.
Permissions
Treat every Workspace API key as full access to its Workspace's resources, and separate access with Workspaces. See Control registry access.
What's next
Triggers
Run an agent in Orca Agent Engine automatically on a cron schedule or, on StreamNative Cloud, from Pulsar and Kafka messages.
Best practices
Guidance for building against Orca Agent Engine - credentials, session lifecycle, idempotency, pagination, streaming, retries, and environment separation.