# How Kortix works The architecture — repo-as-project, the control plane, ephemeral sandboxes on branches, and change requests. Canonical page: https://kortix.com/docs/concepts A **project** is one git repository with a [manifest](/docs/reference/manifest) — `kortix.yaml` by default, or legacy `kortix.toml` — at its root. A **session** runs the agent in an isolated sandbox VM with the repo cloned onto a branch named after the session. The sandbox is disposable; the branch persists. Work reaches the default branch only via a merged **[change request](/docs/concepts/change-requests)**. ## The pieces - **[Projects](/docs/concepts/projects)** — a git repo (Kortix-managed or imported GitHub, pinned to one branch) plus its manifest. - **[Sessions](/docs/concepts/sessions)** — isolated sandbox VMs, each on its own branch. Many run at once without interfering. - **[Agents](/docs/concepts/agents)** — OpenCode, governed by the manifest's `agents:` map and implemented through `.kortix/opencode/`. - **[Change requests](/docs/concepts/change-requests)** — the git merge layer that lands a branch on the default branch. - **[Secrets](/docs/concepts/secrets)** — per-project encrypted values, given to sessions as environment variables. - **[Triggers](/docs/concepts/triggers)** — spawn a session on a schedule or from a webhook. - **[Connections](/docs/concepts/connections)** — let the agent call external tools, brokered server-side. - **[Channels](/docs/concepts/channels)** — drive a project from Slack. - **[Accounts](/docs/concepts/accounts)** — who owns projects and who can access them. ## When a session starts 1. The control plane (the Kortix API) inserts the session (`provisioning`) and cuts a branch from the default branch, named after the session id. 2. It resolves a content-addressed **snapshot** — the platform's default image (or your own Dockerfile, if the manifest declares a [`sandbox.templates`](/docs/reference/manifest) entry for it) plus the Kortix runtime layer, built by the configured sandbox provider. Unchanged projects reuse the cached image. 3. The sandbox provider boots the VM. The `kortix-agent` daemon clones the repo to `/workspace`, fetches git credentials just-in-time, and launches OpenCode. Status → `running`. 4. The agent works with [secrets](/docs/concepts/secrets) as env vars, committing and pushing to the session branch. 5. It opens a change request; you review and merge — the only path to the default branch. > **Git is the durable boundary** > Stop or idle hibernation preserves the same sandbox filesystem but drops memory > and processes; permanent deletion removes provider state. Only > committed-and-pushed branch work is provider-independent, and only a merged > change request makes it permanent on the default branch. ## Two config surfaces The manifest (`kortix.yaml`) + `.kortix/Dockerfile` are Kortix-owned; `.kortix/opencode/` is OpenCode-native runtime behavior. `agents:` is a name-keyed map that is governance only — `connectors`, `secrets`, `kortix_cli`, `skills`, which agent names are launchable, and what each may touch. Everything about how an agent behaves (prompt, mode, tools, permissions) lives in that agent's own `.kortix/opencode/agents/.md`, a stock OpenCode agent file. `default_agent` is required and must name a declared, enabled agent. See [Kortix vs OpenCode config](/docs/reference/config-boundary). Drive any of it from a terminal with the [CLI](/docs/reference/cli) — it controls the cloud, it doesn't run the agent locally. > **Legacy kortix.toml** > Existing projects may still run on `kortix_version: 1` (`kortix.toml`, > `[[agents]]` as an array of tables, no required `default_agent`). Both formats > are read by the platform; migrate in-app via "Upgrade to kortix.yaml." See > [Projects](/docs/concepts/projects).