Skip to content

Changelog

  • The runs list with archived runs included lists the newest archived runs, not the oldest, and a status, source, routing-key or other filter finds matching archived runs anywhere inside the retention window. Before, an organization with many archived runs got an empty page and a count of 0 when its matches were not among the archived runs read first. An archived run that was rerun is now listed and counted once, not twice. A workflow filter finds an archived run that ran that workflow in any of its jobs. If archived runs cannot be read and no current runs match, the request fails with an error instead of an empty list.
  • kici-admin db create-role and db create-readonly-user no longer let the role password reach the PostgreSQL server log or the pg_stat_statements view when they connect as a superuser
  • Add kici-admin db stats-check and kici-admin db analyze. stats-check reports tables with a GIN index whose row estimate (pg_class.reltuples) is NaN, infinite or larger than the table can hold, which a parallel GIN index build before PostgreSQL 18.6 could write. analyze --table <schema.table> runs ANALYZE on the named tables to fix them.
  • kici-admin orchestrator upgrade and kici-admin agent upgrade accept --node <path>. It moves a service that runs from an npm install to another Node.js runtime: the command installs the package with that runtime’s npm, registers the service again, and restarts it. Use it to move a service to Node.js 24.22.1.
  • An npm-source kici-admin orchestrator upgrade (with --version, --restart-only or --node) now takes the pre-upgrade database dump and drains the coordinator before it stops the service, as an archive upgrade does. Before, it restarted the orchestrator with no dump and no drain. --skip-backup, --backup-dir, --no-drain and --drain-timeout apply as on an archive upgrade.
  • The orchestrator and agent container images, and the Node.js runtime that the agent mounts into job containers, now run Node.js 24.22.1. Node.js 24.22.1 blocks the compromised eMudhra intermediate certificates and fixes CVE-2026-111497, CVE-2026-105483, CVE-2026-106069, CVE-2026-106072 and CVE-2026-105482. The packages of this release run on Node.js 24.22.1: before you upgrade a light package, install Node.js 24.22.1 into its cache. If you run kici or kici-admin from npm, update the Node.js that runs them to 24.22.1 or a later Node.js 24 release.
  • A cluster worker retrying a failed agent spawn for a rerouted job now always starts the retry when its backoff ends. Before, the retry could be skipped, and the coordinator then failed the job when its spawn window ran out.
  • Breaking: the dashboard now encrypts every secret value in your browser before it leaves the page, and the hosted Platform refuses a plaintext secret value. secrets.set accepts only the encrypted posture (the default) or disabled; kici-admin org-settings dashboard-writes set —op secrets.set=permissive is refused. Scripts that set secret values use kici-admin secret set.
  • The kici, kici-admin and kici-platform-admin commands now deliver all of their output to a pipe. Before, --json output piped to a slow reader could stop after 8192 bytes, with exit code 0.
  • On an orchestrator attached to the Platform, kici-admin source add github files the source under the attached org, so its runs read that org’s contexts, secrets and trust policy. Existing sources keep their org; kici-admin source list, org list, orchestrator status and trust-policy show report a source outside the attached org. kici-admin runs show names the run’s org. A refused git credential now reaches the job as a refusal instead of an internal error.
  • kici runs rerun, the dashboard re-run and Run now buttons, and the MCP rerun_run and trigger_run tools return the new run id once the orchestrator accepts the request; a dispatch failure fails that run, and a timeout is reported as unconfirmed, not failed
  • kici runs rerun and kici runs cancel accept the first characters of a run id, as kici runs show does, and kici runs show prints why a failed run failed when no init failure explains it
  • A global workflow run that fails before any job starts now records its workflow commit, so you can re-run it.
  • The orchestrator now flags a workflow lock compiled for a compile schema its agents do not implement. The workflows page, kici-admin workflow list, kici-admin diagnose, the startup log and kici-admin orchestrator upgrade show it. A single orchestrator fails such a run before any agent starts. The error names the repository that holds the lock, tells you which compiler to recompile with, and says that a re-run keeps the old lock. A global workflow’s setup check now carries the workflow repository in its name, and a re-run that fails before its jobs start completes all of its checks.
  • A re-run the Platform could not confirm in time now starts the short re-run cooldown, so retrying right away is refused instead of starting a second run
  • A re-run or Run now retried after an unconfirmed start returns the run the first request started instead of starting a second one.
  • The MCP rerun_run tool enforces the 5-second per-run re-run cooldown, as the dashboard and kici runs rerun do.
  • Fleet bring-up retries an SSH connection that times out before the session starts, so a slow target (for example a small board that is unpacking the agent package) no longer fails the bring-up on one late connection.
  • The quickstart examples now run SeaweedFS 4.48, which fixes GHSA-2v4p-qf9q-27wj.
  • The orchestrator and agent container images now run Node.js 24.22.0.
  • The agent image bakes pnpm 11.28.6, which the agent’s isolated dependency install now runs.
  • The orchestrator and agent now resolve @grpc/grpc-js 1.14.6 and axios 1.20.0, which clear GHSA-m9gg-hp2v-232j and seven axios advisories.
  • The published packages resolve moment 2.31.0 for daily log rotation, which clears GHSA-4p3w-j4w9-5jqw.
  • Runtime dependency update in the published packages: oxc-transform 0.154.0, the TypeScript transform under the kici CLI’s workflow loader and the agent.
  • Trigger JSONPath matching (match/not, content requirements, changed-files paths) now runs on jsonpath-plus 11.2.1, which fixes a nested-filter bypass that let a filter expression write to Object.prototype. An explicit zero slice end such as [0:0] now selects nothing instead of the whole array, and a bare @ filter such as [?(@)] is accepted.
  • Runtime dependency updates in the published packages: zod 4.6.7 (all), hono 4.13.13 and @hono/node-server 2.1.5 (agent, orchestrator, shared), ws 8.22.0 (kici CLI, agent, orchestrator), kysely 0.29.7 and pg 8.23.1 (orchestrator, shared), @aws-sdk/* 3.1152.0 (orchestrator and shared storage client), @inquirer/prompts 8.7.3, lru-cache 11.5.3, nanoid 6.0.2 and wsl-utils 1.0.1. kici-admin downloads rolldown 1.2.14 for its lazy-dep @rolldown/binding-* entries.
  • The Node.js runtime that the agent mounts into job containers is now 24.22.0, the same release the agent runs.
  • A re-run now goes through the same dispatch steps as the run it repeats. Its jobs can upload and download artifacts and use the user cache: before, an artifact upload from a re-run failed with the run for this job could not be resolved on the orchestrator. A re-run also keeps needs order (before, every job of a re-run started at once), applies contexts with their secrets and approval gates, runs container jobs in their container, and resolves the same trust tier from the same event as the original run. A re-run of an untrusted fork pull request reads the lock file from the base branch, as the original run did, and goes through the organization trust policy again: a fork pull request the policy holds is held again. This also applies to a re-run of an organization-wide workflow. A run that an internal event started can be re-run only while that event is still stored. A re-run of a re-run of an organization-wide workflow no longer fails to read its payload. A schedule fired by hand from the dashboard had the same faults and now runs as a scheduled fire does.
  • A job whose rule returns false now records each of its steps as skipped, as its check summary shows. kici runs show no longer says such a job ran on an agent with no steps. A step whose rule returns false no longer shows success when its start and its skip reach the orchestrator out of order.
  • kici runs show --json now gives each step a phase: setup, init or steps. An init command also gets initIndex, its position in the job’s init list, so a script no longer needs the stored step index to find it. The steps are in the order they ran, also for a run that an older orchestrator recorded. The MCP get_run tool and kici-admin runs structured --json give each step the same fields. kici runs show also names a failed init command init[n] in its error line.
  • kici-agent —version prints the installed version and kici-agent —help lists the required environment variables; before, both started the agent. Any other argument stops the agent with exit status 2. A configuration error names the environment variable to set, without a stack trace, and the orchestrator’s startup error does the same. In a terminal, a startup failure prints its reason.
  • A job whose rule returns false now ends skipped instead of success. Its GitHub check reports skipped, and the check summary names the rule. Every skipped job’s check now reports skipped instead of cancelled, so a skipped job does not block a required check. This includes a job that a needs edge skipped after its upstream failed: to block a pull request when any job fails, require the workflow check kici/<workflow>. A job that needs a rule-skipped job now follows its when condition: the default skips it, and when: 'on-skip' runs it. A step whose rule returns false is now reported skipped, not failed. Job notifications and job_complete triggers see a rule-skipped job as skipped, so a trigger that filters on success no longer fires for it. A rule-skipped job no longer sends a job.completed webhook event.
  • kici preview names every step the way a run does: a run: shorthand step, a bare function step and a step() with no name show their step-N name instead of an empty bullet, and a parallel() group lists its steps under the group. A step layout that kici compile rejects, such as an empty parallel() group, now also fails kici preview with the same error.
  • kici init scaffolds pr-checks to target the repository’s default branch (origin/HEAD, else the HEAD the origin remote reports, else main), never the branch you have checked out, and writes it in single quotes. The sample fixture uses the branch you have checked out. In a repository with no src/ directory, pr-checks has no paths filter and no require-src-changes rule, so it runs on every pull request.
  • A universal-git SSH clone now finds its known_hosts file when the temporary directory path contains a space; ssh read such a path as two files and failed host-key verification.
  • repo.identifier now reads owner/repo for a checkout cloned over SSH (git@host:owner/repo.git or ssh://host/owner/repo.git); it used to return the whole clone URL.
  • repo.withWrite now opens a write window for the job’s own checkout when it was cloned over SSH or from a file:// URL; the write fence used to refuse it as outside the source repository’s organisation.
  • repo.withWrite({ credential: ‘name’ }) now pushes with the named git credential; git used the job’s default credential inside the window even though the window checked the named one.
  • With KICI_RUNNER_USER set, a bare-metal job can now use the git credential helper; the helper and its socket were owned by the agent user, so the runner user could not reach them and git push had no credential.
  • A job that declares an SSH-key git credential now fails before its first step when no ssh client is on PATH, with an error that names the fix (init: { apt: { packages: [‘openssh-client’] } }); it used to fail at push time, inside repo.withWrite.
  • Under the bubblewrap sandbox (KICI_SANDBOX=true), a bare-metal job can now reach the git credential helper; the sandbox’s private /tmp hid the helper, so git push and repo.withWrite had no credential.
  • An SSH-key git credential (kind: ‘ssh’) now works inside repo.withWrite: git commands in the checkout push to SSH URLs with the key, and host keys are checked strictly against the published GitHub, GitLab and Bitbucket Cloud keys plus ~/.ssh/known_hosts. The universal-git docs now say that sshHostKeyPolicy accept-new accepts any host key.
  • GitHub check runs now link to the run’s page in the KiCI dashboard. An orchestrator connected to the KiCI Platform gets the dashboard address from the Platform, so it needs no configuration; KICI_DASHBOARD_URL overrides it.
  • The dynamic values guide no longer says kici preview lists the injected init job; preview does not show it.
  • kici-admin —help states each option default once. Some options showed their default twice, as in Vault mount path (default: secret) (default: "secret"). These options are fixed: - backend add: —auth-method, —mount-path, —scope-filter and —sync-interval. - runs list, attestations list, event-log list, access-log list and event-dlq list: —limit. - runs logs: —step and —limit. - db backup: —keep and —schedule. - workflow register-manual: —provider-context. - context create-template: —type. The options and their defaults work as before.
  • kici-admin runs show, runs jobs —include-steps, runs structured and runs logs —step init[], kici runs show, kici runs logs and the dashboard now also list an init step recorded by an orchestrator older than 0.21.0 first, as init[], instead of last with step number 1000000. That orchestrator stored init steps with no step type; they are recognized from their stored index and their init: name. In —json, such a step reports phase init and its initIndex.
  • kici-admin orchestrator upgrade now reads the cache storage config and the S3 credentials for the agent package upload from the env file of the instance that it upgrades, not from the shell. An upgrade from a shell that exports no KICI_STORAGE_* variables no longer skips the upload. kici-admin agent package --upload takes --env-file to read the same config.
  • kici run remote <fixture> creates one run per matched workflow, as a webhook does, so two workflows that define a job of the same name no longer collide in one run. A kici older than this release still gets one shared run when the matched workflows’ job names cannot collide, and a clear refusal naming the shared job names when they can.
  • kici run remote follows every run a fixture creates, one per matched workflow. It labels each job <workflow> / <job> in the summary table, the error blocks and the JUnit report when the runs span workflows, gives each failed job its own error block with only its own output, and passes only when every run passed. --json adds runs and each job’s workflowName; the top-level runId is deprecated.
  • A job waiting on needs is no longer swapped for the placeholder of another job whose name starts with its own (build and build-and-push).
  • kici-admin —help no longer prints the admin token. When KICI_ADMIN_TOKEN was set, the help showed its value as the default of —token, so shared help output leaked the token. The help now shows the name of the environment variable, as in (env: KICI_ADMIN_TOKEN), and never its value. KICI_ADMIN_URL and the —base-url option of source trigger-local and source install-hook show their environment variable the same way. The options and environment variables work as before.
  • kici runs show prints an error: line for a job that failed in a step, naming the step and its exit code, even when the job records no error message of its own.
  • A job that builds its container image and also runs init commands no longer loses its image-build step: the two used the same step number, so the init step overwrote the image-build step and their logs mixed.
  • kici-admin runs show, runs jobs —include-steps and runs structured list a job’s init steps first, as init[0], init[1] and so on, instead of last with a step number of 1000000. kici runs show, kici runs logs and the dashboard also list init steps before the steps they ran ahead of. kici-admin runs logs —step accepts init[]. In —json, stepIndex is unchanged, and each step adds phase (setup, init or steps) and, for an init step, initIndex.
  • kici run --local follows every run an event creates, one per matched workflow, and fails when any of them fails. It followed only the first run before, so a failure in a second matched workflow went unreported.
  • The orchestrator now records the type of hook, cache and init steps. It stored them as plain steps, so the dashboard showed no hook badge on an onCancel or cleanup step, and readers could not tell these steps from the job’s own steps.
  • A GitHub check run’s progress summary no longer fills with placeholder ‘Step N’ lines for a job with init commands, an image build, or cache steps. The summary lists each step that reported, init steps first.
  • kici-admin runs show, jobs, logs, structured, ephemeral-key and secret-outputs, kici-admin execution show, and kici runs show and logs, accept a run id prefix of at least 4 characters, like a git short SHA. An ambiguous prefix fails and lists its matches; an unknown prefix or a value that cannot be a run id fails with a message that names it, instead of ‘HTTP 400: Invalid request: malformed value for a typed field’. The orchestrator admin API (run reads, execution show and cancel) answers a run id that is not a full UUID with a 400 that names it (cancel answered it with a 500). The orchestrator and Platform run lists accept ?runIdPrefix=.
  • Cancelling a run through the orchestrator admin API with its run id in uppercase now cancels its jobs too. It marked the run cancelled but sent no cancel to the agents running its jobs.
  • kici run remote <fixture> now sends the event on the branch you have checked out and your HEAD commit, unless the fixture sets branch or sha. A push({ branches }) filter in a fixture no longer stands in for the event branch, and pr() and other non-push fixtures are sent as their own event type instead of a push. With an orchestrator at this version, the run records the real commit SHA, not a branch name.
  • kici run <event> --local now runs on the branch you have checked out in its default isolated mode, so a push({ branches }) filter matches as it does with --in-place. A detached HEAD runs on kici-local.
  • kici init now sets branch on its sample fixture, so kici run remote push-<branch> simulates a push to the default branch from any checkout.
  • A fixture run that matches several workflows now names every one of them. kici-admin runs list, runs show and runs jobs, kici runs list and runs show, and the dashboard show each workflow of the run and the workflow of each job, and the failure summary names each failed job with its workflow. The --json output adds workflowNames on the run and workflowName on each job, and the workflow filter matches a run that spans the named workflow.
  • kici-admin orchestrator upgrade warns when a container scaler label set would keep spawning an old agent: a kici-agent image older than the upgrade target, or kici-agent:latest with imagePullPolicy IfNotPresent. The warning names the scaler, the label set and the image line to set.
  • kici runs cancel cancels on an orchestrator directly when KICI_ORCHESTRATOR_TOKEN selects one, such as the local dev plane, instead of asking the Platform, which has never seen that run
  • kici run —local cancels its run in the local plane whenever it stops following it early (no agent picked it up, a timeout, an error, or Ctrl-C), so a later plane start never dispatches the abandoned run again
  • The bare-metal quickstart’s dual-scaler example (scalers.dual.yaml) pins the kici-agent image to the release instead of :latest, so a host that already pulled :latest no longer keeps an old agent. The compose quickstart’s upgrade steps now re-download scalers.yaml, which pins the agent image.
  • The mise init preset now puts the mise CLI on the step PATH without a pinned version, so a step can run mise run, mise exec or mise —version.
  • kici run remote <fixture> --workflow <name> now sends the fixture’s event to the named workflow only, instead of ignoring --workflow and running every workflow the event matches. A glob or --all runs each fixture against that workflow, and a name the compiled lock file lacks stops the run before upload with a list of the workflows it has.
  • kici run remote --workflow <name> now reports a run the orchestrator rejects and exits 1, instead of printing “Run started” and waiting on a run that does not exist.
  • kici run remote --history --workflow <name> now shows the run history instead of starting a run of that workflow.
  • The generic-init mise example in the SDK docs now puts the mise CLI on the step PATH, not only its shims.
  • The run-detail API names why it served the stored copy, in the X-Detail-Fallback-Reason response header.
  • The mise init preset takes a version (for example 'v2026.5.1'). The agent installs that mise release, puts it first on PATH, and fails the init when a different release answers. job() rejects a value that is not a mise release. Lock files move to schema v43; orchestrators at schema v42 still read them.
  • kici compile, kici run remote, kici run —local and kici doctor warn when .kici/ has workflows but no .kici/package.json, a layout whose workflows cannot resolve @kici-dev/sdk on an agent. kici run —local —in-place stays quiet.
  • The agent image ships common CLI tools (ps, ip, ping, jq, rsync, unzip, zstd, which and more), and apt installs packages in it again. In a container that the auto-scaler starts, steps can run sudo without a password.
  • New apt init preset: init: { apt: { packages: ['make'] } } installs Debian packages before a job’s steps, as root or through passwordless sudo. job() rejects a name that is not a Debian package name. Lock files move to schema v44; orchestrators at schema v42 still read them.
  • An orchestrator’s connection to the Platform no longer drops when a job error, failure reason, init-failure message or commit message is longer than 2,000 characters. The text is shortened and marked instead.
  • kici runs show, kici runs logs and the dashboard run page show steps and logs for kici run remote runs.
  • The dashboard’s orchestration-log history loads for kici run remote runs.
  • The Platform drops a single invalid message from an orchestrator and answers with a nack that names the failing fields, instead of closing the connection. It closes the connection after five invalid messages within a minute.
  • The orchestrator shortens long failure text before it sends run and job status to the Platform, and a job’s bound contexts now reach the Platform with its live status.
  • The orchestrator never sends a status message the Platform would refuse, logs a nack for an invalid message as an error, and keeps buffered status messages when its connection drops during a resend.
  • A failed init command reports init[] failed (exit ): and the last lines of its error output, with progress bars reduced to their final state, instead of its whole output.
  • The MCP run tools (structured result, step logs, cancel, re-run, approve and reject) work for kici run remote runs.
  • Agent job failure messages keep their last 20 lines and up to 1,500 characters; the full output stays in the job log.
  • kici run remote keeps following its run when another orchestrator cluster connects during the run.
  • kici run remote no longer skips the step output a job writes after its init commands.
  • kici runs show and kici runs logs print a note on standard error when step detail or a setup log cannot be read, and name the reason.
  • A job’s init env now reaches the init command, for a hand-written init and for the mise preset. A key that starts with KICI_ or names an operator secret is ignored, and the init step’s log names the key.
  • The mise init preset hands environment values that mise quotes, such as values with spaces or quote characters, to steps without the quotes.
  • An agent fails a job with a named error when the job’s init holds an item the agent does not know, such as a preset added in a newer release.
  • The mise-init example workflow now runs mise trust before mise install, so it works with the committed mise.toml, and hands mise’s environment to steps with mise env --dotenv, so values reach steps without shell quotes.
  • kici init —workspace makes .kici/ a workspace member: it writes .kici/package.json and adds .kici to pnpm-workspace.yaml or to the workspaces of the root package.json, so the workflows it scaffolds run on agents. It no longer adds @kici-dev/sdk to the root package.json.
  • kici compile keys the dependency cache of an npm workspace member on the repo-root package-lock.json, and siblingsDigest follows the workspace packages that npm and yarn link by version range, so an edit to such a package rebuilds the cached dependencies.
  • kici compile reinstalls the dependencies of a .kici/ workspace member with the package manager of the workspace, and kici compile —check uses the TypeScript compiler that a workspace installs at its root.
  • Agents cache and restore the dependencies of a .kici/ npm workspace member, which npm installs at the repository root, and build the workspace packages it depends on before the workflow loads.
  • kici init —workspace on an existing .kici/ adds what a workspace member needs, which is .kici/package.json and the workspace entry, without changing your workflows or the dependencies in your root package.json. It does nothing when the layout is already complete.
  • An agent the container auto-scaler starts from a pool’s own image now runs exactly one job. It stops taking work when it accepts its job and exits when the job ends, so a follow-up job always gets a fresh container.
  • Agents build and cache only the workspace packages a .kici/ npm or yarn workspace member declares, not every package in the workspace, so a failing build in an unrelated package no longer fails the job.
  • For a .kici/ that is a member of an npm or pnpm workspace, agents apply the private-registry auth from registries: to the workspace-root .npmrc, which npm and pnpm read for a member, instead of .kici/.npmrc, which they ignore.
  • kici init writes a version into .kici/package.json, and kici init —workspace adds one to an existing .kici/package.json in a yarn workspace, because yarn classic ignores a workspace member that has no version and installs nothing for it.
  • kici init writes a valid sample fixture for any branch name: on a ci/kici branch the fixture exports pushCiKici with ID push-ci/kici. The scaffolded pr-checks workflow now targets the detected default branch.
  • kici init and kici hook install pin the compile script and the pre-commit hook to the kici version that ran them, instead of kici@latest. kici init writes exact SDK and TypeScript versions when the repository .npmrc sets save-exact=true or an empty save-prefix.
  • kici-admin orchestrator upgrade and kici-admin agent upgrade install into the service’s own npm prefix, also when started through npx. When the service’s launch entry is not in a global npm install, the upgrade stops before it installs anything and names both Node.js runtimes. Use an archive upgrade, or install the package yourself and add --restart-only.
  • kici verify-attestation refuses a trust root that it must fetch over plain HTTP from a host that is not loopback, because an attacker on the network can replace the signing keys of such a trust root. Use an https:// trust root or a trust-root file. To accept the risk, add --allow-insecure-trust-root. Loopback addresses such as http://localhost still work.
  • kici runs show and the dashboard name the orchestrator error when the orchestrator no longer holds a run, instead of reporting that the run has no jobs
  • kici runs list —source matches a source routing key exactly, as documented; a bare provider such as github still filters by provider
  • The kici CLI reference renders option tables whose flags contain a pipe, such as kici init —package-manager <npm|pnpm|yarn>, as one row each.
  • kici-admin orchestrator status prints an Agent binary line: the kici-agent installed with the Node.js runtime the service runs, which is the path to use as a bare-metal binaryPath
  • kici-admin orchestrator upgrade checks every bare-metal scaler binaryPath before it installs anything: it stops when a binaryPath is not executable or is a Node.js version-manager shim, warns about a version-manager alias and about an agent from another runtime, and —rollback and —pick print the same findings as warnings; the orchestrator refuses a shim binaryPath at startup and on a scaler config reload, and its startup error names the kici-agent installed with its own runtime
  • The published @kici-dev/shared package no longer contains declaration files for its own tests.
  • kici-admin db fresh no longer fails with “database is being accessed by other users” when a client reconnects while the database is dropped
  • A Windows orchestrator with the local log store now saves webhook payloads and step logs whose names contain a character Windows refuses in a file name, such as the colons in every generic-source delivery id, and lists stored logs with forward-slash keys
  • An orchestrator with no Platform connection now records webhook deliveries, contexts, secrets, held runs and access-log entries for an organization id longer than 16 characters; a database migration widens those org_id columns to 255 characters without rewriting the tables
  • The published orchestrator package no longer contains internal test helpers.
  • Orchestrators serve the kici run remote test-run API directly at /api/v1/test, authenticated with per-person orchestrator admin tokens, in every mode, so remote runs work with no KiCI Platform account. New permissions test_run.trigger (owner, admin) and test_run.read (owner, admin, auditor).
  • New kici connect and kici disconnect save and remove a direct orchestrator target. kici connect verifies the orchestrator token before saving it and prints who it connected as.
  • kici run remote runs your working tree on an orchestrator directly, without the Platform relay: pass —orchestrator-url , set KICI_ORCHESTRATOR_URL and KICI_ORCHESTRATOR_TOKEN, or save a target with kici connect. It works with independent, hybrid and observed orchestrators; the Platform-routed path is unchanged.
  • kici types reads secret key names from a direct orchestrator target, then the Platform, then the local secret files kici run —local uses (.kici/.secrets, .kici/secrets.yaml). The file header names its source, and local files never replace a key set read from an orchestrator or the Platform. kici compile refreshes types whenever one of these sources exists.
  • kici doctor shows the active run target (a direct orchestrator or the Platform login), checks a direct target’s reachability and token, and skips the Platform checks for a developer who runs only against an orchestrator.
  • kici preview prints each job’s runs-on, runs-on-all and exclude-labels selectors readably instead of [object Object].
  • The first kici run —local no longer prints the embedded PostgreSQL server log; it goes to the local plane’s Postgres log, which a failure now names. A local plane whose first start was interrupted now finishes creating its database.
  • kici run remote now uploads the working-tree overlay to KICI_STORAGE_UPLOAD_ENDPOINT when it is set, as documented; without it the upload URL uses KICI_STORAGE_EXTERNAL_ENDPOINT, then KICI_STORAGE_ENDPOINT.
  • kici run remote —org now lists and answers the run’s approval holds in that organization instead of the active one.
  • kici run remote no longer stops silently after “Creating overlay tarball…” and exits 0 when the repository commits a pnpm node_modules tree: hard-linked files now ship as separate files and the run proceeds.
  • The kici CLI exits 1 with an internal-error message, instead of exiting 0 with its output cut off, when a command stops before it finishes.
  • The agent no longer stalls when it packs a pnpm node_modules tree for the dependency cache, or when it copies a workspace that contains one into a container job. It now packs each hard-linked file as a separate file with its own content.
  • kici run remote no longer writes a .kici-overlay-tmp directory into your working tree, and never uploads one left there by an interrupted run, which made the agent fail the overlay checksum check.
  • Ctrl-C on kici run remote now waits for the orchestrator to accept the cancel before the CLI exits, and prints the outcome; before, the CLI could exit before the cancel request was sent, leaving the run running. Press Ctrl-C again to exit at once. A batch of fixtures also stops after a cancel.
  • Deprecated, removed in v1.0.0: kici-admin api-key (no server serves it; it now points at kici-admin token create), the inert —routing-key flags on kici run remote and kici login, and the unread routingKey field in ~/.kici/config.
  • Breaking: the orchestrator accepts only lock files compiled at schema v42 or newer, and every lock must carry minReaderVersion — recompile with kici compile and push. kici login no longer takes --token: log in with the browser or --device flow, or write a personal access token to the CLI config. The CLI ignores a token key in its config and no longer reads endpoint as the Platform URL. A local dev plane created by an earlier release is recreated once. @kici-dev/compiler no longer exports CapabilityGapError, formatCapabilityGapError or CapabilityGapInfo. When no runs match, kici runs list now fails with an error if it cannot read the recent webhook activity, instead of printing No runs found.
  • Breaking: the wire protocol is version 4. The Platform, orchestrators, agents and cluster peers refuse a connection from any release before it (protocol 3), so upgrade the orchestrator, its agents and its cluster peers in the same window. The orchestrator no longer refuses an organization-wide workflow with a needs generator up front when its init-runner agents lack a capability. The evaluation waits for an agent with the kici:agent-feature:global-eval-skips-result-aware label, and fails at the wait ceiling if none arrives. The kici_orch_ws_platform_capability_gap_total metric is removed.
  • Breaking: the orchestrator refuses KICI_CLUSTER_COORDINATOR_URL (set the comma-separated KICI_CLUSTER_COORDINATOR_URLS instead) and the unused KICI_STORAGE_PATH at boot, and a KICI_PROVIDERS_GITHUB_* variable no longer maps to a config path on reload.
  • Breaking: a scaler pool declares its platform with the structured platform field. A plain platform label (windows, macos, arm64, …) without it is refused when the scaler config loads, and resources takes only the { requests, limits } form.
  • Breaking: every kici-admin command selects the org with —org. The —customer-id and —org-id spellings are removed, and dashboard-writes set —op takes only permissive, encrypted or disabled (the true/false sugar is removed).
  • Breaking: kici-admin cache purge-legacy and kici-admin secret fix-prefixed-scopes are removed. Remove objects under the retired cache layouts with a bucket lifecycle rule.
  • Breaking: a : secret reference resolves only through a scope bound to the context; the scope named after the context is no longer read. /health on the orchestrator and the agent, and kici-admin orchestrator/agent status —json, no longer report buildCommit.
  • Breaking: a database upgrade rewrites stored legacy run statuses and hold types, and the trust-policy approval window is set in seconds. kici-admin trust-policy set takes —approval-expiry (for example 72h or 30m) in place of —approval-expiry-hours and —approval-expiry-seconds, and the dashboard takes the same duration. The Platform trust-policy API (GET and PUT /api/v1/orgs/:customerId/trust-policy) and the orchestrator admin trust-policy API no longer accept or return approvalExpiryHours: send approvalExpirySeconds.
  • Breaking: the dashboard API serves context edits, secrets, secret backends, the event DLQ and global-workflow settings only under /orgs/:customerId/orchestrators/:clusterName/. The organization-level copies of those routes are removed. The organization-level context list and delete, the workflow registration routes and the held-run routes stay.
  • Breaking: the hosted Platform no longer publishes a provenance issuer, so attestation bundles it signed no longer verify. Verify against your configured orchestrator, or the issuer you pass with —trust-root. The auth.success message no longer carries provenanceIssuer.
  • Breaking: kici verify-attestation with no —trust-root and no configured orchestrator now exits 1 and names both remedies, instead of falling back to the hosted Platform’s issuer.
  • kici-admin db schema-diff compares an orchestrator database with the schema the installed release expects, and exits 2 on any difference.
  • kici-admin org list prints every org id the orchestrator holds data for, and the data that names each one (—json; —database-url reads the database directly). kici-admin orchestrator status prints the Platform org of an attached orchestrator when an admin token is set. The org-id help of the secret, context, variable and remote-source commands points at kici-admin org list.
  • Breaking: kici-admin’s default —url is now http://localhost:4000, the orchestrator’s default port; pass —url to reach another address.
  • Breaking: db check-schema exits 1 instead of 2 when it cannot read the database, and its —json output carries a kind field. kici-admin db migrate gains —database-url, which migrates a database directly without a running orchestrator.
  • kici-admin scaler reload applies a scalers.yaml edit on the orchestrator it points at and every orchestrator connected to it, or on that one only with —single, and prints what changed or why the file was refused.
  • A job that a cluster worker dispatches now waits for a concurrency slot as long as the cluster’s concurrency_wait_timeout_ms setting allows. Before, the agent used its own KICI_CONCURRENCY_WAIT_TIMEOUT_MS value for such a job.
  • A routing key with no registered source no longer borrows another GitHub App’s bundle; only a provider-wide default bundle stands in for it.
  • A re-run that reaches the Platform only through the orchestrator’s reconnect replay now records its original run, so it shows in the re-run list of the run it re-runs.
  • The orchestrator admin trust-policy API now refuses an approval window longer than one year, the bound kici-admin and the Platform already applied.
  • The MCP approve_run and reject_run tools now find a remote run’s held approval. The relay carried no organization, so the orchestrator looked the hold up under its own connection organization and missed a hold recorded under the run’s organization.
  • The dashboard attestation detail page now verifies a bundle against the issuer of the orchestrator that signed it, the same issuer the attestation lists use.
  • The scaler no longer provisions a second agent for a job whose first agent is still being provisioned or has not yet taken the job. The pending-job re-offer skips such a job on every coordinator, so an event scaler without maxAgents no longer requests two cloud runners for one job.
  • When kici-admin cannot reach its built-in default address, the error now says to set KICI_ADMIN_URL or pass —url to reach another orchestrator.
  • kici-admin db check-schema no longer reports a content hash mismatch for a database the orchestrator migrated itself: the migration content hash ignores the identifier renames that differ between the CLI and the orchestrator bundle.
  • A coordinator whose previous credential and its replacement were revoked in the same millisecond now reads the newer one when it decides whether its credential was retired or revoked.
  • kici-admin cold-store list-chunks, verify-chunk, peek-chunk, replay-chunk and reconcile now find chunks archived under a cold-retention bucket (access_log, secret_audit_log), which they previously skipped or could not address.
  • kici-admin cold-store reconcile now rebuilds the run-id index of a manifest it recreates, so rerunning an archived run finds a chunk whose manifest reconcile rebuilt.
  • kici-admin cold-store reconcile orders numeric row ids the way the archiver does, so it no longer rejects a chunk with numeric ids as possibly tampered.
  • kici-admin org-settings show prints (cluster default) for an override the orchestrator does not report, and (not reported) for any other setting it does not report, instead of ‘undefined’. It no longer fails when the sandbox capability list is absent.
  • A kici run remote run of a workflow with only dynamic jobs is now marked as a local working-tree run, and its row records the secret context, trust context and test-run stamp like any other run.
  • kici-admin config reload reports the scaler config’s own reload outcome in a scaler field, so a refused scalers.yaml no longer looks like a plain success.
  • A worker-role orchestrator reloads its scaler config on SIGHUP instead of exiting.
  • The orchestrator readiness check (/ready) no longer reports the database as down for a moment after a PostgreSQL failover.
  • The activity and audit-log docs no longer claim that the old organization audit-log URL redirects to the activity page; that URL is gone.
  • The upgrade and rollback guide no longer says migrations are forward-only: it names kici-admin db migrate —to for a schema rollback.
  • Deprecated: kici-admin cold-store reconcile —confirm-cleanup never had an effect. It is still accepted, now prints a deprecation notice, and is removed in v1.0.0.
  • Observed-mode orchestrators accept GitHub App sources. GitHub delivers to the orchestrator’s own URL, so check runs, App clone tokens and /kici approve comments work without the Platform relay. The orchestrator also serves an org-level GitHub App URL, /webhook/<orgId>/github.
  • kici-admin source add github --manifest works in every mode. It sets the App’s webhook URL to the hosted Platform’s URL in platform and hybrid mode, and to the orchestrator’s own URL in observed and independent mode. kici-admin source list prints each GitHub source’s direct ingress URL.
  • kici-admin source refresh reports a GitHub App that lacks an event or permission KiCI needs, such as the Issue comment event, and lists installations that have not accepted a new permission. The daily refresh logs a warning for each such App.
  • kici report keeps the Platform endpoint, OIDC issuer, active organization id and token expiry readable in the bundle. When a run is not found, the bundle names the Platform and organization the command searched. The token and its id stay redacted.
  • An agent on a Debian-packaged Node.js, whose npm is installed separately, now runs a container job’s host install when that npm is 11.10.0 or later. The agent uses Node’s own npm when it has one. Otherwise it uses /usr/share/nodejs/npm, or an npm on its PATH that is not a version-manager shim. When no npm qualifies, the job container installs as before, and the setup log names the npm the agent refused and its version.
  • kici-admin --version now prints the CLI version instead of failing with “unknown option ‘—version’”. -V and --cli-version keep working, and --version after a command name (kici-admin orchestrator upgrade --version <version>) still sets that command’s option.
  • kici-admin now gives a command its own option when that option has the same name as a global option. kici-admin orchestrator upgrade --url <url> and agent upgrade --url <url> download the archive from that URL, and --token reaches join, agent install, backend add and backend test. Before, kici-admin read these values as the global orchestrator URL or admin token, so the command did not get them (join failed with “required option ‘—token ’ not specified”). Put --url or --token before the command name, or use -u or -t, to set the global option for these commands.
  • kici-admin now gives a command option a value that starts with -u, -t or -V, or that is --url or --token, such as db create-role --password -uP4ss. Before, kici-admin read the value as a global option, so the command failed, or kici-admin printed its version and did not run the command.
  • When the agent cannot install a workflow’s .kici dependencies, the job error now gives the installer’s exit code, the end of its stderr and stdout, and for npm the end of npm’s debug log, with registry tokens masked. Before, the error could be only the install command line, for example when npm wrote its error to stdout or exited without output.
  • The agent keeps masking a job’s secrets in its logs, step errors and failure reason after the job requests an OIDC token or mounts a secret as a file. Before, each of those later registrations replaced the set of masked values, so the secrets registered before it showed in clear text. The job failure reason from a failed init command, a concurrency-group error and a runner crash is now masked too.
  • The agent no longer runs as PID 1 in Firecracker microVMs or in the agent container image. tini is PID 1 and runs the agent as its child. It reaps the processes a job leaves behind, so a killed step leaves no zombie processes, and its process group no longer reads as alive. Refresh each Firecracker rootfs with build-agent-rootfs.sh —agent-only to install tini. When the container scaler or a bare-metal scaler runs a job’s own container image as the agent, the agent starts under tini from the KiCI runtime, so the image needs no init.
  • A bare-metal scaler that runs a job’s own container image as the agent (a label set with an image and no binaryPath) now works on an orchestrator that runs as a non-root user. Set requireSudo: true on the scaler, and give the orchestrator’s user a NOPASSWD sudoers rule for nft; the scaler then runs its nftables isolation rules through sudo -n. The orchestrator checks this when it loads the scaler: if it cannot run nft that way, it refuses to start and names both fixes, instead of failing every job. The scaler refuses a rootless container runtime for these agents, because the host’s nftables rules never see a rootless runtime’s traffic. It also creates the kici nftables table before the first rule, and passes its extraHosts to the agent containers. The container scaler and the bare-metal scaler now remove an agent container that gets no address on the isolated network, instead of leaving it running without isolation rules.
  • The dashboard can no longer set reserved kici: host labels or properties, change an existing fleet host, or remove a host its agent or kici-admin confirmed. A host declared from the dashboard stays unconfirmed, and out of runsOnAll fan-out, inventory queries and the unreachable-host alarm, until its agent registers or an operator confirms it with kici-admin host declare.
  • kici-admin host declare without —labels now keeps the host’s stored labels, as documented. It used to clear them, so adding a —prop to an existing host dropped it out of runsOnAll targeting.
  • kici-admin source add github --manifest now creates the GitHub App with the Issues read permission and the Issue comment event, so /kici approve and /kici reject pull-request comments reach the orchestrator. To enable them on an App you already have, see the GitHub App provider guide.
  • The hosted Platform answers 409 to a webhook for a source that only observed-mode orchestrators serve, instead of buffering a delivery that no orchestrator can accept. The response tells you to send the webhook to the orchestrator’s own URL.
  • A GitHub App created with the manifest flow receives its first deliveries at once. The creation ping that reaches the hosted Platform before the source is registered no longer blocks the App for five minutes.
  • The hosted Platform forgets a cached 404 for a webhook source when an orchestrator registers that source, so a source added after a failed delivery receives the next delivery at once.
  • An orchestrator takes a peer’s role and routing key from the join token it issued, not from the token’s editable routing part.
  • Breaking: kici-admin join uses join protocol v2. The join secret never leaves the joining host, and the configuration bundle is sealed to a one-time key the Platform cannot derive. Orchestrators refuse the earlier exchange, so an older kici-admin cannot join, and a new kici-admin cannot join a cluster whose orchestrators are older. Upgrade the cluster’s orchestrators first, then kici-admin. Join tokens keep their format.
  • kici-admin join —platform authenticates with the Platform. It used to send a malformed auth message, and the Platform closed the connection.
  • kici-admin join exits as soon as the join completes, instead of waiting up to 30 seconds.
  • kici-admin peer create-token no longer says a join token can be used once: it works until it expires.
  • An orchestrator answers a join that fails on an internal error, such as a database error, with a generic message; the detail stays in its log.
  • kici-admin join writes KICI_SECRET_KEY even when the cluster’s shared configuration holds no secrets.key: the answering orchestrator hands over its own secrets key, as the env file already said.
  • kici-admin join —env-file and kici-admin orchestrator install —env-file reach the CLI: Node reads —env-file from script arguments too, so the join exited before it started and install loaded the file into the CLI’s own environment.
  • Breaking: peer authentication between orchestrators is mutual and bound to the handshake. An orchestrator accepts no message from a peer it dials until that peer proves it holds the same credential or join token. A join token no longer leaves the joining host. Orchestrators refuse the earlier peer handshake, so upgrade every coordinator and worker in a cluster together. Until the last one is upgraded, upgraded and older orchestrators do not connect to each other. KICI_CLUSTER_PEER_DISCOVERY=static turns off Platform peer discovery, so a coordinator dials only KICI_CLUSTER_PEERS. A peer’s rejection no longer deletes a coordinator’s credential file while the coordinator’s own database holds that credential as valid.
  • An orchestrator no longer marks a peer disconnected when the outbound connection it replaced closes after the new one is up.
  • An orchestrator keeps a peer’s inbound connection when that peer reconnects before its earlier connection closes, and stops the earlier connection’s heartbeats.
  • The website and documentation now lead with KiCI as a complete CI/CD system on your own infrastructure, with a full dev loop your coding agent can own.
  • Maintenance release.
  • An agent with git 2.54 or later now computes the changed files of a pull request or a new branch push. It reported them as unavailable, so a rule or workflow filter that reads the changed files failed
  • The Firecracker API-socket wait is now an operator setting, kici-admin cluster-settings set —firecracker-api-socket-wait-ms, with a 30 s default (it was a fixed 5 s) and the KICI_FIRECRACKER_API_SOCKET_WAIT_MS cluster default. A spawn on a host whose disk is busy with writes no longer fails because the jailer’s copy of the firecracker binary took more than 5 s. Workers receive the value from the leader
  • kici-admin org-settings reroute set takes —spawn-max-attempts and —spawn-retry-backoff: how many agent spawns a worker attempts for a rerouted job, and how long it waits between them
  • A kici.scaler event that matches no subscribing workflow is logged at info and counted in kici_orch_event_unmatched_total, with a panel in the monitoring-pack dashboard
  • kici-admin event list and event show inspect internal events: each event’s dispatch state, whether it matched a workflow (or why none did), and the runs it dispatched
  • kici-admin scaler orphans lists the live Firecracker VMs a node’s orchestrator does not track, on a coordinator or on any worker through —target, and —stop reclaims them after a confirmation
  • kici-admin peer forget drops a peer that left the cluster from the live peer list of every coordinator, without touching its credential, so a coordinator that is gone for good no longer keeps kici-admin scaler orphans from stopping anything. It refuses a peer the coordinator can still treat as alive (inside the reroute flap grace, or inside the event-scaler backstop’s flap grace for a peer that adopted provisions), and asks for confirmation (or —yes) before forgetting the last coordinator peer turns the event-scaler backstop back on.
  • kici-admin cluster-settings show now lists the settings version each coordinator and worker applied, and flags the ones that are not on the current version
  • A failed Firecracker spawn step now reports the command’s exit code or signal, whether its timeout killed it, how long it ran and the end of its stderr. The same detail reaches the scaler.failed event, the job’s provisioning error and the spawn-failure log lines, and failed nftables commands in every scaler report the same fields
  • A Firecracker spawn no longer formats its overlay drive. The orchestrator formats one template for each overlay size on the host and copies it sparsely for each VM, so a spawn stays fast while other processes write heavily to the same disk, instead of timing out at mkfs.ext4
  • A Firecracker VM that stops before its agent registers is now torn down within seconds, and a spawn that never registers has its compute destroyed when the orchestrator gives up on it. Such a VM no longer keeps counting against maxAgents, which could leave a scaler unable to spawn
  • A job rerouted to a worker is no longer failed while the worker is still retrying the agent spawn: the worker retries a bounded number of times, the coordinator waits for its verdict, and a job the coordinator gives up on can no longer run on the worker afterwards
  • A bare-metal scaler on a Windows orchestrator now starts its agents. It runs a .cmd or .bat agent launcher through cmd.exe, because Node.js does not start a batch file directly.
  • Stopping a bare-metal agent on Windows now ends its whole process tree with taskkill. Before, the agent kept running after the scaler released it.
  • A Windows orchestrator now refuses to start when a bare-metal binaryPath batch file contains a character that cmd.exe reads as syntax. The error names the scaler and the label set.
  • When the host refuses to start a bare-metal agent, the scaler now reports the failure in kici-admin diagnose and on the waiting job, and defers that scaler. Before, it retried several times a second.
  • An agent on Windows no longer refuses to start when bash is not on PATH. Workflow steps on Windows run in PowerShell 7, as the agent runtime dependencies describe, and the Git for Windows installer puts only git on PATH.
  • A bare-metal agent that stops before it connects is now reported at once, with its last output, in kici-admin diagnose and on the waiting job. A bare-metal or Firecracker scaler whose agents stop before they connect is now deferred with the provision backoff, instead of starting a new agent each time.
  • kici-admin workflow register-manual over HTTP no longer deletes the repository’s other workflow registrations; it upserts the workflows the lock file names, as documented
  • Orchestrator log lines keep the run and job ids their call site names instead of the surrounding request’s, and agent spawn lines name the job’s own run
  • A pending event-scaler provision whose job is cancelled or finishes is torn down with reason job-complete within one 30-second sweep, and is no longer reported as a provisioning failure and no longer backs the scaler off; the provisioning-failure message now states only that the scale-up event was emitted
  • The event dead-letter queue (kici-admin event-dlq list and the dashboard) no longer shows an event-scaler scale-up’s single-use claim code; it reads [redacted]
  • kici-admin event-dlq list returns event payloads only to a role holding event_log.read_payload; the auditor role reads DLQ metadata only, as documented
  • A Firecracker orchestrator running as a non-root user now stops its VMs through sudo as the jailer user. When it cannot stop a VM, it logs why and keeps the VM’s chroot, so kici-admin scaler orphans can list the VM and stop it later. Before, it left the VM running without a chroot, and no command found it. The unowned-VM reclaim also keeps the chroot of a running process it cannot identify.
  • A Firecracker orchestrator running as root now recognises its own VM processes. It compared the text of the process root link, which reads / from the host for every jailed VM, so it treated each VM as unidentified and never force-killed one during teardown.
  • The orchestrator no longer fails a job that an agent is running when it reaps an older stale job of the same agent that reconnected since; it disconnects only an agent that still holds the stale job
  • A DB-less worker could record a newer cluster-settings version with the values of the previous one and keep them until the next change; a coordinator now serves the snapshot and its version from one read of the settings row
  • The Firecracker host setup guide’s sudoers allowlist now lists chown and chmod, which an orchestrator with requireSudo needs to start VMs
  • kici-admin source trigger-local accepts —before (default: the parent of —sha), install-hook forwards the pushed ref’s old commit, and kici run sends the parent of its snapshot, so path filters and changedFiles rules see a real diff
  • kici-admin peer list —json prints the active peer credentials as JSON, including whether a coordinator issued the credential to itself and which coordinator last validated it
  • Path filters decide from the agent’s clone when the orchestrator cannot fetch the changed files, instead of running every path-filtered workflow
  • A GitHub new-branch push runs the path-filtered workflows whose paths the branch touches, and a diff GitHub or a forge truncated no longer silently skips a path-filtered workflow
  • Path-filtered workflows on local sources, and organization-wide workflows on a repository with no lock file or whose own lock file declares no paths filters, now evaluate their paths filters instead of never matching
  • A failed changed-files lookup no longer fails the whole webhook delivery; the agent decides the affected path filters from its clone
  • A generic webhook routed to a repository applies that repository’s push and pull-request paths filters instead of firing nothing
  • A universal-git source’s own workflows run even when another source’s registrations also match the event or repository
  • On a new-branch push, rules and filters see the files the branch adds instead of every tracked file; on a push with no before commit, ctx.changedFiles throws ChangedFilesUnavailableError as documented
  • A pull request’s changedFiles no longer comes back empty when the agent had to re-fetch the head commit, and the files a new branch adds are computed on the agent’s shallow clone
  • A cache hit that raced a dependency rebuild no longer puts the old dependency pointer back, so the rebuilt dependency tarball stays reachable from the cache
  • A graceful orchestrator stop no longer spends its whole shutdown window waiting for a slow container runtime to stop agents, and the orphaned agent containers a start cleans up are stopped in parallel
  • Jobs that run after a build job restore the dependencies it just cached, instead of installing them again; kici-admin org-settings cache-upload-settle bounds the wait
  • A cluster coordinator now reroutes jobs and forwards cancels to a sibling it reaches only through the sibling’s own connection
  • A run cancel now stops a job whose agent reconnected to a different coordinator, and a forwarded force cancel stays forced, including when the leader sends it again after the first forward failed
  • An agent error for a failed source tarball or overlay download no longer includes the presigned URL’s signature.
  • A dependency cache restore no longer fails when the storage server closes the connection early. The agent now downloads and verifies the tarball before it extracts it, and it continues a cut download. Init and evaluation jobs install their dependencies inline when the restore fails, as execution jobs do. User cache restores (ctx.cache.restore()) and artifact downloads (ctx.artifacts.download()) also download and verify the tarball before they extract it.
  • An autoscaled agent that the orchestrator drops after a missed heartbeat or an unacknowledged dispatch is now destroyed, and the scaler gets its capacity back. Before, each such agent kept a scaler slot, and a scaler at its agent limit stopped starting new agents.
  • When the orchestrator disconnects an agent because its token was revoked or expired, or because the agent did not acknowledge a dispatch, the agent’s running jobs now get the same handling as when an agent loses its connection. Before, those jobs stayed assigned to the gone agent until the stale-run detector failed them. A build, init job, evaluation or global evaluation round that waits on a job of an agent dropped for missed heartbeats now ends when that job fails, instead of at its timeout.
  • An agent can no longer register under the id of a live agent with a different agent token. The orchestrator refuses the registration while the live agent stays connected.
  • A cluster coordinator without a join token issues its own peer credential on its first outbound peer connection, so the connection authenticates instead of closing with “No auth method”; it retires the credential its previous run issued, and never re-issues a credential an operator revoked
  • Join-token errors name the real command to create a new token, kici-admin peer create-token
  • kici-admin peer revoke says that the peer’s next connection attempt is refused and its open connections stay up, instead of claiming the peer is disconnected on its next heartbeat
  • The published packages now require Node.js 24.5.0 or later. The agent downloads dependency caches through the Node.js HTTP client, which honors NODE_USE_ENV_PROXY from Node.js 24.5.0 on. Upgrade Node.js to 24.5.0 or later on every host that runs the orchestrator, the agent or the CLIs.
  • kici-admin agent install —port sets the agent’s HTTP port (KICI_PORT), so several agents can run on one host.
  • Each release now attaches standalone packages to its GitHub release on kici-dev/kici-public: every component, full (Node.js and npm included) and light, for Linux, macOS and Windows on x64 and arm64, with a SHA256SUMS file to verify them.
  • kici-admin orchestrator status and kici-admin agent status now show what the running service reports: health, readiness, version, build date, SDK bundle and uptime for the orchestrator, and agent ID, orchestrator connection, active jobs and version for the agent. The orchestrator section was empty, and the agent section always claimed no labels and an idle agent. agent status also queried port 4001 when the env file set no KICI_PORT, where the agent listens on 8080.
  • kici-admin agent install —labels and the setup wizard wrote the labels as KICI_AGENT_LABELS, a variable the agent does not read and refuses at startup, so an agent installed with labels did not start. They now write KICI_LABELS. The wizard also stops claiming that an agent with no labels accepts every job: such an agent matches only jobs its kici:* labels satisfy.
  • kici-admin orchestrator stop and kici-admin agent stop now succeed on a Windows service that is already stopped. They failed with sc.exe error 1062 (the service has not been started). Any other stop failure, such as access denied, still reports an error.
  • kici-admin orchestrator stop and kici-admin agent stop now stop a macOS (launchd) service and keep it stopped. They sent SIGTERM, and launchd restarted the process at once because the service keeps itself alive. stop now unloads the job and waits for the process to exit. stop also succeeds on a service that is already stopped. start loads the job again and leaves a running service alone, so start no longer restarts a running service. status reports an unloaded service as stopped, and reads a system-level service correctly without sudo. New installs give the process 45 seconds (orchestrator) or 20 seconds (agent) to shut down before launchd kills it. Re-run install to apply this to an existing service.
  • The Dockerfiles in the public source repository now build from the repository root, as the operator guides describe. The repository ships an .npmrc that sets no registry, so the COPY step that reads it no longer fails. The image builds copy this file and add their build-time settings to it.
  • The full standalone packages now carry npm beside their embedded Node.js binary, so a kici-agent started from one passes its builder-role npm check on a host with no npm installed; it stopped at startup with “Builder role requires npm”. When the cached Node.js binary is missing, the light packages’ launchers now print the steps that install it, npm included; they named a kici-admin ensure-node command that does not exist. The Windows light launcher also starts when the user profile path holds a parenthesis or an ampersand; it failed there even with Node.js cached.
  • On macOS, launchd now restarts a KiCI service only when its process fails: the process exits non-zero, or a signal kills it. A service that exits 0, such as an agent that drains after SIGUSR1, now stays stopped; launchd started it again at once. The lifecycle commands now also manage a service whose KiCI plist file is com.kici..plist or .plist, under the label that the plist declares; stop answered that such a service was not installed. install moves such a service to dev.kici.. To apply the new restart behavior to an existing service, re-run install for it.
  • On Windows, stop now gives the process 45 seconds (orchestrator) or 20 seconds (agent) to shut down; the service wrapper killed it 3 seconds after the stop. A service whose —binary is a batch file (.cmd or .bat) now stops when its process exits. Its cmd.exe waited at the “Terminate batch job (Y/N)?” prompt until the wrapper killed cmd.exe, and a process that needed more than 3 seconds to shut down kept running after the service reported stopped. The batch file now runs with its input from NUL, so a command in it that waits for input, such as timeout or pause, does not wait. install refuses a batch-file path that cmd.exe would read as syntax. To apply this to an existing service, re-run install for it, or upgrade it with the kici-admin of this release.
  • An init command’s cache (the mise preset’s, or a cache set on a hand-written init) no longer fails the job when it cannot be restored or saved. A failed restore, such as a network error while downloading the archive, now runs the init command without the cache, as a cache miss does, and a failed save after the command succeeded is reported without failing the job. Either one writes a line to the init step’s log that names the cache key and the error.
  • On Windows, the orchestrator and agent services no longer carry the env file’s values, such as the database URL, the secrets key and the agent token, on their command line, where any local account could read them with sc.exe qc. The service receives only the env file’s path in KICI_ENV_FILE and reads the file when it starts, so an edit to the file takes effect at the next restart. For a system-level service, install also limits access to the instance’s config folder, which holds the env file and the service logs, to LocalSystem and Administrators. Before, every local user could read the env file. To move a service that an older CLI installed onto the new registration, re-run install for it, or upgrade it with the kici-admin of this release, which registers the service again.
  • Building the agent or orchestrator image from a source checkout no longer copies local node_modules, dist, .env or key files (.pem, .key) into the image. A .dockerignore file at the repository root now excludes them from the build context.
  • On Windows, kici-admin orchestrator upgrade and kici-admin agent upgrade without —from or —url now register the service again before they start it, as an archive upgrade does. So an upgraded service gets the registration that the new kici-admin writes, such as the env file’s path in place of its values, and no second install is necessary. —restart-only also registers the service again. The registration an upgrade writes now has the same description as the one install writes. The self-driving upgrade now runs npm when Node.js is installed under C:\Program Files; it failed there before npm started. On Windows, an upgrade, —rollback or —pick to a release from before KICI_ENV_FILE is now refused, and install refuses such a release as —binary. That release can run only with the env file’s values on its command line. A refused command does not change the service, which keeps running.
  • A clustered orchestrator now stops within seconds and exits 0 on SIGTERM. During its own shutdown it could be elected Raft leader again, which restarted its leader-only services. A database query started at that moment, or a WebSocket that did not finish closing, then held the shutdown until the 30-second limit forced the exit, sometimes with exit code 1. A shutdown also no longer loses its last log lines from the KICI_LOG_DIR file. With KICI_LOG_DIR set, the bare-metal scaler no longer leaks an open log file for each agent it starts with the trusted host environment.
  • Re-running kici-admin orchestrator install or kici-admin agent install for an existing instance now updates its manifest instead of replacing it. The migration heads it recorded and its install time stay, so a rollback after a re-install still checks the database schema. The recorded version is now the version of the launcher that the service starts. When the launcher does not show a version, the version recorded before stays. Before, a re-install recorded the version of the kici-admin that ran it. A compose re-install records the release of the image its compose file pins. The warning a rollback prints when no migration head is recorded now also names the other cause: an upgrade that ran without admin access (KICI_ADMIN_TOKEN).
  • kici-admin orchestrator install —platform compose and kici-admin agent install —platform compose now pin the kici-orchestrator or kici-agent image when the service has a custom —name. Before, the compose file named an image after the service, which is not published.
  • An orchestrator no longer shuts down when a database query fails while it handles a message from an agent, a peer or the KiCI Platform. It logs the failure with the message type and the agent, run or request. A registered agent stays connected. An agent whose token check fails on a database error is disconnected and connects again. A queued job whose dispatch failed before it reached the agent goes back to the queue and is offered again.
  • Every standalone package now carries its license (LICENSE), the KiCI license matrix (LICENSES.md), the licenses of the software bundled into it (THIRD-PARTY-NOTICES) and where to get its source code (SOURCE). A full package also carries the license of the Node.js runtime it includes (NODE-LICENSE).
  • The public source repository now includes the scripts that build the standalone packages, so each release’s source tree can rebuild the archives it ships.
  • Firecracker microVMs no longer carry a built-in hosts entry for a development npm registry. A Firecracker scaler now honors extraHosts (host:address, with host-gateway for the bridge gateway), which the VM /init adds to /etc/hosts; a VM gets no mapping by default. The fast rootfs refresh (--agent-only) now also refreshes /init. Upgrade notes: refresh the rootfs with --agent-only so its /init applies the mappings. A Firecracker scaler used to ignore extraHosts; now an entry that is not host:address (for example host=address) stops the orchestrator at startup.
  • kici-admin orchestrator status and kici-admin agent status show the release version of the service, not a build commit. The orchestrator and the agent no longer report a build commit on /health or in their startup log. The buildCommit field of /health and of the status —json output is deprecated: it carries the version, and v1.0.0 removes it.
  • The kici local dev plane stamps the kici build date instead of a build commit. A plane that an older kici started is replaced once, on the next kici local up or kici run —local.
  • kici-admin: the help for access-log list, event-log list, runs list, runs jobs, runs logs and workflow list names the admin API route each command reads.
  • The Firecracker host check now reads the NAT and isolation rules for the bridge subnet. When a second bridge was provisioned into the same nftables table, the VMs on the first bridge lost internet access and could not resolve a host name, but kici-admin firecracker verify, kici-admin diagnose and the orchestrator startup check still reported the bridge healthy. They now report the missing rules, and the orchestrator provisions the bridge again at startup. Give each bridge on a host its own firecracker.table: two bridges in one table remove each other’s NAT.
  • Firecracker scaler: the orphan interface sweep never deletes the configured bridge (firecracker.bridgeName), even when it is named like a per-VM TAP (kici- plus 8 hex characters). The sweep only considers names of that shape, so an interface named otherwise, such as kici-br1, is never deleted.
  • The published kici-orchestrator and kici-agent images’ build provenance names their base images by the public dhi.io reference and digest.
  • The public source repository now carries the Firecracker host scripts in scripts/firecracker/: the host validation, the pinned Firecracker installer, the jailer setup and the agent rootfs build. The operator docs build the rootfs from a checkout of the release tag. The validation script looks for the binaries in /usr/local/bin, where the installer puts them, finds nft for a regular user, and accepts an up bridge with no VM attached; it no longer takes --boot-test, which checked nothing. The rootfs build creates its output directory, stops before any root step when the workspace is not installed and built, and checks only that with --check-workspace. It keeps the stamps of a custom BASE_CACHE_PATH beside that base, so the first build with a custom base cache rebuilds it, and --agent-only refuses until then.
  • The Docker and bare-metal quickstarts say sign-up at app.kici.dev is open to everyone and free.
  • The global workflows guide’s invoke-gate example now sets timeout in milliseconds, the unit a job timeout takes, instead of a string that does not typecheck.
  • The documentation examples use neutral example values: the OIDC workflow examples, the global-workflows CLI examples, the multi-architecture build tags, the log-file names and the scaler names no longer show names from the KiCI team’s own test environment.
  • The public documentation no longer shows an internal build commit, and the multi-architecture page now describes the published multi-arch images and mixed-architecture clusters, not a build script the public repository does not ship.
  • The docs site header, mobile menu, and footer now link back to the kici.dev homepage.
  • The Hetzner autoscaling reaper is a public example, examples/hetzner-autoscale/reap.ts: it runs with plain node, reads Hetzner’s own HCLOUD_TOKEN, and deletes labelled servers older than a TTL when a teardown workflow never ran.
  • Every operation the dashboard-write policy can disable now has a working kici-admin command. New verbs: secret scope create, rename and delete; context source-override list, set and delete; registration disable, enable and delete. The orchestrator admin API adds routes for context source overrides and for disabling a registration. Secret scope create, rename and delete through the admin API now write a createScope, renameScope or deleteScope row to the secret audit log. The command the dashboard shows for a disabled operation now names a command that exists.
  • A kici-admin command that asks for confirmation now stops with a non-zero exit when stdin closes before an answer arrives. Run from a script without —yes, such a command printed the prompt, changed nothing and exited 0. This covers secret delete, secret scope delete, variable delete, registration delete, backend remove, source remove, attestations reverify —all, the signing-key and dashboard-encryption-key prompts, and the upgrade, switch and rollback prompts.
  • The autoscaling guide’s Hetzner example now installs Docker on the instance, which container delivery needs to start the agent.
  • The global workflows guide now says that a repos: workflow is not registered while the fleet-wide switch is off, and that you push the workflow repo’s default branch again after you turn the switch on.
  • kici-admin help text no longer carries internal planning labels: audit —include-archived and cold-store replay-into-pg, list-purgeable and purge-now describe what they do.
  • The event routing guide’s registry refresh example now sends the routingKey field the refresh route requires, so the documented request no longer fails with HTTP 400.
  • The SDK and workflow-pattern reference pages are corrected: every GitHub trigger’s repos option, the requires and commitMessage filters on pr() and push(), the full ContainerConfig and step options, AgentInfo.host, and section headings that sat on the wrong page.
  • Contexts can now be limited to repositories by pattern from kici-admin (—repo-patterns), the admin API and the dashboard. Re-running kici-admin context create no longer clears protection rules whose flags were left out, and no longer re-enables a disabled context unless --enabled is given.
  • Behavior change: existing organization-wide (global) jobs that declare contexts: start receiving those contexts’ secrets on upgrade when an event from another repository fires them. The contexts come from the workflow repository, and their repository and branch rules check that repository and the branch its workflow was registered from. Global workflows now run with the same features as any other workflow: a global job’s container, env, timeout and approval gates take effect for events from other repositories, and kici compile no longer refuses approval on a global workflow. A held fork pull request now holds its global runs, and approving the hold runs them. Global runs can be re-run, which needs repository scope on the workflow repository. OIDC ID tokens gain a workflow_repository claim. The KiCI: Organization workflows check is no longer posted. Existing organization-wide workflows on pull-request triggers that bind contexts start receiving those secrets next to the source repository’s code; if fork pull requests are allowed, set minimumTrust on those contexts before upgrading. A lock whose organization-wide workflow declares approval requires orchestrator schema v42 or newer, so an older orchestrator rejects it instead of running the workflow ungated. A run summoned through invokeSource inherits the global run’s trust tier, so a fork pull request’s summoned run meets the source repository’s minimumTrust gates. The user cache of a global run moves to a namespace per workflow repository and source repository, so expect one round of cache misses after the upgrade. Agents self-report a new kici:agent-feature:global-eval-skips-result-aware label.
  • kici-admin runs show shows where an organization-wide workflow is defined. For a run whose workflow comes from another repository, the header prints Defined in: with that repository, plus the workflow commit and branch the run dispatched. --json and GET /api/v1/admin/runs/:runId carry the same values as workflowRepoIdentifier, workflowSha and workflowBranch.
  • kici-admin context create and kici-admin secret set warn on stderr when the fixed or glob context they touch has no binding. Such a context delivers none of its secrets to the jobs that list it in contexts:. For a fixed context, the warning also says that a <context>:<key> git credential or registry reference still reads the scope named after the context, through a deprecated fallback. The warning names the kici-admin context bind command that fixes it, and the exit code does not change. The orchestrator also logs a warning when it dispatches a job that lists such a context, naming the organization, context, run, and job.
  • Organization-wide workflows now use the dependency cache: a run restores its workflow repository’s dependencies from the cache instead of installing them on every run. An existing organization-wide workflow picks this up on its workflow repository’s next default-branch push.
  • Jobs created by a dynamic job generator now pass the same context protection rules as other jobs — branch and repository restrictions, required reviewers, wait timers and minimum trust — before they receive a context’s secrets. Contexts that a lock file names at the workflow level now pass these rules for every job of the workflow, as if each job named them itself. Before, a workflow-level context gave its secrets to every job with no check.
  • A job held by a context’s protection rule (required reviewers, wait timer, minimum trust or concurrency limit) now receives that context’s secrets and variables when it is released.
  • Cancelling a run that has not started any job now marks it cancelled, and a held run released twice dispatches only once. Cancelling a run that waits for approval also withdraws its approval request: the hold is rejected, its pending checks close as cancelled, and a later approval starts nothing. Cancelling a run also withdraws every approval request it still has open.
  • Organization-wide workflow jobs that run in a container now check out the workflow repository correctly. A job that builds its container image from a Dockerfile during kici run now builds it from the same working tree its steps see, uncommitted changes included.
  • The agent now refuses a local-changes overlay whose file list points outside the repository.
  • A container job whose runner exits without reporting now fails promptly with the exit code and the runner’s error, instead of hanging; an image without Node and no configured runtime fails with a message naming the runtime settings. A container job that fails before its first step now reports the runner’s error.
  • A job is no longer failed after five quick retries when its agent briefly reports busy while it finishes tearing down the previous job. An agent that stays busy still fails the job after its retries.
  • Jobs held for approval or waiting on upstream jobs no longer keep their context secrets in the orchestrator database; the secrets are resolved when the job is dispatched. A secret you rotate while such a job waits now reaches the job with its new value. A job whose context is deleted or recreated while it waits now fails instead of running with the old values. Secrets of queued jobs are encrypted at rest with the orchestrator master key. During a rolling master-key rotation, a coordinator that cannot open a queued job puts it back for a coordinator that holds the new key. It then leaves the job alone for a back-off you set with kici-admin cluster-settings set --sealed-secrets-retry-backoff-ms or KICI_SEALED_SECRETS_RETRY_BACKOFF_MS (1 minute by default). A job that no coordinator can open fails with an error that says to finish the key rotation on every coordinator. Upgrade every coordinator of a cluster together for this release: an older coordinator would dispatch the jobs a newer one queued without their secrets.
  • A clustered orchestrator whose Raft leader runs with provenance signing disabled now creates its provenance signing key: any node with signing enabled creates the db-custody key when no key is active, and nodes that create one at the same moment converge on a single active key. When no key is ready, an identity-token request now answers { deferred: true, code: 'unavailable' } within 7.5 seconds, instead of running past the agent’s 15-second request timeout and failing the step with a timeout error. The orchestrator also logs a rate-limited warning naming the job, the run, and whether the node is the Raft leader when it defers a mint for want of a signing key. kici-admin signing-key list now also works without database access: with no --database-url and no KICI_DATABASE_URL, it reads the key list from the orchestrator admin API (GET /api/v1/admin/signing-keys, unscoped token with secret.read), which returns key metadata only. kici-admin signing-key generate keeps a key an orchestrator node activates while the command runs, and prints that key’s kid instead of replacing it. A node with db custody no longer replaces an active aws-kms or command key with a key of its own: it signs nothing and logs an error naming that key. To move a cluster to db custody, run kici-admin signing-key rotate once after every node runs with db custody.
  • A container job now installs its .kici dependencies on the agent host, so a private registry, or one only the agent host can resolve, no longer needs to be reachable from the job network. The host install runs no code from the repository. It runs only for a plain npm or pnpm project with registry dependencies, only when install scripts are disabled on the agent (the default), the agent injects the KiCI runtime and the run has no cached dependencies. A yarn project, a workspace, a pnpm hook, a git, file, link or URL dependency or override, a symlinked manifest, or an .npmrc with a control character other than LF or TAB (a CRLF line ending included) makes the job container install instead, as before. So does a registry in the job’s registries: or in .kici/.npmrc, or a lockfile tarball URL, on an origin the operator did not allow: the host install contacts only the public npm registry, the registries in the agent user’s own npm config, and the origins listed in the new agent setting KICI_HOST_INSTALL_REGISTRIES. An install secret reaches the host install only under a name a kept registry-auth value references, and a reference to a variable npm, pnpm or Node reads (a proxy, TLS or NODE_* variable, for example) makes the job container install instead. NPM_TOKEN and NODE_AUTH_TOKEN are exempt: no tool reads them. The host install uses the agent’s own npm (11.10.0 or later) or pinned pnpm 11.3.0, outside the checkout, with git dependencies refused. An npm older than 11.15.0 cannot refuse URL, file or directory dependencies itself, so it runs npm ci, and only with a version 2 or 3 lockfile that pins every package to a semver version and a resolved tarball on an allowed registry. Without such a lockfile, the job container installs instead. Its .npmrc is written fresh from the registry and registry-auth keys of .kici/.npmrc, read with npm’s own parser; TLS and proxy keys come only from the operator’s own config. A failed host install fails the job with the installer’s error, with registry tokens and install secrets masked, and cancelling the job stops the install without creating the job container. A container job whose sandbox setup fails partway now has its container and any image built from its Dockerfile removed.
  • A job’s setup log (the clone, the .kici dependency install and the workflow module load, plus a container job’s host-side checkout and install) is now readable everywhere: kici runs logs prints it under a (setup) heading (in --json, under a setup:<job> key whose value is the array of lines, with % and / in the job name written as %25 and %2F; a step key <job>/<step> always contains a /, so a step named setup does not overwrite it), kici run remote output includes it ahead of each job’s steps, the dashboard shows it as the job’s Setup section, and kici-admin runs logs --step=-1 prints it instead of an empty page. A container job’s setup log now also carries the agent’s host-side checkout and dependency install, which were written to the agent’s own log only.
  • kici run remote no longer fails with EISDIR on a repository that tracks a symbolic link to a directory, replaced such a link with a directory, or contains a submodule. A directory link now ships as a link, and the agent recreates it in the workspace when it points inside the repository. A directory link that points outside the repository fails the job with an error that names the link. An agent older than this release leaves such a link out of the workspace. The files of a submodule, or of an untracked nested git repository, are not uploaded, and the CLI prints a warning that names each one. A symbolic link whose target does not exist is not uploaded either, and the CLI names it in a warning. kici run --local no longer fails on these repositories either: its isolated checkout recreates a directory link as a link, turns a link replaced by a directory (or the reverse) into the new shape, and names each skipped submodule, nested repository and dangling link in a warning.
  • kici-admin runs show prints the context a run was bound to, on a Context: line. The line was never printed, because the command read a field name the admin API does not send.
  • Secrets bound to a context whose name is a pattern now reach the jobs that match it; before, only its variables did.
  • A workflow whose registries: or installEnv: name several contexts now has the protection rules of every context checked before its install gate decides. A context that rejects the run now fails it, even when another context holds it. When the hold is released, each context the approval did not cover is checked again, and the run fails without its install tokens if that context no longer admits it or was recreated while the run waited. An install hold that is still waiting when you upgrade the orchestrator to this version has no record of what its approval covers, so it fails when it is released: trigger that run again after the upgrade.
  • A git credential or container registry reference written as <context>:<secret-name> is now read through the bindings of the context whose protection rules admitted it, the same way a job’s context secrets are. A name that only a glob context matches now gets that context’s bound secret. A glob context never reads a scope that merely shares its name. One case keeps its old behaviour, deprecated: when the reference names a context by its exact name and no scope bound to that context carries the secret, the scope named after the context is still read and the orchestrator logs a deprecation warning. That fallback is removed in v1.0.0; bind the scope to the context with kici-admin context bind to keep the reference working. In a kici run remote test run, the values you upload with --env or --context now override a container registry reference the same way they override a job’s context secrets. A git credential reference is still read from the orchestrator’s own store.
  • A finished run no longer shows as running again in the dashboard or in outbound webhooks when a status update arrives late. A failed run no longer reports its failure twice when an agent resends a job’s final status. A run that continues after it was marked failed, for example when its orchestrator reconnects, no longer keeps that failure reason after it succeeds.
  • An organization-wide workflow whose jobs have a dynamic field, or a generator that declares needs, now evaluates the workflow repository’s code, not the source repository’s, when no cached source is available — for example on an orchestrator with no cache storage. A dynamic env, matrix, contexts or concurrencyGroup function of such a workflow now runs with both repositories checked out and the KICI_* source and workflow variables set, and a generator that declares needs now receives the sourceRepo / workflowRepo pair.
  • A job produced by a dynamic job generator that the orchestrator cannot resolve now fails on the run. This happens when its contexts cannot be resolved or its job config cannot be built. Before, the job was only written to the orchestrator log: a job that needed it waited forever, and a run with no such job finished green without it. Now a job that needs it is skipped and the run fails, while its sibling jobs still run.
  • A container: job now runs only on the agent started for its image or on an agent that can start containers, and an agent started in a job’s image no longer picks up other jobs. An agent older than 0.10.0 is not held to this, because it does not report a remote DOCKER_HOST as a runtime. A container job that no connected agent can start, with no scaler backend to start one, now shows a reason naming the missing runtime and fails as unroutable after the grace window, rather than waiting for the queue timeout. A job whose only matching agents were started inside other jobs’ images gets a reason that says so, rather than one saying no agent matches its runsOn. A container job on a host with no container runtime now fails before the clone with a message naming the missing runtime and the agent’s labels, and the agent starts job containers on Podman when that is the only runtime on the host.
  • kici-admin source purge-stale --dry-run now counts the workflow registrations under generic sources, which the purge deletes, so its orphan-registration count matches what --confirm removes.
  • kici-admin source purge-stale no longer deletes the workflow registrations under an organization’s remote source (remote:<orgId>), which it treated as orphaned, and its --dry-run count no longer includes them.
  • Orchestrator, Platform and agent HTTP listeners keep an idle keep-alive connection open for 130 s so a reverse proxy (Caddy, nginx, a load balancer) never sends a request into a socket the server is closing; that race answered 502 to a GitHub push webhook, which providers do not redeliver
  • Archived access-log, audit-log and event-log pages read newest-first and stop once the page is full, fetch manifests and chunks up to 16 at a time, and cache manifests in-process; before this, a filtered page over a long archive could take 34 s and time out the kici-admin CLI
  • Breaking: the SDK root barrel no longer exports runtime internals (use @kici-dev/sdk/internal), buildAgentCloudInit accepts only claim-code credentials, the lock-file inline-expression value shape is no longer parsed, ContributorResolver and getAccessCacheInvalidations are removed from @kici-dev/engine, and the trust tier vocabulary is trusted | unknown
  • Breaking: the wire protocol is now version 3 and the floor equals it. Every 0.8.x (and older) orchestrator, agent and peer sends version 2 and is refused at connect with WS_CLOSE_PROTOCOL_ERROR, so both tiers of a deployment — the orchestrator and its agents and peers — must move to 0.9.0 together. Removed from the wire with it: the Platform-rooted identity mint (oidc.mint.* RPC and the oidcMint Platform capability — the orchestrator’s own signing key mints every token, including the deferred re-mint, so KICI_ORCHESTRATOR_PROVENANCE_ISSUER is required for provenance; the hybrid local dev plane configures it automatically), the job-dispatch and peer-reroute sourceTarHash field (sourceTarDigest is the only digest), artifacts.upload.complete storageKey and sizeBytes, the scalerCapacity[].mandatoryLabels union (labelSetMandatoryLabels is required), the trust-policy unknownContributorPolicy / workflowChangePolicy arms and the enforcement field, the forkPolicy: reject value, the global-workflows elevatedRepos list, boolean dashboard-write policy values (permissive / encrypted / disabled only) and the KICI_OIDC_LEGACY_PR_SUB escape hatch. The artifacts.upload.complete, trust_policy.update policy, scalerCapacity[] and global-workflow settings objects are now strict, so a stale sender is refused rather than silently ignored. The Platform JWKS stays published, so bundles it signed earlier keep verifying.
  • Breaking: kici-admin drops join —config (the join writes only ./kici-orchestrator.env), secret scopes —all-backends (every registered backend is listed by default, qualified as :), cluster-settings —contributor-cache-ttl-ms, org-settings global-workflows elevate-add / elevate-remove (the orchestrator admin API no longer reads or writes elevatedRepos), context —minimum-trust known (only trusted or null), and KICI_CONTRIBUTOR_CACHE_TTL_MS
  • Breaking: the orchestrator admin PATCH /api/v1/admin/cluster-settings body is strict — an unknown field, including the removed contributorCacheTtlMs, is refused with a 400 instead of being dropped
  • Breaking: the per-member CI trust override route and its Members-tab clear control are removed — a member’s CI trust level comes from their roles alone; the webhook test-ping response no longer carries the success alias of delivered; the dashboard no longer reports a separate approver level
  • Breaking: the orchestrator and Platform migrations drop the columns behind removed features — the per-member CI trust override, the global-workflows elevated-repo list, the contributor-cache TTL, the two non-fork trust-policy arms and the dispatch-queue source tar hash — and rewrite stored legacy values: a run’s trust tier known becomes unknown, a fork policy reject becomes ignore, and a context floor of known is cleared
  • Breaking: objects under the pre-0.6 cache layouts are no longer read; kici-admin cache purge-legacy removes them
  • The hosted plan catalogue (tiers, prices, limits) is served publicly at GET /api/v1/billing/plans, and the enforced tier limits are built from one plans.yaml source.
  • kici feedback —draft builds the prefilled agent-report issue-form URL from a JSON draft, so an agent can hand a person a report to review and file
  • A stored trust tier or context floor that predates the current vocabulary is read fail-closed: an unrecognized run tier inherits as unknown and an unrecognized context minimum trust reads as no floor
  • A trust policy stored while the retired reject fork switch was still accepted no longer breaks the Platform’s trust_policy.update push: the stored value is rewritten to ignore (what reject did) by a Platform migration, and both the push and the dashboard settings API render any stored value outside the current enum as ignore. The per-agent mandatoryLabels peer-heartbeat field is now required (a static agent sends []), the orchestrator admin PATCH /trust-policy body is strict so a removed policy arm from an older kici-admin is refused with a 400 instead of being ignored, and an orchestrator without KICI_ORCHESTRATOR_PROVENANCE_ISSUER now logs one warning per retrier drain that its deferred attestations cannot be completed.
  • User-cache quota eviction no longer deletes a concurrent save’s in-flight upload
  • A workflow with a dynamic init job (a filter, a dynamic env or context) no longer fails immediately when no agent or scaler backend can take the init job at dispatch time; the init job waits in the queue for capacity like every other job
  • The agent image and the Node runtime it mounts into every job container move to Node 24.21.0 (OpenSSL 3.5.8, undici 7.29.1, NSS 3.126 root certificates); the orchestrator and platform images share the same base
  • The dashboard’s upgrade cards read tier prices and limits from the Platform’s plan catalogue instead of a hand-maintained copy.
  • Dependency advisories cleared: hono 4.13.7, fast-uri 3.1.7, qs 6.16.0 and postcss-selector-parser 7.1.6 replace the versions carrying GHSA-gqvv-2mrq-wpjv, GHSA-5jgf-p345-68v8, GHSA-x5fp-wj9c-mxmx, GHSA-w9m9-85wc-3x92 and their cohorts
  • The marketing site’s pricing, licence split and llms.txt bundle table are read from their sources at build time and fail the build rather than render a stale copy.
  • The published public-mirror CI workflow pins actions/checkout v7.0.1 (SHA-pinned); it triggers on push and pull_request only, so v7’s fork-PR checkout guard does not apply
  • The agent exits non-zero when the orchestrator refuses its authentication permanently (an invalid or revoked token, or a protocol version below the orchestrator’s floor) instead of staying alive behind its health endpoint; the GitHub Actions one-shot runner workflow carries timeout-minutes: 15 so a wedged run releases its runner
  • The 0.8.0 installer image-digest record, quickstart compose and release-artifacts page carry the manifest-list digests quay.io serves (the resumed 0.8.0 release had left the record on 0.7.0, whose tags were since retired, and the compose without a digest pin); the release chain now reads the digests back from the registry when the image push was already done
  • The dashboard reserves the Support button’s corner under the main area, so the last row of a long table (an API key’s clone/revoke actions, for example) is no longer covered by the floating button
  • kici run —local (connected) describes its identity as plane-signed OIDC + attestation under the plane’s own issuer, and kici local attach says the same, instead of claiming Platform-minted tokens
  • A provenance signing key the orchestrator cannot load (sealed under a master key it does not hold, or db custody with no KICI_SECRET_KEY) is logged once as an error naming the recovery; a mint against it defers immediately instead of stalling for thirty seconds before deferring
  • In a multi-coordinator cluster, a run whose build, init, or dynamic-eval job is claimed by a sibling coordinator’s agent now dispatches its post-build jobs and ends with their status. The precursor result travels on the shared job row, every coordinator finalizes a run only when every job row is terminal, and a registration window on the run row stops a sibling from finishing the run on the build job alone.
  • Dashboard: long pages no longer scroll their last row under the floating Support button — the shell grows with the page instead of pinning to the viewport, except on run detail, which scrolls in place
  • The orchestrator OIDC discovery document now advertises every claim the ID token carries (claims_supported was missing the event-context, origin and attestation claims)
  • The oidc.token() examples in the provenance and SDK runtime docs handle the deferred result the API can return instead of destructuring a token that may be absent
  • A webhook the provider emits twice under two delivery ids — GitHub does this at times, with identical bodies under a second apart — now produces one run: the Platform treats a delivery whose body matches one it accepted for the same source within the last minute as a duplicate, so the second copy is answered as such and never relayed
  • The pricing cards on the kici.dev landing page no longer show a gap between the price and its per-month period.
  • kici docs, kici feedback and the files kici init scaffolds link to docs.kici.dev; the kici.dev/docs form they printed was dead
  • kici feedback lists KiCI version and Environment as two required fields, matching the agent-report issue form
  • The AGENTS.md that kici init scaffolds describes kici docs llm as the CLI accepts it: no topic prints the index, full prints the whole bundle, a topic name prints one task bundle; the —index flag it named does not exist
  • Runtime dependency patches in the published packages — the kici CLI, SDK, agent and orchestrator, and the shared libraries under them: zod 4.6.5, yaml 2.9.1, open 11.0.4 (CLI), @aws-sdk/* 3.1135.0 (orchestrator and shared storage client)
  • Runtime dependency patch in the published packages: oxc-transform 0.150.0, the TypeScript transform under the kici CLI’s workflow loader and the agent (core and shared)
  • The kici-admin —system refusal prints a sudo command that names the resolved node binary and CLI script, so it runs when node comes from a version manager such as nvm or mise
  • The agent container image build retries the Node runtime tarball download on transient network errors instead of failing the whole build on one dropped TLS handshake.
  • The deprecations pages are removed; the changelog starts at 0.8.0
  • Build provenance docs gain worked examples for exchanging the job’s ID token with an external service — a Cloudsmith npm publish and an AWS STS AssumeRoleWithWebIdentity + S3 upload
  • The landing page and the public repository state that KiCI is in public beta
  • KiCI is pronounced /ki-ci/. The landing page and the GitHub repository description now say so.
  • Workload identity with OIDC has its own page, with Cloudsmith and AWS examples generated from tested workflows
  • kici run —local no longer times out on the first run after a cold plane start
  • The local dev plane now rotates its orchestrator and PostgreSQL logs at 50 MB instead of growing without bound
  • kici run —local names the cause when the local plane creates no run: leader election still pending, no lock file at the packed commit, or the plane’s recorded delivery status
  • kici run —local no longer reuses the previous run’s lock file for a dispatch or in-place run: the local source is read from disk on every trigger, so an uncommitted edit runs its own step body instead of the cached bundle
  • A warm pool on an event scaler no longer logs a spurious spawn failure at every orchestrator start; its first fill now runs once the event emitter is ready
  • The kici.scaler.scale-up event’s mandatoryLabels now carries the full gate the spawned agent is registered with, including taints derived from the scaler’s platform field, instead of the configured list alone
  • kici init in dev mode (KICI_DEV=true) pins @kici-dev/sdk to the latest dist-tag: the old >=0.0.1-0 range cannot match a prerelease build past 0.0.1, so npm install in .kici/ failed with ETARGET against a dev registry
  • kici run remote against a fleet-coordinated orchestrator now carries the overlay and test-run provenance onto the routed job; previously the agent cloned an empty repository instead of unpacking the uploaded working tree
  • kici run remote prints each streamed log line’s text instead of the stored JSON envelope ({“ts”,“level”,“msg”}) the relay returns
  • kici run remote fills the Duration column of its summary table: the Platform relay’s run-status response now carries each job’s duration
  • kici runs logs prints each stored log line’s text instead of the orchestrator’s JSON envelope ({“ts”,“level”,“msg”}), in both the default and —follow modes
  • kici run —local prints each streamed log line’s text instead of the local plane’s JSON envelope ({“ts”,“level”,“msg”}), matching kici run remote and kici runs logs
  • An orchestrator restart no longer stalls a container, Firecracker or bare-metal scaler pool: spawns the previous process left in flight are released at startup instead of holding their capacity reservations until the stale-spawn prune
  • kici-admin debug-bundle —fleet no longer fails on an agent whose diagnostic bundle is larger than the orchestrator’s per-connection byte burst: the chunks of a bundle the orchestrator asked for are exempt from that limiter, and an agent now recognises the orchestrator’s rate-limit warning frame instead of logging it as an invalid message
  • A worker orchestrator now re-drives its pending jobs when one of its scalers frees capacity: the worker’s in-memory queue lacked the listing the re-drive reads, so every freed slot logged ‘Pending-scale re-drive failed’ and placed nothing
  • The orchestrator no longer crashes when a container-scaler agent finishes and disconnects before its own spawn has completed: the destroy that disconnect triggers stops the real container (or none, when it does not exist yet) instead of asking the runtime to stop an empty id, which podman answered with a redirect that ended in an uncaught getaddrinfo ENOTFOUND containers
  • Versions before 0.8.0 were retired in a one-time reset before 1.0: they are deprecated on npm and their quay.io tags and GitHub releases are removed. Later versions stay available