@kici-dev/compiler 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/headless-detect.js +1 -1
- package/dist/cli-banner.js +1 -1
- package/dist/cli.js +56 -19
- package/dist/commands/approve.js +1 -1
- package/dist/commands/compile.js +5 -3
- package/dist/commands/diagnostics.d.ts +7 -0
- package/dist/commands/diagnostics.js +58 -0
- package/dist/commands/docs.js +1 -1
- package/dist/commands/drain-worker.js +1 -1
- package/dist/commands/endpoints.js +1 -1
- package/dist/commands/fixture.js +1 -1
- package/dist/commands/held-run-client.js +1 -1
- package/dist/commands/held-run-resolve.js +1 -1
- package/dist/commands/hook.js +1 -1
- package/dist/commands/index.d.ts +16 -4
- package/dist/commands/index.js +10 -4
- package/dist/commands/init.js +2 -2
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +1 -3
- package/dist/commands/logout.js +1 -1
- package/dist/commands/orchestrators.d.ts +25 -0
- package/dist/commands/orchestrators.js +115 -0
- package/dist/commands/org.js +1 -1
- package/dist/commands/reject.js +1 -1
- package/dist/commands/run.d.ts +2 -9
- package/dist/commands/run.js +256 -315
- package/dist/commands/runs/cancel.d.ts +6 -0
- package/dist/commands/runs/cancel.js +32 -0
- package/dist/commands/runs/list.d.ts +6 -0
- package/dist/commands/runs/list.js +49 -0
- package/dist/commands/runs/logs.d.ts +7 -0
- package/dist/commands/runs/logs.js +67 -0
- package/dist/commands/runs/rerun.d.ts +5 -0
- package/dist/commands/runs/rerun.js +21 -0
- package/dist/commands/runs/show.d.ts +5 -0
- package/dist/commands/runs/show.js +65 -0
- package/dist/commands/secrets-list.d.ts +7 -9
- package/dist/commands/secrets-list.js +40 -64
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/test.js +1 -1
- package/dist/commands/types.d.ts +3 -5
- package/dist/commands/types.js +13 -37
- package/dist/commands/verify-attestation.d.ts +12 -0
- package/dist/commands/verify-attestation.js +69 -0
- package/dist/commands/watch.js +1 -1
- package/dist/commands/workflows.js +14 -34
- package/dist/errors/capability-gap.js +1 -1
- package/dist/errors/formatter.js +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/execution/executor.js +1 -1
- package/dist/execution/index.js +1 -1
- package/dist/execution/sdk-alias.js +1 -1
- package/dist/execution/ts-loader.js +1 -1
- package/dist/fixtures/compiler.js +1 -1
- package/dist/fixtures/defaults/index.js +1 -1
- package/dist/format.js +1 -1
- package/dist/generators/secrets-dts.js +2 -2
- package/dist/hooks/detector.js +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/installer.js +1 -1
- package/dist/hooks/templates.js +1 -1
- package/dist/index.js +1 -1
- package/dist/llm-context/llms-full.txt +1047 -132
- package/dist/llm-context/llms.txt +4 -1
- package/dist/local-executor/dag-scheduler.js +1 -1
- package/dist/local-executor/index.js +1 -1
- package/dist/local-executor/job-runner.js +37 -16
- package/dist/local-executor/materializer.js +1 -1
- package/dist/local-executor/output-streamer.js +1 -1
- package/dist/local-executor/payload-generator.js +1 -1
- package/dist/local-executor/picker.js +1 -1
- package/dist/local-executor/secret-loader.js +1 -1
- package/dist/local-executor/to-event-payload.js +1 -1
- package/dist/local-executor/types.js +1 -1
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/lockfile/generator.d.ts +7 -5
- package/dist/lockfile/generator.js +21 -18
- package/dist/lockfile/hash-files.js +1 -1
- package/dist/lockfile/hasher.js +1 -1
- package/dist/lockfile/index.js +1 -1
- package/dist/lockfile/purity-analyzer.js +1 -1
- package/dist/postinstall.js +1 -1
- package/dist/provenance-trust-root.d.ts +14 -0
- package/dist/provenance-trust-root.js +49 -0
- package/dist/remote/config.d.ts +6 -2
- package/dist/remote/config.js +7 -4
- package/dist/remote/dashboard-client.d.ts +92 -0
- package/dist/remote/dashboard-client.js +168 -0
- package/dist/remote/encryption.js +3 -4
- package/dist/remote/history.js +1 -1
- package/dist/remote/oauth.js +1 -1
- package/dist/remote/oidc-discovery.js +1 -1
- package/dist/remote/output/json.js +1 -1
- package/dist/remote/output/junit.js +1 -1
- package/dist/remote/output/streaming.js +1 -1
- package/dist/remote/output/summary.js +1 -1
- package/dist/remote/platform-client.d.ts +142 -0
- package/dist/remote/platform-client.js +187 -0
- package/dist/remote/prod-defaults.js +1 -1
- package/dist/remote/render.d.ts +11 -0
- package/dist/remote/render.js +48 -0
- package/dist/remote/secret-upload.js +1 -1
- package/dist/remote/uploader.d.ts +9 -0
- package/dist/remote/uploader.js +51 -4
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -3
- package/dist/templates/index.js +1 -1
- package/dist/templates/package-json.js +4 -5
- package/dist/templates/tsconfig-json.js +1 -1
- package/dist/templates/workflows/hello-world.js +2 -2
- package/dist/templates/workflows/hello-world.ts +1 -1
- package/dist/templates/workflows/pr-checks.js +3 -3
- package/dist/templates/workflows/pr-checks.ts +2 -2
- package/dist/test-runner/dry-run.js +1 -1
- package/dist/test-runner/event-types.js +1 -1
- package/dist/test-runner/git-detector.js +1 -1
- package/dist/test-runner/index.js +1 -1
- package/dist/test-runner/job-executor.js +1 -1
- package/dist/test-runner/output-formatter.js +1 -1
- package/dist/test-runner/payload-builder.js +1 -1
- package/dist/test-runner/rule-evaluator.js +1 -1
- package/dist/test-runner/secrets-file.js +1 -1
- package/dist/test-runner/step-context.js +10 -6
- package/dist/types.d.ts +8 -5
- package/dist/types.js +2 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/validator.js +1 -1
- package/dist/workflows/hello-world.ts +1 -1
- package/dist/workflows/pr-checks.ts +2 -2
- package/package.json +5 -4
- package/sbom.spdx.json +68 -38
- package/dist/commands/cancel.js +0 -124
- package/dist/commands/status.js +0 -212
- package/dist/remote/client.js +0 -203
- package/dist/remote/observer.js +0 -174
- /package/dist/{chunk-gOLHoazu.js → chunk-BTugEXQM.js} +0 -0
|
@@ -26,7 +26,7 @@ Why the lock file must stay in sync with workflow source, how to commit both tog
|
|
|
26
26
|
|
|
27
27
|
### [CLI reference](cli-reference.md)
|
|
28
28
|
|
|
29
|
-
All CLI commands provided by `@kici-dev/compiler`: `kici compile` (with watch mode and check mode), `kici run` (local and remote execution), `kici test` (event simulation with dry-run, filtering, debug output, and custom payloads), `kici login`/`logout`/`org` (authentication and org management), `kici
|
|
29
|
+
All CLI commands provided by `@kici-dev/compiler`: `kici compile` (with watch mode and check mode), `kici run` (local and remote execution), `kici test` (event simulation with dry-run, filtering, debug output, and custom payloads), `kici login`/`logout`/`org` (authentication and org management), `kici diagnostics` (infrastructure tree) and `kici runs` (`list`/`show`/`logs`/`rerun`/`cancel` — run management), `kici secrets` (secret listing), `kici types` (type generation), `kici fixture` (generate test payloads), `kici init` (interactive project scaffolding), `kici hook` (pre-commit hook installation), `kici endpoints` (webhook entrypoints), and `kici workflows` (workflow listing). Includes environment variables and exit codes.
|
|
30
30
|
|
|
31
31
|
### [Workflow patterns](workflow-patterns.md)
|
|
32
32
|
|
|
@@ -215,6 +215,8 @@ This workflow:
|
|
|
215
215
|
- Runs a `lint` job first
|
|
216
216
|
- Runs a `test` job after lint succeeds (`needs: [lint]`)
|
|
217
217
|
|
|
218
|
+
`runsOn` selects which agents may run a job. Every agent self-reports `kici:os:<platform>`, `kici:arch:<cpu>`, and `kici:host:<hostname>`, so `runsOn: 'kici:os:linux'` targets any connected Linux agent with zero configuration — `kici init` scaffolds workflows with exactly that. Use a custom label such as `'linux'` or `'gpu'` (defined in your scaler's `labelSet`) to target a specific pool instead. See the [runsOn forms](./sdk/core.md#runson-forms) reference for the full label model.
|
|
219
|
+
|
|
218
220
|
**Single-step shortcut.** If a job only has one step, pass `run` directly to `job()` instead of building a `steps: [step(...)]` array:
|
|
219
221
|
|
|
220
222
|
```typescript
|
|
@@ -310,7 +312,7 @@ This updates `.kici/package.json` and generates (or updates) `package-lock.json`
|
|
|
310
312
|
|
|
311
313
|
### Dependency resolution contract
|
|
312
314
|
|
|
313
|
-
Every `.kici/` dependency must be resolvable from the **single cloned repository**. When a job runs, the agent clones only this repository and installs `.kici/` dependencies with your repo's package manager
|
|
315
|
+
Every `.kici/` dependency must be resolvable from the **single cloned repository**. When a job runs, the agent clones only this repository and installs `.kici/` dependencies with your repo's package manager — npm, pnpm, or yarn classic (v1). yarn berry (v2+) is not yet supported. A dependency that points outside the cloned repo cannot be resolved.
|
|
314
316
|
|
|
315
317
|
In practice:
|
|
316
318
|
|
|
@@ -416,6 +418,8 @@ Or add the flag to your root `package.json`:
|
|
|
416
418
|
|
|
417
419
|
## Authoring KiCI workflows with LLM coding agents
|
|
418
420
|
|
|
421
|
+
KiCI is LLM-ready by design. Because workflows are real, typed TypeScript, coding agents reason over the SDK's `.d.ts` signatures instead of guessing a bespoke YAML DSL — and they verify their own pipelines with the same `kici test` and `kici run local` loop you use, so there's no push-to-find-out round-trip. First-class agent context ships in the box, so an agent is briefed the moment it opens the project.
|
|
422
|
+
|
|
419
423
|
KiCI ships first-class context for LLM coding agents (Claude Code, Cursor, Aider, etc.). When you scaffold a project with `kici init`, the CLI writes `.kici/AGENTS.md`, a one-page briefing that tells the agent:
|
|
420
424
|
|
|
421
425
|
- where the SDK type declarations live (`node_modules/@kici-dev/sdk/dist/index.d.ts`)
|
|
@@ -1888,33 +1892,33 @@ function job(options: JobOptions): Job;
|
|
|
1888
1892
|
|
|
1889
1893
|
**Parameters:**
|
|
1890
1894
|
|
|
1891
|
-
| Parameter | Type | Required | Description
|
|
1892
|
-
| -------------------------- | --------------------------------------------------------------- | -------------------- |
|
|
1893
|
-
| `name` | `string` | no | Job name (auto-generated UUID if omitted)
|
|
1894
|
-
| `options.runsOn` | `RunsOn` | yes | Runner label(s) and optional exclusions (see below)
|
|
1895
|
-
| `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`.
|
|
1896
|
-
| `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](#single-step-job-shorthand). Mutually exclusive with `steps`.
|
|
1897
|
-
| `options.needs` | `NeedsEntry[]` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](#job-dependencies-needs)
|
|
1898
|
-
| `options.rules` | `Rule[]` | no | Conditions for conditional execution
|
|
1899
|
-
| `options.description` | `string` | no | Human-readable description
|
|
1900
|
-
| `options.matrix` | `Matrix` | no | Matrix configuration for job expansion
|
|
1901
|
-
| `options.include` | `MatrixInclude[]` | no | Additional matrix combinations
|
|
1902
|
-
| `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove
|
|
1903
|
-
| `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs.
|
|
1904
|
-
| `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container.
|
|
1905
|
-
| `options.environment` | `string \| ((event) => string \| Promise<string>)` | no | Deployment environment for this job. Static string or async/dynamic function -- see [Dynamic values](../dynamic-values.md).
|
|
1906
|
-
| `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](../dynamic-values.md).
|
|
1907
|
-
| `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name (defaults to environment name) -- see [Concurrency](../concurrency.md).
|
|
1908
|
-
| `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled
|
|
1909
|
-
| `options.cleanup` | `HookInput` | no | Hook that always runs after completion
|
|
1910
|
-
| `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds
|
|
1911
|
-
| `options.onFailure` | `HookInput` | no | Hook that runs when the job fails
|
|
1912
|
-
| `options.beforeStep` | `HookInput` | no | Hook that runs before each step
|
|
1913
|
-
| `options.afterStep` | `HookInput` | no | Hook that runs after each step
|
|
1914
|
-
| `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](../hooks.md#hook-timeout).
|
|
1915
|
-
| `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](#timeouts).
|
|
1916
|
-
| `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](#per-job-resources) below.
|
|
1917
|
-
| `options.init` | `
|
|
1895
|
+
| Parameter | Type | Required | Description |
|
|
1896
|
+
| -------------------------- | --------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1897
|
+
| `name` | `string` | no | Job name (auto-generated UUID if omitted) |
|
|
1898
|
+
| `options.runsOn` | `RunsOn` | yes | Runner label(s) and optional exclusions (see below) |
|
|
1899
|
+
| `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`. |
|
|
1900
|
+
| `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](#single-step-job-shorthand). Mutually exclusive with `steps`. |
|
|
1901
|
+
| `options.needs` | `NeedsEntry[]` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](#job-dependencies-needs) |
|
|
1902
|
+
| `options.rules` | `Rule[]` | no | Conditions for conditional execution |
|
|
1903
|
+
| `options.description` | `string` | no | Human-readable description |
|
|
1904
|
+
| `options.matrix` | `Matrix` | no | Matrix configuration for job expansion |
|
|
1905
|
+
| `options.include` | `MatrixInclude[]` | no | Additional matrix combinations |
|
|
1906
|
+
| `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove |
|
|
1907
|
+
| `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs. |
|
|
1908
|
+
| `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container. |
|
|
1909
|
+
| `options.environment` | `string \| ((event) => string \| Promise<string>)` | no | Deployment environment for this job. Static string or async/dynamic function -- see [Dynamic values](../dynamic-values.md). |
|
|
1910
|
+
| `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](../dynamic-values.md). |
|
|
1911
|
+
| `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name (defaults to environment name) -- see [Concurrency](../concurrency.md). |
|
|
1912
|
+
| `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled |
|
|
1913
|
+
| `options.cleanup` | `HookInput` | no | Hook that always runs after completion |
|
|
1914
|
+
| `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds |
|
|
1915
|
+
| `options.onFailure` | `HookInput` | no | Hook that runs when the job fails |
|
|
1916
|
+
| `options.beforeStep` | `HookInput` | no | Hook that runs before each step |
|
|
1917
|
+
| `options.afterStep` | `HookInput` | no | Hook that runs after each step |
|
|
1918
|
+
| `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](../hooks.md#hook-timeout). |
|
|
1919
|
+
| `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](#timeouts). |
|
|
1920
|
+
| `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](#per-job-resources) below. |
|
|
1921
|
+
| `options.init` | `InitConfig` | no | Per-job initialization run after clone, before steps -- provisions a toolchain. A generic config, a typed preset (`'mise'` / `{ mise }`), `'auto'`, or `false`. See [Per-job init](#per-job-init) below. |
|
|
1918
1922
|
|
|
1919
1923
|
**Returns:** `Job` -- an immutable job definition.
|
|
1920
1924
|
|
|
@@ -1935,20 +1939,27 @@ const build = job({
|
|
|
1935
1939
|
|
|
1936
1940
|
#### runsOn forms
|
|
1937
1941
|
|
|
1938
|
-
|
|
1942
|
+
A job's `runsOn` selects which agents may run it. Every label listed must be present on the agent (a subset match). It accepts three forms:
|
|
1939
1943
|
|
|
1940
1944
|
```typescript
|
|
1941
1945
|
// 1. Simple string -- agent must have this label
|
|
1942
|
-
runsOn: 'linux'
|
|
1946
|
+
runsOn: 'kici:os:linux'
|
|
1943
1947
|
|
|
1944
1948
|
// 2. Array of required labels -- agent must have ALL labels
|
|
1945
|
-
runsOn: ['linux', '
|
|
1949
|
+
runsOn: ['kici:os:linux', 'gpu']
|
|
1946
1950
|
|
|
1947
1951
|
// 3. Object form with exclusions -- agent must have ALL required labels
|
|
1948
1952
|
// and NONE of the excluded labels
|
|
1949
|
-
runsOn: { labels: ['linux'
|
|
1953
|
+
runsOn: { labels: ['kici:os:linux'], exclude: ['kici:host:box-01'] }
|
|
1950
1954
|
```
|
|
1951
1955
|
|
|
1956
|
+
**The label model:**
|
|
1957
|
+
|
|
1958
|
+
- Every agent automatically reports `kici:os:<platform>`, `kici:arch:<cpu>`, and `kici:host:<hostname>`, so `runsOn: 'kici:os:linux'` targets any connected Linux agent without configuring labels — a fresh `kici init` matches out of the box.
|
|
1959
|
+
- Use **custom labels** (e.g. `'gpu'`, `'prod-pool'`) — defined in your scaler's `labelSet` — to target a specific agent pool.
|
|
1960
|
+
- You can also target scaler-assigned labels (`kici:agent:<backend>`, `kici:scaler:<name>`), but those names are deployment-specific, so custom labels are more portable.
|
|
1961
|
+
- `runsOn` is a _requirement_ on candidate agents, never a _grant_: targeting a label only narrows the candidate set. Users cannot _set_ `kici:` labels on agents — that namespace is reserved for the scaler and the agent's self-reported platform facts — but they may freely _target_ any label in `runsOn`.
|
|
1962
|
+
|
|
1952
1963
|
**Semantics:**
|
|
1953
1964
|
|
|
1954
1965
|
- **Required labels:** The agent must have every label in the `labels` array (or the string/array form).
|
|
@@ -2108,7 +2119,37 @@ The agent reads both files after the command succeeds, applies the delta, and th
|
|
|
2108
2119
|
|
|
2109
2120
|
**Arrays run in order.** Passing `GenericInitConfig[]` runs the inits sequentially; each one's `$KICI_ENV` / `$KICI_PATH` delta is applied before the next runs, so a later init sees an earlier init's tools on `PATH`. The first init to fail stops the sequence and fails the job.
|
|
2110
2121
|
|
|
2111
|
-
**`init: false`** is an explicit opt-out
|
|
2122
|
+
**`init: false`** is an explicit opt-out; it behaves the same as omitting `init`.
|
|
2123
|
+
|
|
2124
|
+
#### Toolchain presets
|
|
2125
|
+
|
|
2126
|
+
For the common case, a typed preset removes the hand-written `run` block entirely. The agent expands the preset to the same generic init it would otherwise run.
|
|
2127
|
+
|
|
2128
|
+
- **`init: 'mise'`** -- zero-config. Installs mise, trusts and runs `mise install` against the committed mise config (`mise.toml` / `.mise.toml` / `.tool-versions`), hands mise's env + shims dir to subsequent steps, and caches mise's data dir under a key derived from the committed config (so a config change rotates the cache). The committed config is trusted automatically — committing it to your repo is the trust signal.
|
|
2129
|
+
- **`init: { mise: { cache, timeout, env, shell } }`** -- the same preset with overrides. These tune the generic fields a hand-written init exposes (minus `run`): `cache: false` disables caching, a `CacheSpec` replaces the default key/paths, and `timeout` / `env` / `shell` map straight through. `init: 'mise'` is exactly `init: { mise: {} }`.
|
|
2130
|
+
|
|
2131
|
+
```typescript
|
|
2132
|
+
const build = job('build', {
|
|
2133
|
+
runsOn: 'linux',
|
|
2134
|
+
init: 'mise', // committed mise.toml pins the toolchain; jq, node, etc. land on PATH
|
|
2135
|
+
steps: [
|
|
2136
|
+
step('show-jq-version', async (ctx) => {
|
|
2137
|
+
const { stdout } = await ctx.$`jq --version`;
|
|
2138
|
+
ctx.log.info(`jq version: ${stdout.trim()}`);
|
|
2139
|
+
}),
|
|
2140
|
+
],
|
|
2141
|
+
});
|
|
2142
|
+
```
|
|
2143
|
+
|
|
2144
|
+
#### Auto-detect (`init: 'auto'`)
|
|
2145
|
+
|
|
2146
|
+
**`init: 'auto'`** detects the toolchain from committed files instead of naming a preset. The agent scans the clone root and selects a preset when a marker is present: `mise.toml` / `.mise.toml` / `.tool-versions` -> the mise preset. With no markers found, `'auto'` is a logged no-op.
|
|
2147
|
+
|
|
2148
|
+
`'auto'` is opt-in: an **unset** `init` does nothing even when the repo carries a `mise.toml` for local development. Use `'auto'` to enable detection and `false` to keep the explicit opt-out.
|
|
2149
|
+
|
|
2150
|
+
#### Cross-platform
|
|
2151
|
+
|
|
2152
|
+
The mise preset works on Linux, macOS, and Windows. On Linux and macOS mise is installed via its standalone install script; on Windows it is installed from its standalone GitHub release. The resulting toolchain reaches every step the same way on all three. On Windows the standalone mise binary requires the Microsoft Visual C++ runtime (`vc_redist.x64`) to be present on the agent host — install it once when provisioning a Windows agent that uses the mise preset.
|
|
2112
2153
|
|
|
2113
2154
|
## Step & job authoring patterns
|
|
2114
2155
|
|
|
@@ -3049,6 +3090,8 @@ Every variant carries the shared `EventBase` fields — `type`, `action`, `targe
|
|
|
3049
3090
|
|
|
3050
3091
|
Matrix configurations expand a single job into multiple instances, one per parameter combination. Maximum 256 combinations.
|
|
3051
3092
|
|
|
3093
|
+
Expansion happens at **dispatch time**: the orchestrator materializes the matrix into N execution jobs — one per combination, each dispatched to its own agent — before any job runs. Each instance receives its combination as `ctx.matrix`. This is identical whether the workflow runs via `kici run local` or remotely through a webhook trigger, and the dashboard groups the N instances under one parent node.
|
|
3094
|
+
|
|
3052
3095
|
### Static array (single dimension)
|
|
3053
3096
|
|
|
3054
3097
|
```typescript
|
|
@@ -3103,6 +3146,8 @@ The function receives a `DynamicMatrixContext`:
|
|
|
3103
3146
|
|
|
3104
3147
|
Must return `string[]` (single dimension) or `Record<string, string[]>` (multi-dimensional).
|
|
3105
3148
|
|
|
3149
|
+
A dynamic matrix is resolved at runtime, then materialized into N instances exactly like a static matrix. Because the combinations are not known until the function runs, the 256-combination cap (and the "zero combinations" guard) is enforced at that point: a dynamic matrix that resolves to more than 256 combinations, or to none, fails the job with a matrix-expansion error rather than dispatching.
|
|
3150
|
+
|
|
3106
3151
|
### Include and exclude
|
|
3107
3152
|
|
|
3108
3153
|
Fine-tune matrix combinations on multi-dimensional matrices:
|
|
@@ -3141,6 +3186,35 @@ interface MatrixValues {
|
|
|
3141
3186
|
}
|
|
3142
3187
|
```
|
|
3143
3188
|
|
|
3189
|
+
### Consuming matrix outputs downstream
|
|
3190
|
+
|
|
3191
|
+
A downstream job that lists a matrix job in its `needs` receives a **keyed envelope** instead of a flat outputs object, because the upstream produced N sets of outputs (one per combination). `ctx.jobOutputs(matrixJob)` returns a `MatrixJobOutputs`:
|
|
3192
|
+
|
|
3193
|
+
```typescript
|
|
3194
|
+
interface MatrixJobOutputs<T = Record<string, unknown>> {
|
|
3195
|
+
/** Keyed by the combination suffix — the text inside `(...)` of the child name. */
|
|
3196
|
+
byMatrix: Record<string, T>;
|
|
3197
|
+
/** Last-write-wins flat merge across children, in child (name) order. */
|
|
3198
|
+
merged: T;
|
|
3199
|
+
}
|
|
3200
|
+
```
|
|
3201
|
+
|
|
3202
|
+
The suffix key matches the child job's display name: `byMatrix['a']` for a single-dimension `['a', 'b']` matrix, `byMatrix['linux, arm64']` for a multi-dimension combination. Use `isMatrixJobOutputs` (or `'byMatrix' in result`) to discriminate:
|
|
3203
|
+
|
|
3204
|
+
```typescript
|
|
3205
|
+
import { isMatrixJobOutputs } from '@kici-dev/sdk';
|
|
3206
|
+
|
|
3207
|
+
step('collect', async ({ jobOutputs }) => {
|
|
3208
|
+
const out = jobOutputs(buildMatrixJob);
|
|
3209
|
+
if (isMatrixJobOutputs(out)) {
|
|
3210
|
+
console.log(out.byMatrix['a']); // outputs of the `a` combination
|
|
3211
|
+
console.log(out.merged); // last-write-wins across all combinations
|
|
3212
|
+
}
|
|
3213
|
+
});
|
|
3214
|
+
```
|
|
3215
|
+
|
|
3216
|
+
The downstream job waits for **all** matrix combinations to terminate before it dispatches. A non-matrix upstream keeps the flat outputs shape. The envelope is identical under `kici run local` and the remote path.
|
|
3217
|
+
|
|
3144
3218
|
### Matrix type guards
|
|
3145
3219
|
|
|
3146
3220
|
```typescript
|
|
@@ -3556,6 +3630,68 @@ const deploy = job('deploy', {
|
|
|
3556
3630
|
- Maximum 20 secret outputs per job
|
|
3557
3631
|
- Maximum 64 KB per value
|
|
3558
3632
|
|
|
3633
|
+
### ctx.kici.oidc.token({ audience })
|
|
3634
|
+
|
|
3635
|
+
Request a short-lived OIDC ID token for the current job, bound to an `audience`. The token is a signed JWT whose identity claims (`repository`, `ref`, `sha`, `kici_run_id`, `kici_job_id`) are derived by the build platform from the run context — a step cannot spoof them. Use it to authenticate the build to an external service that trusts the platform's OIDC issuer (for example, when generating build provenance).
|
|
3636
|
+
|
|
3637
|
+
```typescript
|
|
3638
|
+
const publish = job('publish', {
|
|
3639
|
+
steps: [
|
|
3640
|
+
step('mint', async (ctx) => {
|
|
3641
|
+
const { token, expiresIn } = await ctx.kici.oidc.token({ audience: 'sigstore' });
|
|
3642
|
+
ctx.log.info(`Got an ID token valid for ${expiresIn}s`);
|
|
3643
|
+
// Hand `token` to a tool that exchanges it with the trusting service.
|
|
3644
|
+
}),
|
|
3645
|
+
],
|
|
3646
|
+
});
|
|
3647
|
+
```
|
|
3648
|
+
|
|
3649
|
+
**Behavior:**
|
|
3650
|
+
|
|
3651
|
+
- The token is short-lived (about 10 minutes) and scoped to the current run and job.
|
|
3652
|
+
- The returned token value is automatically masked in step logs.
|
|
3653
|
+
- The step never holds platform credentials — the request is relayed through the orchestrator, which mints the token on the step's behalf.
|
|
3654
|
+
- Only available inside a running job step; calling it outside one (for example, during local execution) rejects with a clear error.
|
|
3655
|
+
|
|
3656
|
+
### ctx.attestProvenance({ subject })
|
|
3657
|
+
|
|
3658
|
+
Build, sign, and persist a build-provenance attestation for an artifact your step produced. KiCI assembles an in-toto SLSA v1.0 provenance statement whose build identity (`repository`, `ref`, `sha`, run/job ids) comes from the platform — not from the step — so it cannot be spoofed, signs it, and stores a verifiable bundle that the dashboard surfaces and the `kici verify-attestation` CLI checks.
|
|
3659
|
+
|
|
3660
|
+
The artifact is **caller-supplied**: give it either a precomputed digest or a path (relative to the step working directory) that KiCI digests with SHA-256. For a container image, pass the manifest digest your build tool emitted.
|
|
3661
|
+
|
|
3662
|
+
```typescript
|
|
3663
|
+
const publish = job('publish', {
|
|
3664
|
+
steps: [
|
|
3665
|
+
step('build', async (ctx) => {
|
|
3666
|
+
await ctx.$`npm pack`;
|
|
3667
|
+
}),
|
|
3668
|
+
step('attest', async (ctx) => {
|
|
3669
|
+
// Digest a file KiCI hashes for you:
|
|
3670
|
+
const result = await ctx.attestProvenance({
|
|
3671
|
+
subject: { name: 'my-pkg-1.2.3.tgz', path: 'my-pkg-1.2.3.tgz' },
|
|
3672
|
+
});
|
|
3673
|
+
ctx.log.info(`Attestation stored at ${result.storageKey}`);
|
|
3674
|
+
|
|
3675
|
+
// Or supply a precomputed digest (e.g. a container manifest digest):
|
|
3676
|
+
await ctx.attestProvenance({
|
|
3677
|
+
subject: { name: 'ghcr.io/acme/app', digest: { sha256: '<manifest-digest>' } },
|
|
3678
|
+
});
|
|
3679
|
+
}),
|
|
3680
|
+
],
|
|
3681
|
+
});
|
|
3682
|
+
```
|
|
3683
|
+
|
|
3684
|
+
**Behavior:**
|
|
3685
|
+
|
|
3686
|
+
- The attestation is a signed [DSSE](https://github.com/secure-systems-lab/dsse) envelope over an [in-toto](https://in-toto.io) statement carrying the [SLSA v1.0](https://slsa.dev/spec/v1.0/provenance) provenance predicate.
|
|
3687
|
+
- It is signed with an ephemeral key bound to a platform-minted identity token, so it is **offline-verifiable** against the platform's published signing keys — no online lookup needed at verify time.
|
|
3688
|
+
- The bundle is persisted to object storage and recorded so the dashboard can show it and `kici verify-attestation` can retrieve it.
|
|
3689
|
+
- The returned `{ storageKey, subjectDigest, bundleMediaType }` identifies the stored bundle.
|
|
3690
|
+
- Only available inside a running job step; calling it outside one (for example, during local execution) rejects with a clear error.
|
|
3691
|
+
|
|
3692
|
+
See the [build provenance guide](../provenance.md) for the end-to-end attest →
|
|
3693
|
+
verify → view journey, including how to verify a bundle with `kici verify-attestation`.
|
|
3694
|
+
|
|
3559
3695
|
## Secrets
|
|
3560
3696
|
|
|
3561
3697
|
Workflows access secrets through `ctx.secrets` on `StepContext`. Use `await ctx.secrets.get('KEY')` to retrieve a value (rejects with `SecretNotFoundError` if the key is missing, fail-fast on typos), `ctx.secrets.has('KEY')` for a synchronous existence check, and `await ctx.secrets.expose('KEY')` when you need the value as a `process.env` entry for a child process.
|
|
@@ -4603,7 +4739,7 @@ This:
|
|
|
4603
4739
|
|
|
4604
4740
|
1. Revokes the PAT on the server (preventing further use)
|
|
4605
4741
|
2. Clears auth fields from the local config file
|
|
4606
|
-
3. Preserves non-auth settings (
|
|
4742
|
+
3. Preserves non-auth settings (active org, default clusters, Platform endpoint)
|
|
4607
4743
|
|
|
4608
4744
|
## Organization management
|
|
4609
4745
|
|
|
@@ -4623,6 +4759,10 @@ kici org use <name-or-id>
|
|
|
4623
4759
|
|
|
4624
4760
|
Name matching is case-insensitive. You can also use the organization ID directly.
|
|
4625
4761
|
|
|
4762
|
+
The active organization is both the scope for org-scoped commands (`kici runs list`, `kici diagnostics`, `kici secrets list`, …) **and** the default target for `kici run remote`. After `kici login` and `kici org use <org>`, `kici run remote` dispatches to that org through the Platform — that is the complete path to a remote run. Override the target for a single run with `kici run remote --org <id>`.
|
|
4763
|
+
|
|
4764
|
+
If an organization has more than one connected orchestrator cluster, set its default cluster once with `kici orchestrators use <name>` (list them with `kici orchestrators list`). `kici run remote` then targets that cluster unless you pass `--orchestrator <name>`. With a single connected orchestrator the cluster is selected automatically.
|
|
4765
|
+
|
|
4626
4766
|
### Show current organization
|
|
4627
4767
|
|
|
4628
4768
|
```bash
|
|
@@ -4633,18 +4773,15 @@ Displays the currently active organization name and ID.
|
|
|
4633
4773
|
|
|
4634
4774
|
## Auth status
|
|
4635
4775
|
|
|
4636
|
-
|
|
4776
|
+
`kici org current` shows your current login state and active organization:
|
|
4637
4777
|
|
|
4638
4778
|
```bash
|
|
4639
|
-
kici
|
|
4779
|
+
kici org current
|
|
4640
4780
|
```
|
|
4641
4781
|
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
- Active organization name
|
|
4646
|
-
- PAT expiry date and time remaining
|
|
4647
|
-
- Warning if PAT expires within 7 days
|
|
4782
|
+
It reports whether you are logged in and which organization is active. PAT
|
|
4783
|
+
expiry and the full list of your tokens are managed from the dashboard (see
|
|
4784
|
+
"Dashboard management" below).
|
|
4648
4785
|
|
|
4649
4786
|
## Personal access tokens
|
|
4650
4787
|
|
|
@@ -4767,7 +4904,8 @@ The CLI stores authentication data in `~/.kici/config` with `0600` permissions (
|
|
|
4767
4904
|
- PAT token
|
|
4768
4905
|
- PAT expiry date
|
|
4769
4906
|
- Active organization ID
|
|
4770
|
-
-
|
|
4907
|
+
- Per-org default orchestrator clusters
|
|
4908
|
+
- Platform endpoint URL
|
|
4771
4909
|
|
|
4772
4910
|
## Troubleshooting
|
|
4773
4911
|
|
|
@@ -5026,7 +5164,9 @@ kici run local push --keep-going
|
|
|
5026
5164
|
|
|
5027
5165
|
Execute fixtures remotely through the full CI pipeline. Fixtures are defined in `.kici/tests/*.ts` using the `fixture()` factory function. Without arguments, lists available fixtures.
|
|
5028
5166
|
|
|
5029
|
-
|
|
5167
|
+
Remote runs route through the Platform. Authenticate with a personal access token (`kici login`), then target an organization with `kici org use <org>` or the `--org` flag. The Platform relays the run to the org's orchestrator, while your working-tree overlay uploads directly to object storage — see [How the run is routed](#how-the-run-is-routed) and [The two planes](#the-two-planes) below.
|
|
5168
|
+
|
|
5169
|
+
The orchestrator must have **cache storage configured** (`KICI_STORAGE_TYPE` = `s3` or `filesystem`) with a dev-reachable upload endpoint so the CLI's direct upload succeeds; see the [testing guide](testing-guide.md) and [Storage layout](../operator/orchestrator/storage-layout.md) for setup.
|
|
5030
5170
|
|
|
5031
5171
|
```bash
|
|
5032
5172
|
kici run remote [fixture] [options]
|
|
@@ -5040,21 +5180,22 @@ kici run remote [fixture] [options]
|
|
|
5040
5180
|
|
|
5041
5181
|
**Options:**
|
|
5042
5182
|
|
|
5043
|
-
| Option | Default | Description
|
|
5044
|
-
| --------------------------- | ------- |
|
|
5045
|
-
| `--
|
|
5046
|
-
| `--
|
|
5047
|
-
| `--
|
|
5048
|
-
| `--
|
|
5049
|
-
| `--
|
|
5050
|
-
| `--
|
|
5051
|
-
| `--
|
|
5052
|
-
| `--
|
|
5053
|
-
| `--
|
|
5054
|
-
| `--
|
|
5055
|
-
| `--
|
|
5056
|
-
| `--
|
|
5057
|
-
| `--
|
|
5183
|
+
| Option | Default | Description |
|
|
5184
|
+
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------- |
|
|
5185
|
+
| `--org <id>` | active | Target organization for this run (overrides `kici org use`) |
|
|
5186
|
+
| `--orchestrator <name>` | default | Target orchestrator cluster within the org (overrides the per-org default) |
|
|
5187
|
+
| `--all` | `false` | Run all fixtures |
|
|
5188
|
+
| `--workflow <name>` | none | Run a specific workflow directly (bypass triggers) |
|
|
5189
|
+
| `--parallel` | `false` | Run multiple fixtures concurrently |
|
|
5190
|
+
| `--no-wait` | - | Fire and forget (print runIds, don't stream) |
|
|
5191
|
+
| `--quiet` | `false` | Minimal output (only final result) |
|
|
5192
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5193
|
+
| `--junit <path>` | none | JUnit XML output to file for CI integration |
|
|
5194
|
+
| `--history` | `false` | Show table of recent test runs |
|
|
5195
|
+
| `--context <ctx.key=value>` | none | Inject a namespaced context secret, uploaded encrypted (repeatable) |
|
|
5196
|
+
| `--env <KEY=VALUE>` | none | Provide a per-run secret, uploaded encrypted (repeatable) — see [testing guide](testing-guide.md) |
|
|
5197
|
+
| `--debug` | `false` | Verbose internals |
|
|
5198
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
5058
5199
|
|
|
5059
5200
|
**Examples:**
|
|
5060
5201
|
|
|
@@ -5062,9 +5203,15 @@ kici run remote [fixture] [options]
|
|
|
5062
5203
|
# List available fixtures
|
|
5063
5204
|
kici run remote
|
|
5064
5205
|
|
|
5065
|
-
# Run a single fixture
|
|
5206
|
+
# Run a single fixture against the active org
|
|
5066
5207
|
kici run remote push-main
|
|
5067
5208
|
|
|
5209
|
+
# Target a specific org for this run
|
|
5210
|
+
kici run remote push-main --org xyz789ghi012
|
|
5211
|
+
|
|
5212
|
+
# Target a specific orchestrator cluster within the org
|
|
5213
|
+
kici run remote push-main --orchestrator us-east
|
|
5214
|
+
|
|
5068
5215
|
# Run all push-related fixtures
|
|
5069
5216
|
kici run remote push-*
|
|
5070
5217
|
|
|
@@ -5094,19 +5241,75 @@ kici run remote --history
|
|
|
5094
5241
|
| 0 | All matched workflows passed |
|
|
5095
5242
|
| 1 | One or more workflows failed |
|
|
5096
5243
|
|
|
5244
|
+
#### How the run is routed
|
|
5245
|
+
|
|
5246
|
+
A remote run is dispatched to your **active organization** — the one set with `kici org use <org>`, or overridden per-run with `--org <id>`. The org is resolved in this order:
|
|
5247
|
+
|
|
5248
|
+
1. The `--org <id>` flag, if provided.
|
|
5249
|
+
2. Otherwise the active org saved in your global config by `kici org use <org>`.
|
|
5250
|
+
3. If neither is set, the command errors and asks you to select an org with `kici org use` or pass `--org`.
|
|
5251
|
+
|
|
5252
|
+
The orchestrator anchors the org without any manual webhook source: it auto-provisions a system-managed **remote source** (routing key `remote:<orgId>`) that maps to its bound organization, so even a zero-source org is immediately routable for remote runs. You never set a routing key for a remote run — selecting the org is enough.
|
|
5253
|
+
|
|
5254
|
+
When an org has more than one connected orchestrator cluster, the CLI picks the target cluster in this order:
|
|
5255
|
+
|
|
5256
|
+
1. The `--orchestrator <name>` flag, if provided.
|
|
5257
|
+
2. Otherwise the per-org default cluster, set with `kici orchestrators use <name>`.
|
|
5258
|
+
3. If the org has exactly **one** connected orchestrator, it is auto-selected.
|
|
5259
|
+
4. Otherwise the run errors with the list of connected clusters, and you pass `--orchestrator <name>` to choose one. Run `kici orchestrators list` to see the available cluster names.
|
|
5260
|
+
|
|
5261
|
+
#### The two planes
|
|
5262
|
+
|
|
5263
|
+
`kici run remote` uses two independent paths:
|
|
5264
|
+
|
|
5265
|
+
- **Control plane** — run initiation, trigger, status, log retrieval, and cancellation flow from your machine through the Platform, which relays them over a WebSocket connection to the org's orchestrator. Logs are delivered by the CLI polling the Platform for log chunks (tracked by a monotonic line cursor) and run status until the run reaches a terminal state; there is no direct streaming socket to the orchestrator.
|
|
5266
|
+
- **Data plane** — your working-tree overlay tarball uploads **directly** from your machine to the orchestrator's object store via a presigned PUT URL. The overlay never passes through the Platform. This is why the orchestrator's object-store upload endpoint must be reachable from your machine; see [Storage layout](../operator/orchestrator/storage-layout.md).
|
|
5267
|
+
|
|
5268
|
+
An orchestrator with no Platform connection cannot serve remote runs — the Platform is the service that offers them. For executing workflow steps on your own machine without an orchestrator (no scaler, agents, or environments), use [`kici run local`](#kici-run-local).
|
|
5269
|
+
|
|
5097
5270
|
#### Fresh repos (no GitHub remote)
|
|
5098
5271
|
|
|
5099
5272
|
`kici run remote` works even if the repo has never been pushed to GitHub. When no remote is detected:
|
|
5100
5273
|
|
|
5101
5274
|
- The entire repo content is uploaded (not just a diff overlay)
|
|
5102
|
-
- A synthetic routing key `local:<repo-name>` is used
|
|
5103
5275
|
- The lock file is sent inline (no GitHub API fetch)
|
|
5104
5276
|
- Steps that use git commands will fail (no `.git` directory in the remote workspace)
|
|
5105
5277
|
- Build cache (`__build__` jobs) is skipped for local repos
|
|
5106
5278
|
- Environments must have `allowLocalExecution: true` to be accessible from local runs (default is `false`)
|
|
5107
5279
|
|
|
5280
|
+
Destination routing is unchanged for fresh repos: the run still goes to your active org through the Platform.
|
|
5281
|
+
|
|
5108
5282
|
For a detailed guide on writing fixtures, configuring secrets, and understanding the upload flow, see [Testing guide](testing-guide.md).
|
|
5109
5283
|
|
|
5284
|
+
#### kici orchestrators
|
|
5285
|
+
|
|
5286
|
+
List the orchestrator clusters connected to an organization, and set the per-org default cluster used by `kici run remote`. Requires `kici login` and an active org (or pass `--org`).
|
|
5287
|
+
|
|
5288
|
+
```bash
|
|
5289
|
+
kici orchestrators list [--org <id>]
|
|
5290
|
+
kici orchestrators use <clusterName> [--org <id>]
|
|
5291
|
+
```
|
|
5292
|
+
|
|
5293
|
+
**`kici orchestrators list`** prints the org's connected orchestrator clusters, so you know what to pass to `--orchestrator` (or to `kici orchestrators use`).
|
|
5294
|
+
|
|
5295
|
+
**`kici orchestrators use <clusterName>`** sets the default orchestrator cluster for the org, stored per-org in your global config. Subsequent `kici run remote` invocations target that cluster unless overridden with `--orchestrator`.
|
|
5296
|
+
|
|
5297
|
+
**Examples:**
|
|
5298
|
+
|
|
5299
|
+
```bash
|
|
5300
|
+
# List the active org's connected clusters
|
|
5301
|
+
kici orchestrators list
|
|
5302
|
+
|
|
5303
|
+
# List a specific org's clusters
|
|
5304
|
+
kici orchestrators list --org xyz789ghi012
|
|
5305
|
+
|
|
5306
|
+
# Set the default cluster for the active org
|
|
5307
|
+
kici orchestrators use us-east
|
|
5308
|
+
|
|
5309
|
+
# Set the default cluster for a specific org
|
|
5310
|
+
kici orchestrators use us-east --org xyz789ghi012
|
|
5311
|
+
```
|
|
5312
|
+
|
|
5110
5313
|
### kici test
|
|
5111
5314
|
|
|
5112
5315
|
Preview which workflows match a trigger event (dry-run, no execution). Useful for verifying trigger configurations during development.
|
|
@@ -5181,9 +5384,7 @@ kici login [options]
|
|
|
5181
5384
|
| --------------------------- | ------- | ---------------------------------------------- |
|
|
5182
5385
|
| `--token <key>` | none | API key for direct authentication (legacy) |
|
|
5183
5386
|
| `--device` | false | Force device authorization flow (headless/SSH) |
|
|
5184
|
-
| `--endpoint <url>`
|
|
5185
|
-
| `--platform-endpoint <url>` | none | Platform relay URL |
|
|
5186
|
-
| `--routing-key <key>` | none | Routing key for webhook source identification |
|
|
5387
|
+
| `--platform-endpoint <url>` | none | Platform API base URL |
|
|
5187
5388
|
|
|
5188
5389
|
**Environment variables:**
|
|
5189
5390
|
|
|
@@ -5208,11 +5409,8 @@ kici login --device
|
|
|
5208
5409
|
# Legacy API key login
|
|
5209
5410
|
kici login --token kici_sk_abc123...
|
|
5210
5411
|
|
|
5211
|
-
#
|
|
5212
|
-
kici login --
|
|
5213
|
-
|
|
5214
|
-
# Platform relay connection with routing key
|
|
5215
|
-
kici login --token kici_sk_abc123... --platform-endpoint https://platform.kici.dev --routing-key github:42
|
|
5412
|
+
# Log in against a self-hosted Platform
|
|
5413
|
+
kici login --platform-endpoint https://platform.example.com
|
|
5216
5414
|
|
|
5217
5415
|
# Suppress browser opening (print authorize URL to stdout)
|
|
5218
5416
|
KICI_BROWSER_CMD=none kici login
|
|
@@ -5230,7 +5428,7 @@ KICI_CALLBACK_PORT=19876 KICI_CONFIG_DIR=/tmp/kici-test kici login
|
|
|
5230
5428
|
|
|
5231
5429
|
Revoke your personal access token on the server and clear local credentials.
|
|
5232
5430
|
|
|
5233
|
-
If the server is unreachable, local credentials are still cleared (the PAT will expire automatically). Non-auth config fields (
|
|
5431
|
+
If the server is unreachable, local credentials are still cleared (the PAT will expire automatically). Non-auth config fields (active org, default clusters, etc.) are preserved.
|
|
5234
5432
|
|
|
5235
5433
|
```bash
|
|
5236
5434
|
kici logout
|
|
@@ -5296,87 +5494,169 @@ Show the current active organization.
|
|
|
5296
5494
|
kici org current
|
|
5297
5495
|
```
|
|
5298
5496
|
|
|
5299
|
-
### kici
|
|
5497
|
+
### kici diagnostics
|
|
5300
5498
|
|
|
5301
|
-
Show
|
|
5499
|
+
Show the orchestrators, scalers, and agents serving your organization — the
|
|
5500
|
+
terminal equivalent of the dashboard Diagnostics page. Reads the same
|
|
5501
|
+
org-scoped data the dashboard does, so it needs `kici login` and an active org
|
|
5502
|
+
(`kici org use <name>`).
|
|
5302
5503
|
|
|
5303
|
-
The
|
|
5504
|
+
The output has three parts: a one-line header (runs in the last 24h, success
|
|
5505
|
+
rate, average duration, queued/running job counts), any infrastructure alerts
|
|
5506
|
+
(only shown when present), and a tree of each orchestrator with its scalers and
|
|
5507
|
+
agents. Each agent line shows its labels, platform/architecture, active/maximum
|
|
5508
|
+
concurrency, and heartbeat age.
|
|
5304
5509
|
|
|
5305
|
-
|
|
5510
|
+
```bash
|
|
5511
|
+
kici diagnostics [options]
|
|
5512
|
+
```
|
|
5513
|
+
|
|
5514
|
+
**Options:**
|
|
5515
|
+
|
|
5516
|
+
| Option | Default | Description |
|
|
5517
|
+
| --------------------- | ------- | --------------------------------------------------- |
|
|
5518
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5519
|
+
| `--verbose` | `false` | Show extended per-agent fields (host, node, memory) |
|
|
5520
|
+
| `--orchestrator <id>` | all | Scope the tree to one orchestrator connection id |
|
|
5521
|
+
|
|
5522
|
+
**Examples:**
|
|
5306
5523
|
|
|
5307
5524
|
```bash
|
|
5308
|
-
|
|
5525
|
+
# Show the full infrastructure tree
|
|
5526
|
+
kici diagnostics
|
|
5527
|
+
|
|
5528
|
+
# Extended per-agent detail
|
|
5529
|
+
kici diagnostics --verbose
|
|
5530
|
+
|
|
5531
|
+
# Only one orchestrator's scalers and agents
|
|
5532
|
+
kici diagnostics --orchestrator conn-abc123
|
|
5533
|
+
|
|
5534
|
+
# Machine-readable output
|
|
5535
|
+
kici diagnostics --json
|
|
5309
5536
|
```
|
|
5310
5537
|
|
|
5311
|
-
|
|
5538
|
+
### kici runs
|
|
5539
|
+
|
|
5540
|
+
Inspect and manage execution runs from the terminal — the equivalent of the
|
|
5541
|
+
dashboard Runs page. All `kici runs` subcommands read/write the same org-scoped
|
|
5542
|
+
data as the dashboard, so they require `kici login` and an active org
|
|
5543
|
+
(`kici org use <name>`).
|
|
5312
5544
|
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5545
|
+
#### kici runs list
|
|
5546
|
+
|
|
5547
|
+
List runs with optional filters. Output is a table (run id, workflow, status,
|
|
5548
|
+
branch, trigger, started, duration); pagination is reported at the bottom.
|
|
5549
|
+
|
|
5550
|
+
```bash
|
|
5551
|
+
kici runs list [options]
|
|
5552
|
+
```
|
|
5316
5553
|
|
|
5317
5554
|
**Options:**
|
|
5318
5555
|
|
|
5319
|
-
| Option
|
|
5320
|
-
|
|
|
5321
|
-
| `--
|
|
5322
|
-
| `--
|
|
5323
|
-
| `--
|
|
5556
|
+
| Option | Default | Description |
|
|
5557
|
+
| ----------------------- | ------- | --------------------------------------------- |
|
|
5558
|
+
| `--status <s>` | all | Filter by run status |
|
|
5559
|
+
| `--workflow <w>` | all | Filter by workflow name |
|
|
5560
|
+
| `--branch <b>` | all | Filter by branch/ref |
|
|
5561
|
+
| `--repo <r>` | all | Filter by repository |
|
|
5562
|
+
| `--trigger <t>` | all | Filter by trigger type |
|
|
5563
|
+
| `--source <routingKey>` | all | Filter by source routing key |
|
|
5564
|
+
| `--since <ts>` | none | Only runs since this ISO-8601 or epoch ms |
|
|
5565
|
+
| `--page <n>` | `1` | Page number (server page size is fixed at 20) |
|
|
5566
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5324
5567
|
|
|
5325
|
-
|
|
5568
|
+
```bash
|
|
5569
|
+
kici runs list
|
|
5570
|
+
kici runs list --status running
|
|
5571
|
+
kici runs list --workflow ci --branch main
|
|
5572
|
+
kici runs list --json | jq '.runs[].runId'
|
|
5573
|
+
```
|
|
5574
|
+
|
|
5575
|
+
#### kici runs show
|
|
5576
|
+
|
|
5577
|
+
Show a run's summary header plus its jobs-and-steps tree (name, status,
|
|
5578
|
+
duration, exit code). If the run id is not on the Platform but exists in your
|
|
5579
|
+
local run history (from `kici run local`), the local record is shown instead.
|
|
5326
5580
|
|
|
5327
5581
|
```bash
|
|
5328
|
-
|
|
5329
|
-
|
|
5582
|
+
kici runs show <run-id> [options]
|
|
5583
|
+
```
|
|
5330
5584
|
|
|
5331
|
-
|
|
5332
|
-
|
|
5585
|
+
| Option | Default | Description |
|
|
5586
|
+
| -------- | ------- | ---------------------------- |
|
|
5587
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5333
5588
|
|
|
5334
|
-
|
|
5335
|
-
kici
|
|
5589
|
+
```bash
|
|
5590
|
+
kici runs show abc123
|
|
5591
|
+
kici runs show abc123 --json
|
|
5592
|
+
```
|
|
5336
5593
|
|
|
5337
|
-
|
|
5338
|
-
|
|
5594
|
+
#### kici runs logs
|
|
5595
|
+
|
|
5596
|
+
Print each job/step's log lines in order, with headers.
|
|
5597
|
+
|
|
5598
|
+
```bash
|
|
5599
|
+
kici runs logs <run-id> [options]
|
|
5339
5600
|
```
|
|
5340
5601
|
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5602
|
+
| Option | Default | Description |
|
|
5603
|
+
| -------------- | ------- | -------------------------------------- |
|
|
5604
|
+
| `--job <name>` | all | Only print logs for this job |
|
|
5605
|
+
| `-f, --follow` | `false` | Tail logs for a live run until it ends |
|
|
5606
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5345
5607
|
|
|
5346
|
-
|
|
5608
|
+
```bash
|
|
5609
|
+
kici runs logs abc123
|
|
5610
|
+
kici runs logs abc123 --job build
|
|
5611
|
+
kici runs logs abc123 --follow
|
|
5612
|
+
```
|
|
5613
|
+
|
|
5614
|
+
#### kici runs rerun
|
|
5347
5615
|
|
|
5348
|
-
|
|
5616
|
+
Re-trigger a completed run. Prints the new run id. The server enforces a short
|
|
5617
|
+
cooldown between reruns of the same run.
|
|
5349
5618
|
|
|
5350
5619
|
```bash
|
|
5351
|
-
kici
|
|
5620
|
+
kici runs rerun <run-id> [options]
|
|
5352
5621
|
```
|
|
5353
5622
|
|
|
5354
|
-
|
|
5623
|
+
| Option | Default | Description |
|
|
5624
|
+
| -------- | ------- | ---------------------------- |
|
|
5625
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
5626
|
+
|
|
5627
|
+
```bash
|
|
5628
|
+
kici runs rerun abc123
|
|
5629
|
+
```
|
|
5630
|
+
|
|
5631
|
+
#### kici runs cancel
|
|
5632
|
+
|
|
5633
|
+
Cancel a single run, or all in-progress runs on a branch.
|
|
5634
|
+
|
|
5635
|
+
```bash
|
|
5636
|
+
kici runs cancel [run-id] [options]
|
|
5637
|
+
```
|
|
5355
5638
|
|
|
5356
5639
|
| Argument | Required | Description |
|
|
5357
5640
|
| -------- | -------- | ---------------- |
|
|
5358
5641
|
| `run-id` | no | Run ID to cancel |
|
|
5359
5642
|
|
|
5360
|
-
**Options:**
|
|
5361
|
-
|
|
5362
5643
|
| Option | Default | Description |
|
|
5363
5644
|
| ----------------- | ------- | ------------------------------------------- |
|
|
5364
5645
|
| `--force` | `false` | Force cancel (kill immediately, skip hooks) |
|
|
5365
5646
|
| `--branch <name>` | none | Cancel all in-progress runs on this branch |
|
|
5366
5647
|
|
|
5367
|
-
**Examples:**
|
|
5368
|
-
|
|
5369
5648
|
```bash
|
|
5370
|
-
|
|
5371
|
-
kici cancel abc123
|
|
5372
|
-
|
|
5373
|
-
# Force cancel (kill immediately)
|
|
5374
|
-
kici cancel abc123 --force
|
|
5375
|
-
|
|
5376
|
-
# Cancel all runs on a branch
|
|
5377
|
-
kici cancel --branch feature/wip
|
|
5649
|
+
kici runs cancel abc123
|
|
5650
|
+
kici runs cancel abc123 --force
|
|
5651
|
+
kici runs cancel --branch feature/wip
|
|
5378
5652
|
```
|
|
5379
5653
|
|
|
5654
|
+
When `--json` is set on any of these commands, `kici` emits only the JSON
|
|
5655
|
+
document on stdout — the `kici v<version>` banner is suppressed — so the output
|
|
5656
|
+
is safe to pipe into `jq` or `JSON.parse`. The same holds for the other
|
|
5657
|
+
`--json` commands (`kici run remote --json`, `kici workflows list --json`) and
|
|
5658
|
+
for `--quiet`.
|
|
5659
|
+
|
|
5380
5660
|
### kici approve
|
|
5381
5661
|
|
|
5382
5662
|
Approve a held [approval gate](approvals.md) so the run resumes. Identify the held element by run ID, optionally narrowed to a job and step.
|
|
@@ -5779,6 +6059,70 @@ kici admin drain-worker --url http://worker-2.internal:10143
|
|
|
5779
6059
|
| 0 | Drain request accepted |
|
|
5780
6060
|
| 1 | Error (unreachable or request fail) |
|
|
5781
6061
|
|
|
6062
|
+
### kici verify-attestation
|
|
6063
|
+
|
|
6064
|
+
Verify a KiCI build-provenance attestation bundle offline. A bundle is the signed package a workflow step produces via `ctx.attestProvenance(...)`: a DSSE-wrapped SLSA in-toto statement, the ephemeral public key that signed it, and the KiCI identity token that anchors the build context. For the end-to-end attest → verify → view journey, see the [build provenance guide](./provenance.md). Verification establishes the full chain — the identity token verifies against the trusted issuer's JWKS, the DSSE signature verifies against the bundled key, and the statement's build context must match the token's claims (a mismatch is a hard failure). When an `[artifact]` is given, its SHA-256 digest is also matched against the attestation subject.
|
|
6065
|
+
|
|
6066
|
+
```bash
|
|
6067
|
+
kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file> [options]
|
|
6068
|
+
```
|
|
6069
|
+
|
|
6070
|
+
**Arguments:**
|
|
6071
|
+
|
|
6072
|
+
| Argument | Required | Description |
|
|
6073
|
+
| ------------ | -------- | --------------------------------------------------------------------- |
|
|
6074
|
+
| `[artifact]` | no | Artifact path to digest-check against the attestation subject digest. |
|
|
6075
|
+
|
|
6076
|
+
**Options:**
|
|
6077
|
+
|
|
6078
|
+
| Option | Required | Description |
|
|
6079
|
+
| ---------------------------- | -------- | ----------------------------------------------------------------------------------------- |
|
|
6080
|
+
| `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
|
|
6081
|
+
| `--trust-root <url-or-file>` | yes | Trusted issuer (see below). The token issuer is pinned to it, never taken from the token. |
|
|
6082
|
+
| `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
|
|
6083
|
+
| `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
|
|
6084
|
+
|
|
6085
|
+
**Trust root:** the verifier never trusts the issuer named inside the token — you supply the trusted issuer out-of-band via `--trust-root`, in one of two forms:
|
|
6086
|
+
|
|
6087
|
+
- **Online — an HTTPS issuer URL.** The verifier fetches `<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`, and fetches the JWKS. The token's `iss` is pinned to the discovery document's `issuer`.
|
|
6088
|
+
- **Offline — a self-contained trust-root file.** A local JSON file with the issuer and JWKS inlined, so no network access is needed (air-gapped verification):
|
|
6089
|
+
|
|
6090
|
+
```json
|
|
6091
|
+
{
|
|
6092
|
+
"issuer": "https://platform.example/issuer",
|
|
6093
|
+
"jwks": {
|
|
6094
|
+
"keys": [
|
|
6095
|
+
{ "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "alg": "ES256", "kid": "..." }
|
|
6096
|
+
]
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
```
|
|
6100
|
+
|
|
6101
|
+
**Examples:**
|
|
6102
|
+
|
|
6103
|
+
```bash
|
|
6104
|
+
# Online: verify a bundle against a deployed issuer, digest-checking the artifact
|
|
6105
|
+
kici verify-attestation ./dist/app.tgz \
|
|
6106
|
+
--bundle ./app.tgz.kici.json \
|
|
6107
|
+
--trust-root https://platform.example/issuer
|
|
6108
|
+
|
|
6109
|
+
# Offline / air-gapped: verify against a self-contained trust-root file
|
|
6110
|
+
kici verify-attestation ./dist/app.tgz \
|
|
6111
|
+
--bundle ./app.tgz.kici.json \
|
|
6112
|
+
--trust-root ./kici-trust-root.json
|
|
6113
|
+
|
|
6114
|
+
# Machine-readable result for scripting
|
|
6115
|
+
kici verify-attestation --bundle ./app.tgz.kici.json \
|
|
6116
|
+
--trust-root https://platform.example/issuer --json
|
|
6117
|
+
```
|
|
6118
|
+
|
|
6119
|
+
**Exit codes:**
|
|
6120
|
+
|
|
6121
|
+
| Code | Meaning |
|
|
6122
|
+
| ---- | ----------------------------------------------------------------------------------- |
|
|
6123
|
+
| 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
|
|
6124
|
+
| 1 | Not verified, or an error (missing flag, unreadable bundle, unreachable trust root) |
|
|
6125
|
+
|
|
5782
6126
|
## Workflow discovery
|
|
5783
6127
|
|
|
5784
6128
|
The CLI discovers workflows by scanning `.kici/workflows/*.ts` (or `.mjs` in MJS mode). Each file should `export default` a single workflow:
|
|
@@ -6110,7 +6454,7 @@ The lock file (`kici.lock.json`) is a JSON file with the following top-level fie
|
|
|
6110
6454
|
|
|
6111
6455
|
| Field | Description |
|
|
6112
6456
|
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
6113
|
-
| `schemaVersion` | Lock file schema version (currently
|
|
6457
|
+
| `schemaVersion` | Lock file schema version (currently 17). Incremented on breaking format changes. |
|
|
6114
6458
|
| `source` | Reference to the source file and export (e.g., `{ file: “.kici/workflows/ci.ts”, export: “#default” }`). |
|
|
6115
6459
|
| `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
|
|
6116
6460
|
| `lockfileHash` | SHA-256 of the detected package manager's lockfile, used as the dependency cache key. The lockfile is `.kici/package-lock.json` for npm, or the repo-root `pnpm-lock.yaml` / `yarn.lock` for a pnpm/yarn workspace; the hash input is prefixed with the manager name so a manager change is a guaranteed cache miss. Omitted when no lockfile exists. |
|
|
@@ -6428,7 +6772,7 @@ kici run remote push-main --junit results.xml
|
|
|
6428
6772
|
kici run remote push-main --no-wait
|
|
6429
6773
|
|
|
6430
6774
|
# Check status later
|
|
6431
|
-
kici
|
|
6775
|
+
kici runs show <run-id>
|
|
6432
6776
|
```
|
|
6433
6777
|
|
|
6434
6778
|
### Cancellation
|
|
@@ -6596,17 +6940,17 @@ kici run remote --history
|
|
|
6596
6940
|
### Run details
|
|
6597
6941
|
|
|
6598
6942
|
```bash
|
|
6599
|
-
# Show run summary (
|
|
6600
|
-
kici
|
|
6943
|
+
# Show run summary (reads the Platform, falls back to local history)
|
|
6944
|
+
kici runs show <run-id>
|
|
6601
6945
|
|
|
6602
6946
|
# Show full logs
|
|
6603
|
-
kici
|
|
6947
|
+
kici runs logs <run-id>
|
|
6604
6948
|
|
|
6605
6949
|
# Show logs for a specific job
|
|
6606
|
-
kici
|
|
6950
|
+
kici runs logs <run-id> --job build
|
|
6607
6951
|
|
|
6608
6952
|
# Machine-readable output
|
|
6609
|
-
kici
|
|
6953
|
+
kici runs show <run-id> --json
|
|
6610
6954
|
```
|
|
6611
6955
|
|
|
6612
6956
|
## Scaffolding with kici init
|
|
@@ -6723,6 +7067,178 @@ To actually change how you sign in, use your account console as described above.
|
|
|
6723
7067
|
|
|
6724
7068
|
---
|
|
6725
7069
|
|
|
7070
|
+
## Approval gates
|
|
7071
|
+
|
|
7072
|
+
Source: https://docs.kici.dev/user/approvals/
|
|
7073
|
+
|
|
7074
|
+
An **approval gate** pauses execution until an authorized person approves it. Execution resumes from exactly where it paused; a rejection (or an expired hold) fails the run.
|
|
7075
|
+
|
|
7076
|
+
You declare a gate in your workflow with `requireApproval`. It is available at three levels of granularity:
|
|
7077
|
+
|
|
7078
|
+
- **Step** — pause mid-job, before a specific step runs. The agent holds the live workspace (with all prior-step state intact) for the duration of the wait.
|
|
7079
|
+
- **Job** — hold the job before any of its steps run.
|
|
7080
|
+
- **Workflow** — hold the whole run before any job is dispatched.
|
|
7081
|
+
|
|
7082
|
+
Approvers are named as **teams** and **users**. A team is an operator-defined group of org members; your workflow code may name a team but can never change its membership, which is what makes a team clause a real gate rather than a suggestion. See [Approval gates (operator guide)](../operator/approvals.md) for how operators define teams, the approval queue, and expiry; see [the architecture overview](../architecture/approvals.md) for how a hold is evaluated and resumed.
|
|
7083
|
+
|
|
7084
|
+
## Quick start
|
|
7085
|
+
|
|
7086
|
+
Hold a deploy job until a member of the `leads` team approves:
|
|
7087
|
+
|
|
7088
|
+
```typescript
|
|
7089
|
+
import { workflow, job, step, push } from '@kici-dev/sdk';
|
|
7090
|
+
|
|
7091
|
+
export default workflow('deploy', {
|
|
7092
|
+
on: [push({ branches: ['main'] })],
|
|
7093
|
+
jobs: [
|
|
7094
|
+
job('deploy-production', {
|
|
7095
|
+
runsOn: 'default',
|
|
7096
|
+
requireApproval: [{ team: 'leads' }],
|
|
7097
|
+
steps: [step('deploy', async (ctx) => ctx.$`deploy --prod`)],
|
|
7098
|
+
}),
|
|
7099
|
+
],
|
|
7100
|
+
});
|
|
7101
|
+
```
|
|
7102
|
+
|
|
7103
|
+
When the run reaches this job, it is held instead of dispatched. The held run appears in the dashboard approval queue and can be released from there or with the [`kici approve`](#approving-from-the-cli) command. Once a member of `leads` approves, the job dispatches normally.
|
|
7104
|
+
|
|
7105
|
+
## The `requireApproval` field
|
|
7106
|
+
|
|
7107
|
+
`requireApproval` accepts three forms.
|
|
7108
|
+
|
|
7109
|
+
### Shorthand: `true`
|
|
7110
|
+
|
|
7111
|
+
```typescript
|
|
7112
|
+
job('deploy', {
|
|
7113
|
+
runsOn: 'default',
|
|
7114
|
+
requireApproval: true,
|
|
7115
|
+
steps: [
|
|
7116
|
+
/* ... */
|
|
7117
|
+
],
|
|
7118
|
+
});
|
|
7119
|
+
```
|
|
7120
|
+
|
|
7121
|
+
`requireApproval: true` holds the element until **any** org member who can act on approvals signs off — anyone with the `environments:write` or `ci_trust:write` permission. Use it when you want a manual gate without restricting who may release it.
|
|
7122
|
+
|
|
7123
|
+
### Approver list (AND)
|
|
7124
|
+
|
|
7125
|
+
```typescript
|
|
7126
|
+
requireApproval: [{ team: 'leads' }, { user: 'cto' }],
|
|
7127
|
+
```
|
|
7128
|
+
|
|
7129
|
+
A list of approver clauses is an **AND** list: every clause must be satisfied before the element is released.
|
|
7130
|
+
|
|
7131
|
+
- `{ team: 'leads' }` is satisfied once **any** member of the `leads` team approves.
|
|
7132
|
+
- `{ user: 'cto' }` is satisfied once the user `cto` approves.
|
|
7133
|
+
|
|
7134
|
+
A single approver may satisfy more than one clause. If `cto` is also a member of `leads`, one approval from `cto` satisfies both `{ team: 'leads' }` and `{ user: 'cto' }`, releasing the element. A user is named by their KiCI user identifier (their linked identity), and a team by its name as defined by your operator.
|
|
7135
|
+
|
|
7136
|
+
There is no OR or nested logic — clauses are always a flat AND list.
|
|
7137
|
+
|
|
7138
|
+
### Object form: reason and timeout
|
|
7139
|
+
|
|
7140
|
+
```typescript
|
|
7141
|
+
requireApproval: {
|
|
7142
|
+
approvers: [{ team: 'security' }, { team: 'leads' }],
|
|
7143
|
+
reason: 'Production deploy requires security + leads sign-off',
|
|
7144
|
+
timeout: 7200, // seconds
|
|
7145
|
+
},
|
|
7146
|
+
```
|
|
7147
|
+
|
|
7148
|
+
| Field | Type | Description |
|
|
7149
|
+
| ----------- | ------------------ | --------------------------------------------------------------------------------------------------- |
|
|
7150
|
+
| `approvers` | `ApproverClause[]` | The AND list of `{ team }` / `{ user }` clauses. An empty list means "any approval-capable member". |
|
|
7151
|
+
| `reason` | `string` | A human-readable label shown in the dashboard queue and the held-for-approval status check. |
|
|
7152
|
+
| `timeout` | `number` | Per-gate expiry in **seconds**, overriding the org default. On expiry the element is rejected. |
|
|
7153
|
+
|
|
7154
|
+
When `timeout` is omitted, the gate uses the org's default approval expiry (set by the operator). On expiry, the held element is rejected and the run fails — see [expiry](../operator/approvals.md#expiry).
|
|
7155
|
+
|
|
7156
|
+
## Granularity
|
|
7157
|
+
|
|
7158
|
+
The same `requireApproval` field is accepted on a workflow, a job, and a step.
|
|
7159
|
+
|
|
7160
|
+
### Workflow-level
|
|
7161
|
+
|
|
7162
|
+
A workflow-level gate holds the entire run before any job is dispatched:
|
|
7163
|
+
|
|
7164
|
+
```typescript
|
|
7165
|
+
export default workflow('release', {
|
|
7166
|
+
on: [push({ branches: ['main'] })],
|
|
7167
|
+
requireApproval: [{ team: 'release-managers' }],
|
|
7168
|
+
jobs: [buildJob, publishJob],
|
|
7169
|
+
});
|
|
7170
|
+
```
|
|
7171
|
+
|
|
7172
|
+
### Job-level
|
|
7173
|
+
|
|
7174
|
+
A job-level gate holds just that job; other jobs in the run proceed normally:
|
|
7175
|
+
|
|
7176
|
+
```typescript
|
|
7177
|
+
job('publish', {
|
|
7178
|
+
runsOn: 'default',
|
|
7179
|
+
requireApproval: [{ team: 'leads' }],
|
|
7180
|
+
steps: [
|
|
7181
|
+
/* ... */
|
|
7182
|
+
],
|
|
7183
|
+
});
|
|
7184
|
+
```
|
|
7185
|
+
|
|
7186
|
+
### Step-level
|
|
7187
|
+
|
|
7188
|
+
A step-level gate pauses mid-job, immediately before the named step. Earlier steps in the job have already run and their workspace state is preserved across the wait:
|
|
7189
|
+
|
|
7190
|
+
```typescript
|
|
7191
|
+
job('migrate-and-deploy', {
|
|
7192
|
+
runsOn: 'default',
|
|
7193
|
+
steps: [
|
|
7194
|
+
step('build-plan', async (ctx) => ctx.$`./gen-migration-plan.sh`),
|
|
7195
|
+
step('apply-migration', {
|
|
7196
|
+
requireApproval: [{ team: 'dba' }],
|
|
7197
|
+
run: async (ctx) => ctx.$`./apply-migration.sh`,
|
|
7198
|
+
}),
|
|
7199
|
+
step('deploy', async (ctx) => ctx.$`deploy --prod`),
|
|
7200
|
+
],
|
|
7201
|
+
});
|
|
7202
|
+
```
|
|
7203
|
+
|
|
7204
|
+
Here `build-plan` runs, then the job pauses for a `dba` approval. On approval, `apply-migration` runs against the exact workspace `build-plan` produced, followed by `deploy`. A rejection or expiry fails the job.
|
|
7205
|
+
|
|
7206
|
+
Because a step-level hold keeps an agent and its workspace occupied for the whole human wait, prefer job- or workflow-level gates when you do not need prior-step state, and keep step-level timeouts short. See the [operator note on agent occupancy](../operator/approvals.md#agent-occupancy-during-step-level-holds).
|
|
7207
|
+
|
|
7208
|
+
## Mandatory vs. explicit gates
|
|
7209
|
+
|
|
7210
|
+
`requireApproval` is the **explicit** gate — a deliberate "pause for a human here" written by the workflow author. It composes with the **mandatory** gate an operator can attach to a protected environment via required reviewers (see [Environments](environments.md#required-reviewers)). When both apply to the same job, all clauses from both sources must be satisfied before the job is released. The two funnel into one held-element mechanism, so the dashboard queue and `kici approve` work the same way regardless of which source held the element.
|
|
7211
|
+
|
|
7212
|
+
## Approving from the CLI
|
|
7213
|
+
|
|
7214
|
+
Approve or reject a held element with the `kici` CLI:
|
|
7215
|
+
|
|
7216
|
+
```bash
|
|
7217
|
+
# Approve a workflow-level hold
|
|
7218
|
+
kici approve <run-id>
|
|
7219
|
+
|
|
7220
|
+
# Approve a held job
|
|
7221
|
+
kici approve <run-id> --job deploy-production
|
|
7222
|
+
|
|
7223
|
+
# Approve a held step
|
|
7224
|
+
kici approve <run-id> --job migrate-and-deploy --step apply-migration
|
|
7225
|
+
|
|
7226
|
+
# Reject (a reason is required)
|
|
7227
|
+
kici reject <run-id> --job deploy-production --reason "Wrong release branch"
|
|
7228
|
+
```
|
|
7229
|
+
|
|
7230
|
+
You must be eligible for at least one unsatisfied clause — being a member of a named team or being a named user. The orchestrator verifies eligibility against the operator-defined teams, so naming a team in your workflow can never let an ineligible person release the gate. The command reports whether the element was released, how many clauses remain, or that it was rejected. See [`kici approve`](cli-reference.md#kici-approve) for the full command reference.
|
|
7231
|
+
|
|
7232
|
+
You can also approve from the dashboard approval queue. See [Dashboard](dashboard.md#approval-queue).
|
|
7233
|
+
|
|
7234
|
+
## See also
|
|
7235
|
+
|
|
7236
|
+
- [Environments](environments.md) — operator-required reviewers on protected environments.
|
|
7237
|
+
- [Approval gates (operator guide)](../operator/approvals.md) — teams, the approval queue, expiry, and self-approval.
|
|
7238
|
+
- [Approval gates (architecture)](../architecture/approvals.md) — the unified hold model and the step-level round-trip.
|
|
7239
|
+
|
|
7240
|
+
---
|
|
7241
|
+
|
|
6726
7242
|
## Concurrency groups
|
|
6727
7243
|
|
|
6728
7244
|
Source: https://docs.kici.dev/user/concurrency/
|
|
@@ -7199,7 +7715,7 @@ The page uses a responsive multi-panel layout that adapts to screen width:
|
|
|
7199
7715
|
|
|
7200
7716
|
- **Wide desktop (>= 1200px)** -- three-panel layout with a resizable job tree (left), content area (center), and metadata sidebar (right). Two draggable dividers between the panels let you resize them. Panel sizes persist to `localStorage`.
|
|
7201
7717
|
- **Medium desktop (< 1200px)** -- two-panel layout with the job tree and content area. Metadata is accessible via a "Show metadata" drawer button.
|
|
7202
|
-
- **Mobile (< 768px)** -- stacked layout with the job tree at the top and content below. Metadata is available as a tab alongside Logs, Payload, Timeline, and Summary.
|
|
7718
|
+
- **Mobile (< 768px)** -- stacked layout with the job tree at the top and content below. Metadata is available as a tab alongside Logs, Payload, Timeline, Graph, and Summary.
|
|
7203
7719
|
|
|
7204
7720
|
### Run header
|
|
7205
7721
|
|
|
@@ -7265,7 +7781,9 @@ The content area has the following tabs:
|
|
|
7265
7781
|
- **Logs** (default) -- shows log output for the selected job or step
|
|
7266
7782
|
- **Payload** -- webhook payload viewer showing the raw event payload that triggered the run. This tab appears only for runs triggered by a webhook event (and re-runs of those, which copy the original payload); runs started by a schedule, manual schedule, lifecycle event, or another run carry no payload, so the tab is hidden for them
|
|
7267
7783
|
- **Timeline** -- CSS Gantt chart showing the execution timeline of all jobs, with percentage-based bars and striped animation for running jobs. A **Provisioning** milestones section between the dispatch and execution phases plots scaler lifecycle events for the run — including a **Provisioning failed** marker when the scaler could not bring an agent up
|
|
7784
|
+
- **Graph** -- dependency graph (DAG) view of the run's jobs: each job is a node, and arrows point from a job to the jobs that depend on it. Matrix jobs appear as one node per variant. Each node shows the job name, status, and duration; a job's left accent border and the status line are colored by run state (running nodes pulse, failed nodes are red, skipped nodes are dimmed). Click a node to open that job's details (the same selection the Timeline and right panel use); hover a node to highlight what it depends on and what depends on it. Dependency edges flagged to run even when the upstream failed are drawn as dashed orange arrows. The Timeline tab remains the place to see durations and overlap on a time axis
|
|
7268
7785
|
- **Summary** -- contextual overview scoped to the current selection (run-level trigger/repo/timing info, or job-level execution context with environment variables, runtime info, and sandbox details)
|
|
7786
|
+
- **Attestations** -- build-provenance attestations produced by the run's steps (via `ctx.attestProvenance`), one row per attested artifact with a **verified** badge and a bundle download. See [Build provenance and attestations](./provenance.md#viewing-attestations-in-the-dashboard) for what the badge checks and how to verify a bundle against a specific file.
|
|
7269
7787
|
|
|
7270
7788
|
On wide desktop (>= 1200px), Metadata is shown in a dedicated sidebar panel instead of as a tab.
|
|
7271
7789
|
|
|
@@ -9184,29 +9702,37 @@ registries: [
|
|
|
9184
9702
|
|
|
9185
9703
|
## Security model
|
|
9186
9704
|
|
|
9187
|
-
- **Per-environment scoping.** Every `tokenSecret` and `installEnv` entry is qualified with an environment name. The orchestrator runs the same protection-rule pipeline (branch / trust / concurrency / reviewer / wait-timer) against each named environment **before** resolving any secret, so a workflow that wants a `production` token from a feature branch is rejected exactly like a job that tries to deploy to `production` from a feature branch.
|
|
9705
|
+
- **Per-environment scoping.** Every `tokenSecret` and `installEnv` entry is qualified with an environment name. The orchestrator runs the same protection-rule pipeline (branch / trust / concurrency / reviewer / wait-timer) against each named environment **before** resolving any secret, so a workflow that wants a `production` token from a feature branch is rejected exactly like a job that tries to deploy to `production` from a feature branch. A reviewer-gated install environment **pauses** the whole workflow dispatch as a workflow-scoped held run instead of resolving the token — see [Reviewer-gated installs](#reviewer-gated-installs) below.
|
|
9188
9706
|
- **Untrusted contributors get no tokens.** When a fork PR is dispatched and the contributor-trust resolution returns anything other than `trusted`, the orchestrator strips both `npmRegistries` and `installEnvSecrets` out of the dispatch. The install runs without auth and fails naturally on the first private dep — fork PRs cannot ever observe a registry token, even if a misconfigured environment lacks an explicit `requiredTrustTier`.
|
|
9189
9707
|
- **Lifecycle scripts disabled.** Whenever a private registry is in scope, the agent runs the install with `--ignore-scripts` (npm or pnpm alike). A malicious `preinstall` / `postinstall` hook in committed `package.json` cannot read the synthesized token env vars, even though they exist in the install subprocess. For a pnpm workspace, the agent builds your in-repo dependency closure as a separate step **after** the install's auth is torn down, so build scripts never see the tokens either.
|
|
9190
9708
|
- **Stderr is redacted.** If the install fails, the agent masks every token literal out of the surfaced stderr / stdout chunks before logging.
|
|
9191
9709
|
- **Job-scoped env-var names.** The synthesized auth env var is `KICI_NPM_TOKEN_<jobIdShort>_<i>` where `jobIdShort` is the first 8 chars of the dispatched job id. The name is unguessable from outside the install subprocess and not reused across jobs.
|
|
9192
9710
|
- **`.npmrc` restored.** Whatever the agent appended for one install is stripped (or the file unlinked) on cleanup, so the workspace is never permanently modified.
|
|
9193
9711
|
|
|
9712
|
+
## Reviewer-gated installs
|
|
9713
|
+
|
|
9714
|
+
When the named install environment carries a protection rule that holds — a required reviewer (`hold`) or a wait timer (`wait`) — the install gate **pauses the whole workflow dispatch** instead of rejecting it. The run is created in the `held` state, no jobs are queued, and a workflow-scoped row appears on the held-runs page with a `Workflow` scope badge.
|
|
9715
|
+
|
|
9716
|
+
- **Reviewer hold:** the run waits for an approver. On approval the dispatch resumes from the install gate, resolves the token, and dispatches its jobs as a normal run. On rejection the run transitions to `cancelled` — no jobs ever run.
|
|
9717
|
+
- **Wait timer:** the run waits out the timer and resumes automatically when it elapses.
|
|
9718
|
+
|
|
9719
|
+
A `reject` protection outcome (for example a disabled environment or a branch the environment forbids) still fails the dispatch loudly with a clear reason, exactly as before — the orchestrator never dispatches a run with an unresolved install token.
|
|
9720
|
+
|
|
9194
9721
|
## Limitations
|
|
9195
9722
|
|
|
9196
9723
|
- **`registries:` is workflow-level only in v1.** Per-job overrides aren't supported — there is one shared `.kici/` per workspace, so a per-job `registries:` would be physically nonsensical.
|
|
9197
|
-
- **A non-pass protection-rule outcome rejects the whole workflow dispatch.** Today, if the named environment requires reviewer approval (`hold` action) or hits concurrency (`queue` / `wait`) for the install gate, the entire workflow dispatch is rejected with a clear reason. Workflow-scoped held-runs (which would let the install wait for an approver instead of failing outright) are tracked as a follow-up — until then, choose an environment whose protection rules `pass` for the branches that need to install private deps.
|
|
9198
9724
|
- **Container registries (Docker Hub, ECR, GHCR) are out of scope.** This feature covers **npm** registry auth only. Container image pulls travel through the executor backend's own credential paths.
|
|
9199
9725
|
|
|
9200
9726
|
## Observability
|
|
9201
9727
|
|
|
9202
9728
|
The orchestrator exposes Prometheus counters and a histogram under the `kici_orch_install_secrets_*` prefix on its `/metrics` endpoint. They populate the **Install secrets resolution** Grafana dashboard and let operators graph install-secrets activity without digging through Loki.
|
|
9203
9729
|
|
|
9204
|
-
| Metric | Type | Labels | What it tells you
|
|
9205
|
-
| ------------------------------------------------------------- | --------- | ------------------------------ |
|
|
9206
|
-
| `kici_orch_install_secrets_decisions_total` | Counter | `decision`, `reason` | Pass / reject volume. Reject reasons enumerate the failure mode: `malformed_ref`, `invalid_url_scheme`, `env_not_found`, `protection_rule_block`, `missing_token`, `missing_install_env`, etc. |
|
|
9207
|
-
| `kici_orch_install_secrets_npm_registry_used_total` | Counter | `channel`, `provider`, `scope` | Per-channel + per-scope usage. `channel=registries` is Option A, `channel=install_env` is Option C. `scope=default` marks a no-scope default registry; `scope=-` marks Option C entries.
|
|
9208
|
-
| `kici_orch_install_secrets_contributor_stripped_total` | Counter | `trust_tier` | Number of dispatches where registry tokens were stripped because the contributor tier wasn't `trusted` (fork PRs from unknown / known contributors). Expected to be 0 in single-tenant orgs.
|
|
9209
|
-
| `kici_orch_install_secrets_token_resolution_duration_seconds` | Histogram | `environment` | Latency of per-environment secret resolution. Pathological tails (>500ms) usually mean a Vault timeout or a slow Postgres replica.
|
|
9730
|
+
| Metric | Type | Labels | What it tells you |
|
|
9731
|
+
| ------------------------------------------------------------- | --------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9732
|
+
| `kici_orch_install_secrets_decisions_total` | Counter | `decision`, `reason` | Pass / reject / hold volume. `decision=hold` (reason `held`) counts dispatches paused at a reviewer-gated install environment. Reject reasons enumerate the failure mode: `malformed_ref`, `invalid_url_scheme`, `env_not_found`, `protection_rule_block`, `missing_token`, `missing_install_env`, etc. |
|
|
9733
|
+
| `kici_orch_install_secrets_npm_registry_used_total` | Counter | `channel`, `provider`, `scope` | Per-channel + per-scope usage. `channel=registries` is Option A, `channel=install_env` is Option C. `scope=default` marks a no-scope default registry; `scope=-` marks Option C entries. |
|
|
9734
|
+
| `kici_orch_install_secrets_contributor_stripped_total` | Counter | `trust_tier` | Number of dispatches where registry tokens were stripped because the contributor tier wasn't `trusted` (fork PRs from unknown / known contributors). Expected to be 0 in single-tenant orgs. |
|
|
9735
|
+
| `kici_orch_install_secrets_token_resolution_duration_seconds` | Histogram | `environment` | Latency of per-environment secret resolution. Pathological tails (>500ms) usually mean a Vault timeout or a slow Postgres replica. |
|
|
9210
9736
|
|
|
9211
9737
|
The dashboard JSON lives at `infra/terraform/modules/grafana/dashboards/install-secrets.json`; if you maintain your own monitoring stack, you can import it directly.
|
|
9212
9738
|
|
|
@@ -9218,6 +9744,197 @@ The dashboard JSON lives at `infra/terraform/modules/grafana/dashboards/install-
|
|
|
9218
9744
|
|
|
9219
9745
|
---
|
|
9220
9746
|
|
|
9747
|
+
## Build provenance and attestations
|
|
9748
|
+
|
|
9749
|
+
Source: https://docs.kici.dev/user/provenance/
|
|
9750
|
+
|
|
9751
|
+
Build provenance is a signed, verifiable statement of **what produced an
|
|
9752
|
+
artifact** — the source repository, commit, ref, workflow path, and builder that
|
|
9753
|
+
ran. When a workflow step attests an artifact, KiCI records that statement,
|
|
9754
|
+
signs it, and makes it retrievable so anyone can later prove the artifact came
|
|
9755
|
+
from a specific KiCI run and was not swapped along the way.
|
|
9756
|
+
|
|
9757
|
+
This is the same idea behind supply-chain attestation systems like
|
|
9758
|
+
[SLSA](https://slsa.dev/spec/v1.0/provenance): a downstream consumer (a release
|
|
9759
|
+
gate, a security audit, a `"show me the provenance"` request) can verify the
|
|
9760
|
+
artifact's origin without trusting the person who handed it over.
|
|
9761
|
+
|
|
9762
|
+
## What an attestation contains
|
|
9763
|
+
|
|
9764
|
+
An attestation is a self-contained bundle holding three things:
|
|
9765
|
+
|
|
9766
|
+
- An **in-toto SLSA v1.0 statement** describing the build: the subject artifact
|
|
9767
|
+
(name + content digest) and the provenance predicate (source repository,
|
|
9768
|
+
commit, ref, workflow, run/job identifiers, timestamps).
|
|
9769
|
+
- A **[DSSE](https://github.com/secure-systems-lab/dsse) signature** over that
|
|
9770
|
+
statement, made with an ephemeral signing key generated for the run.
|
|
9771
|
+
- A short-lived **OIDC identity token** issued by the KiCI platform that binds
|
|
9772
|
+
the signature to the build identity. The token's identity claims
|
|
9773
|
+
(`repository`, `ref`, `sha`, run/job ids) are derived by the platform from the
|
|
9774
|
+
run itself — a step cannot forge them.
|
|
9775
|
+
|
|
9776
|
+
Because the bundle carries the identity token and the public signing key, it is
|
|
9777
|
+
**offline-verifiable**: a verifier checks it against the platform's published
|
|
9778
|
+
signing keys with no per-attestation online lookup.
|
|
9779
|
+
|
|
9780
|
+
## Attesting an artifact in a workflow
|
|
9781
|
+
|
|
9782
|
+
Call `ctx.attestProvenance({ subject })` from a step after you have produced the
|
|
9783
|
+
artifact:
|
|
9784
|
+
|
|
9785
|
+
```typescript
|
|
9786
|
+
import { workflow, job, step } from '@kici-dev/sdk';
|
|
9787
|
+
|
|
9788
|
+
export default workflow('release', {
|
|
9789
|
+
on: { push: { branches: ['main'] } },
|
|
9790
|
+
jobs: [
|
|
9791
|
+
job('publish', {
|
|
9792
|
+
steps: [
|
|
9793
|
+
step('build', async (ctx) => {
|
|
9794
|
+
await ctx.$`npm pack`;
|
|
9795
|
+
}),
|
|
9796
|
+
step('attest', async (ctx) => {
|
|
9797
|
+
const result = await ctx.attestProvenance({
|
|
9798
|
+
subject: { name: 'my-pkg-1.2.3.tgz', path: 'my-pkg-1.2.3.tgz' },
|
|
9799
|
+
});
|
|
9800
|
+
ctx.log.info(`Attestation stored at ${result.storageKey}`);
|
|
9801
|
+
}),
|
|
9802
|
+
],
|
|
9803
|
+
}),
|
|
9804
|
+
],
|
|
9805
|
+
});
|
|
9806
|
+
```
|
|
9807
|
+
|
|
9808
|
+
The **subject is caller-supplied** — you name the artifact and give KiCI either a
|
|
9809
|
+
path or a precomputed digest:
|
|
9810
|
+
|
|
9811
|
+
- `{ name, path }` — a path relative to the step working directory. KiCI reads
|
|
9812
|
+
the file and computes its SHA-256 digest.
|
|
9813
|
+
- `{ name, digest }` — a precomputed digest. For a container image, pass the OCI
|
|
9814
|
+
manifest digest your build tool emitted:
|
|
9815
|
+
|
|
9816
|
+
```typescript
|
|
9817
|
+
await ctx.attestProvenance({
|
|
9818
|
+
subject: { name: 'ghcr.io/acme/app', digest: { sha256: '<manifest-digest>' } },
|
|
9819
|
+
});
|
|
9820
|
+
```
|
|
9821
|
+
|
|
9822
|
+
The identity token is fetched and masked in logs automatically — you never
|
|
9823
|
+
handle it. The call returns `{ storageKey, subjectDigest, bundleMediaType }`
|
|
9824
|
+
identifying the stored bundle.
|
|
9825
|
+
|
|
9826
|
+
`ctx.attestProvenance` is only available inside a running job step. Calling it
|
|
9827
|
+
during local execution rejects with a clear error.
|
|
9828
|
+
|
|
9829
|
+
### Requesting a raw identity token
|
|
9830
|
+
|
|
9831
|
+
`ctx.attestProvenance` builds on a lower-level primitive you can call directly
|
|
9832
|
+
when you need the identity token for a different tool:
|
|
9833
|
+
|
|
9834
|
+
```typescript
|
|
9835
|
+
step('mint', async (ctx) => {
|
|
9836
|
+
const { token, expiresIn } = await ctx.kici.oidc.token({ audience: 'sigstore' });
|
|
9837
|
+
ctx.log.info(`Got an ID token valid for ${expiresIn}s`);
|
|
9838
|
+
// Hand `token` to a tool that exchanges it with a service trusting the issuer.
|
|
9839
|
+
});
|
|
9840
|
+
```
|
|
9841
|
+
|
|
9842
|
+
The token is a short-lived (about 10 minutes) signed JWT scoped to the current
|
|
9843
|
+
run and job. Its identity claims (`repository`, `ref`, `sha`, `kici_run_id`,
|
|
9844
|
+
`kici_job_id`) are derived by the platform from the run context, so a step cannot
|
|
9845
|
+
spoof them. The returned token value is automatically masked in step logs, and
|
|
9846
|
+
the step never holds platform credentials — the request is relayed through the
|
|
9847
|
+
orchestrator, which mints the token on the step's behalf. Like
|
|
9848
|
+
`attestProvenance`, it is only available inside a running job step.
|
|
9849
|
+
|
|
9850
|
+
## Verifying an attestation
|
|
9851
|
+
|
|
9852
|
+
Verify a bundle with the `kici verify-attestation` command. It establishes the
|
|
9853
|
+
full chain offline: the identity token verifies against the trusted issuer's
|
|
9854
|
+
JWKS, the DSSE signature verifies against the bundled signing key, and the
|
|
9855
|
+
statement's build context must match the token's identity claims (a mismatch is
|
|
9856
|
+
a hard failure).
|
|
9857
|
+
|
|
9858
|
+
```bash
|
|
9859
|
+
kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file>
|
|
9860
|
+
```
|
|
9861
|
+
|
|
9862
|
+
You supply the trusted issuer out-of-band via `--trust-root` — the verifier
|
|
9863
|
+
never trusts the issuer named inside the token. There are two forms:
|
|
9864
|
+
|
|
9865
|
+
- **Online — an HTTPS issuer URL.** The verifier fetches
|
|
9866
|
+
`<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`,
|
|
9867
|
+
and fetches the JWKS. The token's `iss` is pinned to the discovery document's
|
|
9868
|
+
`issuer`.
|
|
9869
|
+
- **Offline — a self-contained trust-root file.** A local JSON file with the
|
|
9870
|
+
issuer and JWKS inlined, for air-gapped verification:
|
|
9871
|
+
|
|
9872
|
+
```json
|
|
9873
|
+
{
|
|
9874
|
+
"issuer": "https://platform.example/issuer",
|
|
9875
|
+
"jwks": {
|
|
9876
|
+
"keys": [
|
|
9877
|
+
{ "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "alg": "ES256", "kid": "..." }
|
|
9878
|
+
]
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
```
|
|
9882
|
+
|
|
9883
|
+
Pass an optional `[artifact]` to also digest-check the file against the
|
|
9884
|
+
attestation subject — this is what binds the attestation to a specific set of
|
|
9885
|
+
bytes. Omit it to verify the signatures and identity only. Use `--json` for a
|
|
9886
|
+
machine-readable result. The command exits `0` when everything verifies and `1`
|
|
9887
|
+
when it does not (or on an error such as a missing flag or unreachable trust
|
|
9888
|
+
root).
|
|
9889
|
+
|
|
9890
|
+
```bash
|
|
9891
|
+
# Verify a bundle against a deployed issuer, digest-checking the artifact:
|
|
9892
|
+
kici verify-attestation ./dist/app.tgz \
|
|
9893
|
+
--bundle ./app.tgz.kici.json \
|
|
9894
|
+
--trust-root https://platform.example/issuer
|
|
9895
|
+
|
|
9896
|
+
# Air-gapped: verify against a self-contained trust-root file:
|
|
9897
|
+
kici verify-attestation ./dist/app.tgz \
|
|
9898
|
+
--bundle ./app.tgz.kici.json \
|
|
9899
|
+
--trust-root ./kici-trust-root.json
|
|
9900
|
+
```
|
|
9901
|
+
|
|
9902
|
+
The full flag reference is in the [CLI reference](./cli-reference.md#kici-verify-attestation).
|
|
9903
|
+
|
|
9904
|
+
## Viewing attestations in the dashboard
|
|
9905
|
+
|
|
9906
|
+
<!-- help:run-attestations#viewing-attestations-in-the-dashboard -->
|
|
9907
|
+
|
|
9908
|
+
The run detail page has an **Attestations** tab listing each artifact a run's
|
|
9909
|
+
steps attested (via `ctx.attestProvenance`), one row per artifact.
|
|
9910
|
+
|
|
9911
|
+
Each row shows:
|
|
9912
|
+
|
|
9913
|
+
- **Status** — a **verified** badge computed in your browser. It checks the
|
|
9914
|
+
attestation's signature, the build identity, and the build context against the
|
|
9915
|
+
trusted provenance issuer. **verified** (green) means all of those pass;
|
|
9916
|
+
**failed** (red) shows why in a tooltip; **unverifiable** means the provenance
|
|
9917
|
+
issuer is not configured.
|
|
9918
|
+
- **Job / Artifact / Digest / Created** — the producing job, the artifact name,
|
|
9919
|
+
its content digest, and when it was recorded.
|
|
9920
|
+
- **Download** — saves the signed bundle as a `.sigstore.json` file.
|
|
9921
|
+
|
|
9922
|
+
The badge does **not** re-hash the artifact bytes — the dashboard does not have
|
|
9923
|
+
the artifact. To bind the attestation to a specific file, run
|
|
9924
|
+
`kici verify-attestation <artifact> --bundle <bundle>`. A run with no
|
|
9925
|
+
attestations shows an empty state.
|
|
9926
|
+
|
|
9927
|
+
<!-- /help:run-attestations -->
|
|
9928
|
+
|
|
9929
|
+
## See also
|
|
9930
|
+
|
|
9931
|
+
- [SDK runtime reference](./sdk/runtime.md) — the `ctx.attestProvenance` and
|
|
9932
|
+
`ctx.kici.oidc.token` step APIs in full.
|
|
9933
|
+
- [CLI reference](./cli-reference.md#kici-verify-attestation) — every
|
|
9934
|
+
`kici verify-attestation` flag and exit code.
|
|
9935
|
+
|
|
9936
|
+
---
|
|
9937
|
+
|
|
9221
9938
|
## Secrets
|
|
9222
9939
|
|
|
9223
9940
|
Source: https://docs.kici.dev/user/secrets/
|
|
@@ -9438,6 +10155,45 @@ export default workflow('deploy', {
|
|
|
9438
10155
|
});
|
|
9439
10156
|
```
|
|
9440
10157
|
|
|
10158
|
+
### Injecting decrypted sops values into the environment
|
|
10159
|
+
|
|
10160
|
+
KiCI does **not** scan your repository for `*.enc.yaml` files and auto-decrypt them into the environment at job init — nothing in a job runs `sops` on your behalf, and resolved secrets are never auto-injected as environment variables (see [Security notes](./sdk/runtime.md#security-notes)). Decryption is always something your workflow does explicitly: provision the age (or other) decryption key as a KiCI secret, expose it for the step, run `sops -d`, and decide what to do with the output.
|
|
10161
|
+
|
|
10162
|
+
When you want the decrypted values available as environment variables — not just written to a file — decrypt early and export the values through `$KICI_ENV` (or `ctx.setEnv`). Anything appended to `$KICI_ENV` becomes an environment variable for every later step in the same job, so a single decrypt step can populate the environment for the whole job:
|
|
10163
|
+
|
|
10164
|
+
```typescript
|
|
10165
|
+
import { workflow, job, step, push } from '@kici-dev/sdk';
|
|
10166
|
+
|
|
10167
|
+
export default workflow('deploy', {
|
|
10168
|
+
on: push({ branches: ['main'] }),
|
|
10169
|
+
jobs: [
|
|
10170
|
+
job('decrypt-and-deploy', {
|
|
10171
|
+
runsOn: 'default',
|
|
10172
|
+
environment: 'production',
|
|
10173
|
+
steps: [
|
|
10174
|
+
step('decrypt-to-env', async (ctx) => {
|
|
10175
|
+
await ctx.secrets.exposeFile('SOPS_AGE_KEY_FILE', {
|
|
10176
|
+
sources: ctx.secrets.list().filter((k) => k.startsWith('AGE_KEY_')),
|
|
10177
|
+
divider: '\n',
|
|
10178
|
+
});
|
|
10179
|
+
// Decrypt to dotenv format, then append every KEY=value line to
|
|
10180
|
+
// $KICI_ENV so subsequent steps see them as environment variables.
|
|
10181
|
+
await ctx.$`sops -d --output-type dotenv secrets.enc.yaml >> "$KICI_ENV"`;
|
|
10182
|
+
}),
|
|
10183
|
+
step('deploy', async (ctx) => {
|
|
10184
|
+
// Values decrypted above are now ordinary env vars here.
|
|
10185
|
+
await ctx.$`./deploy.sh`;
|
|
10186
|
+
}),
|
|
10187
|
+
],
|
|
10188
|
+
}),
|
|
10189
|
+
],
|
|
10190
|
+
});
|
|
10191
|
+
```
|
|
10192
|
+
|
|
10193
|
+
Decrypted values exported this way follow the same rules as any other `$KICI_ENV` / `ctx.setEnv` export: last-write-wins on a repeated key, and a key that collides with an operator-injected secret is ignored (the operator value wins). See [Exporting env from shell commands](./sdk/runtime.md#exporting-env-from-shell-commands-kici_env--kici_path) for the full `$KICI_ENV` contract.
|
|
10194
|
+
|
|
10195
|
+
If you only need the decrypted material as a file on disk (the common `kubectl` / `gcloud` case), skip the env hop and redirect to a file instead — see the [canonical sops example](#canonical-sops-example) above.
|
|
10196
|
+
|
|
9441
10197
|
## API reference
|
|
9442
10198
|
|
|
9443
10199
|
| Method | Signature | Description |
|
|
@@ -9800,6 +10556,123 @@ and confirm the App is installed on that repo.
|
|
|
9800
10556
|
|
|
9801
10557
|
---
|
|
9802
10558
|
|
|
10559
|
+
## Local filesystem (file://) source
|
|
10560
|
+
|
|
10561
|
+
Source: https://docs.kici.dev/user/providers/local-file/
|
|
10562
|
+
|
|
10563
|
+
The **local** provider lets the orchestrator run workflows from a git
|
|
10564
|
+
repository that already lives on the agent's filesystem — cloned via a
|
|
10565
|
+
`file://` URL rather than fetched from a remote forge. There is no webhook
|
|
10566
|
+
from GitHub or GitLab; the operator drives runs explicitly with the
|
|
10567
|
+
`kici-admin` CLI or a generated `post-receive` hook.
|
|
10568
|
+
|
|
10569
|
+
> **This is an operator-curated source, not daily developer CI.** Use it for
|
|
10570
|
+
> globally-registered or policy workflows the operator controls — a vendored
|
|
10571
|
+
> repo baked into an agent image, a repo synced onto a host out-of-band, a
|
|
10572
|
+
> golden internal pipeline. For ordinary per-developer CI driven by pull
|
|
10573
|
+
> requests and pushes, use the [GitHub App provider](./github.md) or the
|
|
10574
|
+
> [universal-git provider](./universal-git.md) against a real forge.
|
|
10575
|
+
|
|
10576
|
+
## Trust caveat (read first)
|
|
10577
|
+
|
|
10578
|
+
A local source uses signature verification `none` — there is no remote forge
|
|
10579
|
+
to sign the webhook payload, so the orchestrator cannot authenticate the
|
|
10580
|
+
trigger. **Only register repos you trust.** Anyone who can reach the
|
|
10581
|
+
orchestrator's webhook route for this source, or push to the repo on disk,
|
|
10582
|
+
can drive a run. Treat the repo path as a trusted operator input, the same
|
|
10583
|
+
way you treat the orchestrator's own configuration.
|
|
10584
|
+
|
|
10585
|
+
## Register a source
|
|
10586
|
+
|
|
10587
|
+
```bash
|
|
10588
|
+
kici-admin source add local \
|
|
10589
|
+
--org <orgId> \
|
|
10590
|
+
--path /abs/path/to/repo \
|
|
10591
|
+
--name my-local-repo
|
|
10592
|
+
```
|
|
10593
|
+
|
|
10594
|
+
- `--path` must be an **absolute** directory on the agent filesystem. It is
|
|
10595
|
+
the base path the orchestrator's lock-file fetcher reads
|
|
10596
|
+
(`<path>/.kici/kici.lock.json`) and the base for the `file://` clone the
|
|
10597
|
+
agent performs.
|
|
10598
|
+
- `--clone-url-base <url>` is optional. By default the agent clones via
|
|
10599
|
+
`file://<path>`. Supply a `git://` or `http://` base when the agent does
|
|
10600
|
+
**not** share the orchestrator's filesystem and must fetch the repo over a
|
|
10601
|
+
git server instead (see "Per-scaler reachability" below).
|
|
10602
|
+
|
|
10603
|
+
Update the path or name later:
|
|
10604
|
+
|
|
10605
|
+
```bash
|
|
10606
|
+
kici-admin source update-local <id> --path /new/abs/path
|
|
10607
|
+
kici-admin source update-local <id> --name new-name
|
|
10608
|
+
```
|
|
10609
|
+
|
|
10610
|
+
Remove it:
|
|
10611
|
+
|
|
10612
|
+
```bash
|
|
10613
|
+
kici-admin source remove <routingKey> --local
|
|
10614
|
+
```
|
|
10615
|
+
|
|
10616
|
+
List and inspect (local sources render their `repoBasePath`):
|
|
10617
|
+
|
|
10618
|
+
```bash
|
|
10619
|
+
kici-admin source list --org <orgId>
|
|
10620
|
+
kici-admin source get <id>
|
|
10621
|
+
```
|
|
10622
|
+
|
|
10623
|
+
## Trigger runs
|
|
10624
|
+
|
|
10625
|
+
A local repo has no forge to send webhooks, so you trigger runs yourself.
|
|
10626
|
+
|
|
10627
|
+
**One-shot, by hand:**
|
|
10628
|
+
|
|
10629
|
+
```bash
|
|
10630
|
+
kici-admin source trigger-local <id>
|
|
10631
|
+
```
|
|
10632
|
+
|
|
10633
|
+
The command reads the repo's current HEAD ref and commit SHA, builds a
|
|
10634
|
+
GitHub-shaped `push` payload, and POSTs it to the orchestrator's generic
|
|
10635
|
+
webhook route. Override the ref/sha/event explicitly when needed:
|
|
10636
|
+
|
|
10637
|
+
```bash
|
|
10638
|
+
kici-admin source trigger-local <id> --event push --ref refs/heads/main --sha <sha>
|
|
10639
|
+
```
|
|
10640
|
+
|
|
10641
|
+
**On every push, via a hook:**
|
|
10642
|
+
|
|
10643
|
+
```bash
|
|
10644
|
+
kici-admin source install-hook <id>
|
|
10645
|
+
```
|
|
10646
|
+
|
|
10647
|
+
This writes a `post-receive` hook into the repo so that every push to it
|
|
10648
|
+
triggers a run automatically — the local equivalent of a forge webhook.
|
|
10649
|
+
|
|
10650
|
+
## Per-scaler reachability (operator's responsibility)
|
|
10651
|
+
|
|
10652
|
+
The orchestrator accepts a local source on **any** scaler backend and does
|
|
10653
|
+
**not** verify that the repo is actually reachable inside the agent. Making
|
|
10654
|
+
the path reachable is the operator's job. On a container or Firecracker
|
|
10655
|
+
scaler the orchestrator logs a reachability warning when it registers the
|
|
10656
|
+
source, but it does not reject it.
|
|
10657
|
+
|
|
10658
|
+
| Scaler | How the repo must be reachable in the agent |
|
|
10659
|
+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
10660
|
+
| bare-metal | The agent runs on the host, so the `--path` host directory is used directly. |
|
|
10661
|
+
| container | Bake the repo into the agent image, or bind-mount it at the **same absolute path** the source was registered with. Alternatively register `--clone-url-base` pointing at a git server the container can reach. |
|
|
10662
|
+
| Firecracker | The repo must be present on the microVM rootfs at the registered path, or reachable via a `--clone-url-base` git server. |
|
|
10663
|
+
|
|
10664
|
+
If the path is not reachable inside the agent, the clone fails at run time —
|
|
10665
|
+
the run is created and then fails, rather than being silently dropped.
|
|
10666
|
+
|
|
10667
|
+
## See also
|
|
10668
|
+
|
|
10669
|
+
- [Universal-git provider](./universal-git.md) — for a remote forge (or any
|
|
10670
|
+
`http://` git server) when there is no shared filesystem.
|
|
10671
|
+
- [GitHub App provider](./github.md) — the flagship source for pull-request
|
|
10672
|
+
CI with Checks.
|
|
10673
|
+
|
|
10674
|
+
---
|
|
10675
|
+
|
|
9803
10676
|
## Universal-git provider
|
|
9804
10677
|
|
|
9805
10678
|
Source: https://docs.kici.dev/user/providers/universal-git/
|
|
@@ -9826,6 +10699,13 @@ No mirror, no GitHub App, no `checkout: false` escape hatch. The same
|
|
|
9826
10699
|
trigger matching, global-workflow policy, and agent execution pipeline
|
|
9827
10700
|
that back the GitHub App source also serve universal-git sources.
|
|
9828
10701
|
|
|
10702
|
+
> **No shared filesystem between orchestrator and agent?** Universal-git is
|
|
10703
|
+
> the right choice for the **remote-agent** case — point it at an `http://`
|
|
10704
|
+
> git server and the agent clones over the network. When the repo instead
|
|
10705
|
+
> lives on the agent's own filesystem (a vendored / operator-curated repo),
|
|
10706
|
+
> use a [local `file://` source](./local-file.md) and drive it with the
|
|
10707
|
+
> `kici-admin` CLI.
|
|
10708
|
+
|
|
9829
10709
|
## Which preset do I need?
|
|
9830
10710
|
|
|
9831
10711
|
KiCI ships canonical presets so you don't have to spell out JSONPath for
|
|
@@ -10045,7 +10925,7 @@ with the right PEM.
|
|
|
10045
10925
|
|
|
10046
10926
|
Source: https://docs.kici.dev/architecture/data-flows/
|
|
10047
10927
|
|
|
10048
|
-
This document describes the key data flows through the KiCI architecture: webhook delivery, job execution, dependency caching, re-run and cancel, trace ID propagation, internal event routing, and generic webhook ingestion.
|
|
10928
|
+
This document describes the key data flows through the KiCI architecture: webhook delivery, job execution, developer-initiated remote runs, dependency caching, re-run and cancel, trace ID propagation, internal event routing, and generic webhook ingestion.
|
|
10049
10929
|
|
|
10050
10930
|
> **Lock file schema version:** The lock file uses schema version 15, which adds per-job init config on top of v14's declarative cache specs, v11's `LockInlineValue` for pure function inline evaluation, v10's simplified negative patterns (! prefix in repos/paths arrays), v9's global workflow repos matching, and v8's runsOn polymorphic type support.
|
|
10051
10931
|
|
|
@@ -10117,6 +10997,41 @@ The agent delegates job execution to an `ExecutionSandbox` (container, bare-meta
|
|
|
10117
10997
|
7. **Report** -- Send final `job.status` back to orchestrator with step results and timing
|
|
10118
10998
|
8. **Cleanup** -- Tear down sandbox and remove work directory
|
|
10119
10999
|
|
|
11000
|
+
## Remote run flow (`kici run remote`)
|
|
11001
|
+
|
|
11002
|
+
A developer running `kici run remote` from a working tree initiates a run through the three-tier relay without a provider webhook. The flow splits into two independent planes: a **control plane** through the Platform relay, and a **data plane** that uploads the working-tree overlay straight to object storage.
|
|
11003
|
+
|
|
11004
|
+
```
|
|
11005
|
+
Developer machine Platform relay Orchestrator Object store
|
|
11006
|
+
| | | |
|
|
11007
|
+
|-- upload-init (control) ----->|--- WS relay ----------->| |
|
|
11008
|
+
| (org, cluster, overlay | |-- mint presigned |
|
|
11009
|
+
| metadata, inline lock) | | PUT URL ---------->|
|
|
11010
|
+
|<-- presigned PUT URL ---------|<--- WS relay -----------| |
|
|
11011
|
+
| | | |
|
|
11012
|
+
|== overlay tarball PUT (data plane) ===========================================>|
|
|
11013
|
+
| | | |
|
|
11014
|
+
|-- trigger (control) --------->|--- WS relay ----------->|-- dispatch jobs |
|
|
11015
|
+
| | | (agents fetch |
|
|
11016
|
+
| | | overlay) |
|
|
11017
|
+
|-- poll logs + status -------->|--- WS relay ----------->| |
|
|
11018
|
+
|<-- log chunks + status -------|<--- WS relay -----------| |
|
|
11019
|
+
```
|
|
11020
|
+
|
|
11021
|
+
### Control plane
|
|
11022
|
+
|
|
11023
|
+
Run initiation (`upload-init`), the trigger, status polling, log retrieval, and cancellation all flow from the developer machine to the Platform, which relays them over its WebSocket connection to the org's orchestrator. The developer machine never talks to the orchestrator's HTTP API directly. Logs are delivered by the CLI polling the Platform for log chunks — tracked by a monotonic line cursor — and run status until the run reaches a terminal state; there is no direct streaming socket between the developer machine and the orchestrator.
|
|
11024
|
+
|
|
11025
|
+
### Data plane
|
|
11026
|
+
|
|
11027
|
+
The working-tree overlay tarball uploads **directly** from the developer machine to the orchestrator's object store via a pre-signed PUT URL minted during `upload-init`. The overlay bytes never pass through the Platform. Because of this split, only the object store needs to be reachable from the developer machine — the orchestrator can sit behind a private network. See [Storage layout](../operator/orchestrator/storage-layout.md) for the upload-endpoint configuration.
|
|
11028
|
+
|
|
11029
|
+
### Org anchor
|
|
11030
|
+
|
|
11031
|
+
The run is dispatched to the developer's active organization (selected with `kici org use`, or overridden per-run). The orchestrator anchors its bound organization with a system-managed **remote source** (routing key `remote:<orgId>`) that it auto-provisions — no manual webhook source is required, so a zero-source org is immediately routable for remote runs. The Platform forces the run's routing key to `remote:<orgId>` server-side; the developer never sets a routing key. When an org has more than one connected orchestrator cluster, the CLI selects the target cluster explicitly (or relies on the per-org default), and a single connected cluster is auto-selected.
|
|
11032
|
+
|
|
11033
|
+
Remote runs are offered by the Platform; an orchestrator with no Platform connection cannot serve them. Executing workflow steps on the developer machine with no orchestrator is the separate `kici run local` path.
|
|
11034
|
+
|
|
10120
11035
|
## Source and dependency caching flow
|
|
10121
11036
|
|
|
10122
11037
|
KiCI runs two orchestrator-side caches — the **source tarball cache** (raw `.kici/` directory minus `node_modules/`) and the **dependency tarball cache** (packed `node_modules/`). Both use a build-then-execute pattern: the orchestrator checks the caches before dispatching execution jobs, and if the source cache is cold a build agent populates both in one pass.
|
|
@@ -10947,7 +11862,7 @@ Shared business logic used by all three tiers. Single source of truth for cross-
|
|
|
10947
11862
|
- Environment allowlist (safe env var filtering)
|
|
10948
11863
|
- Secrets management (secret context resolution)
|
|
10949
11864
|
- Environment model (scoped secrets, env merge, protection gates)
|
|
10950
|
-
- Label utilities (platform label derivation, runsOn normalization, `kici:*` reserved namespace, role labels)
|
|
11865
|
+
- Label utilities (platform label derivation, runsOn normalization, `kici:*` set-only reserved namespace, role labels)
|
|
10951
11866
|
- Audit policy and retention (per-action access-log sampling, warm-retention windows for cold-store eligibility, federated activity row schema)
|
|
10952
11867
|
- Scaler backend type enum (`container`, `bare-metal`, `firecracker`, `kubernetes`)
|
|
10953
11868
|
- Registration trigger type enum (registerable trigger discriminator)
|