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
| Command | What it does |
|---|---|
kortix secrets ls | List 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 IDENTIFIER | Remove 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.
| Scope | Injected into the sandbox? | Notes |
|---|---|---|
runtime | Yes, as an environment variable | Supports both shared and personal-override values. |
connector | No | Resolved server-side, inside the connector executor. GET /secrets does not list connector-scope secrets. |
Format rules for the two names:
| Name | Format |
|---|---|
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}.
| Method | Path | Description |
|---|---|---|
| GET | /secrets | List secrets. Scoped to the caller's grant if the caller is a scoped agent token. |
| POST | /secrets | Create or update the shared value. Body: {name, identifier?, value}. |
| DELETE | /secrets/{name} | Delete the shared value. Personal overrides stay in place. |
| PUT | /secrets/{name}/personal | Set or turn on the caller's personal override. |
| DELETE | /secrets/{name}/personal | Remove 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:
- Kortix builds a new environment snapshot, using the running agent's
secretsgrant. - The sandbox writes the snapshot to the live agent environment. New tool calls pick up the change right away.
- If the changed secret is an LLM provider credential, Kortix also
restarts the sandbox's
opencodeprocess. 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.