# KiCI Getting started This bundle covers: Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow. ## User guide Source: https://docs.kici.dev/user/ KiCI runs your CI/CD on infrastructure you control -- your own orchestrator and agents clone the code and run every job, while the hosted platform relays webhooks and renders the dashboard without ever seeing your source or secrets. You author workflows in real, typed TypeScript and run them locally before you push, so the pipeline you test on your laptop is the pipeline that runs in production. This section is for the people writing those workflows. ## Start here 1. **[Green run in ~5 minutes](https://docs.kici.dev/user/quickstart/)** -- stand up an orchestrator and agent (Docker / Podman or bare metal) and watch your first workflow go green. 2. **[Getting started](https://docs.kici.dev/user/getting-started/)** -- install the SDK and compiler, write your first workflow, compile it to a lock file, and test it locally with simulated events. 3. **[Why KiCI](https://docs.kici.dev/user/why-kici/)** -- the case for running CI on your own infrastructure with typed TypeScript workflows. 4. **[GitHub App provider](https://docs.kici.dev/user/providers/github/)** -- connect your first source and route real pull-request and push events. ## What's in the user guide - **Authoring** -- the [SDK reference](https://docs.kici.dev/user/sdk-reference/) covers every factory function, trigger, rule, and matrix option; [workflow patterns](https://docs.kici.dev/user/workflow-patterns/) show monorepo builds, conditional jobs, dynamic matrices, and scheduling; [how your workflow code executes](https://docs.kici.dev/user/execution-model/) maps compile, orchestrator, and agent time. - **Running and testing** -- the [CLI reference](https://docs.kici.dev/user/cli-reference/) documents every command; the [testing guide](https://docs.kici.dev/user/testing-guide/) covers `kici run remote`, fixtures, and overlay mode; the [dashboard](https://docs.kici.dev/user/dashboard/) is the web UI for watching runs. - **Wiring sources** -- the [GitHub App](https://docs.kici.dev/user/providers/github/) and [universal-git](https://docs.kici.dev/user/providers/universal-git/) providers connect your forge; [global workflows](https://docs.kici.dev/user/global-workflows/) run cross-repo. - **Configuration and secrets** -- [contexts](https://docs.kici.dev/user/contexts/), [secrets](https://docs.kici.dev/user/secrets/), [dynamic values](https://docs.kici.dev/user/dynamic-values/), [concurrency groups](https://docs.kici.dev/user/concurrency/), [lifecycle hooks](https://docs.kici.dev/user/hooks/), and [environment variables](https://docs.kici.dev/user/env-vars/). The left sidebar is the full index for the user guide -- every page in curated reading order. --- ## How your workflow code executes Source: https://docs.kici.dev/user/execution-model/ Your workflow is plain TypeScript, but different parts of it run at three distinct moments, on three different machines. Knowing which part runs where is the difference between a workflow that behaves and one that surprises you. This page is the map. ## The three phases | Phase | Where it runs | What runs | When | | ---------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | **Compile** | Your dev machine or CI (`kici compile`) | Load your workflow modules, validate the DAG, assign step IDs, emit `kici.lock.json` | Before anything is pushed | | **Orchestrator** | Your orchestrator (no repo clone) | Match triggers against the lock and dispatch jobs; it never evaluates workflow code | On each incoming event | | **Agent** | An ephemeral agent (fresh clone per job) | Load the workflow module, evaluate job and step rules, run step bodies and hooks, run dynamic-value init steps and `dynamicJob` generators (both forms) | After dispatch | The lock file is the seam. Everything left of it is decided once at compile time and frozen into JSON; everything right of it reads that JSON. See [the lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/) and [the three-tier architecture](https://docs.kici.dev/architecture/overview/) for the wider picture. ## Compile time `kici compile` loads your `.kici/workflows/*.ts`, validates dependencies (no cycles, no missing `needs`), assigns compile-time step IDs (unnamed steps become `step-1`, `step-2`, …), and writes `kici.lock.json`. The compiler runs your module's **top-level code** to build the workflow object — but that execution's side effects and in-memory state do not travel. Only the resulting workflow structure lands in the lock. Anything your top-level code computes that isn't part of the returned workflow object doesn't exist past this point. See [compile the workflow](https://docs.kici.dev/user/getting-started/#compile-the-workflow) for the command in context. ## What serializes into the lock file The lock is portable JSON. It carries: - Workflow and trigger metadata. - The job and step DAG, with compile-time step IDs. - Static values, verbatim. - Markers noting which fields are dynamic, so the orchestrator knows to resolve them on the agent's init step. It does **not** carry: - Your module's runtime state or module-level variables. - Closures over those variables. - Live instances of modules you imported. - Anything computed at top level that isn't part of the returned workflow object. The consequence is blunt: if a value isn't in the lock, the orchestrator can't see it — it has no copy of your repository. ## Orchestrator time On each event the orchestrator matches triggers using only the lock — it never clones your repository and never evaluates workflow code. Dynamic `context`, `env`, and `concurrencyGroup` functions are not run here: the orchestrator dispatches a short init step to an agent to resolve them (see below). Trigger matching can query the **contents** of individual source files, not just their paths: a `pr()`, `push()`, or `tag()` trigger with a [`requires`](https://docs.kici.dev/user/sdk/triggers/#content-requirements-requires) filter is matched by reading the named files at the event's commit and evaluating the filter as declarative data — still with no repository clone and no workflow code executed. A `requires` regex is checked for catastrophic (ReDoS) shapes at `kici compile` time and rejected there, so only safe patterns reach the orchestrator. The orchestrator also does **not** run `dynamicJob` generator bodies itself: for the event-only (function) form it dispatches a dedicated dynamic-evaluation job to an agent at event time; the generator function then runs agent-side (see below). A workflow-level `filter` predicate is the same shape: the orchestrator sees only the lock's `hasFilter` flag, never the predicate, so it dispatches an evaluation job and lets an agent decide whether the workflow applies. Prefer the declarative filters where they answer the question — `commitMessage` on the trigger and `requires` over source files cost no evaluation job at all. See [dynamic values](https://docs.kici.dev/user/dynamic-values/) for how dynamic `context`, `env`, and `concurrencyGroup` functions resolve. ## Agent time After dispatch, each job runs in its own ephemeral agent sandbox: a shallow clone at the dispatch ref (or a source-tarball extract for non-build jobs), then the workflow module is loaded fresh — TypeScript is transformed on import. On the agent, in order: 1. **Job-level rules** are evaluated. By this point the agent has already spawned and the source has already been restored, so a job that its rules skip has **still** paid for that spawn and clone; only its steps are avoided. 2. **Step-level rules**, then each step's `run()` body and its hooks. 3. **Dynamic values** (`context`, `env`, `concurrencyGroup` functions) are resolved here, via a short `__init__` job that runs the function before the real job runs; this shows in the run timeline as an `Init:` entry. 4. **`dynamicJob` generators run here — both forms.** The event-only (function) form runs in a dedicated evaluation job dispatched at event time; the result-aware (options) form is deferred until its declared `needs` complete, then run with the upstream outputs frozen as `ctx.needs`. 5. **A workflow-level `filter` predicate runs here too**, before the jobs it gates. A global workflow evaluates it once per (event × workflow repo), before any run row exists, so a `false` verdict leaves no run at all. A same-repo workflow evaluates it once per job that reaches dispatch and once per job generator, after the run row exists, so a `false` verdict leaves a run whose only entries are the evaluation jobs. Keep the predicate cheap, pure, and side-effect free — a ten-job workflow calls it ten times for one event. See [narrowing with a filter](https://docs.kici.dev/user/global-workflows/#narrowing-with-a-filter). See [job execution](https://docs.kici.dev/architecture/execution/job-execution/) and [hooks and rules](https://docs.kici.dev/user/hooks/) for the details. ## How a reused agent stays clean between jobs An ephemeral agent is discarded after one job, so residue never matters. A **reused agent** — a long-lived process on a shared host (the bare-metal profile) — serves many jobs in turn. Between them, the agent runs a supervisor-owned cleanup phase so one job's leftovers never reach the next: 1. **It reaps the finished job's process tree.** A step may background a daemon that outlives the job. The agent runs each job's process in its own process group and signals the whole group when the job ends, so a stray daemon does not survive into the next job. Set `KICI_AGENT_ORPHAN_CLEANUP=false` to keep only the runner and leave a backgrounded process alive on purpose. 2. **It re-runs declared cleanup after a hard kill.** A job's `cleanup` / `onFailure` hooks normally run in the job process. If that process is killed hard (out of memory, forced stop), the agent re-runs the declared cleanup against the preserved work directory. 3. **It deletes the work directory.** 4. **It runs an optional operator reset command.** Set `KICI_AGENT_BETWEEN_JOBS_RESET_COMMAND` to a host-reset command (for example, pruning a container cache). It runs after the reap and work-directory deletion. A failure never fails the finished job. This phase is the primary cross-job cleanup. The agent's startup temp-directory sweep stays as a backstop for anything a between-jobs phase missed. See [agent configuration](https://docs.kici.dev/operator/agent/configuration/) for the full env-var reference. ## What re-evaluates where | Construct | Runs on | When | | ---------------------------- | ---------------- | ---------------------------------------------------- | | Static value | Compile → lock | Never re-evaluated | | Dynamic value | Agent init step | Per event | | Job-level rules | Agent | After clone | | Step-level rules | Agent | Per step | | `dynamicJob` (function form) | Agent (eval job) | Dispatched at event time | | `dynamicJob` (options form) | Agent | Deferred until `needs` complete | | Workflow `filter` predicate | Agent (eval job) | Per event (global) / per job + generator (same-repo) | | Step / job body + hooks | Agent | Per job | **Determinism note.** `ctx.event` and `ctx.needs` are frozen snapshots — captured once and replayed unchanged on any re-evaluation. A generator that derives its output from them is stable across re-evaluations; one that reads the wall clock (`Date.now()`) or a random source (`Math.random()`) is not. ## OutputProxy: how outputs flow `step(...).result` and `job(...).result` return an `OutputProxy` — a lazy proxy that, at the type level, mirrors the shape of the step or job's declared outputs so that reading `result.foo` is type-checked, and at runtime defers each property read to a shared outputs map populated as the run progresses. ```typescript import { workflow, job, step, z } from '@kici-dev/sdk'; const build = job('build', { runsOn: 'default', steps: [ step('compile', { outputs: { artifact: z.string() }, run: async () => ({ artifact: 'app.tar.gz' }), }), step('publish', { // `compile.result.artifact` is typed from the `outputs` schema above. run: async ({ steps }) => { await Promise.resolve(steps.compile.result.artifact); }, }), ], }); export default workflow('build-and-publish', { jobs: [build] }); ``` Outputs are typed across the job boundary too: reading `jobRef.result.…` or `ctx.jobOutputs(jobRef)` on a **job reference** threads the upstream job's inferred output shape through — from any job, in a step body or a `run:` shorthand — so a typo on an output field or a renamed step is a compile error. Typed `ctx.needs.jobRef.result.…` additionally works in a `run:` shorthand job (where the run function's `ctx` derives from the enclosing job's `needs` tuple). Name your steps and use the options form (`step('name', { run })`) to give a job a typed output shape, and pass references rather than string names — string-form `needs` stay loosely typed. See [output chaining](https://docs.kici.dev/user/sdk/core/#output-chaining) for the authoring rules. ## Common footguns | Symptom | Why | Fix | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | | A top-level `let seen = 0` (or a cache filled in job A) is empty in job B | Each job loads the workflow module fresh in its own agent process after its own clone — there is no shared memory between jobs | Pass data through step/job **outputs** (`OutputProxy` / `needs`), not module variables | | Fan-out job identities shift between re-evaluations | `ctx.event` / `ctx.needs` are frozen and replayed, but `Date.now()` / `Math.random()` are not | Derive job identity only from the frozen event/needs snapshot | | A rule-skipped job still spawned an agent and cloned | Job-level rules evaluate agent-side, after dispatch and clone — not on the orchestrator | This is by design: rules can read true runtime context (`$`, `changedFiles`, `env`). See [step-level rules](https://docs.kici.dev/user/hooks/#step-level-rules) | ## See also - [Dynamic values](https://docs.kici.dev/user/dynamic-values/) - [Hooks and rules](https://docs.kici.dev/user/hooks/) - [Lock file and drift](https://docs.kici.dev/user/lock-file-and-drift/) - [Job execution (architecture)](https://docs.kici.dev/architecture/execution/job-execution/) - [SDK: rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/) --- ## Getting help Source: https://docs.kici.dev/user/getting-help/ When something goes wrong, the fastest path to a fix is a report that already carries the context. This page covers what to try first, what to send, and how to send it privately. ## Diagnose it yourself first Two commands answer most problems without anyone else involved: ```bash # Check your own setup: login, org, lock file, orchestrator, agent labels kici doctor # Look at the org's infrastructure: orchestrators, scalers, agents kici diagnostics ``` `kici doctor` prints the next command to run for each problem it finds. Work down its output before reporting — a stale lock file or an expired login is a one-command fix. If your workflow ran and failed, read its logs: ```bash kici runs show kici runs logs ``` [Common failures](https://docs.kici.dev/user/common-failures/) covers the errors people hit most. ## Report a problem When you cannot resolve it yourself, gather a diagnostic bundle: ```bash kici report --run ``` The command writes a ZIP and prints its path and `sha256`. It sends nothing. The bundle holds: - your CLI, Node, and orchestrator versions, - your redacted KiCI configuration, - your project's workflow list and lock-file state, - the failing run's detail and logs, when you pass `--run`, - a collection report saying which of those the command could and could not read. Open the file and read it. It is yours until you decide to share it. ### What gets redacted KiCI removes known secret shapes before anything enters the bundle: - API keys and access tokens (AWS, GitHub, Slack, KiCI agent tokens), - `Authorization` headers and JSON web tokens, - passwords inside connection URLs, - private keys and encrypted-value blocks, - values assigned to a secret-named key, such as `api_key=` or `password=`. Configuration is redacted twice: an allowlist keeps only known-safe fields, and the free-text scrubber runs over what remains. **Redaction is best effort.** A secret in a format KiCI does not recognize can survive it. Review the bundle before you share it. `--no-redact` turns redaction off and prints a warning — use it only for a bundle you keep. ## Send it privately Reports contain your data, so there is no public tracker for them. Add `--upload` to send the bundle to KiCI directly: ```bash kici report --run --upload --message "matrix job hangs on macOS" ``` A defect in KiCI itself is different: if you can reproduce it without your own data — the docs promise something the tool does not do — it belongs in the public tracker instead. See [Reporting a discrepancy](https://docs.kici.dev/user/reporting-discrepancies/). The command prints a reference id. Quote it in any conversation about the problem. The bundle goes straight from your machine to KiCI storage over a one-time upload link — it never passes through the dashboard. Add `--email` if you want a reply address attached to the report. ## Manage what you have sent An upload is not permanent, and it is yours to revoke: ```bash # See the reports you have uploaded kici report list # Delete an uploaded bundle kici report withdraw ``` Uploaded bundles expire automatically after 90 days. Anyone in your organization can upload a report. By default you see and withdraw your own. A member with the `support:admin` permission can manage every report in the organization, which is how an owner cleans up on behalf of someone who has left. ## Reporting a security issue Do not use `kici report` for a suspected vulnerability. Follow the disclosure process in [SECURITY.md](https://github.com/kici-dev/kici-public/blob/main/SECURITY.md) instead. ## See also - [Common failures](https://docs.kici.dev/user/common-failures/) — the errors people hit most, and their fixes - [CLI reference](https://docs.kici.dev/user/cli-reference/) — every `kici` command - [Dashboard](https://docs.kici.dev/user/dashboard/) — inspecting runs in the browser --- ## Getting started with workflows Source: https://docs.kici.dev/user/getting-started/ KiCI lets you define CI/CD workflows in TypeScript instead of YAML. You get full language power -- type safety, autocompletion, loops, conditionals, and async/await -- for your build pipelines. ## Prerequisites - **Node.js 24+** (LTS recommended) - **pnpm** (or npm/yarn -- examples use pnpm) - Familiarity with TypeScript ## Quick start with kici init The recommended way to start a new project is `kici init`. It scaffolds the directory structure, lets you pick a starter template, and installs dependencies for you: ```bash npx kici init ``` This will: 1. Create `.kici/` directory with `workflows/`, `tests/`, `types/`, `package.json`, and `tsconfig.json`. The `types/` folder holds a local development aid — TypeScript declarations that `kici types` (and an authenticated `kici compile`) generate from your orchestrator's secret contexts. Its content is a snapshot of one org's secret keys, so it is not committed. 2. Create two `.kiciignore` files with sensible defaults: one at the repo root, which selects the working-tree files a remote run uploads, and one inside `.kici/`, which declares the paths the per-workflow content hash skips 3. Let you choose from starter workflow templates (hello-world, pr-checks) 4. Install dependencies using the package manager detected for your repo (npm, pnpm, or yarn) 5. Update `.gitignore` to exclude `.kici/node_modules/`, and write `.kici/.gitignore` to keep the generated `types/` declarations untracked (`kici.lock.json` stays tracked — the orchestrator fetches it from your repo) 6. Optionally install a pre-commit hook to auto-compile workflows The package manager is detected from your repo's `packageManager` field, lockfile, or the manager that invoked `kici`, defaulting to npm. Pass `--package-manager ` to override it. ### Options | Flag | Description | | ------------------------------------- | ------------------------------------------------------------ | | `--force` | Overwrite existing `.kici/` directory | | `--skip-install` | Create files without installing dependencies | | `--package-manager ` | Force a package manager for the install step (default: auto) | | `--mjs` | JavaScript-only mode (no TypeScript, no deps) | | `--workspace` | Integrate `.kici/` into the surrounding workspace | | `--standalone` | Force a self-contained `.kici/` even inside a workspace | ### Workspace integration If you run `kici init` inside a pnpm, npm, or yarn workspace, it offers to **integrate** `.kici/` into that workspace instead of scaffolding a self-contained folder. In integrate mode there is no `.kici/package.json`: `@kici-dev/sdk` is added to your workspace-root `package.json`, and your workflows can `import` your other workspace packages directly — for example shared build or deploy utilities. Pass `--workspace` to opt in non-interactively, or `--standalone` to keep the self-contained layout. In CI the default is standalone. See the [`kici init` reference](https://docs.kici.dev/user/cli/account-and-org/#kici-init) for details. ### MJS mode If you prefer plain JavaScript without TypeScript compilation: ```bash npx kici init --mjs ``` This creates `.mjs` workflow files that run directly without a build step. After running `kici init`, jump straight to [Compile the workflow](https://docs.kici.dev/user/getting-started/#compile-the-workflow) below to compile and preview the scaffolded workflow. ## Manual setup If you'd rather wire things up by hand instead of using `kici init`, install the SDK (runtime definitions) and the compiler (CLI tooling) yourself, then create your first workflow. ### Install the SDK and compiler ```bash pnpm add @kici-dev/sdk pnpm add -D @kici-dev/compiler ``` The examples use pnpm, but npm and yarn work too. With npm: ```bash npm install @kici-dev/sdk npm install -D @kici-dev/compiler ``` With yarn: ```bash yarn add @kici-dev/sdk yarn add -D @kici-dev/compiler ``` ### Create the workflow directory KiCI looks for workflows in `.kici/workflows/`: ```bash mkdir -p .kici/workflows ``` ### Write a workflow Create `.kici/workflows/ci.ts`: ```typescript import { workflow, job, step, pr } from '@kici-dev/sdk'; const lint = job('lint', { runsOn: 'kici:os:linux', steps: [ step('install', async ({ $ }) => { await $`pnpm install --frozen-lockfile`; }), step('lint', async ({ $ }) => { await $`pnpm lint`; }), ], }); const test = job('test', { runsOn: 'kici:os:linux', needs: [lint], steps: [ step('install', async ({ $ }) => { await $`pnpm install --frozen-lockfile`; }), step('run-tests', async ({ $ }) => { await $`pnpm test`; }), ], }); export default workflow('ci', { on: pr({ target: 'main' }), jobs: [lint, test], }); ``` This workflow: - Triggers on pull requests targeting `main` - Runs a `lint` job first - Runs a `test` job after lint succeeds (`needs: [lint]`) `runsOn` selects which agents may run a job. Every agent self-reports `kici:os:`, `kici:arch:`, and `kici:host:`, so `runsOn: 'kici:os:linux'` targets any connected Linux agent with zero configuration. `kici init` scaffolds workflows targeting your own host's OS label (`kici:os:linux`, `kici:os:macos`, or `kici:os:windows`) so your first local run dispatches on this machine. Use a custom label such as `'linux'` or `'gpu'` (defined in your scaler's `labelSet`) to target a specific pool instead. See the [runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms) reference for the full label model. **Single-step shortcut.** If a job only has one step, pass `run` directly to `job()` instead of building a `steps: [step(...)]` array: ```typescript const deploy = job('deploy', { runsOn: 'default', run: async ({ $, log }) => { await $`./scripts/deploy.sh`; log.info('Deployed'); }, }); ``` `run` and `steps` are mutually exclusive. The shorthand is ideal for deploy/notify/smoke-test jobs. See [Single-step job shorthand](https://docs.kici.dev/user/sdk/core/#single-step-job-shorthand) in the SDK reference for details (output access on the resulting `job.result` is flat -- no step-name nesting). ## Compile the workflow The compiler validates your workflow and generates a lock file: ```bash npx kici compile ``` Expected output: ``` ✓ Compiled workflows → .kici/kici.lock.json (1 workflow) ``` The lock file (`kici.lock.json`) is a JSON representation of your workflow that the KiCI agent uses for execution. Commit this file alongside your workflow source. See [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/) for why and how to keep them in sync. ## Preview trigger matching Use `kici preview` to preview which workflows match a trigger event (dry-run, no execution): ```bash npx kici preview pr:open ``` Expected output (simplified): ``` 🔍 DRY RUN - No commands will be executed Workflow: ci Triggers: - pr ✓ Matched trigger 1 Jobs (2): lint runs-on: linux test runs-on: linux Decision Summary: ci: ✓ matched ✓ Dry run complete ``` ## Run locally Execute matched workflows locally with `kici run --local`: ```bash npx kici run pr:open --local ``` This compiles, matches triggers, and runs all matched jobs on this machine — which joins as an ephemeral agent through the warm local dev plane — with DAG-based parallel scheduling. If a run never appears or a webhook seems ignored, run `kici doctor` — it walks your login, organization, orchestrator connection, and compiled lock file, and tells you the exact command to fix the first broken step. ## Workflow dependencies KiCI workflows can use any npm package. Dependencies are declared in `.kici/package.json`, which `kici init` generates automatically. ### Adding dependencies To add a package to your workflows: ```bash cd .kici npm install lodash ``` This updates `.kici/package.json` and generates (or updates) `package-lock.json`. ### Dependency resolution contract Every `.kici/` dependency must be resolvable from the **single cloned repository**. When a job runs, the agent clones only this repository and installs `.kici/` dependencies with your repo's package manager — npm, pnpm, yarn classic (v1), and yarn berry (v2+). A dependency that points outside the cloned repo cannot be resolved. In practice: - **From a registry** — the common case. Pin a published version (a private registry works — see [Private registries](https://docs.kici.dev/user/private-registries/)). Available for any package manager. - **From an in-repo workspace sibling** — if your `.kici/` is a member of a **pnpm workspace** or a **yarn berry workspace** (a `workspaces` array in the repo-root `package.json`), it can depend on a sibling package in the same repo via `workspace:*` (yarn berry also accepts `portal:`). The whole repo is cloned, so the sibling is present and resolves; the agent also builds your `.kici/` dependency closure after install, so a sibling's build output exists before the workflow that imports it loads. A `file:`/`link:`/`portal:` path is allowed only when it stays inside the repository. What fails fast (with an actionable error naming the dependency, not a raw package-manager error): a `workspace:` dependency in an **npm** project (npm has no workspace protocol — pin a published version or switch to pnpm), a `workspace:`/`portal:` dependency in a **yarn classic** project (v1 has neither — use a version range, pnpm, or yarn berry), a `workspace:` dependency in a **yarn berry** project whose repo-root `package.json` has no `workspaces` array, and any `file:`/`link:`/`portal:` path that points outside the cloned repo. Then use the package in your workflow: ```typescript import { workflow, job, step, push } from '@kici-dev/sdk'; import _ from 'lodash'; export default workflow('deploy', { on: push({ branches: 'main' }), jobs: [ job('process', { runsOn: 'default', steps: [ step('transform', async ({ log }) => { const data = _.merge({ a: 1 }, { b: 2 }); log.info(`Merged: ${JSON.stringify(data)}`); }), ], }), ], }); ``` ### How dependencies are cached When the KiCI agent runs your workflow, dependencies are handled automatically: 1. **First run (cache miss):** A build agent installs dependencies from `.kici/package.json`, packs the resolved dependency tree into a tarball, and uploads it to cache storage. For a pnpm workspace this closure includes the shared store and any in-repo workspace siblings `.kici` resolves. 2. **Subsequent runs (cache hit):** The execution agent downloads the cached tarball and extracts it -- no install needed. 3. **Lockfile changes:** When your lockfile changes (`.kici/package-lock.json` for npm, or the repo-root `pnpm-lock.yaml` for a pnpm workspace), the cache is invalidated and a fresh build runs. This means the first run after a dependency change is slower (build + execution), but all subsequent runs are fast. ### The .kici/package.json file Every KiCI project needs a `.kici/package.json`. This file: - Declares workflow dependencies (including `@kici-dev/sdk`) - Signals the agent to run the dependency cache step - Is generated automatically by `kici init` If you are setting up a project manually (without `kici init`), create a minimal `.kici/package.json`: ```json { "name": "@kici-dev/workflows", "private": true, "type": "module", "devDependencies": { "@kici-dev/sdk": "^0.0.1" } } ``` Then run `npm install` in `.kici/` to generate the lockfile. Commit both `package.json` and `package-lock.json` to your repository. ## Development mode When developing the KiCI SDK itself (or testing against a local fork), enable development mode. ### sdkPath in .kici/package.json Point to a local SDK checkout for IDE autocompletion: ```json { "name": "my-project-kici", "devDependencies": { "@kici-dev/sdk": "latest" }, "kici": { "sdkPath": "../../packages/sdk" } } ``` The `sdkPath` field tells the compiler where to resolve TypeScript path mappings for `@kici-dev/sdk`. ### KICI_DEV environment variable Set `KICI_DEV=true` to pin the SDK to the `latest` dist-tag in generated files, which resolves prerelease builds from a local Verdaccio registry: ```bash KICI_DEV=true npx kici init ``` Or add the flag to your root `package.json`: ```json { "kici": { "development": true } } ``` ## Authoring KiCI workflows with LLM coding agents KiCI is LLM-ready by design. Because workflows are real, typed TypeScript, coding agents reason over the SDK's `.d.ts` signatures instead of guessing a bespoke YAML DSL — and they verify their own pipelines with the same `kici preview` and `kici run --local` loop you use, so there's no push-to-find-out round-trip. First-class agent context ships in the box, so an agent is briefed the moment it opens the project. KiCI ships first-class context for LLM coding agents (Claude Code, Cursor, Aider, etc.). When you scaffold a project with `kici init`, the CLI writes `.kici/AGENTS.md`, a one-page briefing that tells the agent: - where the SDK type declarations live (`node_modules/@kici-dev/sdk/dist/index.d.ts`) - the five canonical authoring patterns with runnable examples - the anti-patterns that catch agents off-guard (no YAML, no `/dist/...` imports, no top-level `await`) - the local commands the agent should drive (`kici compile --check`, `kici preview`, `kici run --local`, `kici docs llm`) If you don't want the file, pass `--no-agents-md` to `kici init`, or delete the file afterwards — KiCI never reads it at runtime. For coding agents that want the entire documentation set up front, KiCI follows the [llms.txt convention](https://llmstxt.org/): - `https://kici.dev/llms.txt` — curated link index grouped by SDK / patterns / CLI / architecture. - `https://kici.dev/llms-full.txt` — concatenated markdown of every page indexed above. - `kici docs llm` — print the same curated `llms.txt` index to stdout, offline, straight from the installed `@kici-dev/compiler` package. `kici docs llm ` prints one task bundle (`getting-started`, `patterns`, `sdk`, `sdk-runtime`, `cli`, `cli-remote`, `features`, `features-execution`, `providers`, `architecture`), and `kici docs llm full` prints the whole `llms-full.txt`. Add `--out ` to write to a file. The agent can pipe the output into its own context buffer with no network call. - `kici docs` — open the docs site in your browser. The offline bundle is regenerated from `docs/` every time the package is built, so it always matches the version of KiCI you've installed. ## Watch mode During development, run the compiler in watch mode to recompile automatically when workflows change: ```bash npx kici compile --watch ``` The compiler watches `.kici/workflows/*.ts` and recompiles on every save. ## Next steps - **[5-minute quickstart](https://docs.kici.dev/user/quickstart/)** -- ready to run your workflow on real infrastructure? Stand up an orchestrator + agent (Docker / Podman or bare metal) - **[How your workflow code executes](https://docs.kici.dev/user/execution-model/)** -- the mental model: which parts of your workflow run at compile time, on the orchestrator, and on the agent - **[SDK reference](https://docs.kici.dev/user/sdk-reference/)** -- complete API for workflows, jobs, steps, triggers, rules, and matrix - **[CLI reference](https://docs.kici.dev/user/cli-reference/)** -- all CLI commands with options and examples - **[Workflow patterns](https://docs.kici.dev/user/workflow-patterns/)** -- common patterns for real-world CI/CD workflows ## How KiCI works KiCI uses a three-layer architecture: ``` SDK (define) -> Compiler (validate) -> Lock file -> Agent (execute) ``` 1. **SDK**: You write workflows in TypeScript using factory functions (`workflow()`, `job()`, `step()`). The SDK provides type-safe definitions with full IDE support. 2. **Compiler**: The `kici compile` command loads your TypeScript workflows, validates the dependency graph (no cycles, no missing references), and generates `kici.lock.json`. 3. **Lock file**: A portable JSON file containing all workflow metadata. The lock file enables the orchestrator to evaluate triggers without cloning your repository. 4. **Agent**: The agent receives dispatch instructions, clones your repository, and executes the steps defined in your workflows. Agents are self-hosted and label-routed. The lock file approach means the orchestrator stays git-agnostic -- it only needs the lock file to decide which jobs to run. The agent handles the actual code checkout and step execution. ## See also - [SDK reference](https://docs.kici.dev/user/sdk-reference/) -- complete API for workflows, jobs, steps, triggers, rules, and matrix - [CLI reference](https://docs.kici.dev/user/cli-reference/) -- all CLI commands with options and examples - [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/) -- common patterns for real-world CI/CD workflows - [Architecture overview](https://docs.kici.dev/architecture/overview/) -- how the three-tier runtime executes your workflows --- ## Migrating from GitHub Actions Source: https://docs.kici.dev/user/migrating-from-github-actions/ ## Before you start KiCI workflows are real, typed TypeScript in `.kici/workflows/*.ts` instead of YAML in `.github/workflows/*.yml`. The compiler validates them ahead of time, and at run time the agent clones your repository and executes them on your own infrastructure. This guide maps the GitHub Actions concepts you already know to their KiCI equivalents, translates one realistic workflow side-by-side, and lists honestly what has no equivalent yet. Follow [getting started](https://docs.kici.dev/user/getting-started/) for the full setup. The mapping below assumes you have `@kici-dev/sdk` installed. ## Concept mapping | GitHub Actions | KiCI | Notes | | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Workflow file `.github/workflows/ci.yml` | TypeScript module `.kici/workflows/ci.ts` that default-exports `workflow('ci', {...})` | Workflows are modules built from `workflow()`, `job()`, and `step()` factories. | | `on: pull_request:` | `on: pr({ target: 'main' })` | `pr()` is a trigger factory; `on` takes one trigger or an array of triggers. | | `on: push: branches: [main]` | `on: push({ branches: ['main'] })` | `push()` matches by branch pattern. | | `on: schedule: - cron:` | `on: schedule({ cron: '0 2 * * *', timezone: 'UTC' })` | `cron` is required; `timezone` defaults to `UTC`. In a cluster only the leader evaluates schedules. | | `on: workflow_dispatch: inputs:` | `on: dispatch({ inputs: defineDispatchInputs({...}) })`, read typed via `inputs.from(ctx)` | Dispatch inputs are declared with Zod schemas and read back fully typed. | | `jobs:` map keyed by name | `jobs: [job('lint', {...}), job('test', {...})]` | Jobs are an array of `job()` results. | | `needs: [build]` (string references) | `needs: [buildJob]` (object references) | You reference the `job()` value directly; the compiler validates the dependency graph for cycles and missing references. | | `runs-on: ` | `runsOn: 'kici:os:linux'` or a custom scaler label | Every agent self-reports labels such as `kici:os:linux`, `kici:arch:x64`, and `kici:host:`. A GitHub-hosted runner label matches no KiCI agent. | | `steps: - run: npm test` | `steps: [step('test', async ({ $ }) => { await $\`npm test\` })]` | The `$` in a step body is a zx shell; a single-step job can use the `run:` function shorthand instead of `steps:`. | | `steps: - uses: actions/checkout@v4` | (nothing) | The agent clones the repository automatically before steps run, so there is no explicit checkout step. | | `steps: - uses: actions/setup-node@v4` | Provision the toolchain in a step or from the agent image | KiCI has no `uses:`-style setup actions; the toolchain comes from the agent environment or an explicit step. | | `${{ secrets.NPM_TOKEN }}` | `await ctx.secrets.get('NPM_TOKEN')`, or `ctx.secrets.expose('NPM_TOKEN')` to place it in the environment | Secrets are never auto-injected into `process.env`; access is always explicit. | | `environment: production` (+ protection rules) | `context: 'production'` (or `contexts: ['staging', 'prod']`) | A context carries variables, bound secrets, and protection rules (branch restrictions, required reviewers, wait timers, concurrency limits, minimum trust). | | `env:` (job or step level) | `env: { KEY: 'val' }` on a job | Job-level `env` accepts a static object or a `(event) => ({...})` function for dynamic values. | | `strategy: matrix: node: [18, 20, 22]` | `matrix: ['18', '20', '22']` or `{ node: [...], os: [...] }` | A single-dimension array exposes `matrix.value`; an object expands all combinations and exposes each dimension by name. A dynamic function form is also supported. | | `if: github.ref == 'refs/heads/main'` | Native TypeScript conditionals plus `rule()` / `skip()` and dynamic values | Conditions are real TypeScript branching; dynamic values are pure functions of the normalized event. | | `concurrency: group: ...` | `concurrencyGroup: 'production-api'` (static) or a dynamic function | Set at the job level; a workflow-level concurrency group also exists. | | `jobs..outputs` | Structured job and step outputs consumed downstream via `needs`; `ctx.setSecretOutput(key, value)` for encrypted outputs | Outputs pass values between jobs; secret outputs are encrypted. | | `- uses: actions/cache@v4` | `cache` field on a job or step (declarative) or `ctx.cache.restore` / `ctx.cache.save` (imperative) | A keyed cache, immutable once written, org- and ref-scoped, backed by the orchestrator's object storage. | | Reusable workflows / composite actions / marketplace `uses:` | `@kici-dev/action-*` building blocks or any npm package you `import` | Reusable logic is imported as functions, not referenced by `uses:`. | ### Secrets are explicit KiCI never copies secrets into `process.env` for you. A step reads a value with `ctx.secrets.get('KEY')` or injects it into the environment with `ctx.secrets.expose('KEY')`, and can mount a secret as a file with `ctx.secrets.mountFile(...)`. Every access is tracked. See [secrets](https://docs.kici.dev/user/secrets/). ### Environments become contexts A GitHub environment maps to a KiCI context bound at the job level with `context:` (or `contexts:` for several). A context carries variables, bound secrets, and protection rules — branch restrictions, required reviewers, wait timers, concurrency limits, and a minimum-trust gate. See [contexts](https://docs.kici.dev/user/contexts/). ### `if:` becomes real TypeScript There is no expression mini-language. Conditions are ordinary TypeScript, and values that depend on the event are pure functions of the normalized event object. See [dynamic values](https://docs.kici.dev/user/dynamic-values/). ### Matrix A single-dimension matrix is an array (`matrix: ['18', '20', '22']`) and exposes the current value as `matrix.value` in the step context. A multi-dimension matrix is an object and exposes each dimension by name. See [conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/). ### Caching Declare a `cache` on a job or step, or drive it imperatively with `ctx.cache.restore(spec)` and `ctx.cache.save(spec)`. Cache entries are keyed and immutable once written. See [caching](https://docs.kici.dev/user/sdk/caching/). ## A real workflow, translated Here is a pull-request CI workflow that runs tests across a Node version matrix and uploads coverage using a secret. The GitHub Actions version: ```yaml name: ci on: pull_request: branches: [main] jobs: test: # kici-lint-allow-github-runner: GitHub-hosted runner shown for contrast runs-on: ubuntu-latest strategy: matrix: node: ['18', '20', '22'] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: npm ci - run: npm test - run: npx codecov env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ``` The KiCI translation: ```typescript import { workflow, job, step, pr } from '@kici-dev/sdk'; const test = job('test', { runsOn: 'kici:os:linux', matrix: ['18', '20', '22'], context: 'ci', steps: [ step('install', async ({ $ }) => { // The agent already cloned the repo — no checkout step needed. await $`npm ci`; }), step('test', async ({ $, matrix }) => { // matrix.value is the Node version for this cell ('18' | '20' | '22'). // Select it however your agent provisions toolchains, e.g. a version manager. await $`nvm use ${matrix!.value}`; await $`npm test`; }), step('coverage', async (ctx) => { await ctx.secrets.expose('CODECOV_TOKEN'); await ctx.$`npx codecov`; }), ], }); export default workflow('ci', { on: pr({ target: 'main' }), jobs: [test], }); ``` What changed and why: - `on: pull_request` → `on: pr({ target: 'main' })`. - `runs-on: ubuntu-latest` → `runsOn: 'kici:os:linux'`, an auto-label every Linux agent reports; a GitHub hosted-runner label would match no agent. See [runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms). - `actions/checkout` → removed; the agent clones the repository before steps run. - `actions/setup-node` with `matrix.node` → `matrix: ['18', '20', '22']`, with the current value available as `matrix.value` in the step context. KiCI has no built-in setup-node, so toolchain selection is a step or agent-image concern. See [conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/). - `${{ secrets.CODECOV_TOKEN }}` → `ctx.secrets.expose('CODECOV_TOKEN')`, explicit and never auto-injected. See [secrets](https://docs.kici.dev/user/secrets/). - `environment` → `context: 'ci'`. See [contexts](https://docs.kici.dev/user/contexts/). The `nvm use` line is illustrative — KiCI does not install a Node version for you; use whatever your agent image or step provides. ## What has no equivalent yet **File artifacts between jobs.** KiCI has no first-class store for uploading a build directory as a named artifact and downloading it in a later job or from the run UI. It does have structured job and step outputs (and secret outputs) for passing _values_, and a keyed [cache](https://docs.kici.dev/user/sdk/caching/) for reusing files across runs. For build outputs you need to hand between jobs, use the cache or an external object store. **A community action marketplace.** GitHub Actions has thousands of third-party marketplace actions addressable by `uses: owner/repo@ref`. KiCI's reusable blocks are the published `@kici-dev/action-*` packages plus any npm package you import — there is no marketplace of community-contributed actions. **`uses:`-style step references.** KiCI steps are TypeScript functions, so you call reusable logic as imported library functions rather than referencing a composite or container action. This is a model shift rather than a missing feature, but a drop-in `uses:` translation does not exist. **Provider breadth.** KiCI is GitHub-first. Other git hosts are reachable through the universal-git and local-file providers, but the richest event coverage is for GitHub. See [the GitHub provider](https://docs.kici.dev/user/providers/github/). ## Next steps - [Getting started](https://docs.kici.dev/user/getting-started/) - [SDK reference — runsOn forms](https://docs.kici.dev/user/sdk/core/#runson-forms) - [Conditionals and matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/) - [Secrets](https://docs.kici.dev/user/secrets/) and [contexts](https://docs.kici.dev/user/contexts/) - [CLI reference](https://docs.kici.dev/user/cli-reference/) --- ## 5-minute quickstart Source: https://docs.kici.dev/user/quickstart/ KiCI offers two equally-supported quickstart paths. Pick the one that fits your machine — both end with the same working pipeline (orchestrator + agent + your first workflow run visible in the dashboard). Each guide is split into two parts. **Part 1** gets you a green run against your own orchestrator with `kici run remote` — no GitHub App needed, just sign up, bring up the stack, and run. **Part 2** then wires up real GitHub pushes so your team's commits trigger runs automatically. You can stop after Part 1 and come back to Part 2 whenever you're ready. ## Bring a coding agent Workflows are TypeScript, so a coding agent can write them. KiCI ships its docs in a form an agent reads directly: point yours at [llms.txt](https://kici.dev/docs/llms.txt), or pipe a task bundle straight into its context with `kici docs llm` (`kici docs llm sdk` for the authoring API, `kici docs llm patterns` for recipes). An agent briefed that way can scaffold your first workflow, compile it, and read the failure when it breaks. If your agent finds that these docs promise something KiCI does not do, that is worth telling us — `kici feedback` prints how to report it, and [Reporting a discrepancy](https://docs.kici.dev/user/reporting-discrepancies/) is the full guide. ## Option A — Docker / Podman (recommended) Two containers brought up with `docker compose up -d` (orchestrator + PostgreSQL), plus one short-lived agent container spawned per job by the container scaler. Minimal host setup, perfect for a laptop, home server, or a tiny VM. No need to install PostgreSQL or any other system service. [Start with the Docker / Podman quickstart →](https://docs.kici.dev/user/quickstart/compose/) ## Option B — Bare-metal install Native systemd services managed by `kici-admin orchestrator install` / `kici-admin agent install` — the orchestrator and agents run as native processes. The backing PostgreSQL runs as a single container by default (one `docker compose up -d`), or you can install it natively if you'd rather not run a container runtime at all. Best for a long-lived Linux host. [Start with the bare-metal quickstart →](https://docs.kici.dev/user/quickstart/bare-metal/) ## Which should I pick? | | Docker / Podman | Bare metal | | ------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------- | | Host requirements | `docker` or `podman` with compose v2.20+ | systemd, Node.js 24+, PostgreSQL 18 (container — needs `docker`/`podman` — or native) | | First local run (Part 1) | ~5 minutes | ~10 minutes | | First GitHub run (Part 2) | + ~10 minutes | + ~10 minutes | | Upgrades | `docker compose pull` + restart | `kici-admin orchestrator restart` after `npm install -g kici-admin@latest` | | Best for | Quick evaluation, ephemeral hosts | Long-lived production hosts | If you're not sure, pick Docker / Podman. ## Looking for the laptop-only path? Both quickstarts deploy a real orchestrator + agent. If you only want to write a workflow and dry-run it on your laptop with no infrastructure, [Getting started](https://docs.kici.dev/user/getting-started/) covers `kici preview` and `kici run --local` instead. --- ## Reporting a discrepancy Source: https://docs.kici.dev/user/reporting-discrepancies/ This page is addressed to a **coding agent** working with KiCI. KiCI is built to be driven by an LLM, so an agent is often the first to notice a mismatch. A documented flag does not exist; a command prints something the docs never promised. You can report that. A human reader can use this page too. The rules below exist because the tracker is public and you are filing under someone else's GitHub identity. Run `kici feedback` to print this contract in your terminal, or `kici feedback --json` to read it as structured data. ## The rule, in one sentence You find a checkable mismatch between what the docs or the CLI advertise and what KiCI actually does. You draft an issue carrying a reproduction and a justification, and show it to the person you are working with. You file it only after they say yes. You never file on your own initiative. ## What qualifies Report it when you can point at the claim and at the contradicting behaviour: - A documented flag, command, or option that does not exist in the version you ran. - Documented output — a shape, a field, an exit code — that differs from what the command produced. - A CLI `--help` description that contradicts the published docs. - A documented behaviour that does not happen, or a documented guarantee that does not hold. - A documented error or limit that the tool does not actually enforce. Two conditions apply to all of them. You ran the command and saw the result — inference from reading the docs is not a reproduction. And you ran a published version: check with `kici --version`. ## What does not qualify - Usage questions, or behaviour you find surprising but that the docs describe correctly. - Feature requests and design preferences. - Anything you inferred from reading docs without running the command. - Anything reproduced only on a locally built or unreleased version. - A failure that is your workflow, your credentials, or your environment. If you are unsure whether the docs are wrong or you misread them, you are not ready to file. Say so to the person you are working with and let them decide. ## Never file these publicly **A suspected vulnerability is never a public issue.** Do not file it, and do not describe it in one. Open a private [security advisory](https://github.com/kici-dev/kici-public/security/advisories/new) instead, or tell the person you are working with to. This holds even if the issue looks minor to you. **A problem with someone's own runs is not a tracker issue.** That covers a failing run, a broken deployment, and anything that needs their logs. Use the private path in [Getting help](https://docs.kici.dev/user/getting-help/) instead: `kici report --run --upload` sends a redacted bundle straight to KiCI. **Never put customer data in a public issue:** - No secrets, tokens, or credentials — not even redacted-looking ones. - No private repository names, internal hostnames, organization ids, or run ids. - No log excerpts you have not read line by line. - Reproduce with a minimal synthetic workflow, never the real one you were working on. Rewriting the reproduction against a throwaway workflow in an empty directory is the reliable way to satisfy all four at once. ## Search before you draft The tracker is small. The same finding arriving once per agent that reads the docs is worse than not hearing it at all: ```bash gh issue list --repo kici-dev/kici-public --search "" --state all ``` Search closed issues too — a closed one may carry the answer, or the decision that the behaviour is intended. If a matching issue is open, add your reproduction as a comment instead of opening a second one, under the same approval rule. ## What the report must carry Five things. A report missing any of them cannot be acted on: | Field | What it holds | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | What the docs or CLI advertise | The exact claim, quoted, plus its source: a docs URL or the command whose `--help` says it. | | What actually happened | The real output or behaviour, quoted, with any error text. | | Minimal reproduction, including setup | Every step from an empty directory: the setup commands, a minimal synthetic workflow, and the exact command you ran. | | Version and environment | Output of `kici --version`, plus Node version and OS. | | Why this is a discrepancy | One or two sentences ruling out the likely misreads — why the docs cannot be read to match what you observed. | The last field is the one agents skip, and it is the one that makes a report usable. "The docs say `--foo` exists and it does not" is a claim. "`--foo` is documented at , and `kici bar --help` on 0.1.16 lists no such flag" is a finding someone can act on in one pass. Include setup steps whenever your reproduction needed any. A reproduction that starts from state a maintainer cannot recreate is not a reproduction. ## Filing it The tracker is [kici-dev/kici-public](https://github.com/kici-dev/kici-public), and the form is **Agent report**: ```bash kici feedback --open ``` Draft the complete body first. Show it to the person you are working with — the whole body, not a summary of it. File only after they say yes: ```bash gh issue create --repo kici-dev/kici-public --template agent_report.yml ``` The form asks you to confirm the report was drafted by an agent and reviewed by a person before filing. Answer honestly; it is what lets maintainers triage agent-filed reports as a group. If they say no, that is the end of it. Do not re-file the same finding in another form, and do not open it as a question instead. ## See also - [Getting help](https://docs.kici.dev/user/getting-help/) — the private path for a problem with your own runs - [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/) — the MCP server, and the agent safety model - [Common failures](https://docs.kici.dev/user/common-failures/) — check here before concluding the docs are wrong --- ## Why KiCI Source: https://docs.kici.dev/user/why-kici/ Most CI platforms ask you to hand your source, your secrets, and your build machines to someone else. KiCI is built the other way around: your code runs on infrastructure you control, and you describe the pipeline in real TypeScript you can run before you push. This page is the short argument for that trade -- what it buys you, and what it costs. ## Your infrastructure runs the code KiCI is a three-tier relay. The hosted platform is a thin webhook router: it verifies the incoming webhook signature and relays the event to your orchestrator over a WebSocket. Your **orchestrator** decides what to run and dispatches jobs to your **agents**, which clone the repository, execute the steps, and stream logs back. The orchestrator and agents run on machines you own. Because of that split, the hosted platform sees only the envelope, never the payload: - **What it receives:** the webhook event, run metadata (workflow and job names, statuses, timings), aggregate operational metrics, and log lines while you are streaming them to the dashboard. - **What it never sees:** your source, your secrets, your artifacts, or your signing material. Those stay on your orchestrator and agents. Log content is relayed only in transit for the live dashboard view -- the platform never stores it. The hosted platform is operated by KiCI; you do not run your own. What you run is the orchestrator and the agents, and that is where every byte of your code and every secret lives. For the field-level breakdown of what does and does not leave your infrastructure, see [Data residency](https://docs.kici.dev/operator/data-residency/), and for the honest security posture of self-hosting the agents, see [Is self-hosting the agents a security risk?](https://docs.kici.dev/operator/security/self-hosting-security/). ## Workflows are TypeScript A KiCI workflow is a TypeScript program, not a YAML document. Jobs, steps, triggers, and matrices are typed values you compose with the full language -- loops, conditionals, functions, `async`/`await`, and your editor's autocompletion and type checking. Invalid pipelines fail at compile time, in your editor, instead of failing on the tenth push. The same TypeScript runs everywhere. `kici run --local` executes your workflow on your own machine against a simulated event, and that is the same execution model the agents use in production. Your local run is the production pipeline, so you debug a green run before it ever reaches a source event. This also makes workflows something an AI coding agent can author, type-check, and run before it opens a pull request. ## The honest trade Running your own infrastructure is not free. You operate the orchestrator: a Docker / Podman or bare-metal service you stand up (in minutes for the quickstart topology), keep patched, back up, and upgrade. In exchange, you own your data, your egress, and your isolation model -- no third party executes your code or holds your secrets. KiCI is pre-1.0, so pin versions for production. See [Deploying the orchestrator](https://docs.kici.dev/operator/orchestrator/getting-started/) for what running it involves. ## Compared to specific tools If you are weighing KiCI against a specific incumbent -- GitHub Actions, GitLab CI, CircleCI, Jenkins, Buildkite, and others -- the point-by-point comparisons live on the marketing site, kept current with sourced references. Start with the [GitHub Actions comparison](https://kici.dev/compare/github-actions), or browse [all comparisons](https://kici.dev/compare). ## Next steps - **[Green run in ~5 minutes](https://docs.kici.dev/user/quickstart/)** -- stand up an orchestrator and agent and watch a workflow go green. - **[Getting started](https://docs.kici.dev/user/getting-started/)** -- write your first workflow and test it locally. ---