@kici-dev/compiler 0.1.17 → 0.1.18

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.
@@ -312,14 +312,14 @@ This updates `.kici/package.json` and generates (or updates) `package-lock.json`
312
312
 
313
313
  ### Dependency resolution contract
314
314
 
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.
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, yarn classic (v1), and yarn berry (v2+). A dependency that points outside the cloned repo cannot be resolved.
316
316
 
317
317
  In practice:
318
318
 
319
319
  - **From a registry** — the common case. Pin a published version (a private registry works — see [Private registries](./private-registries.md)). Available for any package manager.
320
- - **From an in-repo workspace sibling** — if your `.kici/` is a member of a **pnpm workspace**, it can depend on a sibling package in the same repo via `workspace:*`. The whole repo is cloned, so the sibling is present and resolves; the agent also builds your `.kici/` dependency closure after install, so a sibling's build output exists before the workflow that imports it loads. A `file:`/`link:`/`portal:` path is allowed only when it stays inside the repository.
320
+ - **From an in-repo workspace sibling** — if your `.kici/` is a member of a **pnpm workspace** or a **yarn berry workspace** (a `workspaces` array in the repo-root `package.json`), it can depend on a sibling package in the same repo via `workspace:*` (yarn berry also accepts `portal:`). The whole repo is cloned, so the sibling is present and resolves; the agent also builds your `.kici/` dependency closure after install, so a sibling's build output exists before the workflow that imports it loads. A `file:`/`link:`/`portal:` path is allowed only when it stays inside the repository.
321
321
 
322
- What fails fast (with an actionable error naming the dependency, not a raw package-manager error): a `workspace:` dependency in an **npm** project (npm has no workspace protocol — pin a published version or switch to pnpm), and any `file:`/`link:`/`portal:` path that points outside the cloned repo.
322
+ What fails fast (with an actionable error naming the dependency, not a raw package-manager error): a `workspace:` dependency in an **npm** project (npm has no workspace protocol — pin a published version or switch to pnpm), a `workspace:`/`portal:` dependency in a **yarn classic** project (v1 has neither — use a version range, pnpm, or yarn berry), a `workspace:` dependency in a **yarn berry** project whose repo-root `package.json` has no `workspaces` array, and any `file:`/`link:`/`portal:` path that points outside the cloned repo.
323
323
 
324
324
  Then use the package in your workflow:
325
325
 
@@ -1150,6 +1150,46 @@ export const postDeploy = workflow('post-deploy', {
1150
1150
  });
1151
1151
  ```
1152
1152
 
1153
+ `workflowComplete()` / `jobComplete()` start a **separate** workflow run that reacts to the prior one finishing, gated on its status. They are the right tool when a _different_ workflow should respond. When you instead need to add more jobs to the **same** run based on what a job just produced — fanning out follow-up work from a prior job's outputs — use a result-aware generator (next section), not a completion-event chain.
1154
+
1155
+ ### Same-run discovery → fan-out
1156
+
1157
+ A result-aware [`dynamicJob(group, { needs, generate })`](../sdk/rules-matrix-dynamic.md#dynamicjob--result-aware-generation) is deferred until its declared upstreams complete, then runs with their frozen outputs as `ctx.needs` — so a discovery job can emit a list at runtime and the generator fans out one follow-up job per item, all in the same run:
1158
+
1159
+ ```typescript
1160
+ import { workflow, job, step, push, dynamicJob, z } from '@kici-dev/sdk';
1161
+
1162
+ const discover = job('discover', {
1163
+ runsOn: 'linux',
1164
+ steps: [
1165
+ step('list-services', {
1166
+ outputs: { services: z.array(z.string()) },
1167
+ run: async ({ $ }) => {
1168
+ const out = await $`ls services/`;
1169
+ return { services: out.stdout.trim().split('\n') };
1170
+ },
1171
+ }),
1172
+ ],
1173
+ });
1174
+
1175
+ const deployEach = dynamicJob('deploys', {
1176
+ needs: ['discover'],
1177
+ generate: async ({ ctx }) =>
1178
+ ctx.needs.discover.result.services.map((svc) =>
1179
+ job(`deploy-${svc}`, {
1180
+ runsOn: 'linux',
1181
+ run: async ({ $ }) => {
1182
+ await $`./scripts/deploy.sh ${svc}`;
1183
+ },
1184
+ }),
1185
+ ),
1186
+ });
1187
+
1188
+ export default workflow('deploy-discovered-services', { on: push(), jobs: [discover, deployEach] });
1189
+ ```
1190
+
1191
+ Contrast: this keeps everything in one run with results flowing job→job. A cross-workflow `jobComplete()` chain (above) reacts to a job finishing but only sees its _status_, in a new run — use that when the reacting logic belongs to a different workflow.
1192
+
1153
1193
  ### Using custom events
1154
1194
 
1155
1195
  For richer payload data, emit custom events from steps using `ctx.emit()`:
@@ -1939,7 +1979,7 @@ const build = job({
1939
1979
 
1940
1980
  #### runsOn forms
1941
1981
 
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:
1982
+ 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, and each label can be an exact string, a glob, or a regular expression (see [Targeting by pattern](#targeting-by-pattern) below):
1943
1983
 
1944
1984
  ```typescript
1945
1985
  // 1. Simple string -- agent must have this label
@@ -1981,6 +2021,48 @@ const deploy = job('deploy', {
1981
2021
  });
1982
2022
  ```
1983
2023
 
2024
+ #### Targeting by pattern
2025
+
2026
+ Every selector element — in `runsOn`, in `runsOnAll`, on both the include and the exclude side — can be a plain string, a glob pattern, or a regular expression. KiCI picks the matching mode from the value itself:
2027
+
2028
+ - **Plain string → exact match.** `'kici:os:linux'` matches the label `kici:os:linux` and nothing else.
2029
+ - **String with glob metacharacters (`*`, `?`, `[]`, `{}`) → glob.** `'kici:host:web-*'` matches every host label starting with `kici:host:web-`. `'kici:host:box-0[1-3]'` matches `box-01`, `box-02`, `box-03`.
2030
+ - **`RegExp` literal → regular expression.** `/kici:host:box-0[1-3]/` matches any label the expression matches.
2031
+
2032
+ Both the required (include) side and the excluded side accept all three forms:
2033
+
2034
+ ```typescript
2035
+ // Glob include + regex exclude, single-agent targeting.
2036
+ const build = job('build', {
2037
+ runsOn: { labels: ['kici:os:linux', 'kici:host:web-*'], exclude: [/.*-canary$/] },
2038
+ steps: [compile],
2039
+ });
2040
+
2041
+ // A bare regex picks any agent whose label the expression matches.
2042
+ const probe = job('probe', {
2043
+ runsOn: /kici:host:box-0[1-3]/,
2044
+ steps: [smoke],
2045
+ });
2046
+ ```
2047
+
2048
+ In the `runsOnAll` array form, a leading `!` still routes an entry to the exclude side. The `!` is stripped **before** the matching mode is decided, so `'!kici:host:box-*'` is an exclude **glob** and `'!box-01'` an exclude **exact** match. Regular-expression exclusions use the structured `exclude: [/…/]` form (a `RegExp` cannot carry a `!` prefix). The structured `runsOnAll` form below targets every Linux host in the `db` or `replica` role except those whose hostname ends in `-canary`:
2049
+
2050
+ ```typescript
2051
+ const fanout = job('deploy', {
2052
+ runsOnAll: {
2053
+ include: [{ all: ['kici:os:linux', 'kici:host:web-*'] }],
2054
+ exclude: [/.*-canary$/],
2055
+ },
2056
+ run: async (ctx) => {
2057
+ /* runs once per matched host */
2058
+ },
2059
+ });
2060
+ ```
2061
+
2062
+ **Edge case — custom labels that contain glob metacharacters.** Because the matching mode is inferred from the value, a custom label that literally contains `*`, `?`, `[]`, or `{}` is always treated as a glob and can no longer be matched exactly. Avoid glob metacharacters in label names you intend to target by exact string.
2063
+
2064
+ **ReDoS protection.** Glob patterns are linear by construction. A regular expression you supply is validated for catastrophic-backtracking (ReDoS) when you run `kici compile` — a pattern that could hang on a crafted input is rejected with an error, so it never reaches the orchestrator. The orchestrator re-validates every pattern when it loads the lock file.
2065
+
1984
2066
  ### step(name, run) / step(name, options)
1985
2067
 
1986
2068
  Create a step with a run function or with typed outputs.
@@ -3186,6 +3268,29 @@ interface MatrixValues {
3186
3268
  }
3187
3269
  ```
3188
3270
 
3271
+ ### Bounding matrix concurrency (maxParallel / failFast)
3272
+
3273
+ A matrix fan-out runs every combination at once by default. The fan-out-generic
3274
+ `maxParallel` and `failFast` job options bound it the same way they bound a
3275
+ [`runsOnAll`](./runs-on-all.md#rolling-rollout-maxparallel--failfast) host fan-out:
3276
+
3277
+ ```typescript
3278
+ const test = job('test', {
3279
+ runsOn: 'linux',
3280
+ matrix: { os: ['ubuntu', 'macos', 'windows'] },
3281
+ maxParallel: 1, // run one combination at a time (sliding window)
3282
+ failFast: true, // stop launching combinations after the first failure
3283
+ run: async (ctx) => {
3284
+ /* ctx.matrix.os */
3285
+ },
3286
+ });
3287
+ ```
3288
+
3289
+ `maxParallel` is a sliding window (each combination that finishes releases the next;
3290
+ `1` = serial; must be `>= 1`); `failFast` halts the fan-out on the first failure and
3291
+ skips the held remainder (default `false`). They are ignored on a job with no `matrix`
3292
+ or `runsOnAll`.
3293
+
3189
3294
  ### Consuming matrix outputs downstream
3190
3295
 
3191
3296
  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`:
@@ -3275,6 +3380,53 @@ export default workflow('ci', {
3275
3380
  });
3276
3381
  ```
3277
3382
 
3383
+ ### dynamicJob — result-aware generation
3384
+
3385
+ `dynamicJob(group, fnOrConfig)` tags a generator with a group name (so static jobs can depend on it via `needs: [dynamicGroup('group')]`). It is polymorphic:
3386
+
3387
+ - **Function form** — event-only, dispatched at webhook time: `dynamicJob('shards', async ({ ctx }) => [...])`.
3388
+ - **Options-object form** — result-aware, deferred until its declared `needs` complete, then run with the upstreams' frozen outputs as `ctx.needs`: `dynamicJob('reports', { needs, generate })`.
3389
+
3390
+ ```typescript
3391
+ import { workflow, job, step, dynamicJob, dynamicGroup, z } from '@kici-dev/sdk';
3392
+
3393
+ // Upstream job A discovers a list of targets at runtime.
3394
+ const discover = job('discover', {
3395
+ runsOn: 'linux',
3396
+ steps: [
3397
+ step('emit', {
3398
+ outputs: { targets: z.array(z.string()) },
3399
+ run: async () => ({ targets: ['api', 'web'] }),
3400
+ }),
3401
+ ],
3402
+ });
3403
+
3404
+ // Result-aware generator fans out one report job per discovered target.
3405
+ const reports = dynamicJob('reports', {
3406
+ needs: ['discover'],
3407
+ generate: async ({ ctx }) => {
3408
+ const targets = ctx.needs.discover.result.targets; // OutputProxy over discover's outputs
3409
+ return targets.map((target) =>
3410
+ job(`report-${target}`, {
3411
+ runsOn: 'linux',
3412
+ run: async ({ log }) => log.info(`reporting on ${target}`),
3413
+ }),
3414
+ );
3415
+ },
3416
+ });
3417
+
3418
+ export default workflow('discovery-fan-out', { jobs: [discover, reports] });
3419
+ ```
3420
+
3421
+ `ctx.needs` shape:
3422
+
3423
+ | Need form | `ctx.needs[...]` value |
3424
+ | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
3425
+ | `'jobName'` / `{ name, ifFailed }` | `{ result }` — `result` is an `OutputProxy` (`ctx.needs.<job>.result.<step>.<field>`; single-step `run` jobs flatten to `ctx.needs.<job>.result.<field>`) |
3426
+ | `dynamicGroup('g')` / `dynamicGroup('g', { ifFailed })` | ordered array of `{ name, result }`, one per group member |
3427
+
3428
+ `ctx.needs` is deterministic — a snapshot of upstream outputs frozen at first eval and replayed unchanged on re-eval, like `ctx.event`. Use result-aware generation for same-run fan-out from a prior job's result; use [`jobComplete()`](./triggers.md) for cross-workflow reactions to a job finishing. See the architecture deep-dive in [dynamic jobs](../../architecture/execution/dynamic-jobs.md#result-aware-generation).
3429
+
3278
3430
  ### JobOrFactory
3279
3431
 
3280
3432
  The `jobs` array in `WorkflowOptions` accepts both static jobs and dynamic generators:
@@ -3303,6 +3455,218 @@ for (const item of workflow.jobs) {
3303
3455
 
3304
3456
  ---
3305
3457
 
3458
+ ## SDK reference: runsOnAll host fan-out
3459
+
3460
+ Source: https://docs.kici.dev/user/sdk/runs-on-all/
3461
+
3462
+ ## runsOnAll
3463
+
3464
+ `runsOnAll` fans a single job out to **every** host in the orchestrator's declared
3465
+ roster that matches a label predicate — one pinned execution per host. Use it for
3466
+ fleet-wide operations: patch every web tier, smoke-test every node, collect uptime
3467
+ from the fleet.
3468
+
3469
+ `runsOnAll` is mutually exclusive with [`runsOn`](/user/sdk/core/): a job declares one
3470
+ or the other. Where `runsOn` picks a **single** agent that satisfies the labels,
3471
+ `runsOnAll` targets **all** matching hosts and runs the job once on each, pinned to
3472
+ that specific host.
3473
+
3474
+ ```typescript
3475
+ import { job } from '@kici-dev/sdk';
3476
+
3477
+ // Run on every host labelled role:web.
3478
+ const patch = job('patch', {
3479
+ runsOnAll: 'role:web',
3480
+ run: async (ctx) => {
3481
+ await ctx.$`sudo apt-get update && sudo apt-get upgrade -y`;
3482
+ ctx.log.info(`patched ${ctx.host}`);
3483
+ },
3484
+ });
3485
+ ```
3486
+
3487
+ ### Input forms
3488
+
3489
+ `runsOnAll` accepts three shapes:
3490
+
3491
+ - **Bare string** — one required label.
3492
+
3493
+ ```typescript
3494
+ runsOnAll: 'role:web';
3495
+ ```
3496
+
3497
+ - **Array** — every positive entry is required (AND); a `!`-prefixed entry excludes a host.
3498
+
3499
+ ```typescript
3500
+ runsOnAll: ['kici:os:linux', 'role:db', '!kici:host:db-01'];
3501
+ ```
3502
+
3503
+ - **Structured** — explicit OR-of-AND include groups plus excludes.
3504
+
3505
+ ```typescript
3506
+ runsOnAll: {
3507
+ include: [{ all: ['kici:os:linux', 'role:db'] }, { all: ['role:replica'] }],
3508
+ exclude: ['kici:host:db-01'],
3509
+ };
3510
+ ```
3511
+
3512
+ A host matches when it satisfies **any** include group (all labels in that group)
3513
+ and carries **none** of the exclude labels.
3514
+
3515
+ #### Targeting by pattern
3516
+
3517
+ Every entry in any of these forms — include or exclude — can be an exact string, a
3518
+ glob, or a regular expression, exactly like [`runsOn`](./core.md#targeting-by-pattern):
3519
+
3520
+ - **Plain string → exact match** (`'role:web'`).
3521
+ - **String with glob metacharacters (`*`, `?`, `[]`, `{}`) → glob** (`'kici:host:web-*'`).
3522
+ - **`RegExp` literal → regular expression** (`/.*-canary$/`).
3523
+
3524
+ In the array form, a leading `!` routes an entry to the exclude side and is stripped
3525
+ **before** the matching mode is decided, so `'!kici:host:box-*'` is an exclude **glob**
3526
+ and `'!box-01'` an exclude **exact** match. A regular-expression exclusion uses the
3527
+ structured `exclude: [/…/]` form (a `RegExp` cannot carry a `!` prefix):
3528
+
3529
+ ```typescript
3530
+ const fanout = job('deploy', {
3531
+ runsOnAll: {
3532
+ include: [{ all: ['kici:os:linux', 'kici:host:web-*'] }],
3533
+ exclude: [/.*-canary$/],
3534
+ },
3535
+ run: async (ctx) => {
3536
+ /* runs once per matched host */
3537
+ },
3538
+ });
3539
+ ```
3540
+
3541
+ A custom label that literally contains glob metacharacters is always treated as a glob
3542
+ and can no longer be matched exactly. A regular expression you supply is validated for
3543
+ catastrophic-backtracking (ReDoS) when you run `kici compile` and rejected if it could
3544
+ hang on a crafted input.
3545
+
3546
+ ### Per-host execution model
3547
+
3548
+ Each matching host runs the job as its own pinned child, named `<job> (<hostname>)`
3549
+ (e.g. `patch (web-01)`). The children fan in for downstream `needs:` exactly like a
3550
+ matrix job — a downstream that needs the base job waits for every host child.
3551
+
3552
+ The job runs once per host with concurrency `unlimited` (all hosts in parallel).
3553
+
3554
+ ### ctx.host and ctx.agent
3555
+
3556
+ Inside a `runsOnAll` step, two extra context fields identify the host the child is
3557
+ running on:
3558
+
3559
+ - `ctx.host` — the hostname (string).
3560
+ - `ctx.agent` — the resolved agent facts: `{ host, labels, platform?, arch? }`.
3561
+
3562
+ ```typescript
3563
+ run: async (ctx) => {
3564
+ ctx.log.info(`running on ${ctx.host} (${ctx.agent?.platform}/${ctx.agent?.arch})`);
3565
+ };
3566
+ ```
3567
+
3568
+ Both are `undefined` for jobs that do not use `runsOnAll`.
3569
+
3570
+ ### byHost outputs
3571
+
3572
+ A downstream that `needs:` a `runsOnAll` job receives a **byHost** envelope instead
3573
+ of a flat outputs object — keyed by hostname, with a per-host summary:
3574
+
3575
+ ```typescript
3576
+ import { isHostJobOutputs } from '@kici-dev/sdk';
3577
+
3578
+ const report = job('report', {
3579
+ runsOn: 'role:control',
3580
+ needs: [patch],
3581
+ run: async (ctx) => {
3582
+ const outputs = ctx.jobOutputs(patch);
3583
+ if (isHostJobOutputs(outputs)) {
3584
+ ctx.log.info(`succeeded: ${outputs.summary.succeededHosts.join(', ')}`);
3585
+ ctx.log.info(`failed: ${outputs.summary.failedHosts.join(', ')}`);
3586
+ // Per-host outputs, keyed by hostname:
3587
+ const version = outputs.byHost['web-01']?.version;
3588
+ // Array view of one output key across every host:
3589
+ const allVersions = outputs.summary.outputs.version;
3590
+ }
3591
+ },
3592
+ });
3593
+ ```
3594
+
3595
+ Unlike the matrix envelope's last-write-wins `merged`, the host summary never collapses
3596
+ to a single scalar: `summary.outputs[key]` is an array of every host's value, and
3597
+ `succeededHosts` / `failedHosts` record each host's terminal outcome.
3598
+
3599
+ ### onUnreachable: skip | fail | hold
3600
+
3601
+ Resolution is backed by the **declared host roster** (see the operator
3602
+ [host roster](/operator/orchestrator/host-roster/) doc), not just the live registry.
3603
+ This lets KiCI surface an expected-but-absent host instead of silently fanning out to a
3604
+ partial fleet. The `onUnreachable` policy controls what happens when a **durable**
3605
+ (static) host in the roster is matched but not currently connected:
3606
+
3607
+ - **`hold`** (default) — queue a pinned child for the absent host and wait for it to
3608
+ reconnect. The fan-out is honest: a 5-host fleet with 1 host rebooting reports
3609
+ `4 ran, 1 held`, not a silent 4-of-5 success.
3610
+ - **`skip`** — omit the absent durable host and run only on the reachable hosts.
3611
+ - **`fail`** — fail the run init if any expected durable host is unreachable.
3612
+
3613
+ ```typescript
3614
+ const patch = job('patch', {
3615
+ runsOnAll: 'role:web',
3616
+ onUnreachable: 'skip',
3617
+ run: async (ctx) => {
3618
+ /* ... */
3619
+ },
3620
+ });
3621
+ ```
3622
+
3623
+ Ephemeral (scaled-down) hosts that are no longer connected are **always** skipped,
3624
+ independent of `onUnreachable` — a scaled-down node may never return. A `runsOnAll`
3625
+ that matches zero usable hosts fails the run rather than reporting a silent zero-child
3626
+ success.
3627
+
3628
+ ### Rolling rollout: maxParallel + failFast
3629
+
3630
+ By default a `runsOnAll` fan-out dispatches to every matched host at once — fine for
3631
+ collecting state across the fleet, dangerous for a deploy that takes the whole tier
3632
+ down simultaneously. Two job options bound the rollout:
3633
+
3634
+ - **`maxParallel`** — the fan-out width: at most this many hosts run at once. It is a
3635
+ sliding window — each host that finishes (success or failure) releases the next held
3636
+ host. `maxParallel: 1` is a strictly serial, one-host-at-a-time rolling deploy. Must
3637
+ be `>= 1`.
3638
+ - **`failFast`** — when `true`, the first host failure halts the rollout: no further
3639
+ held hosts are started, and the remaining ones are marked skipped. Default `false`
3640
+ (every host runs regardless of sibling outcomes — the same as the unbounded fan-out).
3641
+
3642
+ ```typescript
3643
+ const deploy = job('deploy', {
3644
+ runsOnAll: 'role:web',
3645
+ onUnreachable: 'skip', // see the caveat below
3646
+ maxParallel: 1, // strictly one host at a time
3647
+ failFast: true, // stop the roll on the first failure
3648
+ run: async (ctx) => {
3649
+ /* patch ctx.host */
3650
+ },
3651
+ });
3652
+ ```
3653
+
3654
+ Both options are **fan-out-generic** — they bound a `matrix` fan-out exactly the same
3655
+ way (the children are matrix combinations instead of hosts). They are ignored on a job
3656
+ with neither `matrix` nor `runsOnAll` (there is no fan-out to bound).
3657
+
3658
+ **Caveat — use `onUnreachable: 'skip'` or `'fail'` for rolling deploys, not `'hold'`.**
3659
+ A held host occupies a wave slot indefinitely while it waits to reconnect, stalling the
3660
+ roll behind an absent box. `skip` (run only reachable hosts) or `fail` (refuse the roll
3661
+ if any expected host is down) keep the window moving.
3662
+
3663
+ ### Limits (v0)
3664
+
3665
+ - Per-host secret scoping is not yet available — all hosts receive the job's resolved
3666
+ secrets.
3667
+
3668
+ ---
3669
+
3306
3670
  ## SDK reference: runtime
3307
3671
 
3308
3672
  Source: https://docs.kici.dev/user/sdk/runtime/
@@ -3313,25 +3677,26 @@ All types are exported from `@kici-dev/sdk` as type-only imports.
3313
3677
 
3314
3678
  ### Core types
3315
3679
 
3316
- | Type | Description |
3317
- | ----------------- | ------------------------------------------------------------------------------------- |
3318
- | `Workflow` | Workflow definition returned by `workflow()` |
3319
- | `WorkflowOptions` | Options for `workflow()` factory |
3320
- | `Job` | Job definition returned by `job()` |
3321
- | `JobOptions` | Options for `job()` factory |
3322
- | `Step<TOutputs>` | Step definition returned by `step()` |
3323
- | `StepOptions<T>` | Options for `step()` factory (full form with outputs) |
3324
- | `StepRunFn` | Simple step function type: `(ctx) => Promise<void>` |
3325
- | `BareStepFn` | Bare step function (no options, just `(ctx) => ...`) |
3326
- | `StepInput` | Union of step input forms accepted by `job()` |
3327
- | `OutputSchema` | Record of Zod types for step outputs |
3328
- | `InferOutputs<T>` | Infer output type from output schema |
3329
- | `ContainerConfig` | Container config for job execution (`image`, `env?`) |
3330
- | `RunsOn` | Union of `runsOn` forms: `string \| string[] \| RunsOnSelector` |
3331
- | `RunsOnSelector` | Object form for `runsOn` with `labels` (required) and `exclude` (optional) properties |
3332
- | `Fixture` | Test fixture definition returned by `fixture()` |
3333
- | `FixtureOptions` | Options for `fixture()` factory |
3334
- | `Registry` | Private npm registry declaration used in `WorkflowOptions.registries` |
3680
+ | Type | Description |
3681
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3682
+ | `Workflow` | Workflow definition returned by `workflow()` |
3683
+ | `WorkflowOptions` | Options for `workflow()` factory |
3684
+ | `Job` | Job definition returned by `job()` |
3685
+ | `JobOptions` | Options for `job()` factory |
3686
+ | `Step<TOutputs>` | Step definition returned by `step()` |
3687
+ | `StepOptions<T>` | Options for `step()` factory (full form with outputs) |
3688
+ | `StepRunFn` | Simple step function type: `(ctx) => Promise<void>` |
3689
+ | `BareStepFn` | Bare step function (no options, just `(ctx) => ...`) |
3690
+ | `StepInput` | Union of step input forms accepted by `job()` |
3691
+ | `OutputSchema` | Record of Zod types for step outputs |
3692
+ | `InferOutputs<T>` | Infer output type from output schema |
3693
+ | `ContainerConfig` | Container config for job execution (`image`, `env?`) |
3694
+ | `RunsOn` | Union of `runsOn` forms: `string \| RegExp \| (string \| RegExp)[] \| RunsOnSelector`. A plain string matches exactly, a string with glob metacharacters (`*?[]{}`) is a glob, and a `RegExp` is a regular expression. See [Targeting by pattern](./core.md#targeting-by-pattern). |
3695
+ | `RunsOnSelector` | Object form for `runsOn` with `labels` (required) and `exclude` (optional) properties. Each element accepts the exact / glob / regex forms on both sides. |
3696
+ | `RunsOnAllInput` | Union of `runsOnAll` host fan-out forms: `string \| RegExp \| (string \| RegExp)[] \| { include: { all: (string \| RegExp)[] }[]; exclude?: (string \| RegExp)[] }`. Same exact / glob / regex semantics per element. See [runsOnAll](./runs-on-all.md#targeting-by-pattern). |
3697
+ | `Fixture` | Test fixture definition returned by `fixture()` |
3698
+ | `FixtureOptions` | Options for `fixture()` factory |
3699
+ | `Registry` | Private npm registry declaration used in `WorkflowOptions.registries` |
3335
3700
 
3336
3701
  ### Trigger types
3337
3702
 
@@ -4262,6 +4627,8 @@ jobComplete({ workflow: 'CI', job: 'build', status: ['success'] }); // Success o
4262
4627
  jobComplete({ workflow: 'CI', job: 'build', source: 'org/repo' }); // Cross-repo
4263
4628
  ```
4264
4629
 
4630
+ `jobComplete()` starts a **new** workflow run that reacts to another job finishing (gated on the prior job's status). For same-run fan-out — generating follow-up jobs from a prior job's _outputs_ within the same run — use a result-aware [`dynamicJob(group, { needs, generate })`](./rules-matrix-dynamic.md#dynamicjob--result-aware-generation) instead.
4631
+
4265
4632
  ### genericWebhook()
4266
4633
 
4267
4634
  Create a generic webhook trigger. Fires when a non-GitHub webhook is received from an external source configured via the admin API. Returns a frozen `GenericWebhookTriggerConfig`.
@@ -4860,7 +5227,6 @@ The dashboard is a browser SPA on top of the same `/api/v1/*` surface, so anythi
4860
5227
  - **Workflows & runs:** `/orgs/:customerId/{runs,registrations,workflows,held-runs,environments,secrets,global-workflows}`
4861
5228
  - **Webhooks & event log:** `/orgs/:customerId/{sources,webhook-endpoints,event-log}`
4862
5229
  - **Diagnostics & activity:** `/orgs/:customerId/{diagnostics,activity,access-log}`
4863
- - **Admin (kici-admin org only):** `/admin/{orgs,connections,audit-log,grafana/*}`
4864
5230
 
4865
5231
  The full route tree is the source of truth — every method, request schema, and response schema is enumerated server-side. There is currently no auto-generated OpenAPI spec; the typed `DashboardApiType` export is the canonical contract for TypeScript clients.
4866
5232
 
@@ -6582,8 +6948,8 @@ The command is remote-only -- all execution happens on the orchestrator and agen
6582
6948
  :::note[Orchestrator prerequisite: cache storage]
6583
6949
  `kici run remote` uploads your working-tree overlay to the orchestrator's **cache storage** via a pre-signed URL, and the agent fetches it from there (see [Repo state transfer](#repo-state-transfer)). The target orchestrator must therefore have cache storage enabled (`KICI_STORAGE_TYPE` = `s3` or `filesystem`).
6584
6950
 
6585
- - **The [Docker / Podman quickstart](quickstart/compose.md) wires this up for you** it ships a SeaweedFS service, so `kici run remote` works there out of the box (see its "run a workflow without pushing" step).
6586
- - **The [bare-metal quickstart](quickstart/bare-metal.md) does not configure storage by default** — enable a backend before using `kici run remote`:
6951
+ - **Both quickstarts wire this up for you** — the [Docker / Podman quickstart](quickstart/compose.md) and the [bare-metal quickstart](quickstart/bare-metal.md) each ship a SeaweedFS object store and pre-fill the orchestrator's `KICI_STORAGE_*` block, so `kici run remote` works out of the box (see each guide's "run a workflow without pushing" step).
6952
+ - **A hand-rolled orchestrator deploy does not configure storage by default** — enable a backend before using `kici run remote`:
6587
6953
  - **`filesystem`** — simplest for a single-host orchestrator: set `KICI_STORAGE_TYPE=filesystem` and `KICI_STORAGE_FS_PATH=/var/lib/kici/cache`. No external service needed; blobs are served through the orchestrator's own HMAC-signed HTTP route.
6588
6954
  - **`s3`** — any S3-compatible bucket. **A non-public / self-hosted endpoint works**: set `KICI_STORAGE_TYPE=s3`, `KICI_STORAGE_BUCKET`, `KICI_STORAGE_ENDPOINT=https://your-endpoint` and (for most self-hosted services) `KICI_STORAGE_FORCE_PATH_STYLE=true`. If the developer machine running `kici run remote` reaches the bucket at a different address than the orchestrator, set `KICI_STORAGE_UPLOAD_ENDPOINT` to the developer-reachable address; if agents reach it at yet another address (e.g. agents in containers), set `KICI_STORAGE_EXTERNAL_ENDPOINT` to the agent-routable URL.
6589
6955
 
@@ -7229,7 +7595,7 @@ kici reject <run-id> --job deploy-production --reason "Wrong release branch"
7229
7595
 
7230
7596
  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
7597
 
7232
- You can also approve from the dashboard approval queue. See [Dashboard](dashboard.md#approval-queue).
7598
+ You can also approve from the dashboard approval queue. See [Dashboard](dashboard/environments-and-secrets.md#approval-queue).
7233
7599
 
7234
7600
  ## See also
7235
7601
 
@@ -7468,1034 +7834,20 @@ _Source: `packages/sdk/src/types.ts` (WorkflowOptions.concurrency, JobOptions.co
7468
7834
 
7469
7835
  Source: https://docs.kici.dev/user/dashboard/
7470
7836
 
7471
- The KiCI dashboard is a web-based interface for monitoring workflow runs, inspecting job and step details, and reading log output. It is a browser single-page application that authenticates via OIDC and communicates with the Platform tier through REST API endpoints.
7472
-
7473
- ## Getting started
7474
-
7475
- <!-- help:getting-started-overview#getting-started -->
7476
-
7477
- The getting-started page is a six-step checklist that takes you from zero to your first workflow run.
7478
-
7479
- - **Self-checked steps** -- install the CLI, scaffold a workflow, and run it locally. These run on your own machine, so you tick them off yourself; the dashboard remembers your choices in the browser.
7480
- - **Auto-detected steps** -- connect an orchestrator, add a webhook source, and trigger your first run. These tick automatically as the dashboard observes the matching activity in your organization.
7481
-
7482
- Each step links to the relevant settings page or documentation. A progress bar tracks overall completion, and the sidebar entry shows a `done/total` badge until you finish or dismiss reminders.
7483
-
7484
- <!-- /help:getting-started-overview -->
7485
-
7486
- When you first sign in to a brand-new organization with no orchestrator, no webhook source, and no runs, the dashboard opens this page automatically. Once your organization has any activity, the run list becomes your landing page instead. The **Getting started** sidebar entry stays available so you can return to the checklist at any time.
7487
-
7488
- The six steps are:
7489
-
7490
- 1. **Install the kici CLI** -- `npm install -g kici`.
7491
- 2. **Create a workflow** -- `kici init` scaffolds a `.kici/` directory in your repository.
7492
- 3. **Run a workflow locally** -- `kici run local pr:open` executes a workflow on your machine with no orchestrator required.
7493
- 4. **Connect an orchestrator** -- deploy an orchestrator and connect it with a join token from **Settings → Orchestrator keys**.
7494
- 5. **Add a webhook source** -- register a source under **Settings → Sources** so pushes and pull requests trigger runs.
7495
- 6. **Trigger your first run** -- push to your repository to produce your first run through the relay.
7496
-
7497
- ## Navigation
7498
-
7499
- ### Sidebar
7500
-
7501
- The left sidebar provides persistent navigation across all org-scoped pages:
7502
-
7503
- - **Org switcher** -- dropdown at the top to switch between organizations
7504
- - **Getting started** -- onboarding checklist (shows a `done/total` badge until complete or dismissed)
7505
- - **Runs** -- the default landing page, showing your workflow run history
7506
- - **Workflows** -- permanently registered workflows listening for events
7507
- - **Diagnostics** -- infrastructure health, execution metrics, and recent errors
7508
- - **Metrics** -- time-series charts of orchestrator health (dispatch & agents, execution, webhooks, caching, logs, errors), scoped to this org
7509
- - **Environments** -- deployment environments with protection rules
7510
- - **Secrets** -- secret scope management with environment bindings
7511
- - **Approval queue** -- held runs pending approval (shows a badge with pending count)
7512
- - **Activity** -- federated forensic log merging upstream tenant-plane mutations and orchestrator reads (`access_log`) into one chronological stream
7513
- - **DLQ** -- dead-letter queue of internal events whose dispatch retries were exhausted (shows a badge with the current depth)
7514
- - **Settings** -- organization settings with tabbed sub-pages
7515
-
7516
- The sidebar footer shows the WebSocket connection indicator, your user profile, UTC/local time toggle, theme toggle, and a collapse button.
7517
-
7518
- <!-- help:sidebar-build-info#sidebar -->
7519
-
7520
- Below the KiCI logo, the sidebar shows build information for both the dashboard UI and the Platform API backend:
7521
-
7522
- - Git commit hash.
7523
- - Relative build timestamp (e.g. "2h ago").
7524
-
7525
- This makes it easy to confirm which version is currently deployed.
7526
-
7527
- <!-- /help:sidebar-build-info -->
7528
-
7529
- ### Mobile navigation
7530
-
7531
- On screens narrower than 768px (the `sm` breakpoint), the sidebar collapses and is replaced by a bottom tab bar with six navigation items: Runs, Workflows, Envs (environments), Secrets, Health (diagnostics), and Settings. Note that the mobile tab bar shows a subset of the full sidebar navigation -- activity and approval queue are only available in the full desktop sidebar.
7532
-
7533
- <!-- help:run-list-overview#run-list -->
7534
-
7535
- The run list is your organization's default landing page, showing all workflow runs with status, trigger, branch, and timing. Use filters and sorting to find specific runs, or enable commit grouping to see all runs triggered by a single push.
7536
-
7537
- <!-- /help:run-list-overview -->
7538
-
7539
- <!-- help:run-list-commit-grouping#commit-grouped-view -->
7540
-
7541
- Commit grouping collapses runs that share the same commit SHA under a single header. This is useful when a push triggers multiple workflows -- you can see their aggregate status at a glance instead of scanning individual rows.
7542
-
7543
- <!-- /help:run-list-commit-grouping -->
7544
-
7545
- ## Run list
7546
-
7547
- The run list is the default page when entering an organization (`/orgs/:customerId/runs`).
7548
-
7549
- ### Columns
7550
-
7551
- Each run is displayed in a table row (desktop) or card (mobile) with:
7552
-
7553
- - **Status** -- colored badge (green = success, red = failed/error/timed out, amber = running/cancelling, yellow = queued/pending, gray = cancelled/skipped)
7554
- - **Trigger** -- icon indicating the event type (push, pull request, tag, dispatch, etc.)
7555
- - **Workflow** -- the workflow name from your `.kici/workflows/` directory
7556
- - **Branch** -- the git ref that triggered the run
7557
- - **Commit** -- the first 7 characters of the commit SHA, linked to the provider (GitHub)
7558
- - **Duration** -- how long the run took (e.g. "2m 30s")
7559
- - **Time** -- relative timestamp (e.g. "5 minutes ago")
7560
-
7561
- ### Filters
7562
-
7563
- Dropdown filters appear above the table:
7564
-
7565
- - **Status** -- filter by success, failed, running, or cancelled
7566
- - **Workflow** -- filter by workflow name
7567
- - **Branch** -- filter by git branch
7568
- - **Repository** -- filter by repository
7569
-
7570
- A "More filters" button reveals additional filters:
7571
-
7572
- - **Trigger type** -- filter by push, pull_request, tag, dispatch, etc.
7573
-
7574
- Filters persist in URL query parameters (e.g. `/runs?status=failed&branch=main`), making filtered views shareable and bookmark-friendly. A "Clear filters" button appears when any filter is active.
7575
-
7576
- ### Sorting
7577
-
7578
- Click any column header to sort the table by that column. Clicking the same header toggles between ascending and descending order. The current sort is reflected in the URL (e.g. `?sort=workflowName&dir=desc`), so sorted views are shareable.
7579
-
7580
- Sorting is server-side -- the API returns results in the requested order.
7581
-
7582
- ### Column visibility
7583
-
7584
- A gear icon button (labeled "Toggle columns") next to the filter bar opens a menu of toggleable columns. Uncheck a column to hide it from the table. Column visibility preferences are saved per organization in `localStorage`.
7585
-
7586
- ### Commit grouped view
7587
-
7588
- A "Group by commit" toggle switch groups runs by their commit SHA. When enabled, runs sharing the same commit are collapsed under a group header showing the commit SHA (first 7 characters), commit message, and aggregate status dots. This is useful for seeing all workflow runs triggered by a single push.
7589
-
7590
- ### Compile indicator
7591
-
7592
- Runs where the lock file was recompiled during execution show a hammer icon next to the workflow name. Hover over the icon to see the tooltip "Lock file recompiled".
7593
-
7594
- ### Pagination
7595
-
7596
- The run list shows 20 runs per page with numbered pagination controls. A footer displays the current range and total count (e.g. "Showing 1-20 of 237 runs").
7597
-
7598
- ### Empty states
7599
-
7600
- - **No runs, WS disconnected** -- "No orchestrator connected" with guidance to check orchestrator configuration and a link to settings.
7601
- - **No runs, WS connected** -- "No runs yet" with guidance to push code to trigger a workflow run.
7602
- - **No filter matches** -- "No matching runs" with guidance to adjust filters.
7603
-
7604
- <!-- help:run-detail-job-tree#job-tree -->
7605
-
7606
- The job tree shows the hierarchical structure of your run's jobs and steps. Click a job to see combined logs from all its steps, or expand a job to select an individual step.
7607
-
7608
- Failed runs auto-expand the first failed job for quick diagnosis.
7609
-
7610
- <!-- /help:run-detail-job-tree -->
7611
-
7612
- <!-- help:run-detail-metadata#metadata -->
7613
-
7614
- The metadata panel displays detailed context about the selected run, job, or step:
7615
-
7616
- - IDs, status, duration.
7617
- - Orchestrator and agent assignment.
7618
- - Matrix values (when present).
7619
- - Provider links — commit SHA, trigger event, workflow source file on GitHub.
7620
-
7621
- Use it as the quick-reference card when you need to jump from the dashboard to the underlying VCS or infrastructure.
7622
-
7623
- <!-- /help:run-detail-metadata -->
7624
-
7625
- <!-- help:run-detail-source#metadata -->
7626
-
7627
- The source row identifies which webhook source produced this run. Orchestrators register sources at startup with a friendly name and a fine-grained subtype — GitHub App, generic webhook, universal Git, or internal.
7628
-
7629
- The dimmed routing key under the name (e.g. `github:12345` or `generic:org:src-id`) is the unique identifier Platform uses to route the webhook back to your orchestrator.
7630
-
7631
- Two repos with the same path served by different sources are distinguished here, so you can tell at a glance which deployment a run came from.
7632
-
7633
- <!-- /help:run-detail-source -->
7634
-
7635
- <!-- help:run-detail-job-labels#metadata -->
7636
-
7637
- Labels show the routing constraints used to match this job to an agent.
7638
-
7639
- Label categories:
7640
-
7641
- - **Platform labels:** `kici:os:linux`, `kici:arch:x64`, etc.
7642
- - **Scaler labels:** added by the scaler that provisioned the agent.
7643
- - **Role labels:** e.g. `kici:role:builder`.
7644
- - **Custom labels:** anything you set via `runsOn` in your workflow definition.
7645
-
7646
- <!-- /help:run-detail-job-labels -->
7647
-
7648
- <!-- help:run-detail-trust-context#metadata -->
7649
-
7650
- The trust context shows the security evaluation for PR-triggered runs:
7651
-
7652
- - **Trust tier:** trusted, known, or unknown contributor.
7653
- - **Lock file source:** head branch or base branch.
7654
- - **Secrets access level:** what the run was permitted to read.
7655
-
7656
- Use this to understand why a run was held for approval or ran with restricted permissions.
7657
-
7658
- <!-- /help:run-detail-trust-context -->
7659
-
7660
- <!-- help:summary-job-contexts#tabs -->
7661
-
7662
- The job contexts section in the run summary shows execution context per job:
7663
-
7664
- - **Sandbox type** — container, Firecracker, bare-metal.
7665
- - **Runtime environment** — image, OS, arch.
7666
- - **Dependency cache status** — hit, miss, or skipped.
7667
- - **Available secret keys** — which scopes the job could read.
7668
-
7669
- This gives a quick overview of every job's execution environment without needing to select each job individually.
7670
-
7671
- <!-- /help:summary-job-contexts -->
7672
-
7673
- <!-- help:summary-scaler-context#tabs -->
7674
-
7675
- The scaler configuration section shows the execution mode and backend-specific settings used to provision the agent that ran this job. The execution mode describes how steps run — for example, "bare-metal" means direct processes, even inside containers where the container is the isolation layer.
7676
-
7677
- Backend-specific fields:
7678
-
7679
- - **Container backends:** image name, runtime (Docker/Podman), resource limits, and network isolation settings.
7680
- - **Firecracker backends:** rootfs and kernel paths, vCPU/memory allocation, and the VM's IP address.
7681
- - **Bare-metal backends:** binary path and resource hints.
7682
-
7683
- Hover over the execution mode badge for a contextual explanation.
7684
-
7685
- <!-- /help:summary-scaler-context -->
7686
-
7687
- <!-- help:summary-job-outputs#tabs -->
7688
-
7689
- Shows plain outputs and secret output keys produced by this job.
7690
-
7691
- - **Plain outputs:** values returned by step functions, grouped by step name.
7692
- - **Secret outputs:** set via `ctx.setSecretOutput()`, shown here as masked key names — the values are encrypted and never sent to the dashboard.
7693
-
7694
- Downstream jobs that declare this job in their `needs` array can read these outputs.
7695
-
7696
- <!-- /help:summary-job-outputs -->
7697
-
7698
- <!-- help:summary-step-secrets#tabs -->
7699
-
7700
- When a step is selected, the secrets-accessed section shows which secret keys the step read via `ctx.secrets.get()` or `ctx.secrets.expose()` during execution.
7701
-
7702
- Only key names are shown, never values — use this to audit which steps access sensitive credentials.
7703
-
7704
- Available for runs executed after this feature was deployed; older runs show no data.
7705
-
7706
- <!-- /help:summary-step-secrets -->
7707
-
7708
- ## Run detail
7709
-
7710
- Click any run in the list to open its detail page (`/orgs/:customerId/runs/:runId`).
7711
-
7712
- ### Layout
7713
-
7714
- The page uses a responsive multi-panel layout that adapts to screen width:
7715
-
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`.
7717
- - **Medium desktop (< 1200px)** -- two-panel layout with the job tree and content area. Metadata is accessible via a "Show metadata" drawer button.
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.
7719
-
7720
- ### Run header
7721
-
7722
- A summary bar above the two panels shows:
7723
-
7724
- - **Breadcrumbs** -- Runs > github > owner/repo > commit SHA > #runId > workflow name (each segment is clickable and filters the run list by that dimension)
7725
- - **Status badge** -- the current run status
7726
- - **Trigger icon** -- visual indicator of the event type
7727
- - **Branch** -- the git ref with a branch icon
7728
- - **Commit SHA** -- linked to the provider's commit page
7729
- - **Duration** -- total run time
7730
- - **Timestamp** -- relative time since the run started (hover for absolute time)
7731
- - **Re-run button** -- available for terminal-state runs (success, failed, cancelled, error, timed out) triggered by webhooks. Opens a confirmation dialog before re-running on the same commit. After confirmation, navigates to the new run.
7732
- - **Cancel button** -- available for pending, running, cancelling, or queued runs. For running runs, sends a graceful cancel; for already-cancelling runs, a "Force cancel" button appears to immediately kill without cleanup.
7733
- - **Lineage badge** -- if the run is a re-run, a badge shows the parent/child relationship with a link to the original run.
7734
-
7735
- ### Job tree
7736
-
7737
- The left panel shows a tree of jobs and their steps:
7738
-
7739
- - Each job shows a **status dot**, **name**, and **duration** (live timer while running)
7740
- - Click a job row to select the job and view its combined logs (all steps merged with sticky headers)
7741
- - Click the expand chevron on a job to expand/collapse its steps
7742
- - Each step shows a **status dot**, **name**, and **duration**
7743
- - Click a step to select it and view its individual logs
7744
-
7745
- **Job-level selection** -- clicking a job row selects it and shows combined logs from all of its steps, with sticky step headers separating each step's output. This provides a unified view of the entire job's execution without needing to click through steps individually.
7746
-
7747
- **Matrix jobs** are grouped under a parent node. For example, a matrix with 3 Node.js versions appears as "Test (3 variants)" with expandable sub-entries like "Test (node:18)", "Test (node:20)", "Test (node:22)".
7748
-
7749
- **Hook steps** -- lifecycle hook steps (e.g. `onCancel`, `cleanup`, `onSuccess`) are displayed with a distinct badge to differentiate them from regular steps.
7750
-
7751
- <!-- help:run-detail-setup-jobs#job-tree -->
7752
-
7753
- **Setup jobs** are rows prefixed with `__init__`, `__build__`, or `__dynamic__`. They run before (or alongside) your workflow jobs.
7754
-
7755
- In the tree they appear with:
7756
-
7757
- - A pretty display name — `Init: foo`, `Build: foo`, `Evaluate: foo`.
7758
- - A muted "setup" visual variant that distinguishes them from regular jobs without hiding them.
7759
- - A single synthetic step-0 log that captures everything the workflow source and dynamic functions write — explicit `log.*` calls, `console.*` output, and subprocess stdout from `await $` inside a `DynamicJobFn`.
7760
-
7761
- Their elapsed time is intentionally visible: clone, dependency install, and dynamic evaluation can consume real user-observable time, and hiding it would obscure where the run is actually spending itself.
7762
-
7763
- <!-- /help:run-detail-setup-jobs -->
7764
-
7765
- **Auto-expand on failure** -- when viewing a failed run, the first failed job is automatically expanded and the failed step is selected.
7766
-
7767
- **URL sync** -- selecting a job updates the URL to `/runs/:runId/jobs/:jobId`, and selecting a step updates it to `/runs/:runId/jobs/:jobId/steps/:stepIndex`, making selections bookmarkable and shareable.
7768
-
7769
- ### Keyboard navigation
7770
-
7771
- The job tree supports keyboard navigation:
7772
-
7773
- - **Arrow Up/Down** -- move focus through tree items
7774
- - **Enter** -- select a job (show combined logs) or select a step (show step logs)
7775
- - **Escape** -- deselect the current selection and navigate to the first job
7776
-
7777
- ### Tabs
7778
-
7779
- The content area has the following tabs:
7780
-
7781
- - **Logs** (default) -- shows log output for the selected job or step
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
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
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.
7787
-
7788
- On wide desktop (>= 1200px), Metadata is shown in a dedicated sidebar panel instead of as a tab.
7789
-
7790
- ### Metadata
7791
-
7792
- The metadata panel shows detailed information organized into sections:
7793
-
7794
- - **Run metadata** -- run ID, status, trigger event, branch, commit SHA (linked to provider), workflow name (linked to source file on provider), duration, and timestamps
7795
- - **Job metadata** -- job name, status, agent ID, matrix values (if present), duration
7796
- - **Step metadata** -- step name, step index, status, duration
7797
- - **Trust context** (PR-triggered runs only) -- shows the contributor's trust tier (trusted, known, or unknown), lock file source (head or base branch), and secrets access level
7798
-
7799
- Provider-specific links (e.g., GitHub commit URL, branch URL, PR link, workflow source file link) are automatically generated based on the repository context. The workflow name in the metadata panel is a clickable link to the `.kici/workflows/<name>.ts` source file on the provider (e.g. GitHub blob view).
7800
-
7801
- ### WebSocket connection indicator
7802
-
7803
- A small indicator in the sidebar footer shows the real-time WebSocket connection status:
7804
-
7805
- - **Green dot** -- connected and receiving live updates
7806
- - **Red dot (pulsing)** -- disconnected
7807
-
7808
- ## Log viewer
7809
-
7810
- The log viewer renders step output with full terminal color support.
7811
-
7812
- ### ANSI color rendering
7813
-
7814
- Log lines containing ANSI escape codes are rendered with color. Supported sequences include:
7815
-
7816
- - Standard 16 colors (red, green, blue, etc.) and bright variants
7817
- - 256-color palette
7818
- - Truecolor (24-bit RGB)
7819
- - Bold, faint, italic, underline, and inverse text
7820
-
7821
- Colors use CSS classes with a dark background (similar to a terminal), regardless of the dashboard's light/dark theme setting.
7822
-
7823
- ### Timestamps
7824
-
7825
- A clock icon button next to the search bar toggles per-line timestamps in the log viewer. When enabled, each log line shows the timestamp in the gutter alongside the line number. The timestamp format respects the UTC/local time preference. The setting persists to `localStorage`.
7826
-
7827
- ### Search
7828
-
7829
- A search bar at the top of the log viewer provides:
7830
-
7831
- - **Debounced search** -- type a query and matches are highlighted after 300ms
7832
- - **Match count** -- shows "N of M" with the current and total match count
7833
- - **Navigation** -- up/down arrows to jump between matches (also Enter/Shift+Enter)
7834
- - **Clear** -- press Escape or click the X button to clear the search
7835
- - **Wraparound** -- navigation wraps from the last match back to the first
7836
-
7837
- ### Permalink
7838
-
7839
- Click any line number in the gutter to:
7840
-
7841
- 1. Highlight that line with a blue tint
7842
- 2. Update the URL hash to `#L42` (for line 42)
7843
-
7844
- Sharing the URL scrolls the recipient directly to the highlighted line.
7845
-
7846
- ### Copy to clipboard
7847
-
7848
- Hover over any line to reveal a copy button on the right. Clicking it copies the line's **plain text** (ANSI escape codes are stripped) to the clipboard. A "Copied!" tooltip confirms the action.
7849
-
7850
- ### Live log streaming
7851
-
7852
- When viewing a running job, logs appear in real time as the agent executes steps. The dashboard maintains a WebSocket connection to the Platform tier and subscribes to log updates for the currently selected step.
7853
-
7854
- **Auto-scroll** -- new lines automatically scroll into view as they arrive. If you scroll up to review earlier output, auto-scroll pauses and a **"Jump to bottom"** button appears. Clicking it resumes auto-scroll.
7855
-
7856
- **Streaming indicator** -- a pulsing "Streaming" badge appears next to the Logs tab header while a step is actively running.
7857
-
7858
- **Completion banner** -- when a step finishes, a banner appears at the bottom of the log viewer showing the final status (success or failed) and total line count.
7859
-
7860
- **Status updates** -- the run list and run detail pages update live as jobs and steps change state. You do not need to refresh the page to see a run complete.
7861
-
7862
- **Known limitations**:
7863
-
7864
- - Live streaming requires an active WebSocket connection. Some corporate proxies may block WebSocket upgrades.
7865
- - If the WS connection drops, the dashboard reconnects automatically and refetches all cached data to catch up on missed updates.
7866
- - Log lines received during streaming are held in memory. For very long-running steps with massive output, the REST endpoint is the authoritative source for complete logs.
7867
-
7868
- ### Provisioning logs
7869
-
7870
- Above the step logs, a collapsible **Provisioning logs** section shows the orchestrator-side lifecycle of the agent that ran the job — the scaler lifecycle events emitted while bringing an agent up. It starts expanded while provisioning is in progress (no step logs yet) and collapses once steps begin producing output.
7871
-
7872
- When the scaler **fails** to provision an agent (for example a missing binary, an unpullable container image, or a microVM that fails to boot), the failure appears here along with a bounded tail of the agent process's own stdout/stderr captured by the scaler. This is the surface to check for a run that fails with no step logs at all — the agent never started, so the cause lives in the provisioning lifecycle rather than in any step's output.
7873
-
7874
- ### Performance
7875
-
7876
- The log viewer uses virtualized scrolling to handle large outputs. Only the visible lines plus a small buffer are rendered in the DOM, keeping performance smooth even for logs with 10,000+ lines.
7877
-
7878
- <!-- help:settings-general#settings -->
7879
-
7880
- General settings show your organization's basic information, including the org name (editable by owners) and the unique organization ID. Use this to rename your org or reference the ID for API calls and configuration.
7881
-
7882
- <!-- /help:settings-general -->
7883
-
7884
- <!-- help:settings-members#settings -->
7885
-
7886
- The members tab lets you manage your team:
7887
-
7888
- - Invite new members by email.
7889
- - Assign roles.
7890
- - Suspend or remove members.
7891
- - Configure per-user CI trust levels.
7892
-
7893
- Each member's linked provider accounts (e.g. GitHub) are also visible here.
7894
-
7895
- <!-- /help:settings-members -->
7896
-
7897
- <!-- help:settings-roles#settings -->
7898
-
7899
- Roles define granular permissions across 15 resource categories (runs, secrets, members, etc.) with 5 access levels: `none`, `read`, `read_payload`, `write`, `admin`.
7900
-
7901
- Create custom roles to restrict what team members can do, or use the built-in **Owner** role for full access.
7902
-
7903
- <!-- /help:settings-roles -->
7904
-
7905
- <!-- help:settings-teams#settings -->
7906
-
7907
- Teams are named groups of organization members. A role granted to a team is inherited by every member, so you can manage permissions for a whole group in one place.
7908
-
7909
- Team names can also be referenced in workflow approval gates (`requireApproval: [{ team: 'leads' }]`) — any member of the named team can satisfy that gate.
7910
-
7911
- Managing teams (create / rename / delete, membership, role grants) requires the **Teams** permission at `admin`; `read` shows a view-only list.
7912
-
7913
- <!-- /help:settings-teams -->
7914
-
7915
- <!-- help:settings-api-keys#settings -->
7916
-
7917
- API keys allow programmatic access to the KiCI API for automation, scripts, and CI integrations.
7918
-
7919
- Each key is scoped to this organization with a custom permission matrix and an optional expiry date. Keys can be revoked individually.
7920
-
7921
- Use a key's clone button to open the creation modal prefilled with that key's name, expiry, and permissions — handy for recreating an expired key or deriving a new key from an existing one.
7922
-
7923
- <!-- /help:settings-api-keys -->
7924
-
7925
- <!-- help:settings-orchestrator-keys#orchestrator-keys -->
7926
-
7927
- Orchestrator keys authenticate the WebSocket connection between your orchestrator and the KiCI Platform relay.
7928
-
7929
- Create a key here and set it as the `KICI_PLATFORM_TOKEN` environment variable in your orchestrator configuration. Keys can optionally be restricted to specific routing patterns.
7930
-
7931
- Use a key's clone button to open the creation modal prefilled with that key's name and description.
7932
-
7933
- <!-- /help:settings-orchestrator-keys -->
7934
-
7935
- <!-- help:settings-sources#sources -->
7936
-
7937
- Webhook sources are registered automatically when an orchestrator connects to the Platform and sends a `source.register` message.
7938
-
7939
- Each source shows its routing key and full webhook URL — configure this URL in your provider's webhook settings (e.g. GitHub App).
7940
-
7941
- To retrieve the webhook secret for signature verification, use the `kici-admin source get-webhook-secret <routingKey>` command shown below each source.
7942
-
7943
- <!-- /help:settings-sources -->
7944
-
7945
- <!-- help:settings-billing#settings -->
7946
-
7947
- The billing tab shows your current plan (Free, Pro, or Team), resource usage meters, and lets you upgrade to a paid tier.
7948
-
7949
- Choose Monthly or Annual billing, click "Upgrade to Pro" or "Upgrade to Team" to start a Stripe Checkout, or use "Manage payment" to switch tiers and update payment methods via the Stripe Billing Portal.
7950
-
7951
- The usage meters track:
7952
-
7953
- - **Members:** invited users in this org.
7954
- - **Orchestrator connections:** direct WebSocket connections from your orchestrators to the Platform. Only coordinators (and standalone orchestrators) open a connection; peer/worker nodes in a Raft cluster share their coordinator's connection and don't count separately.
7955
- - **Relayed webhooks (this month):** webhooks delivered through the Platform relay during the current billing window.
7956
- - **Live log minutes (today):** log streaming time consumed in the current UTC day.
7957
- - **Retention period:** how long execution history is kept.
7958
-
7959
- The diagnostics page may show a higher orchestrator count than this tab — diagnostics counts cluster nodes, billing counts billable connections.
7960
-
7961
- <!-- /help:settings-billing -->
7962
-
7963
- <!-- help:settings-billing-orch-connections#settings -->
7964
-
7965
- The orchestrator-connections counter measures the number of **direct WebSocket connections** that your orchestrator processes hold open against the KiCI Platform — one count per live connection.
7966
-
7967
- **What counts as one connection:**
7968
-
7969
- - One standalone orchestrator (single process, no cluster) → **1 connection**.
7970
- - One Raft cluster (1 coordinator + N peers) → **1 connection** — only the coordinator opens a Platform WebSocket. The peers gossip through the coordinator and never connect to Platform directly, so they do **not** count toward your billing limit.
7971
- - N independent orchestrator deployments (e.g., one per environment, one per region) → **N connections**.
7972
-
7973
- This is why the diagnostics page can show more orchestrator **nodes** than the billing page shows **connections**: diagnostics counts every node in your topology (coordinator + peers), while billing only counts the WebSocket connections you pay for. A 4-connection org running two 3-node clusters and two standalones will show 4 on the billing meter and 8 on the diagnostics page — both numbers are correct, they measure different things.
7974
-
7975
- When you hit the cap, the next coordinator that tries to connect is rejected with WebSocket close code 4020 (`WS_CLOSE_PLAN_LIMIT`). Existing connections are never disconnected. Upgrade your plan to lift the cap; the meter updates immediately.
7976
-
7977
- <!-- /help:settings-billing-orch-connections -->
7978
-
7979
- <!-- help:settings-billing-relayed-webhooks#settings -->
7980
-
7981
- The relayed-webhooks counter only includes webhooks delivered through the KiCI Platform relay — the route at `kici.dev` that signature-verifies an inbound webhook and forwards it over WebSocket to your orchestrator.
7982
-
7983
- Webhooks pointed directly at your orchestrator's public ingest endpoint never reach the Platform, so they're invisible to this counter and uncapped on every Hosted tier. If you have a public orchestrator ingress, you can mix-and-match: use the relay for sources you can't expose publicly, and point GitHub (or any provider / generic webhook) straight at your orchestrator for the rest.
7984
-
7985
- Every webhook the relay forwards counts — **including ones your workflows ultimately ignore**. Trigger matching runs on your orchestrator, not on the Platform, so the relay forwards each signature-verified webhook before any trigger is evaluated. A source that sends many events you filter down to a handful of runs still consumes one relayed webhook per event. If a high-volume source mostly produces no run, point it directly at your orchestrator (see above) to keep it off this counter entirely.
7986
-
7987
- When you hit the cap, new relayed webhooks are rejected with `429 Plan limit reached`. Upgrade in the Stripe Billing Portal to lift the cap immediately; usage resets monthly on your billing anniversary.
7988
-
7989
- <!-- /help:settings-billing-relayed-webhooks -->
7990
-
7991
- <!-- help:settings-billing-currency#settings -->
7992
-
7993
- Switch the prices shown on the tier cards between US dollars and euros. The choice you pick here is also the currency Stripe charges in when you click "Upgrade".
7994
-
7995
- The default is detected from your browser language. EU, EFTA, and UK locales default to euros; everywhere else defaults to dollars.
7996
-
7997
- Your choice persists in a 90-day cookie (`kici_pricing_currency`), so it survives across reloads and applies on every billing page.
7998
-
7999
- <!-- /help:settings-billing-currency -->
8000
-
8001
- <!-- help:billing-payment-failure#settings -->
8002
-
8003
- This banner appears when your organization's latest payment to Stripe has failed. Your subscription remains active during the retry period, but you should update your payment method promptly to avoid service interruption.
8004
-
8005
- <!-- /help:billing-payment-failure -->
8006
-
8007
- <!-- help:activity-overview#activity -->
8008
-
8009
- Activity is your forensic log — every Platform mutation (invites, role changes, sources, plans) and every orchestrator action (reads, run cancels, secret reveals, environment edits) merged into one chronological stream.
8010
-
8011
- Each row shows the actor, the action, the target, and the outcome.
8012
-
8013
- - **Audit rows:** expand for field-level change tracking.
8014
- - **Access rows:** expand for the request ID, origin, and any error message.
8015
-
8016
- <!-- /help:activity-overview -->
8017
-
8018
- <!-- help:activity-filters#activity -->
8019
-
8020
- Filters live entirely in the URL — bookmark or share a filtered view to replay it.
8021
-
8022
- - **Search:** full-text match against access-log error messages and the JSON body of audit entries.
8023
- - **Run ID:** combine with another filter to scope all activity touching a specific run.
8024
-
8025
- Click a row's run target to jump straight to the run detail page.
8026
-
8027
- <!-- /help:activity-filters -->
8028
-
8029
- ## DLQ
8030
-
8031
- The DLQ (dead-letter queue) page lists internal events whose dispatch attempts were exhausted (or that hit a non-retryable error). Each row shows when the event landed in the DLQ, the event name, the attempt count, the failure reason, and the last error message.
8032
-
8033
- <!-- help:dlq#dlq -->
8034
-
8035
- The DLQ holds events your org emitted that could not be dispatched within the retry budget. The sidebar badge shows the current depth so you can spot a building backlog without opening the page.
8036
-
8037
- Per-row actions (visible when you have `event_dlq:write`):
8038
-
8039
- - **Retry:** clears the DLQ flag and re-publishes the event. A healthy orchestrator picks it up immediately.
8040
- - **Discard:** permanently deletes the row. Use when the payload is corrupt or the routing target no longer exists.
8041
-
8042
- Members with only `event_dlq:read` see the list but cannot retry or discard. Org owners have both actions by default.
8043
-
8044
- <!-- /help:dlq -->
8045
-
8046
- <!-- help:settings-ci-trust#settings -->
8047
-
8048
- CI trust policy controls how your organization handles PR-triggered runs from different contributor types. Configure the default trust level for unknown contributors and set per-member overrides to control who can run workflows with full secrets access.
8049
-
8050
- <!-- /help:settings-ci-trust -->
8051
-
8052
- <!-- help:settings-global-workflows#settings -->
8053
-
8054
- Global workflows let a single "workflow repo" define jobs that run when events happen in other repos in the same org.
8055
-
8056
- This tab exposes the security knobs as independent axes:
8057
-
8058
- - **Master enable toggle:** turn the whole feature on or off.
8059
- - **Authoring allow-list:** which repos may **define** global workflows.
8060
- - **Source deny-list:** **source** repos whose events never trigger globals (forks, public-contrib).
8061
- - **Elevated-access list:** authoring repos that need source-repo secrets during execution.
8062
-
8063
- See the [user guide](global-workflows.md) and the [architecture reference](../architecture/global-workflows.md) for the full model.
8064
-
8065
- <!-- /help:settings-global-workflows -->
8066
-
8067
- <!-- help:settings-global-workflows-enable#settings -->
8068
-
8069
- Master kill-switch for global workflows in this org.
8070
-
8071
- - **OFF:** the orchestrator will **not register** any workflow that declares `repos:` patterns, and will **not dispatch** cross-repo triggers — effectively rolling the org back to per-repo-only semantics. All other settings on this page are ignored.
8072
- - **ON:** the other toggles become your safety rails. Turn ON to opt in.
8073
-
8074
- <!-- /help:settings-global-workflows-enable -->
8075
-
8076
- <!-- help:settings-global-workflows-authors#settings -->
8077
-
8078
- Restricts which repos in this org may **define** global workflows (the "authoring axis").
8079
-
8080
- - **OFF:** any repo in the org may declare a workflow with `repos:` patterns and have it registered.
8081
- - **ON:** only repos whose identifier matches one of the entries below may author globals. Non-matching repos have their global workflows dropped at registration time, with a warning in the orchestrator log.
8082
- - **ON + empty list:** **no repo** may author globals — use as a temporary lock-down.
8083
-
8084
- Each entry has two parts:
8085
-
8086
- - **Source:** pick a configured source (a specific GitHub App or universal-git source) to pin the entry to that source only, or leave it as **Any source** to match across every source in the org.
8087
- - **Pattern:** a glob matched against the authoring repo identifier (e.g. `myorg/ci-*`, `myorg/platform-*`).
8088
-
8089
- Pinning by source is useful when the same `owner/repo` could legitimately exist on more than one configured source and you only want to trust one of them as an author.
8090
-
8091
- <!-- /help:settings-global-workflows-authors -->
8092
-
8093
- <!-- help:settings-global-workflows-blocked-sources#settings -->
8094
-
8095
- Deny-list for **source** repos whose events must never trigger a global workflow (the "source axis").
8096
-
8097
- Use this for untrusted territory — forks, public-contrib mirrors, sandboxes — where a single push shouldn't be able to fan out org-wide automation.
8098
-
8099
- Evaluated at dispatch time against the repo that emitted the event, independently of the authoring allow-list: a global workflow whose author is allowed will still be skipped if the _source_ repo is denied. Both lists can be active simultaneously.
8100
-
8101
- Each entry has two parts:
8102
-
8103
- - **Source:** pick a configured source to deny only events delivered on that source, or leave it as **Any source** to deny across the org.
8104
- - **Pattern:** a glob matched against the source repo identifier (e.g. `myorg/fork-*`, `myorg/public-*`).
8105
-
8106
- Pinning by source is the right move when the same `owner/repo` is reachable through more than one configured source (e.g. a public forge and a trusted mirror) and you want to drop deliveries from only one of them.
8107
-
8108
- <!-- /help:settings-global-workflows-blocked-sources -->
8109
-
8110
- <!-- help:settings-global-workflows-elevated#settings -->
8111
-
8112
- Authoring repos listed here receive **elevated access to source-repo secrets** during global workflow execution.
8113
-
8114
- - **Without elevation:** a global workflow job runs with only the workflow repo's own credentials — it can clone both repos but can't read the source repo's scoped secrets.
8115
- - **With elevation:** the job gets the source repo's secret context injected, so deploy / release / cross-repo automation flows work.
8116
-
8117
- Treat elevated repos as effective owners of every source repo's CI secrets — only add repos you fully trust.
8118
-
8119
- Each entry has two parts:
8120
-
8121
- - **Source:** pick a configured source to elevate only when the authoring repo lives on that source, or leave it as **Any source** to elevate across the org.
8122
- - **Pattern:** a glob matched against the **workflow-authoring** repo, not the source repo (e.g. `myorg/ci-deploy`, `myorg/release-automation`).
8123
-
8124
- Pinning by source narrows the trust window: if the same `owner/repo` is configured on more than one source, only the source you pick will grant elevation.
8125
-
8126
- <!-- /help:settings-global-workflows-elevated -->
8127
-
8128
- <!-- help:settings-webhooks#settings -->
8129
-
8130
- Configure outbound webhook endpoints to receive notifications when runs and jobs change status. Each endpoint receives HMAC-SHA256 signed payloads with event details.
8131
-
8132
- For each endpoint you can:
8133
-
8134
- - **Subscribe to event types:** `run.started`, `run.completed`, `run.failed`, `job.started`, `job.completed`, `job.failed`.
8135
- - **View delivery logs:** HTTP response codes and retry counts.
8136
- - **Send a test ping:** verify connectivity before going live.
8137
-
8138
- <!-- /help:settings-webhooks -->
8139
-
8140
- <!-- help:settings-security-dashboard-policy#settings -->
8141
-
8142
- Read-only view of the orchestrator's dashboard-write policy.
8143
-
8144
- Each row toggles one mutating dashboard action — setting a secret, approving a held run, retrying a dead-lettered webhook, and so on. The orchestrator operator decides which actions stay on the dashboard and which become **CLI-only**. The dashboard cannot change the policy itself — that's the point: disabled actions stay out of the SaaS Platform's trust path.
8145
-
8146
- Manage the policy with:
8147
-
8148
- - **Show the full policy:** `kici-admin org-settings dashboard-writes show`
8149
- - **Disable an operation:** `kici-admin org-settings dashboard-writes set --op <name>=false`
8150
- - **Reset to permissive defaults:** `kici-admin org-settings dashboard-writes reset`
8151
-
8152
- The summary strip at the top shows total / enabled / disabled counts plus whether your orchestrator is currently connected. A disconnected orchestrator means the page falls back to the cached policy from the most recent connection.
8153
-
8154
- <!-- /help:settings-security-dashboard-policy -->
8155
-
8156
- <!-- help:settings-support-access#settings -->
8157
-
8158
- Controls whether KiCI support staff may open read-only support sessions against your organization. Sessions are **off by default** — nobody outside your org can read your data until you opt in here.
8159
-
8160
- When enabled:
8161
-
8162
- - KiCI staff can open time-boxed, read-only sessions to investigate an issue.
8163
- - Every read they perform is recorded in your audit trail with the support reason.
8164
- - No writes are ever possible during a session.
8165
-
8166
- Disabling the toggle immediately ends any in-progress support session. Only users with the `support:admin` permission (owners by default) can change this setting.
8167
-
8168
- <!-- /help:settings-support-access -->
8169
-
8170
- <!-- help:settings-webhooks-delivery-log#settings -->
8171
-
8172
- The delivery log shows recent webhook deliveries for an endpoint, including the HTTP status code, number of retry attempts, and the event payload.
8173
-
8174
- Retry behavior:
8175
-
8176
- - Deliveries are retried up to 3 times with exponential backoff.
8177
- - After 10 consecutive failures, the endpoint is automatically disabled — you can re-enable it from this view.
8178
-
8179
- <!-- /help:settings-webhooks-delivery-log -->
8180
-
8181
- <!-- help:settings-event-log#event-log -->
8182
-
8183
- The event log shows every inbound webhook this organization has received, regardless of whether it came in via the Platform relay or directly to an orchestrator.
8184
-
8185
- Each row joins two records:
8186
-
8187
- - **Platform side:** event metadata and a SHA-256 hash of the body (no payload stored).
8188
- - **Orchestrator side:** full payload and processing outcome.
8189
-
8190
- Filter by routing key, event type, status, or delivery ID. Click a row for the full per-tier breakdown.
8191
-
8192
- <!-- /help:settings-event-log -->
8193
-
8194
- <!-- help:settings-event-log-detail#event-log -->
8195
-
8196
- The detail panel shows both tiers' projections side-by-side.
8197
-
8198
- - **Platform record:** answers "did the delivery arrive at the relay and where was it routed".
8199
- - **Orchestrator record:** answers "what was the body and what happened next" — including the matched workflow count and spawned run links.
8200
- - **Payload:** the raw webhook body. Streams over the dashboard's existing WebSocket connection in 64 KiB chunks so Platform never buffers the full body and you see progress for large deliveries. Requires `event_log:read_payload`.
8201
-
8202
- Oversized or storage-failed payloads show an "omitted" badge, with the hash preserved for correlation against raw logs.
8203
-
8204
- <!-- /help:settings-event-log-detail -->
8205
-
8206
- ## Activity
8207
-
8208
- The activity page (`/orgs/:customerId/activity`) is the org-level forensic log. It federates two streams into one chronological view: the upstream tenant-plane audit log (every tenant-plane mutation -- invites, role changes, source registrations, plan changes) and orchestrator `access_log` rows (every read and admin action -- run cancels, secret reveals, environment edits, dashboard data fetches via the Platform proxy). Filters live in the URL via search params so a filtered view is bookmarkable and shareable. The page uses cursor-based pagination and supports filtering by source (audit / access_log / all), free-text search, run ID, and other dimensions. Requires `audit:read` permission. The legacy `/orgs/:customerId/audit-log` URL redirects here to preserve bookmarks.
8209
-
8210
- ## Settings
8211
-
8212
- The settings page (`/orgs/:customerId/settings`) uses a tabbed layout:
8213
-
8214
- 1. **General** -- displays the organization name (editable by owners via inline click-to-edit) and the organization ID
8215
- 2. **Members** -- team management with invite, role assignment, and member removal
8216
- 3. **Roles** -- custom role management with granular permission matrix
8217
- 4. **API keys** -- API key creation and revocation for dashboard/programmatic access
8218
- 5. **Orchestrator keys** -- orchestrator API key management for Platform WebSocket connections
8219
- 6. **Sources** -- read-only list of registered webhook sources (see below)
8220
- 7. **Billing** -- plan and payment management (hidden in the `kici-admin` org)
8221
- 8. **CI trust** -- trust policy configuration for CI runs (visible with `ci_trust:read` permission)
8222
- 9. **Global workflows** -- org-level security knobs for cross-repo workflows (visible with `org_settings:read` permission)
8223
- 10. **Webhooks** -- outbound webhook endpoint management with delivery logs and test ping
8224
- 11. **Event log** -- inbound webhook delivery log (visible with `event_log:read` permission)
8225
- 12. **Security** -- read-only view of the orchestrator's dashboard-write policy matrix (visible with `org_settings:read` permission)
8226
- 13. **Support access** -- opt-in switch that controls whether KiCI support staff may open read-only support sessions against your org (visible with `support:read`; toggled with `support:admin`)
8227
-
8228
- Audit-log-style entries are no longer a settings tab; they live on the dedicated **Activity** page accessible from the sidebar.
8229
-
8230
- Tab selection syncs with the URL path (`/settings/members`, `/settings/api-keys`, etc.), making tabs bookmarkable.
8231
-
8232
- ### Support access
8233
-
8234
- The Support access tab controls whether KiCI support staff may open a read-only **support session** against your organization to help diagnose an issue. The setting is **off by default** -- until you opt in here, no one outside your org can read your data.
8235
-
8236
- When support access is enabled:
8237
-
8238
- - A KiCI operator can open a time-boxed (30-minute, renewable), read-only support session scoped to a stated reason.
8239
- - A support session is **runs-only**: the operator can browse your run list and, by confirming each run individually, view that run's detail and step logs. Nothing else is visible, and no write is ever possible.
8240
- - Every run an operator opens is recorded in your [Activity](#activity) audit trail, attributed to the operator with the support reason -- so you can see exactly what was looked at and why.
8241
-
8242
- **Disabling immediately ends any active session.** Toggling the switch off closes every in-progress support session for your org at once. Enabling and disabling the setting is itself audited, attributed to the user who changed it.
8243
-
8244
- Viewing the setting requires the `support:read` permission; changing it requires `support:admin` (granted to owners by default).
8245
-
8246
- ### Orchestrator keys
8247
-
8248
- The orchestrator keys tab manages API keys used to authenticate orchestrator-to-Platform WebSocket connections. These are separate from user API keys (which grant dashboard/API access).
8249
-
8250
- **List view** -- shows all active orchestrator keys with name, description, key prefix, creation date, and last used date.
8251
-
8252
- **Create** -- opens a modal to enter a name and optional description. After creation, the raw key is shown once in a copyable box. Set this key as the `KICI_PLATFORM_TOKEN` environment variable in your orchestrator configuration.
8253
-
8254
- **Revoke** -- opens a confirmation modal before soft-deleting the key. Any orchestrators using the revoked key will be disconnected.
8255
-
8256
- ### Sources
8257
-
8258
- The sources tab shows webhook sources registered by connected orchestrators. Sources appear here **automatically** when an orchestrator connects to the Platform via WebSocket and sends a `source.register` message -- there is no manual "add source" action in the UI.
8259
-
8260
- **What causes a source to appear:**
8261
-
8262
- 1. An orchestrator is configured with one or more providers (e.g., a GitHub App with `appId: 12345`)
8263
- 2. The orchestrator connects to the Platform using an orchestrator API key for your organization
8264
- 3. On connection, the orchestrator sends `source.register` with its provider sources (e.g., `github:12345`)
8265
- 4. The Platform records the source against your organization
8266
- 5. The source immediately appears in the dashboard
8267
-
8268
- **Each source displays:**
8269
-
8270
- - **Routing key** -- the source identifier (e.g., `github:12345` for a GitHub App, `generic:my-source` for a generic webhook)
8271
- - **Webhook URL** -- the URL to configure in your provider's webhook settings (constructed by the Platform based on the provider type and org ID)
8272
- - **Registered at** -- when the orchestrator first registered this source
8273
- - **Copy button** -- copies the webhook URL to the clipboard
8274
-
8275
- **Read-only** -- sources cannot be created, edited, or deleted from the dashboard. They are managed entirely by orchestrator connections. When an orchestrator disconnects, its sources remain visible (they are not automatically removed).
8276
-
8277
- **Empty state** -- if no orchestrator has connected yet, the tab shows "No webhook sources registered" with a link to the operator setup guide.
8278
-
8279
- **Webhook secrets** -- webhook HMAC secrets are not visible in the dashboard. They are stored in the orchestrator's database (`webhook_secrets` table) and pushed to the Platform via the `source.secrets` WebSocket message after registration. The Platform uses these secrets to verify incoming webhook signatures. Secrets are configured in the orchestrator's database, not through the UI.
8280
-
8281
- **Adding a new source** requires:
8282
-
8283
- 1. Configure a new provider in the orchestrator (e.g., add a GitHub App to the orchestrator's provider config)
8284
- 2. Seed the webhook secret in the orchestrator's `webhook_secrets` database table
8285
- 3. Restart the orchestrator -- it will register the new source with the Platform on connection
8286
- 4. Configure the webhook URL (shown in the sources tab) in the provider's settings (e.g., GitHub App webhook URL)
8287
-
8288
- ### Event log
8289
-
8290
- The event log tab (`/orgs/:customerId/settings/event-log`) shows every inbound webhook this organization has received. Each row joins two tiers of records:
8291
-
8292
- 1. **Platform record** -- written by the Platform relay on every delivery: routing key, event, action, repo, routing target, status, SHA-256 payload hash. The Platform never persists the payload (trust boundary).
8293
- 2. **Orchestrator record** -- written by the destination orchestrator when it processes the delivery: full payload (in object storage), processing outcome (`processed` / `duplicate` / `lockfile_missing` / `failed`), matched workflow count, first run spawned (if any), and a payload hash that matches the Platform record for cross-tier correlation.
8294
-
8295
- The list view supports filters for routing key, event type, status, and free-text delivery ID search. Click a row to open a detail panel with both tiers' projections side-by-side, plus the payload viewer.
8296
-
8297
- **Permissions:**
8298
-
8299
- - `event_log:read` -- list rows and view metadata in the detail panel.
8300
- - `event_log:read_payload` -- additionally view the raw webhook payload body. (Owners and admins inherit this. Lower-tier roles see "Payload not available" with a hint to ask for an elevated role.)
8301
-
8302
- **Edge cases the UI surfaces:**
8303
-
8304
- - **Payload omitted** -- when the inbound payload exceeded the orchestrator's `eventLog.maxPayloadBytes` soft cap (default 5 MB) or the object-storage write failed, the row is still recorded with `payload_omitted=true`. The hash is preserved so operators can correlate against `KICI_WEBHOOK_PAYLOAD_DIR` or raw logs.
8305
- - **Orchestrator unavailable** -- when the orchestrator does not respond within 2 seconds of the merge fan-out, the list still loads with Platform-side metadata only, marked with an `orchestrator_unavailable` banner.
8306
- - **Orchestrator-only deliveries** -- direct-ingress deliveries (independent / hybrid mode) that never crossed the Platform appear with `platform.status = orchestrator_only`.
8307
-
8308
- Retention is 30 days on both tiers, matching the Platform `event_log` audit window.
8309
-
8310
- <!-- help:personal-profile#account -->
8311
-
8312
- Account settings let you view your profile information (name, email) and manage your KiCI account. Changes here apply across all organizations you belong to.
8313
-
8314
- <!-- /help:personal-profile -->
8315
-
8316
- <!-- help:personal-pats#account -->
8317
-
8318
- Personal access tokens (PATs) are long-lived credentials for programmatic API access.
8319
-
8320
- Create a PAT to authenticate CLI tools or scripts without going through the OIDC login flow. Tokens can be revoked at any time.
8321
-
8322
- Use a token's clone button to open the creation modal prefilled with that token's name, expiry, and permissions.
8323
-
8324
- <!-- /help:personal-pats -->
8325
-
8326
- <!-- help:personal-linked-accounts#account -->
8327
-
8328
- Linked accounts connect your external provider identities (like GitHub) to your KiCI account. Linking shows your provider username in run metadata and sets your contributor trust level.
8329
-
8330
- **Unlinking here removes the display link only** — it does not remove a sign-in method. To change how you sign in (add a password, remove GitHub login), use the **Account console** link, or see [Account and sign-in](./account-and-login.md).
8331
-
8332
- <!-- /help:personal-linked-accounts -->
8333
-
8334
- <!-- help:orgs-list#organizations -->
8335
-
8336
- Organizations are the top-level container for your CI/CD resources. Each org has its own runs, settings, environments, secrets, and team members. Select an organization to manage its workflows and configuration.
8337
-
8338
- <!-- /help:orgs-list -->
8339
-
8340
- <!-- help:orchestrators-list#orchestrators -->
8341
-
8342
- The Orchestrators page lists every orchestrator currently connected to this org, keyed by **cluster name**. Each row shows:
8343
-
8344
- - **Cluster** — the human-friendly cluster name set on the orch via `kici-admin cluster-name set <name>`, or an auto-generated `cluster-<6hex>` if no operator has renamed it.
8345
- - **Role** — `coordinator` (talks to Platform directly) or `worker` (relays through a coordinator).
8346
- - **Version**, **mode**, **routing keys**, and **last heartbeat**.
8347
-
8348
- Click a cluster to drill into its per-orch surfaces (security policy, environments, secrets, DLQ, registrations, global workflows). Different clusters in the same org can have different settings — this page is the entry point that lets you pick which cluster you're configuring.
8349
-
8350
- <!-- /help:orchestrators-list -->
8351
-
8352
- <!-- help:orchestrators-scope#orchestrators -->
8353
-
8354
- Every panel inside this view scopes to the named cluster. Settings shown here come from that orchestrator's own database — a sibling orchestrator in the same org may have a different security policy, different environments, and different secrets.
8355
-
8356
- When the cluster shows **disconnected**, the orch is offline and its current state can't be queried. Most child pages will return 404 in that state; return to the orchestrator list to find a connected cluster.
8357
-
8358
- To rename a cluster, run `kici-admin cluster-name set <new>` on the orchestrator host and restart the orch service so the new name reaches Platform on the next `source.register`.
8359
-
8360
- <!-- /help:orchestrators-scope -->
8361
-
8362
- ## Workflows
8363
-
8364
- The workflows page (`/orgs/:customerId/workflows`) shows permanently registered workflows listening for events. It displays a filterable table with columns for workflow name, repository, trigger types, last triggered time, next fire time (for scheduled workflows), source repos, and actions.
8365
-
8366
- Each row is expandable to show trigger configuration details. Rows include action controls: a "Run now" button for manual triggering, a toggle switch to enable/disable the workflow, and a delete button with a confirmation modal (optionally cancelling active runs). Stale workflows (no triggers in the last 30 days) show a yellow "Stale" badge. Registry health indicators (version, sync status, last updated) appear above the table.
8367
-
8368
- Filters include trigger type, repository, and workflow name.
8369
-
8370
- ## Diagnostics
8371
-
8372
- The diagnostics page (`/orgs/:customerId/diagnostics`) provides infrastructure health monitoring. It has four sections:
8373
-
8374
- 1. **Execution metrics** -- cards showing total runs (24h), success rate, average duration, and active jobs (queued + running). Refreshes every 30 seconds.
8375
- 2. **Infrastructure alerts** -- banner summarizing any critical or warning alerts from connected orchestrators
8376
- 3. **Infrastructure tree** -- hierarchical view of orchestrators, their scalers, and agents. Refreshes every 10 seconds. Each orchestrator row shows:
8377
- - **`orchestrator:`** (bold monospace, left group) -- the orchestrator's cluster instance ID, set via `KICI_CLUSTER_INSTANCE_ID` env var or auto-generated as a UUID. If no instance ID is set, the first 8 characters of the connection ID are shown here instead.
8378
- - **`conn:`** (dimmed monospace, left group) -- first 8 characters of the WebSocket connection ID assigned by the Platform relay. Only shown when an explicit instance ID is present.
8379
- - Connection status badge, role badge (coordinator or worker), version badge (left group, after the ID labels)
8380
- - **`host:`** badge (right side) -- the system hostname of the machine running the orchestrator process
8381
- - Additional badges on the right side: running-as user, CPU count, memory usage, uptime
8382
-
8383
- Each orchestrator lists its **scalers** (indented at level 1) showing scaler name, type badge (container/firecracker/bare-metal), active/max agent count, and a config info popover. Below each scaler, its **agents** (indented at level 2) display agent ID, platform/arch, heartbeat age, hostname, running-as user, CPU count, memory, uptime, and version. Labels (both user-defined and auto-generated `kici:` prefixed) are shown on a separate row beneath scalers and stateful agents, with a tooltip distinguishing user labels from auto labels.
8384
-
8385
- 4. **Secret backends** -- health cards for each configured secret backend (e.g. OpenBao), showing connection status with sync and test actions. Allows triggering a manual sync or connectivity test per backend.
8386
-
8387
- ## Environments
8388
-
8389
- The environments page (`/orgs/:customerId/environments`) lists all deployment environments for the organization. Each environment shows its name, type (fixed or glob pattern), protection status (branch restrictions, concurrency limits, required reviewers, wait timers), and enabled/disabled state.
8390
-
8391
- Users with `environments:admin` permission can create new environments via a modal dialog, choosing between fixed and glob (pattern-matching) types. Clicking an environment row navigates to the environment detail page.
8392
-
8393
- ### Environment detail
8394
-
8395
- The environment detail page (`/orgs/:customerId/environments/:environmentId`) shows a header with the environment name, type badge, enabled/disabled toggle, and a delete button. Below the header, a tabbed layout provides four sections:
8396
-
8397
- 1. **Variables** (default) -- environment-scoped variables
8398
- 2. **Secrets** -- secrets bound to this environment
8399
- 3. **Protection** -- protection rules (branch restrictions, concurrency limits, required reviewers, wait timers)
8400
- 4. **History** -- audit history of changes to this environment
8401
-
8402
- Tab selection syncs with the URL path (`/orgs/:customerId/environments/:environmentId/variables`, `/orgs/:customerId/environments/:environmentId/protection`, etc.).
8403
-
8404
- ## Secrets
8405
-
8406
- The secrets page (`/orgs/:customerId/secrets`) provides a scope-centric view of all secrets in the organization. Secrets are organized into a scope tree with environment binding checkboxes, allowing you to control which secret scopes are available in which environments.
8407
-
8408
- Permission-gated: `secrets:read` to view scopes, `secrets:write` to add or delete secrets, `environments:write` to modify environment bindings.
8409
-
8410
- ### Where secrets live
8411
-
8412
- Secret values are stored in the orchestrator's secret store and authorized through the orchestrator's RBAC. The dashboard surfaces secret **names** and scope membership for every secret regardless of where the value was entered.
8413
-
8414
- Whether secret **values** can be set from the dashboard depends on the orchestrator's [dashboard-write policy](/operator/security/dashboard-write-policy):
8415
-
8416
- - **Permissive (default):** the "Add secret" and "Edit value" controls accept plaintext directly in the dashboard. This is how a typical SaaS CI tool works and is the right default for small teams.
8417
- - **`secrets.set` disabled by policy:** the controls render with a lock icon, grayed out. Hovering shows the exact `kici-admin secret set` invocation needed; a copy button puts it on the clipboard. The control is inert — the dashboard issues no mutating request. Use the CLI to enter values; the dashboard refreshes within ~30 seconds and shows the new secret name.
8418
-
8419
- The policy state is visible at three layers in the UI:
8420
-
8421
- - A **lock-icon prefix** on every disabled control, with a per-control CLI hint.
8422
- - A **per-page banner** on any page containing at least one disabled operation, listing every disabled op on that page and its CLI equivalent.
8423
-
8424
- The Security policy page (Settings → Security → Dashboard policy) renders the full 24-row read-only matrix with the current state and the `kici-admin` command for each row. The policy itself cannot be changed from the dashboard — the orchestrator operator manages it via `kici-admin org-settings dashboard-writes`. See [Dashboard-write policy](/operator/security/dashboard-write-policy) for the operator-side details.
8425
-
8426
- ## Approval queue
8427
-
8428
- The approval queue page (`/orgs/:customerId/approval-queue`) shows held runs that are pending approval. Runs can be held due to environment protection rules (required reviewers, wait timers). The page supports filtering by status (pending, approved, rejected, expired) and provides approve/reject actions for users with `environments:write` permission. Users with `environments:admin` permission can skip wait timers.
8429
-
8430
- ## Account
8431
-
8432
- The standalone account page (`/account`) provides access to personal settings outside of any organization context. It has three tabs:
8433
-
8434
- - **Profile** -- view your name and email
8435
- - **Personal access tokens** -- create and revoke PATs for programmatic API access
8436
- - **Linked accounts** -- connect external provider identities (e.g. GitHub) to your KiCI account
8437
-
8438
- Linked accounts control run-attribution metadata only — unlinking a provider here does not remove it as a way to sign in. To change how you sign in, see [Account and sign-in](./account-and-login.md).
8439
-
8440
- This page is also accessible within an org context via the user menu in the sidebar (`/orgs/:customerId/account`).
8441
-
8442
- ## Admin section
8443
-
8444
- When viewing the `kici-admin` organization, the dashboard switches to an admin-mode interface for platform-wide management. The admin pages are:
8445
-
8446
- - **Overview** (`/orgs/kici-admin/admin`) -- embedded Grafana dashboards with three tabs: System, Orgs, and Execution
8447
- - **Organizations** (`/orgs/kici-admin/admin/orgs`) -- table of all organizations with plan type, member count, Stripe status, and creation date; rows link to org detail pages
8448
- - **Org detail** (`/orgs/kici-admin/admin/orgs/:orgId`) -- org info summary, plan limit controls, current usage stats with over-limit warnings, quick actions, and a tabbed section with audit log
8449
- - **Connections** (`/orgs/kici-admin/admin/connections`) -- table of connected orchestrators showing org, routing keys, heartbeat age, running jobs, and force-disconnect action
8450
- - **Scheduled jobs** (`/orgs/kici-admin/admin/jobs`) -- table of Platform scheduled background jobs with cron schedule, last run status, consecutive failure count, estimated next run time, and a "Run now" action to trigger immediate execution
8451
- - **Audit log** (`/orgs/kici-admin/admin/audit-log`) -- paginated table of platform-level admin actions with expandable JSON details
8452
- - **Metrics** -- external link to the Grafana instance
8453
-
8454
- ## Organizations
8455
-
8456
- The organizations page (`/orgs`) lists all organizations your account has access to.
8457
-
8458
- Organizations are sorted alphabetically by display name. Each entry shows your role (owner or member). A "Create organization" button opens an inline form to create a new org by name.
8459
-
8460
- ## Theme
8461
-
8462
- The dashboard supports three theme modes:
8463
-
8464
- - **System** (default) -- follows your operating system's dark/light preference
8465
- - **Dark** -- forced dark mode
8466
- - **Light** -- forced light mode
8467
-
8468
- Toggle between modes using the sun/moon icon in the sidebar footer. The selection persists to `localStorage`.
8469
-
8470
- ## Date and time preferences
8471
-
8472
- A toggle button in the sidebar lets you switch between **local time** and **UTC time** display. When UTC mode is enabled:
8473
-
8474
- - All timestamps in the run list, run detail header, metadata panel, and log viewer show UTC times
8475
- - Tooltips on relative timestamps (e.g. "5 minutes ago") show the absolute time in UTC
8476
- - The timeline Gantt chart uses UTC for time labels
8477
-
8478
- The preference persists to `localStorage`.
8479
-
8480
- ## Keyboard shortcuts
8481
-
8482
- | Key | Context | Action |
8483
- | ------------- | ---------- | ---------------------- |
8484
- | Arrow Up/Down | Job tree | Move focus |
8485
- | Enter | Job tree | Select job or step |
8486
- | Escape | Job tree | Navigate to first job |
8487
- | Enter | Log search | Jump to next match |
8488
- | Shift+Enter | Log search | Jump to previous match |
8489
- | Escape | Log search | Clear search |
8490
-
8491
- ## Error pages
7837
+ The KiCI dashboard is the browser interface for monitoring workflow runs, inspecting jobs and logs, and managing your organization. It signs in via OIDC and talks to KiCI over its API.
8492
7838
 
8493
- The dashboard shows informative error pages instead of blank screens:
7839
+ This guide is split across the following pages:
8494
7840
 
8495
- - **404** -- "Page not found" with a "Go home" button linking to the organizations page
8496
- - **500** -- "Failed to load" with an error message, a trace ID for support, and a "Go home" button (shown when API requests fail)
8497
- - **Client-side rendering errors** -- caught by the error boundary, showing "Something went wrong" with a trace ID and a "Reload page" button
8498
- - **Auth errors** -- authentication failures on the OIDC callback page show the error message with a retry mechanism and a "Back to login" link
7841
+ | Page | Covers |
7842
+ | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
7843
+ | [Getting started](./dashboard/getting-started.md) | Onboarding checklist and your organizations list |
7844
+ | [Navigation and layout](./dashboard/navigation.md) | Sidebar, mobile nav, theme, time display, shortcuts, error pages |
7845
+ | [Runs and logs](./dashboard/runs.md) | Run list, run detail, the log viewer |
7846
+ | [Settings](./dashboard/settings.md) | Members, roles, teams, keys, sources, billing, security, support access |
7847
+ | [Workflows, diagnostics, and orchestrators](./dashboard/workflows-and-diagnostics.md) | Registered workflows, infra health, per-cluster views |
7848
+ | [Environments, secrets, and approvals](./dashboard/environments-and-secrets.md) | Environments, secret scopes, approval queue |
7849
+ | [Activity and DLQ](./dashboard/activity-and-dlq.md) | Forensic activity log and dead-letter queue |
7850
+ | [Account](./dashboard/account.md) | Personal account settings |
8499
7851
 
8500
7852
  ---
8501
7853
 
@@ -10927,7 +10279,7 @@ Source: https://docs.kici.dev/architecture/data-flows/
10927
10279
 
10928
10280
  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.
10929
10281
 
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.
10282
+ > **Lock file schema version:** The lock file uses schema version 17, which widens per-job init to typed presets (`mise` / `{ mise }`) and `auto` detection on top of v16's normalized approval config, v15's per-job init config, 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.
10931
10283
 
10932
10284
  ## Webhook delivery flow
10933
10285
 
@@ -11133,7 +10485,7 @@ Dep cache misses alone do **not** trigger a build job. Deps are platform-specifi
11133
10485
 
11134
10486
  ### Cross-source / no-contentHash workflows
11135
10487
 
11136
- - **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is 15.
10488
+ - **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is 17.
11137
10489
  - **Cross-source / global-workflow dispatch** (a workflow registered against source A fired by a webhook on source B) bypasses both caches. The registration's lock file entry still carries `contentHash`, but the cross-source path always clone-and-installs — the eval temp dir doesn't ship `@kici-dev/sdk`. The execution agent still verifies `contentHash` against the cloned source for drift detection.
11138
10490
 
11139
10491
  ### Build deduplication
@@ -11850,7 +11202,7 @@ The agent is the execution worker. It runs on customer infrastructure and has fu
11850
11202
 
11851
11203
  ### `@kici-dev/engine`
11852
11204
 
11853
- Shared business logic used by all three tiers. Single source of truth for cross-tier concerns. Has no internal `@kici-dev/*` dependencies (only zod, picomatch, and jsonpath-plus).
11205
+ Shared business logic used by all three tiers. Single source of truth for cross-tier concerns. Has no internal `@kici-dev/*` dependencies -- only a handful of third-party libraries.
11854
11206
 
11855
11207
  - Protocol message schemas (Zod-based, direction-specific unions including dashboard REST-over-WS, browser live streaming, test run lifecycle, observer channel, log pull, run events, peer-to-peer, cluster join, and source registration)
11856
11208
  - Provider interfaces (WebhookNormalizer, LockFileFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder, ContributorResolver, CheckStatusPoster)