Vault

A workflow refers to its secrets by name, such as vault:github, and never contains a value. The same name resolves from two places:

  • the local Vault, an encrypted file in your repository that oci workflow run reads when you run in the container; and
  • the workspace Vault in OutcomeCI, which cloud runs and oci workflow run --cloud read.

Store each secret under the same path in both, and the workflow runs unchanged in either place.

How a credential authenticates

A workflow binds a secret to a connector and says nothing about how it authenticates:

secrets:
  github: vault:github
 
apis:
  github: {uses: github, auth: secrets.github}

The credential stored at that path decides. A plain value is a token. A typed credential carries its type, its settings, and its secret fields, and the runtime authenticates with it the way its type describes: it sends a token as it is, or exchanges client credentials, a refresh token, or a signing key for a short-lived token first. Switching a workflow from a token to an OAuth app is a change in the Vault, not in the workflow.

Each connector lists the kinds of credential its API accepts (see Connectors). A credential the connector cannot use fails before any request is sent, with a message that names the connector, the Vault entry, and the kinds it accepts:

github cannot use a basic credential (Vault entry github); it accepts: token, app_installation

The connector supplies what it already knows, such as a token endpoint or the headers its API expects, so you only store what belongs to your account.

Credential types

Type Stores Settings
plain value the secret itself, used as a token none
auth_header value --header-name, --scheme
api_key api_key --header-name, --prefix
basic username and password none
oauth2 client_secret, and refresh_token for the refresh_token grant --client-id (required), --grant-type, --scope, --token-url, --audience, --account-id
oidc client_secret --client-id (required), --issuer-url, --scope, --audience
jwt_bearer private_key --issuer (required), --subject, --token-url, --audience, --scope
app_installation private_key --app-id and --installation-id (required)

Settings a connector fixes, such as a token URL, can be left out. Supply secret fields through stdin: --value-stdin fills a type's single secret field, and --secrets-json-stdin reads a JSON object when a type has several, such as a username and password.

When a provider replaces a secret as it is used, for example a refresh token that is valid only once, the runtime saves the new value back to the Vault it came from before the run continues. You never re-enter a rotated secret.

Local Vault

Create the local Vault once per repository:

oci vault local init

This writes .outcomeci/vault.enc, encrypted with AES-256-GCM, and adds it to .gitignore. The key is written separately to ~/.config/outcomeci/vault-keys/<vault-id>.key with owner-only permissions. Set OUTCOMECI_CONFIG_HOME to keep keys somewhere else, or OUTCOMECI_VAULT_KEY_FILE to point at one key file. Back up the key: nothing can recover a lost one.

Store a secret under the path the workflow references. Pipe it through stdin so it stays out of your shell history:

printf %s "$GITHUB_TOKEN" | oci vault local put github --value-stdin

Store a typed credential with --credential-type and its settings:

# A GitHub App installation, with its private key from a file.
oci vault local put github --credential-type app_installation \
  --app-id 123456 --installation-id 7890123 --value-stdin < app.private-key.pem
 
# A Slack app with token rotation.
printf '{"client_secret": "%s", "refresh_token": "%s"}' "$SECRET" "$REFRESH" |
  oci vault local put slack/app --credential-type oauth2 \
    --client-id "$CLIENT_ID" --grant-type refresh_token --secrets-json-stdin

put checks a typed credential's fields before storing it, and refuses one that is incomplete. Without a value option, it prompts for the value. List what is stored, with paths and timestamps but never values:

oci vault local list

oci workflow run resolves only the secrets the workflow references, and passes their values to the runner container on stdin. The key and the encrypted file never enter the container. oci workflow sync never uploads vault.enc.

Agent logins

The local Vault can also hold the agent login a container run uses, under agents/<provider>:

Agent Where oci workflow run finds its login
Codex ~/.codex/auth.json, or $CODEX_HOME/auth.json, from codex login
Claude Code CLAUDE_CODE_OAUTH_TOKEN, or the local Vault entry agents/claude
OpenCode OPENROUTER_API_KEY, or the local Vault entry agents/opencode

For Claude Code, create a long-lived token with claude setup-token and store it:

oci vault local put agents/claude

Workspace Vault

The workspace Vault holds the credentials that cloud runs use. A workflow can use a credential only after it is granted access, and secret values are write-only: they never return to the browser, the CLI, or an agent.

Store a credential

In the dashboard, open Vault and choose Add credential. Pick the credential type, enter its settings and secret values, and select the workflows that can use it.

From the CLI, pipe the value through stdin so it stays out of your shell history:

printf %s "$GITHUB_TOKEN" | oci vault put github \
  --workspace-id WORKSPACE_ID --value-stdin --workflow-id WORKFLOW_ID

oci vault put takes the same --credential-type and settings as oci vault local put. A typed credential also needs --provider, the service it belongs to:

printf '{"username": "%s", "password": "%s"}' "$USER" "$PASSWORD" |
  oci vault put service/login --workspace-id WORKSPACE_ID \
    --provider service --credential-type basic --secrets-json-stdin

List the workspace's entries, with their IDs and grants but never their values:

oci vault list --workspace-id WORKSPACE_ID

Grant access to a workflow

A workflow can read a credential only while it holds a grant. In the dashboard, choose Manage workflow access on the credential. A workflow's Settings page links to the same place under Vault access.

From the CLI, grant sets the complete list of workflows that can use the entry:

oci vault grant ENTRY_ID --workspace-id WORKSPACE_ID \
  --workflow-id WORKFLOW_ID --workflow-id OTHER_WORKFLOW_ID

Each cloud run receives a short-lived lease scoped to its workflow's grants. Cancelling the run revokes the lease immediately. When a provider rotates a leased secret during a run, the run saves the new value as a new version of the entry.

Rotate and revoke

Rotating stores a new version of the secret value. Choose Rotate secret on the credential, or:

printf %s "$NEW_TOKEN" | oci vault rotate ENTRY_ID \
  --workspace-id WORKSPACE_ID --value-stdin

The next run uses the new value. Rotation does not create a workflow version, so no workflow needs to be synced again.

Revoking disables an entry for every workflow. Choose Revoke credential, or:

oci vault revoke ENTRY_ID --workspace-id WORKSPACE_ID

Rotating a revoked entry makes it active again with the new value.

Let an agent request a credential

An agent connected to the OutcomeCI MCP server never asks for a secret in chat. Its create_credential_deposit tool creates a single-use link, valid for 15 minutes, where you enter the secret directly into the Vault. The credential is granted to the workflow the agent is configuring, and get_credential_deposit_status tells the agent when it has arrived.

Deposits support every credential type. A type with several secret fields, such as basic or oauth2 with a refresh token, shows one input per field.

Agents and workspace encryption

The Vault page also holds the workspace's Agents: the Codex, Claude Code, and OpenCode connections that managed runners execute with. They are not granted per workflow.

Workspace encryption is the versioned AES-256 key that protects managed artifacts. OutcomeCI manages it and never exports it; you can rotate it from the Vault page, and earlier versions remain available for data they already protect.