Skip to content
- 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; a filtered page over a long archive took 34 s on staging and timed 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 the tested E2E 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