Secrets

How Kortix stores, injects, and rotates encrypted project credentials.

A secret is a per-project credential — an API key, token, or connection string — that a session needs but that must not live in the repository. Kortix stores secrets on the project, never on the account, and encrypts every value with AES-256-GCM using a key derived per project.

Identifier and name

Each secret has two names. The identifier is the handle you use in the CLI and in an agent's secrets grant. The name is the uppercase environment-variable key injected into the sandbox, for example STRIPE_API_KEY. In most projects the identifier and the name match. They differ only when a project holds several candidate values for one name — for example, a primary and a backup Google Maps key can both resolve to GOOGLE_MAPS_API_KEY.

Shared and personal scope

A secret is either shared or a personal override:

  • Shared — the project-wide value. Every project member with read access sees every shared secret. Kortix has no per-member sharing control for a single secret.
  • Personal override — your own value for one name, used instead of the shared row for sessions you start. Today Kortix uses this only for one OAuth login credential.

Kortix never lets an LLM provider key, for example ANTHROPIC_API_KEY, become a personal override. The model gateway always reads the shared row.

An agent only receives the secrets its manifest grants. In kortix.yaml, the agent's secrets field lists identifiers, or is empty to deny all — the default when you omit it. See Agents.

A secret with scope connector is different: it authenticates a connector, not a session. Kortix resolves it server-side and never injects it into a sandbox.

Add a secret

Set the value from the CLI

kortix secrets set STRIPE_API_KEY=sk_live_...

Kortix saves it as a shared secret for the project. To store more than one value under the same name, add --identifier <id>. Names can't start with KORTIX_ — Kortix reserves that prefix for platform values.

Or use the dashboard

Open the project's Environment variables page, enter the key and value, and save. Kortix encrypts the value immediately.

Grant it to an agent

Add the identifier to the agent's secrets list in kortix.yaml. A session only receives the secrets its agent is granted.

List your secrets

Run kortix secrets ls to see which secrets a project declares and which ones have a value set.

Rotate a secret

Set a new value

Run the same command with the new value, or set it again on the project's Environment variables page:

kortix secrets set STRIPE_API_KEY=sk_live_new...

Kortix pushes it to running sessions

Kortix pushes the new value to every sandbox with an active session for the project, on a best-effort basis. If the key is a model or gateway credential, Kortix also restarts the agent process so it picks up the new value.

Remove a secret

Run kortix secrets unset STRIPE_API_KEY (or unset <identifier>), or delete it from the project's Environment variables page.

Removal is immediate, propagation is not

Kortix deletes a shared secret right away. Push to already-running sandboxes is best-effort, the same as rotation.

Share a value without seeing it

Run kortix secrets request STRIPE_API_KEY --scope runtime --expires 60 to create a link. Anyone with the link can enter the value. You never see it.

CLI commands

CommandWhat it does
kortix secrets lsList secrets declared and set for the project
kortix secrets set KEY=VALUE [--identifier <id>]Create or update a secret. KEY=- reads the value from stdin
kortix secrets unset IDENTIFIERRemove a secret
kortix secrets request NAME [--scope runtime|connector] [--expires <min>]Create a link so someone else can enter a value
kortix env push --from <path>Upload a .env file as secrets
kortix env pull [--out <path>] [--force]Export secret names, not values, to a .env file

Scopes reference

Every secret has a scope. Default: runtime.

ScopeInjected into the sandbox?Notes
runtimeYes, as an environment variableSupports both shared and personal-override values.
connectorNoResolved server-side, inside the connector executor. GET /secrets does not list connector-scope secrets.

Format rules for the two names:

NameFormat
identifier^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$
name^[A-Z_][A-Z0-9_]{0,63}$

Reading or writing a secret needs the project.secret.read or project.secret.write permission. A custom project role can withhold either permission, even from a manager.

REST routes

All routes sit under /v1/projects/{projectId}.

MethodPathDescription
GET/secretsList secrets. Scoped to the caller's grant if the caller is a scoped agent token.
POST/secretsCreate or update the shared value. Body: {name, identifier?, value}.
DELETE/secrets/{name}Delete the shared value. Personal overrides stay in place.
PUT/secrets/{name}/personalSet or turn on the caller's personal override.
DELETE/secrets/{name}/personalRemove the caller's personal override.

POST /secrets rejects names that start with KORTIX_. It returns 409 if the identifier already exists with a different name. It rejects the exact name CODEX_AUTH_JSON with 400 — Kortix manages that secret through ChatGPT subscription onboarding.

Rotation and propagation

A secret write does not wait for a session restart. Kortix pushes the change to every active sandbox in the project:

  1. Kortix builds a new environment snapshot, using the running agent's secrets grant.
  2. The sandbox writes the snapshot to the live agent environment. New tool calls pick up the change right away.
  3. If the changed secret is an LLM provider credential, Kortix also restarts the sandbox's opencode process. The next prompt in the session uses the new credential.

This push is best-effort. The API call that changes the secret returns before the push finishes. A failed push is only logged, not retried. A sandbox with a failed push keeps the old value until the next successful push, or until the session restarts.

Secrets – Kortix Docs