kortix.yaml reference
Every kortix.yaml key with its type, default and validation rule, how to validate the file, and how to migrate from kortix.toml.
kortix.yaml is the manifest at the root of a project repository. It declares the agents and what each one may reach, plus the triggers, connectors, secret names, sandbox templates, harness settings and Apps of the project. This page is the key-by-key reference for people who write or review the file by hand. For the repository layout, see Projects.
# yaml-language-server: $schema=https://kortix.com/schema/kortix.v2.schema.json
kortix_version: 2
default_agent: kortix
env:
optional: [STRIPE_API_KEY]
agents:
kortix:
file: agents/kortix.md
connectors: all
secrets: all
skills: all
kortix_permissions: all
File and schema
The project’s manifest_path (default kortix.yaml) sets where Kortix looks. Change it with kortix projects set --manifest <path> or in Customize → Settings. Kortix reads the first file that exists of kortix.yaml, kortix.yml and kortix.toml at that path. A repository with both a YAML and a TOML file uses the YAML file.
Add the schema line at the top of the file. A YAML editor then validates and completes every key:
# yaml-language-server: $schema=https://kortix.com/schema/kortix.v2.schema.json
| Schema | URL |
|---|---|
| v1 | https://kortix.com/schema/kortix.v1.schema.json |
| v2 | https://kortix.com/schema/kortix.v2.schema.json |
| v3 | https://kortix.com/schema/kortix.v3.schema.json |
All versions (selects by kortix_version) |
https://kortix.com/schema/kortix.schema.json |
kortix schema prints the combined schema. kortix schema --version 2 prints one version, and --url prints its URL instead of the body.
Validate before you push:
kortix validate # ./kortix.yaml
kortix validate --file config/kortix.yaml --json
kortix validate --scopes # print every grantable kortix_permissions entry
| Flag | Effect |
|---|---|
--file <path> |
Validate this file instead of the project manifest in the current directory. |
--json |
Print a JSON report: valid, path, issues. |
--scopes |
Print the grantable kortix_permissions entries and their aliases, then exit. |
--no-dockerfile-lint |
Skip the checks on sandbox template Dockerfiles. |
kortix validate exits 0 with no errors, 1 with one or more errors, and 2 when the file is missing or unreadable. The same schema check runs in kortix ship and in the change request merge check. An error blocks the push or the merge. A warning never does. Kortix ignores unknown top-level keys.
The CLI also checks the files around the manifest. In a v2 manifest it reports these as errors:
- a missing agent
.md; - a missing tool module;
- a skill with no
skills/<name>/SKILL.md; - an agent
connectorsorconnectors_requiredslug thatconnectorsdoes not declare (kortix_slack,kortix_teams,kortix_emailandcomputeralways count as declared); - an agent
secretsor triggersecret_envname thatenvdoes not declare.
It lints each template Dockerfile. It warns when the files in Git exceed 512 MiB or when one file reaches 10 MiB.
Versions
kortix_version is required and picks the schema.
| Version | Format | Agent behavior | Notes |
|---|---|---|---|
1 |
kortix.toml or YAML |
In the agent .md |
Legacy. Reads and runs. See Migrate from kortix.toml. |
2 |
YAML only | In agents/<name>.md (prompt and frontmatter) |
kortix init and Create project write v2. The dashboard agent editor writes v2. An omitted grant means none. |
3 |
YAML only | Inline in kortix.yaml: prompt or prompt_file, model, mode, permission, description |
Rejects agents.<name>.file, agents.<name>.disable and the top-level opencode key. Write and edit it by hand. The dashboard agent editor edits only v2 manifests and shows a v3 agent read-only. |
A version above 3 is an error. A kortix.toml that declares version 2 or 3 is an error. No automatic migration from v2 to v3 exists.
A v3 agent carries its behavior in the manifest:
kortix_version: 3
default_agent: support
agents:
support:
description: Answers questions from the help desk.
mode: primary
prompt_file: prompts/support.md # or prompt: "You answer…", not both
permission:
bash: ask
secrets: [HELPDESK_TOKEN]
A v3 agent block also takes variant, temperature, top_p, steps, color, hidden and options. prompt_file is a repository-relative .md path. A v3 agent gets no project repository in /workspace unless it sets workspace: { project_repository: true }. See Workspace.
Top-level keys
| Key | Type | Default | Rule |
|---|---|---|---|
kortix_version |
integer | required | 1, 2 or 3. |
default_agent |
string | required (v2, v3) | Names a declared agent that is not enabled: false. Sessions and triggers that name no agent use it. |
agents |
map | required (v2, v3) | Agent name to agent block. At least one agent. See agents. |
imports |
list | none | Other YAML files whose collections merge into this one. See imports. |
project |
map | none | name, description. For people who read the repository. The project name in Kortix does not come from here. |
env |
map | none | required, optional: secret names. See env. |
runtime |
string | opencode |
opencode or pi. See harnesses, opencode and pi. |
harnesses |
map | none | pi.packages, opencode.plugins. |
opencode.config_dir |
path | harnesses/opencode, then .kortix/opencode |
OpenCode config directory. An error in v3. |
pi.config_dir |
path | harnesses/pi, then .kortix/pi |
pi config directory. |
tools |
map | all five Kortix tools | Tool name to kortix:<name> or a module path. See Tools. |
commands |
map | none | v3 only. Slash command name to its template. See commands (v3). |
sandbox |
map | platform image | templates, default. See sandbox. |
triggers |
list | none | Schedules, webhooks, monitors, app events. |
connectors |
list | none | Apps, APIs and MCP servers an agent can call. |
apps |
map | none | App slug to App configuration. |
policy.default_mode |
string | allow_all |
allow_all or risk. See policy and policies. |
policies |
list | none | Project-wide connector rules. |
approvals.ttl |
string | 24h |
How long a held connector call waits for a decision. See policy and policies. |
A v2 or v3 manifest rejects channels, sandboxes, and the image keys image, dockerfile, slug, cpu, memory, disk, entrypoint, context, context_dir and gpu set directly on sandbox.
imports
imports splits one manifest across several YAML files. The platform resolves every import and works on the merged document: validation, triggers, agent grants and connector sync all see one manifest.
# kortix.yaml
kortix_version: 2
default_agent: kortix
imports:
- triggers/ # every .yaml and .yml file below this directory
- agents.yaml # one file
agents:
kortix:
connectors: all
# triggers/reports/weekly.yaml
triggers:
- slug: weekly-report
type: cron
agent: kortix
cron: "0 0 15 * * 0"
prompt: Build the weekly report.
- A path is relative to the repository root.
.., absolute paths and globs are errors. A path that matches no.yamlor.ymlfile is an error. - A directory import takes every
.yamland.ymlfile below it, at any depth, sorted by path. - An imported file declares
triggers,connectors,agents,apps,toolsandimports.kortix_version,default_agent,runtime,project,env,opencode,sandbox,policyandpoliciesin an imported file are errors. Kortix drops any other key in an imported file, for exampleharnesses. - Imports nest up to 8 levels and 200 files. A cycle is an error. A file reached twice merges once.
- One name, one file. A trigger slug, connector slug, agent name, App slug or tool name declared in two files is an error that names both files.
- Entries merge in order: the importing file first, then its imports as listed.
A broken import fails the whole manifest:
imports: triggers "weekly-report" is declared in both triggers/a.yaml and triggers/b.yaml — one name, one file
agents
agents maps an agent name to its governance block: whether it may run and what it may reach. In v2 the behavior (prompt, model, mode, permissions) lives in the agent .md. What each grant means is on Agents & permissions.
An agent name matches [a-z0-9][a-z0-9_-]{0,127}.
| Field | Default | Rule |
|---|---|---|
file |
agents/<name>.md |
v2 only. The agent .md, relative to the repository root. |
enabled |
true |
Boolean. false: Kortix does not start the agent. |
connectors |
none | Grant set of connector slugs. |
secrets |
none | Grant set of secret names the agent receives in its sandbox. |
skills |
the .md decides |
Grant set of skill names. Set, it overrides permission.skill in the agent .md. Omitted, the .md rule applies. |
apps |
none | Grant set of App slugs the agent may open when the App access is restricted or private. |
commands |
none | v3 only. Grant set of command names from the top-level commands. The agent runs only these. |
kortix_permissions |
none | Grant set of project permissions. Capped by the agent’s ceiling role, never by the role of the person who starts the session. See What an agent may do. |
tools |
all | all, none, a list of tool names, or { exclude: [names] }. The map form { bash: false } also works. |
sandbox |
project default | A sandbox template slug, or default. |
workspace.project_repository |
v2 true, v3 false |
Boolean. true: new sessions of this agent check out the project repository. false: no project repository in /workspace and no repository access. See Workspace. |
harnesses |
none | Per-agent pi.packages, pi.exclude, opencode.plugins, opencode.exclude. |
network_egress |
none | Declaration only. Accepts { version: 1, default: deny, rules: [] } and warns that egress is not enforced. |
A grant set is all (or *), none (or ""), or a list of names. A list that contains * means all.
file rules. The path ends in .md. It does not start with / or -. It has no ., .. or empty segment. Each segment uses letters, digits, _, -, . and spaces. With file omitted, Kortix reads agents/<name>.md, then .kortix/opencode/agents/<name>.md, or <opencode.config_dir>/agents/<name>.md when that key is set. The API refuses a request that changes file (400, invalid_config). To move an agent, move its .md and edit file in the same commit.
kortix_permissions rules. Each entry is a project permission. kortix validate --scopes prints the list. Account permissions such as member.*, billing.* and project.create are errors. The alias entries project.cr.open, project.cr.merge, project.customize.read and project.customize.write resolve to their current permissions and warn. The no-op entries project.session.exec, project.gateway.routing.edit, project.schedule.*, project.webhook.* and channel.* are errors in v2 and v3.
Errors in a v2 agent block. env (use secrets), and every behavior key: description, model, mode, variant, temperature, top_p, options, color, steps, hidden, prompt, prompt_file, permission, disable, opencode. The message points at the agent .md.
Deprecated fields.
connectors_required: parsed, must be a subset ofconnectors, not read at session start.connectors_personal: alias ofconnectors_required. Warns.kortix_cli: alias ofkortix_permissions. Warns. A different value fromkortix_permissionsis an error.repository_access: alias ofworkspace.project_repository. A different value is an error.workspaceas a string:branchmeansproject_repository: true, andruntimemeansfalse.readwithout an explicit boolean refuses session start (409,WORKSPACE_MODE_UNAVAILABLE).
Agent workspace
workspace sets what new sessions of the agent get in /workspace. It has one key today:
agents:
engineer:
workspace:
project_repository: true # v3 default false, v2 default true
project_repository: true: the session checks out the project repository on its own session branch. Set it on every agent that reads or edits the repository, runs scripts from it, or opens change requests.project_repository: false: the session gets no checkout, no git variables, and no repository API access (403). Its project tools still load: the sandbox receives only the modules of the tools the agent may call.- Omitted:
falseinkortix_version: 3,truein versions 1 and 2. A v2 project keeps its behavior. The starter,kortix init, Create project, the agent editor andkortix migratewrite the value explicitly, so a new or migrated agent states it. - A running session keeps the value it started with. A change applies to new sessions.
- Any other key under
workspaceis an error. A typo does not fall back to the default: Kortix withholds the repository from that agent until the block is fixed.
commands (v3)
In v3, a slash command is declared in kortix.yaml. The project registers it under commands, and each agent that may run it enables it by name. v3 reads no harness folder, so harnesses/opencode/commands/ and harnesses/pi/prompts/ are not read.
kortix_version: 3
default_agent: support
commands:
triage:
description: Sort a ticket by urgency.
prompt: "Read ticket $1 and reply with its urgency: low, normal or high."
release-notes:
prompt_file: prompts/release-notes.md
agents:
support:
prompt_file: prompts/support.md
commands: [triage, release-notes] # or: all
| Key | Rule |
|---|---|
| name | Lowercase letters and digits, in words joined by -. At most 64 characters. |
prompt |
The template, as a non-empty string. |
prompt_file |
A repository path to a .md file. Kortix uses the whole file as the template; it does not remove a frontmatter block. |
description |
Optional. Shown in the / menu. |
- Set exactly one of
promptorprompt_file. Any other key is an error. Anagentkey points you toagents.<name>.commands. $ARGUMENTStakes everything typed after the command.$1,$2and so on take the words in order, and the highest one takes the rest. A template with neither gets the arguments appended.- An agent runs only the commands its
commandsgrant names:all,noneor a list. Omitted means none. A command that no agent enables is left out. - Running a command on an agent that does not enable it answers
403with{"error": "command_not_enabled", "command": "<name>", "agent": "<agent>"}. - Commands come from the manifest on the project’s default branch.
env
env declares the secret names the project expects. Values live in Secrets, never in the manifest.
env:
required: [DATABASE_URL]
optional: [STRIPE_API_KEY]
| Field | Type | Effect |
|---|---|---|
required |
list of names | The dashboard and kortix ship prompt for a missing value. A missing value never blocks a session. |
optional |
list of names | Offered by the same prompts. |
A name matches ^[A-Z_][A-Z0-9_]*$. Any other key under env warns. kortix ship --no-env skips the prompts. In a v2 manifest, kortix validate requires every name in an agent secrets list and every trigger secret_env to appear here.
The Secrets store refuses a name longer than 64 characters or one that starts with KORTIX_. Such a name validates here but never gets a value. Only the Teams connection sets MS_TEAMS_* names, and only ChatGPT subscription onboarding sets CODEX_AUTH_JSON.
sandbox
sandbox.templates lists named sandbox images. With no template, every session boots the platform image. What Kortix adds on top of an image is on Sandboxes & previews.
sandbox:
default: ml
templates:
- slug: ml
name: ML development
dockerfile: Dockerfile.ml
cpu: 4
memory: 16
disk: 50
| Field | Default | Rule |
|---|---|---|
slug |
required | Unique. default is reserved. A slug over 64 characters warns: the runtime drops it. |
name |
slug | Label in the template picker. |
dockerfile |
— | Repository-relative path. Set exactly one of dockerfile and image. |
image |
— | Public image with a tag or a digest. No tag is an error. :latest warns. |
entrypoint |
the image’s | Overrides the container entrypoint. |
cpu |
provider default | vCPU cores, 1–32. |
memory |
provider default | GiB, 1–128. |
disk |
provider default | GiB, 1–500. |
container_runtime |
false |
true runs Docker in the sandbox. See Hardware and Docker. |
A hardware value is a positive integer. A value below the minimum is an error. A value above the maximum warns and is clamped. gpu warns and does nothing.
sandbox.default names a template in this manifest, or default for the platform image. A session picks its template in this order:
- The
sandbox_slugof the session request. - The
sandboxof the agent the session starts with. sandbox.default.- The platform image.
A template change applies to the next session, not to a running one.
harnesses, opencode and pi
A session runs one of two harnesses. The project’s pi_harness feature flag (on by default) or runtime: pi selects pi. runtime decides only while the flag is off. See Harnesses: OpenCode and pi.
| Key | Default | Holds |
|---|---|---|
opencode.config_dir |
harnesses/opencode, then .kortix/opencode |
Files only OpenCode reads: opencode.jsonc, plugins/, tools/. Without the key, Kortix uses the first directory that holds an opencode.json or opencode.jsonc. |
pi.config_dir |
harnesses/pi, then .kortix/pi |
Files only pi reads: settings.json, extensions/, skills/, prompts/. |
harnesses.pi.packages lists the pi packages every pi session loads:
runtime: pi
harnesses:
pi:
packages:
- npm:pi-web-access@0.30.0 # exact version
- source: npm:pi-mcp-adapter@2.36.0
extensions: ["extensions/*.ts"] # load only these files
skills: [] # load none of its skills
- ./extensions/audit.ts # one extension file in this repository
agents:
reviewer:
harnesses:
pi:
exclude: [pi-web-access] # package name, no version
packages: [npm:@my-org/pi-review@1.2.0] # this agent only
- An entry is
npm:<name>@<x.y.z>(an exact version), a./path that stays inside the repository, or{ source, extensions, skills, prompts, themes }. An omitted filter loads everything.[]loads nothing. - A
./path to a.tsor.jsfile is one extension. It can live anywhere in the repository, so a project needs noharnesses/pi/folder. It loads from the session’s config release, and a change to a file in its folder restarts the pi runtime. - A session reads the package list when it starts. A running session keeps its list until it restarts.
- At most 20 entries. A range, a missing version and a git source are errors.
- An agent loads the top-level list, minus its
exclude, plus its ownpackages.excludeexists only on an agent. - The change request merge builds the packages. A session never runs
npm install. A session that starts before the build exists boots without the project packages.
harnesses.pi.codemode sets pi’s codemode tool. The model writes one JavaScript script that calls the other tools (await tools.bash({ command })), and only the script’s output reaches the model, so one call can batch, chain and filter several tool calls.
harnesses:
pi:
codemode: on # on (default) | only | off
agents:
reviewer:
harnesses:
pi:
codemode: off # this agent only
| Value | The model gets |
|---|---|
on (default) |
every tool, plus codemode |
only |
codemode alone; every other tool runs from a script |
off |
no codemode |
- An agent’s value replaces the top-level one. Read when the session starts.
- A script’s calls pass the same permission policy as direct calls: a
denyrule blocks them, and anaskrule pauses the script until someone answers. - A script cannot start a subagent (
task) or ask a question (question). The model calls those directly. - Scripts run in a sandboxed JavaScript VM (QuickJS) with no file system, network or timers. They reach the box only through the tools.
harnesses.opencode.plugins (v2) lists the files in <opencode config dir>/plugins/ that load, for example audit.ts. Without the key, every plugin file loads. An agent adds its own with agents.<name>.harnesses.opencode.plugins and drops top-level ones with exclude. A listed file that does not exist fails the config build.
tools
tools maps a tool name to kortix:<name> (a Kortix tool) or a repository-relative .ts or .js module. With no tools key, sessions get all five Kortix tools. With a tools key, sessions get only the tools it lists. The rules, errors and module contract are on Tools.
triggers, connectors, apps
A trigger or connector entry is identified by its slug, and an App by its key under apps. Each one matches [a-z0-9][a-z0-9_-]{0,127} and is unique in its collection.
triggers. Full reference: Triggers & reminders.
| Field | Rule |
|---|---|
slug, type, prompt |
Required. type is cron, webhook, monitor or event. prompt is a non-empty template. |
name, agent, enabled, model |
Optional. agent defaults to default_agent and names a declared agent. model is provider/model. |
session_mode |
fresh (default; reuse for a monitor), reuse, pinned (needs session_id) or keyed (needs session_key). |
filter |
Optional. Map of dotted path to expected string. A fire runs only when every path matches. See Triggers & reminders. |
cron type |
cron (6 fields, seconds first) or run_at (ISO-8601), and timezone (IANA, default UTC). Fires at most once every 60 seconds. |
webhook type |
secret_env: the name of the signing secret. |
monitor type |
run (one command line, at most 1,024 characters) and mode (poll or stream). poll needs interval, at least 30s. Optional expect_event_within, at least 5m. Needs the monitors feature flag, off by default. |
event type |
connector and event. Optional account, source, config. Needs the event_triggers feature flag, off by default. |
A monitor trigger rejects cron, run_at, timezone and secret_env. An event trigger rejects every cron, webhook and monitor field. connector, event, account, source and config are errors on any type except event.
connectors. Full reference: Connectors. Credentials live in the platform, never in the manifest.
| Field | Rule |
|---|---|
slug, provider |
Required. provider is composio, pipedream, mcp, openapi, postman, graphql, http or channel. |
| Provider field | composio and pipedream: app. mcp: url, optional transport (http or sse). openapi and postman: spec. graphql: endpoint. http: base_url. channel: platform (slack, teams or email). |
name, enabled |
Optional. |
authorization_strategy |
project (default) or user. |
credential |
shared only. |
sensitive |
true: an unmatched call needs approval. |
auth |
type: bearer, basic, custom, api_key, oauth1, hmac, aws_sigv4, mtls or none. in: header (default), query or cookie. name for custom and api_key. oauth1 only on openapi, postman and http. |
headers |
Static headers, at most 32. Never a credential. |
policies |
List of { match, action }. match is a glob over tool names. action is always_run, require_approval or block. |
computer is not a valid provider: connecting a computer creates that connector. The slugs kortix_slack, kortix_teams, kortix_email and computer are reserved for the built-in connectors. Connecting a channel writes its channel connector for you: see Slack, Teams & email.
apps. Full reference: Configure in kortix.yaml.
apps maps an App slug to its configuration. The keys are path, type (static, bundle, dockerfile, oci_image), image, dockerfile, command, port, root, output_dir, install_command, build_command, spa, readiness_path, idle_timeout_seconds, always_on, uses, resources, env, secrets, processes, http_service, services, mounts, vm, deploy and labels. Any other key is an error.
policy and policies
policy.default_mode decides a connector call that no rule matches:
allow_all(default): the call runs.risk: a read runs; a write or destructive call needs approval.
policies holds project-wide rules. A rule matches a fully qualified tool path, <connector-slug>.<tool>, with * globs. Kortix checks project rules first, then the connector’s own policies. The first matching rule wins. conditions limits a rule to calls whose arguments match:
policy:
default_mode: risk
policies:
- match: gmail.send_email
action: require_approval
conditions:
- arg: to
match: "*@example.com" # a glob, or /regex/ in slashes
- match: gmail.send_email
action: block
approvals.ttl sets how long a held call waits for a decision: an integer and m, h or d, such as 30m, 4h or 7d. Kortix clamps it to 5 minutes through 30 days. Without it, or with any other value, a held call waits 24 hours. A held call that nobody decides in time expires.
approvals:
ttl: 4h
All conditions of a rule must hold. negate: true inverts one condition. How approvals reach a person is on Policies and approvals.
kortix validate does not check policy or policies. When Kortix syncs connectors, it skips a malformed rule and reports it as a connector sync error. Check the rules in the connector settings after the merge.
Dashboard and CLI edits
The dashboard and the API edit the manifest in the project repository. kortix triggers add|rm|enable|disable edit the local file, and kortix ship pushes it. With --apply, they change the project now. In each case:
- An edit to an entry rewrites only the file that declares it, the root or an imported file. A new entry goes to the root file.
- Keep
kortix_versionas the first key. - Write trigger fields in the order the editor writes them:
slug,name,type,agent,model,enabled, the session fields,filter, the type fields, thenprompt.
A manifest edit takes effect when it reaches the default branch. A dashboard, API, SDK or --apply edit commits there at once. Any other edit takes effect when kortix ship, a direct push or a change request merge brings it there. See How a change takes effect.
Migrate from kortix.toml
Kortix reads and runs a v1 kortix.toml. To move it to v2, open Customize → Settings → Upgrades and run Migrate manifest to v2 (kortix.yaml), or run:
kortix projects upgrade [<id>]
Both start a session with the project’s default agent. The agent rewrites the manifest, runs kortix validate and opens a change request. The session never merges it. Starting it needs permission to start sessions in the project.
To migrate by hand:
- Turn the
[[agents]]array into theagents:map, keyed by name, and adddefault_agent. - Rename each agent
envtosecrets. v1 gives an agent every secret whenenvis omitted. v2 gives none, so list every secret and connector each agent needs. - Delete
[[channels]]and reconnect each channel from the dashboard. - Write each
[[sandbox.templates]],[[triggers]]and[[connectors]]table as a YAML list entry with the same fields. - Remove
credential = "per_user",agent_scope, and the no-opkortix_permissionsentries. All three are errors in v2. - Run
kortix validate --file kortix.yaml, then deletekortix.toml.