@kici-dev/compiler 0.1.22 → 0.1.23

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.
Files changed (39) hide show
  1. package/dist/cli.js +20 -6
  2. package/dist/commands/compile.d.ts +6 -0
  3. package/dist/commands/compile.js +6 -3
  4. package/dist/commands/docs.d.ts +8 -8
  5. package/dist/commands/docs.js +35 -16
  6. package/dist/commands/org.js +2 -2
  7. package/dist/commands/run.d.ts +16 -1
  8. package/dist/commands/run.js +86 -14
  9. package/dist/commands/test.d.ts +4 -0
  10. package/dist/commands/types.d.ts +2 -0
  11. package/dist/commands/types.js +1 -1
  12. package/dist/fixtures/describe-event.d.ts +6 -0
  13. package/dist/fixtures/describe-event.js +18 -0
  14. package/dist/fixtures/picker.d.ts +19 -0
  15. package/dist/fixtures/picker.js +64 -0
  16. package/dist/llm-context/llms-architecture.txt +1440 -0
  17. package/dist/llm-context/llms-cli.txt +2386 -0
  18. package/dist/llm-context/llms-features.txt +2389 -0
  19. package/dist/llm-context/llms-full.txt +976 -317
  20. package/dist/llm-context/llms-getting-started.txt +519 -0
  21. package/dist/llm-context/llms-patterns.txt +1324 -0
  22. package/dist/llm-context/llms-providers.txt +805 -0
  23. package/dist/llm-context/llms-sdk.txt +3725 -0
  24. package/dist/llm-context/llms.txt +13 -0
  25. package/dist/local-executor/index.js +40 -3
  26. package/dist/local-executor/job-runner.d.ts +2 -0
  27. package/dist/local-executor/job-runner.js +36 -4
  28. package/dist/local-executor/types.d.ts +2 -0
  29. package/dist/lockfile/generator.js +13 -4
  30. package/dist/remote/platform-client.d.ts +6 -0
  31. package/dist/remote/uploader.js +1 -0
  32. package/dist/templates/package-json.js +1 -1
  33. package/dist/test-runner/rule-evaluator.d.ts +1 -1
  34. package/dist/test-runner/rule-evaluator.js +2 -1
  35. package/dist/test-runner/step-context.d.ts +1 -1
  36. package/dist/test-runner/step-context.js +7 -2
  37. package/dist/types.d.ts +6 -2
  38. package/package.json +4 -4
  39. package/sbom.spdx.json +35 -35
@@ -0,0 +1,805 @@
1
+ # KiCI Providers
2
+
3
+ This bundle covers: Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://.
4
+
5
+ ## GitHub App provider
6
+
7
+ Source: https://docs.kici.dev/user/providers/github/
8
+
9
+ The **GitHub App** is KiCI's flagship source. A single App:
10
+
11
+ 1. receives `push`, `pull_request`, and related events from every repo it's installed on,
12
+ 2. clones repos with a short-lived installation token (no deploy key to manage),
13
+ 3. posts workflow / job / step Check runs back to the pull request (see
14
+ [GitHub checks architecture](https://docs.kici.dev/architecture/webhooks/github-checks/)).
15
+
16
+ You don't need an App for every scenario — if you only care about `push`
17
+ events, don't want to install an App, or are using a non-GitHub forge,
18
+ use the [universal-git provider](https://docs.kici.dev/user/providers/universal-git/) instead.
19
+
20
+ ## GitHub App vs. `github-repo` preset
21
+
22
+ Both paths reach the same trigger pipeline; they differ in what the
23
+ forge side looks like:
24
+
25
+ | Capability | GitHub App (this guide) | `github-repo` preset on universal-git |
26
+ | ---------------------------------------- | -------------------------------------- | ------------------------------------------- |
27
+ | Webhook source | App-level webhook (one per App) | Per-repo webhook (one per repo) |
28
+ | Clone auth | Installation token (auto, short-lived) | PAT or SSH deploy key (you manage rotation) |
29
+ | Check runs on pull requests | Yes — full KiCI Checks UI | No (status post only via custom step) |
30
+ | Cross-repo install in seconds | Yes (install the App on more repos) | No (new webhook per repo) |
31
+ | Works without a GitHub org admin | No (App creation is org-scoped) | Yes (per-repo webhook is repo-admin) |
32
+ | Works on Forgejo / Gitea / Gogs / GitLab | No | Yes (other presets) |
33
+
34
+ Use the App when you can; the `github-repo` preset is a fallback for
35
+ repos where you can't install an App.
36
+
37
+ ## One-click setup (recommended)
38
+
39
+ `kici-admin source add github --manifest` creates **and** configures the
40
+ GitHub App for you via GitHub's App Manifest flow. KiCI builds a manifest
41
+ with the exact permissions, events, webhook URL, and webhook secret baked
42
+ in, so you never pick permissions, paste a URL, generate a secret, or
43
+ download a `.pem` by hand — the App is correct by construction.
44
+
45
+ ```bash
46
+ kici-admin --url http://<orchestrator-host>:4000 --token $KICI_BOOTSTRAP_ADMIN_TOKEN \
47
+ source add github --manifest --name my-org --github-org my-org
48
+ ```
49
+
50
+ `--github-org <slug>` creates the App under a GitHub **organization** (the
51
+ `<slug>` is the org's `github.com/<slug>` URL slug, not its display name) — the
52
+ recommended default, since org-owned Apps can be installed across the org. Drop
53
+ the flag only when you deliberately want a personal-account App, which can be
54
+ installed solely on repos you own. You need permission to create Apps in that
55
+ org (be an org owner, or have the org allow member App creation).
56
+
57
+ What happens:
58
+
59
+ 1. The CLI resolves your org's webhook URL and opens GitHub with a
60
+ pre-filled App manifest. You click **"Create GitHub App"** once — the
61
+ only manual step.
62
+ 2. GitHub redirects back to a localhost callback; the CLI exchanges the
63
+ returned setup code for the App's id, private key, and webhook secret.
64
+ **The private key is exchanged and stored only on your orchestrator
65
+ host — it never transits the KiCI Platform.**
66
+ 3. The CLI stores the credentials encrypted under `KICI_SECRET_KEY` and
67
+ registers the routing key `github:<appId>`, reusing the same storage
68
+ path as the manual flow.
69
+ 4. It opens the App's install page so you can pick repos, then verifies
70
+ end-to-end: it waits for the installation, mints an installation token,
71
+ and confirms repo access before declaring success.
72
+
73
+ ```
74
+ $ kici-admin source add github --manifest --name my-org --github-org my-org
75
+ → Opening GitHub to create your App…
76
+ → ✓ App created (id 12345), credentials captured
77
+ → ✓ Stored on orchestrator (encrypted), registered as github:12345
78
+ → Install the App on your repos: https://github.com/apps/my-org/installations/new
79
+ → ✓ Installation detected (account my-org)
80
+ → ✓ Credentials verified (3 repositories reachable)
81
+
82
+ GitHub App "my-org" is live.
83
+ Webhook: https://<platform-host>/webhook/<orgId>/github
84
+ ```
85
+
86
+ **Flags:**
87
+
88
+ | Flag | Effect |
89
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | `--name <name>` | The App name you _request_ on GitHub (required). GitHub assigns the final name + slug; the stored, displayed name always comes from GitHub (see [Display name and slug](https://docs.kici.dev/user/providers/github/#display-name-and-slug)). |
91
+ | `--github-org <slug>` | Create the App under a GitHub organization instead of your personal account. |
92
+ | `--webhook-url <url>` | **Advanced / self-hosted.** Bake this `https://` URL into the App's webhook verbatim and skip the platform-mode webhook-URL resolution (so it works even where the auto-resolved KiCI Platform URL is unavailable). See [Self-hosted webhook URL](https://docs.kici.dev/user/providers/github/#self-hosted-webhook-url-override). |
93
+ | `--no-browser` | Headless mode: the CLI prints a `kici.dev` URL to open, then reads the setup code you paste back. The page is pure client-side — it only displays the short-lived code, which is useless once the CLI exchanges it. |
94
+
95
+ The manifest flow always creates a **new** App on GitHub. If a source for
96
+ that App id already exists on the orchestrator, the command refuses — use
97
+ `source update` to rotate an existing App's credentials.
98
+
99
+ If any step after App creation fails (e.g. storage), the CLI prints the
100
+ captured App id and writes the private key to a `0600` file, then tells
101
+ you how to finish with the manual `source add github` command — so a
102
+ created App is never orphaned.
103
+
104
+ Independent-mode orchestrators have no GitHub-App ingress (it is
105
+ Platform-relayed), so the manifest flow is unavailable there; use a
106
+ generic webhook source instead.
107
+
108
+ ## Manual setup (fallback)
109
+
110
+ When you'd rather create the App by hand — or your environment can't run
111
+ the manifest flow — follow these steps.
112
+
113
+ ### Create the GitHub App on GitHub's side
114
+
115
+ 1. **Decide the App scope.** User-owned Apps can only be installed on
116
+ repos you own; organization-owned Apps can be installed anywhere in
117
+ the org. For production, create the App under the org.
118
+
119
+ 2. **Create the App.** Go to _Settings -> Developer settings -> GitHub
120
+ Apps -> New GitHub App_ (org-level is _Settings -> Developer
121
+ settings -> GitHub Apps_ on the org page).
122
+
123
+ 3. **Set the webhook URL.** KiCI exposes one webhook endpoint per org:
124
+
125
+ ```
126
+ https://<platform-host>/webhook/<orgId>/github
127
+ ```
128
+
129
+ GitHub App webhooks are always delivered to this Platform endpoint and
130
+ relayed to your orchestrator over its outbound connection — platform and
131
+ hybrid orchestrators both receive GitHub events this way. Independent-mode
132
+ orchestrators have no Platform connection and therefore no GitHub-App
133
+ ingress; use a generic webhook source instead. The `<orgId>` segment is
134
+ the KiCI organization ID the source belongs to; the `<appId>` is
135
+ discovered from `X-GitHub-Hook-Installation-Target-ID` at request time and
136
+ is _not_ part of the URL.
137
+
138
+ 4. **Set the webhook secret.** Generate a random hex string (e.g.
139
+ `openssl rand -hex 32`) and save it for step 4 of the orchestrator
140
+ registration below. GitHub uses this secret to HMAC-sign every
141
+ webhook; KiCI rejects mismatches.
142
+
143
+ 5. **Pick permissions.** Minimum required:
144
+
145
+ | Scope | Access | Why |
146
+ | --------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
147
+ | Repository -> Contents | Read | Clone the repo to read the lock file |
148
+ | Repository -> Metadata | Read (auto) | Default for every App; also lets KiCI look up a pull-request author's repository access level for CI trust |
149
+ | Repository -> Pull requests | Read | Match `pull_request` triggers |
150
+ | Repository -> Checks | Read & write | Post KiCI's enriched Check runs |
151
+ | Organization -> Members | Read | (optional, org installs) Receive `organization` / `membership` / `team` events so KiCI's CI-trust permission cache invalidates promptly on access changes |
152
+
153
+ The first four rows cover the core flow (clone, trigger matching,
154
+ Check runs). The **Organization -> Members** row is only relevant if
155
+ you use [CI trust tiers](https://docs.kici.dev/architecture/security/ci-security/)
156
+ on an org-level install — see the event note below.
157
+
158
+ 6. **Subscribe to events.** At minimum: `push`, `pull_request`,
159
+ `check_run`, `check_suite`. Add others (`issues`, `release`, ...) if
160
+ your workflows use those triggers.
161
+
162
+ **For CI trust (optional but recommended on org installs):** also
163
+ subscribe to `member`, `organization`, `membership`, and `team`.
164
+ KiCI caches each pull-request author's repository access level (used
165
+ to decide whether workflow changes take effect immediately or are
166
+ held for approval — see
167
+ [CI security](https://docs.kici.dev/architecture/security/ci-security/)). These
168
+ events let the orchestrator drop stale cache entries the moment a
169
+ contributor's access changes. They are not required for correctness:
170
+ without them the cache simply ages out on its own 15-minute TTL, so a
171
+ permission change can take up to 15 minutes to take effect. The
172
+ `organization` / `membership` / `team` events require the
173
+ **Organization -> Members** read permission and an org-level
174
+ installation; `member` is a repository event covered by the default
175
+ Metadata permission.
176
+
177
+ 7. **Generate a private key.** Scroll to the bottom of the App settings
178
+ and click _Generate a private key_. A `.pem` file downloads —
179
+ store it safely; you cannot redownload it.
180
+
181
+ 8. **Copy the App ID.** It's the numeric ID near the top of the App
182
+ settings page. You'll need it for `--app-id` below.
183
+
184
+ 9. **Install the App on target repos.** Under the App's _Install App_
185
+ tab, install it on the repos (or whole org) that should trigger
186
+ KiCI runs. Re-install to add repos later — this is live and
187
+ revocable without redeploying the App.
188
+
189
+ ### Register the App with the orchestrator
190
+
191
+ With the App ID, private key `.pem`, and webhook secret in hand:
192
+
193
+ ```bash
194
+ kici-admin --url http://<orchestrator-host>:4000 --token $KICI_BOOTSTRAP_ADMIN_TOKEN \
195
+ source add github \
196
+ --name my-org \
197
+ --app-id 12345 \
198
+ --private-key @/path/to/private-key.pem \
199
+ --webhook-secret <the-webhook-secret-from-step-4>
200
+ ```
201
+
202
+ The command prints the routing key (always `github:<appId>`) and the public
203
+ webhook URL to paste into the GitHub App's "Webhook URL" field:
204
+
205
+ ```
206
+ Source added: github:<appId> (my-org)
207
+ Webhook URL: https://<platform-host>/webhook/<orgId>/github
208
+ ↳ Paste this into your GitHub App's "Webhook URL" field.
209
+ ```
210
+
211
+ When the orchestrator runs in independent mode (no Platform connection) the
212
+ URL line reads `(unavailable — this orchestrator runs in independent mode)`,
213
+ because GitHub-App ingress is Platform-relayed. The private key and webhook
214
+ secret are stored encrypted in the orchestrator database under
215
+ `KICI_SECRET_KEY`; no restart needed — the orchestrator accepts webhooks from
216
+ this App immediately.
217
+
218
+ **Secret input modes** (for `--private-key` and `--webhook-secret`):
219
+
220
+ | Mode | Syntax | Example |
221
+ | -------------------- | ----------------------- | --------------------------------------------------------- |
222
+ | Direct value | `--private-key <value>` | `--webhook-secret mysecret` |
223
+ | File (`@` prefix) | `--private-key @<path>` | `--private-key @/path/to/key.pem` |
224
+ | Environment variable | `--from-env <var>` | `--from-env GITHUB_PRIVATE_KEY` |
225
+ | Standard input | `--stdin` | `cat key.pem \| kici-admin source add github --stdin ...` |
226
+
227
+ Use `@file` for private keys — it reads the full PEM including
228
+ newlines without quoting pitfalls.
229
+
230
+ To list and inspect:
231
+
232
+ ```bash
233
+ kici-admin source list # All configured sources
234
+ kici-admin source get-webhook-secret github:12345 # Fetch the secret (for debugging)
235
+ ```
236
+
237
+ For the full CLI reference see the `source` section of the
238
+ [kici-admin CLI reference](https://docs.kici.dev/operator/orchestrator/kici-admin-cli/).
239
+
240
+ ## Routing keys
241
+
242
+ Every GitHub App source has routing key `github:<appId>`. It's the
243
+ identifier every other KiCI surface uses to talk about the source:
244
+
245
+ - `kici-admin source update github:<appId> ...` for rotation / updates
246
+ - `kici-admin source remove github:<appId>` to decommission
247
+ - `kici-admin org-settings global-workflows ... --customer-id <orgId> [--source github:<appId>]` for policy (org-scoped row, optional per-entry source qualifier)
248
+ - The orchestrator's source records and event-log entries key on
249
+ `github:<appId>`; org-level settings key on `customer_id` (one row
250
+ per org)
251
+
252
+ If you install the same App across multiple KiCI orgs, each org has
253
+ its own source record and the orchestrator looks up the right one by
254
+ combining the URL's `<orgId>` with the App ID from the
255
+ `X-GitHub-Hook-Installation-Target-ID` header.
256
+
257
+ ## Display name and slug
258
+
259
+ For a GitHub App source, **GitHub is the source of truth for the displayed
260
+ name**. `--name` is only the name you _request_ when the App is created;
261
+ GitHub assigns the final display name and a URL-safe **slug**
262
+ (`my-org` → `my-org-1` if the name was taken). KiCI captures both at creation
263
+ and shows them in the dashboard **Sources** tab — the display name prominently,
264
+ with the slug as dimmed secondary text.
265
+
266
+ If you later rename the App in GitHub's UI, KiCI keeps the displayed name in
267
+ sync two ways:
268
+
269
+ - **Automatically**, on a daily schedule. The orchestrator re-reads each GitHub
270
+ source's name + slug from GitHub and updates the dashboard if they changed.
271
+ The interval is configurable via `KICI_GITHUB_APP_NAME_REFRESH_INTERVAL_MS`
272
+ (default 24h).
273
+ - **On demand**, with `source refresh`:
274
+
275
+ ```bash
276
+ kici-admin source refresh github:<appId> # one source
277
+ kici-admin source refresh --all # every GitHub source
278
+ ```
279
+
280
+ It prints `old → new` for any name or slug that changed, and is a no-op when
281
+ GitHub already matches what KiCI has stored. Non-GitHub routing keys are
282
+ rejected — name/slug sync applies only to GitHub App sources.
283
+
284
+ ## Self-hosted webhook URL override
285
+
286
+ By default the manifest flow bakes the KiCI Platform webhook endpoint
287
+ (`https://<platform-host>/webhook/<orgId>/github`) into the App. If you run
288
+ your own ingress and want GitHub to deliver events to it instead, pass
289
+ `--webhook-url` when creating the App:
290
+
291
+ ```bash
292
+ kici-admin source add github --manifest --name my-org \
293
+ --webhook-url https://hooks.my-infra.example/github
294
+ ```
295
+
296
+ The supplied URL must be an absolute `https://` URL; it is written into the
297
+ App's webhook configuration **verbatim**. This is the operator asserting "I own
298
+ webhook delivery": KiCI adds **no** ingress at this URL and does **not** receive
299
+ events there — your own infrastructure is responsible for accepting GitHub's
300
+ deliveries and routing them onward. Supplying the flag also decouples App
301
+ creation from platform-mode URL resolution, so it works even in a configuration
302
+ where the auto-resolved KiCI Platform URL is unavailable.
303
+
304
+ ## Global workflows
305
+
306
+ A GitHub App source opts in to org-wide global workflows using the
307
+ org-scoped settings row. Pass `--customer-id <orgId>` (alias `--org`)
308
+ to select the row; on `*-add` mutators, pass `--source github:<appId>`
309
+ when you want a list entry pinned to this specific App rather than
310
+ applying to any source in the org:
311
+
312
+ ```bash
313
+ # Enable global workflows for the org
314
+ kici-admin org-settings global-workflows set-enabled true \
315
+ --customer-id <orgId>
316
+
317
+ # Allow the listed repo as an author for any source in the org
318
+ kici-admin org-settings global-workflows allow-add 'my-org/ci-workflows/*' \
319
+ --customer-id <orgId>
320
+
321
+ # Allow the listed repo as an author only when authored on this App
322
+ kici-admin org-settings global-workflows allow-add 'my-org/ci-workflows/*' \
323
+ --customer-id <orgId> --source github:12345
324
+
325
+ # Deny events from untrusted repos delivered on this App
326
+ kici-admin org-settings global-workflows deny-add 'my-org/contrib/*' \
327
+ --customer-id <orgId> --source github:12345
328
+ ```
329
+
330
+ Global workflows authored in a GitHub App repo can dispatch against
331
+ events from universal-git sources in the same org, and vice versa,
332
+ with each clone using its own source's credentials. See
333
+ [Global workflows](https://docs.kici.dev/architecture/global-workflows/) for the
334
+ policy model and cross-source dispatch contract.
335
+
336
+ ## Check runs
337
+
338
+ Once registered, the App's Check-runs permission lets KiCI post
339
+ enriched Check runs:
340
+
341
+ - `kici/{workflowName}` — overall pass/fail for the workflow
342
+ - `kici/{workflowName}/job/{jobName}` — per-job detail with step progress
343
+ - `kici/{workflowName}/setup` — (optional) build / dependency-install check
344
+
345
+ Step progress, log tails, and source-location annotations are all
346
+ driven by the orchestrator's reporting module; no workflow
347
+ configuration is required beyond installing the App with the
348
+ `checks: write` permission.
349
+
350
+ For architecture details see
351
+ [GitHub checks architecture](https://docs.kici.dev/architecture/webhooks/github-checks/).
352
+
353
+ ## Rotation
354
+
355
+ ### Rotate the webhook secret
356
+
357
+ 1. Generate a new random hex: `openssl rand -hex 32`.
358
+ 2. Update GitHub: _App settings -> Webhook -> Webhook secret_. GitHub
359
+ will sign new deliveries with this immediately.
360
+ 3. Update the orchestrator:
361
+
362
+ ```bash
363
+ kici-admin source update github:12345 --webhook-secret <new-secret>
364
+ ```
365
+
366
+ The orchestrator verifies signatures against every cached secret
367
+ during a dual-secret window, so brief mismatches during rotation
368
+ don't drop deliveries. The HMAC verifier iterates over all stored
369
+ secrets for the routing key.
370
+
371
+ ### Rotate the private key
372
+
373
+ 1. In GitHub's App settings click _Generate a private key_ — this
374
+ does **not** revoke existing keys. Download the new `.pem`.
375
+ 2. Push it to the orchestrator:
376
+
377
+ ```bash
378
+ kici-admin source update github:12345 --private-key @/path/to/new-key.pem
379
+ ```
380
+
381
+ 3. After confirming clones work on the new key, delete the old key
382
+ from GitHub's App settings.
383
+
384
+ ### Decommission
385
+
386
+ ```bash
387
+ kici-admin source remove github:12345
388
+ ```
389
+
390
+ After removal the routing-key row and its secrets are purged; GitHub
391
+ deliveries to the endpoint will be rejected as "Unknown routing key".
392
+ Uninstall the App from GitHub's side separately.
393
+
394
+ ## Troubleshooting
395
+
396
+ **Webhook hits the endpoint but KiCI replies 404 `Unknown
397
+ organization`.** The `<orgId>` segment of the webhook URL doesn't
398
+ match the org that owns the source. Check the URL registered in
399
+ _App settings -> Webhook_ against `kici-admin source list`.
400
+
401
+ **Webhook hits the endpoint but KiCI replies 401 `Invalid
402
+ signature`.** The webhook secret in the App settings doesn't match
403
+ the one stored with the source. Rotate it via the steps above.
404
+
405
+ **Webhook hits the endpoint but KiCI replies 400 `Missing GitHub App
406
+ target headers`.** The request isn't actually from a GitHub App
407
+ (missing `X-GitHub-Hook-Installation-Target-Type: integration` +
408
+ `X-GitHub-Hook-Installation-Target-ID`). If you're test-firing a
409
+ webhook, use the App's _Recent Deliveries_ tab on GitHub to re-send a
410
+ real one.
411
+
412
+ **Webhook arrives but no run fires.** The orchestrator accepted the
413
+ webhook but no workflow registration matched. Causes (in order of
414
+ likelihood): the repo isn't registered with the orchestrator yet
415
+ (push a commit that touches `.kici/kici.lock.json` first), the event
416
+ type isn't one the workflow's triggers list, or
417
+ `global_workflow_denied_repos` filtered out the source repo. Check
418
+ `kici-admin event-log list --routing-key github:12345` and the
419
+ orchestrator logs for `no registrations for event`.
420
+
421
+ **Clone fails with 401 / 403.** The installation token minted from
422
+ the App private key was refused. Usually means the App was uninstalled
423
+ from the repo, or the private key on the orchestrator no longer matches
424
+ the one GitHub knows about (rotate it).
425
+
426
+ **Check runs don't appear on pull requests.** The App is missing the
427
+ `checks: write` permission or wasn't installed on the target repo.
428
+ Re-request permissions in _App settings -> Permissions & events_
429
+ (GitHub will prompt installers to accept the new scope on next visit)
430
+ and confirm the App is installed on that repo.
431
+
432
+ ## See also
433
+
434
+ - [Universal-git provider](https://docs.kici.dev/user/providers/universal-git/) — for Forgejo / Gitea /
435
+ Gogs / GitLab, and for plain-GitHub repos without an App
436
+ - [GitHub checks architecture](https://docs.kici.dev/architecture/webhooks/github-checks/)
437
+ - [Global workflows](https://docs.kici.dev/architecture/global-workflows/)
438
+ - [kici-admin CLI reference](https://docs.kici.dev/operator/orchestrator/kici-admin-cli/)
439
+ - [Event routing](https://docs.kici.dev/operator/event-routing/) — operator-level
440
+ routing-key mechanics
441
+
442
+ ---
443
+
444
+ ## Local filesystem (file://) source
445
+
446
+ Source: https://docs.kici.dev/user/providers/local-file/
447
+
448
+ The **local** provider lets the orchestrator run workflows from a git
449
+ repository that already lives on the agent's filesystem — cloned via a
450
+ `file://` URL rather than fetched from a remote forge. There is no webhook
451
+ from GitHub or GitLab; the operator drives runs explicitly with the
452
+ `kici-admin` CLI or a generated `post-receive` hook.
453
+
454
+ > **This is an operator-curated source, not daily developer CI.** Use it for
455
+ > globally-registered or policy workflows the operator controls — a vendored
456
+ > repo baked into an agent image, a repo synced onto a host out-of-band, a
457
+ > golden internal pipeline. For ordinary per-developer CI driven by pull
458
+ > requests and pushes, use the [GitHub App provider](https://docs.kici.dev/user/providers/github/) or the
459
+ > [universal-git provider](https://docs.kici.dev/user/providers/universal-git/) against a real forge.
460
+
461
+ ## Trust caveat (read first)
462
+
463
+ A local source uses signature verification `none` — there is no remote forge
464
+ to sign the webhook payload, so the orchestrator cannot authenticate the
465
+ trigger. **Only register repos you trust.** Anyone who can reach the
466
+ orchestrator's webhook route for this source, or push to the repo on disk,
467
+ can drive a run. Treat the repo path as a trusted operator input, the same
468
+ way you treat the orchestrator's own configuration.
469
+
470
+ ## Register a source
471
+
472
+ ```bash
473
+ kici-admin source add local \
474
+ --org <orgId> \
475
+ --path /abs/path/to/repo \
476
+ --name my-local-repo
477
+ ```
478
+
479
+ - `--path` must be an **absolute** directory on the agent filesystem. It is
480
+ the base path the orchestrator's lock-file fetcher reads
481
+ (`<path>/.kici/kici.lock.json`) and the base for the `file://` clone the
482
+ agent performs.
483
+ - `--clone-url-base <url>` is optional. By default the agent clones via
484
+ `file://<path>`. Supply a `git://` or `http://` base when the agent does
485
+ **not** share the orchestrator's filesystem and must fetch the repo over a
486
+ git server instead (see "Per-scaler reachability" below).
487
+
488
+ Update the path or name later:
489
+
490
+ ```bash
491
+ kici-admin source update-local <id> --path /new/abs/path
492
+ kici-admin source update-local <id> --name new-name
493
+ ```
494
+
495
+ Remove it:
496
+
497
+ ```bash
498
+ kici-admin source remove <routingKey> --local
499
+ ```
500
+
501
+ List and inspect (local sources render their `repoBasePath`):
502
+
503
+ ```bash
504
+ kici-admin source list --org <orgId>
505
+ kici-admin source get <id>
506
+ ```
507
+
508
+ ## Trigger runs
509
+
510
+ A local repo has no forge to send webhooks, so you trigger runs yourself.
511
+
512
+ **One-shot, by hand:**
513
+
514
+ ```bash
515
+ kici-admin source trigger-local <id>
516
+ ```
517
+
518
+ The command reads the repo's current HEAD ref and commit SHA, builds a
519
+ GitHub-shaped `push` payload, and POSTs it to the orchestrator's generic
520
+ webhook route. Override the ref/sha/event explicitly when needed:
521
+
522
+ ```bash
523
+ kici-admin source trigger-local <id> --event push --ref refs/heads/main --sha <sha>
524
+ ```
525
+
526
+ **On every push, via a hook:**
527
+
528
+ ```bash
529
+ kici-admin source install-hook <id>
530
+ ```
531
+
532
+ This writes a `post-receive` hook into the repo so that every push to it
533
+ triggers a run automatically — the local equivalent of a forge webhook.
534
+
535
+ ## Per-scaler reachability (operator's responsibility)
536
+
537
+ The orchestrator accepts a local source on **any** scaler backend and does
538
+ **not** verify that the repo is actually reachable inside the agent. Making
539
+ the path reachable is the operator's job. On a container or Firecracker
540
+ scaler the orchestrator logs a reachability warning when it registers the
541
+ source, but it does not reject it.
542
+
543
+ | Scaler | How the repo must be reachable in the agent |
544
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
545
+ | bare-metal | The agent runs on the host, so the `--path` host directory is used directly. |
546
+ | container | Bake the repo into the agent image, or bind-mount it at the **same absolute path** the source was registered with. Alternatively register `--clone-url-base` pointing at a git server the container can reach. |
547
+ | Firecracker | The repo must be present on the microVM rootfs at the registered path, or reachable via a `--clone-url-base` git server. |
548
+
549
+ If the path is not reachable inside the agent, the clone fails at run time —
550
+ the run is created and then fails, rather than being silently dropped.
551
+
552
+ ## See also
553
+
554
+ - [Universal-git provider](https://docs.kici.dev/user/providers/universal-git/) — for a remote forge (or any
555
+ `http://` git server) when there is no shared filesystem.
556
+ - [GitHub App provider](https://docs.kici.dev/user/providers/github/) — the flagship source for pull-request
557
+ CI with Checks.
558
+
559
+ ---
560
+
561
+ ## Universal-git provider
562
+
563
+ Source: https://docs.kici.dev/user/providers/universal-git/
564
+
565
+ The **universal-git** provider lets KiCI treat any git forge that speaks a
566
+ GitHub-shaped webhook payload as a first-class source. That covers Forgejo,
567
+ Gitea, Gogs, GitLab, plain GitHub (without the App), and any custom
568
+ webhook-driven forge you can describe in JSONPath.
569
+
570
+ > **Want Check runs on pull requests?** Use the [GitHub App
571
+ > provider](https://docs.kici.dev/user/providers/github/) instead — it clones via short-lived installation
572
+ > tokens and drives KiCI's enriched Checks UI out of the box. The
573
+ > universal-git `github-repo` preset is the right fallback when you
574
+ > can't install an App.
575
+
576
+ The orchestrator:
577
+
578
+ 1. receives the forge's webhook,
579
+ 2. clones the repo via HTTPS (PAT) or SSH (deploy key) to read the lock
580
+ file at `.kici/kici.lock.json`,
581
+ 3. dispatches workflows that match the push / pull_request event.
582
+
583
+ No mirror, no GitHub App, no `checkout: false` escape hatch. The same
584
+ trigger matching, global-workflow policy, and agent execution pipeline
585
+ that back the GitHub App source also serve universal-git sources.
586
+
587
+ > **No shared filesystem between orchestrator and agent?** Universal-git is
588
+ > the right choice for the **remote-agent** case — point it at an `http://`
589
+ > git server and the agent clones over the network. When the repo instead
590
+ > lives on the agent's own filesystem (a vendored / operator-curated repo),
591
+ > use a [local `file://` source](https://docs.kici.dev/user/providers/local-file/) and drive it with the
592
+ > `kici-admin` CLI.
593
+
594
+ ## Which preset do I need?
595
+
596
+ KiCI ships canonical presets so you don't have to spell out JSONPath for
597
+ every forge:
598
+
599
+ | Preset | Forge | Webhook header |
600
+ | ------------- | --------------------------------------- | ---------------- |
601
+ | `forgejo` | Forgejo | `X-Gitea-Event` |
602
+ | `gitea` | Gitea | `X-Gitea-Event` |
603
+ | `gogs` | Gogs | `X-Gogs-Event` |
604
+ | `gitlab-repo` | GitLab (per-project webhooks) | `X-Gitlab-Event` |
605
+ | `github-repo` | Plain GitHub (per-repo webhook, no App) | `X-GitHub-Event` |
606
+ | `custom` | Anything else | You supply it |
607
+
608
+ Pick `custom` only when the forge's payload structure or event header
609
+ deviates from GitHub's — you'll then supply `payloadPaths` and
610
+ `eventMapping` explicitly.
611
+
612
+ ## Create a source (PAT)
613
+
614
+ ```bash
615
+ kici-admin source add generic \
616
+ --org <orgId> \
617
+ --name forgejo-main \
618
+ --verification hmac_sha256 \
619
+ --secret <random-hex> \
620
+ --preset forgejo \
621
+ --git-url-template 'https://forgejo.example.com/{owner}/{name}.git' \
622
+ --credential-ref pat \
623
+ --credential-type pat \
624
+ --credential-user bot-user
625
+ ```
626
+
627
+ Then seed the PAT under the source's own secret scope:
628
+
629
+ ```bash
630
+ # The scope __source__/<sourceId> is the orchestrator's convention for
631
+ # source-level credentials. Use the sourceId printed by `source add`.
632
+ kici-admin secret set <orgId> "__source__/<sourceId>" pat --value "<your-forgejo-pat>"
633
+ ```
634
+
635
+ Finally, configure the forge to deliver webhooks to:
636
+
637
+ ```
638
+ https://<platform-host>/webhook/<orgId>/generic/<source-name>
639
+ ```
640
+
641
+ with the same secret you passed to `--secret`.
642
+
643
+ ## SSH deploy key
644
+
645
+ For SSH instead of HTTPS:
646
+
647
+ 1. **Generate an Ed25519 deploy key.** Ed25519 is the recommended default.
648
+
649
+ ```bash
650
+ ssh-keygen -t ed25519 -N '' -C 'kici-forgejo-deploy-key' -f ~/.ssh/forgejo-deploy-key
651
+ ```
652
+
653
+ This produces `~/.ssh/forgejo-deploy-key` (private, OpenSSH PEM) and
654
+ `~/.ssh/forgejo-deploy-key.pub` (public).
655
+
656
+ 2. **Register the public key as a deploy key on the forge.** On Forgejo
657
+ / Gitea this is _Repository -> Settings -> Deploy Keys -> Add Key_
658
+ (paste the `.pub` contents). On GitLab it's _Settings -> Repository
659
+ -> Deploy keys_. On plain GitHub it's _Settings -> Deploy keys_.
660
+ Read-only access is enough — KiCI only clones.
661
+
662
+ 3. **Capture the forge's host keys** (needed only for
663
+ `--ssh-host-key-policy pinned`):
664
+
665
+ ```bash
666
+ ssh-keyscan -t ed25519,rsa forgejo.example.com > forgejo.known_hosts
667
+ ```
668
+
669
+ Inspect the file before trusting it (compare against what the forge
670
+ publishes in its docs) — this is your one chance to pin the key
671
+ out-of-band rather than trust-on-first-use.
672
+
673
+ 4. **Create the source:**
674
+
675
+ ```bash
676
+ kici-admin source add generic \
677
+ --org <orgId> \
678
+ --name forgejo-ssh \
679
+ --verification hmac_sha256 \
680
+ --secret <random-hex> \
681
+ --preset forgejo \
682
+ --git-url-template 'ssh://git@forgejo.example.com:22/{owner}/{name}.git' \
683
+ --credential-ref deploy-key \
684
+ --credential-type ssh \
685
+ --ssh-host-key-policy pinned \
686
+ --ssh-known-hosts-pem "@/path/to/forgejo.known_hosts"
687
+ ```
688
+
689
+ The `@` prefix on `--ssh-known-hosts-pem` tells the CLI to read the
690
+ file contents.
691
+
692
+ 5. **Store the private key PEM under the source scope:**
693
+
694
+ ```bash
695
+ kici-admin secret set <orgId> "__source__/<sourceId>" deploy-key \
696
+ --value "$(cat ~/.ssh/forgejo-deploy-key)"
697
+ ```
698
+
699
+ The orchestrator materialises this PEM into a tempfile (mode `0600`)
700
+ at every clone and drives `git` with a purpose-built
701
+ `GIT_SSH_COMMAND` (`IdentitiesOnly=yes`, `BatchMode=yes`, plus the
702
+ host-key flags below). The tempdir is cleaned up as soon as the
703
+ clone finishes.
704
+
705
+ **Host-key policy:** `accept-new` (default) auto-trusts the forge on
706
+ first connection (TOFU) and logs a one-time warning. `pinned` sets
707
+ `StrictHostKeyChecking=yes` with `UserKnownHostsFile=<the PEM you
708
+ supplied>` and rejects any host key that doesn't match — use this for
709
+ production supply-chain hardening. `pinned` requires
710
+ `--ssh-known-hosts-pem` (or the equivalent `sshKnownHostsPem` field on
711
+ update); the CLI rejects the request otherwise.
712
+
713
+ **Updating an existing source:** use `kici-admin source update-generic
714
+ <id>` with the same flags to switch an HTTPS/PAT source to SSH, rotate
715
+ the host-key policy, or flip presets. Pass `--clear-git-config` to
716
+ revert the source back to a payload-only generic webhook.
717
+
718
+ ## Credential rotation
719
+
720
+ To rotate a PAT or SSH key, overwrite the value under the same scope +
721
+ key and the next clone picks it up:
722
+
723
+ ```bash
724
+ kici-admin secret set <orgId> "__source__/<sourceId>" pat --value "<new-pat>"
725
+ ```
726
+
727
+ The orchestrator re-reads the secret at each clone. No source update
728
+ needed.
729
+
730
+ ## Global workflows
731
+
732
+ Universal-git sources participate in the org-wide global-workflow model
733
+ exactly like GitHub App sources — a global workflow authored in one
734
+ source can dispatch against pushes from a different source in the same
735
+ org (including across forges), with each clone using its own bundle's
736
+ credentials.
737
+
738
+ Enable and tune the policy via the org-settings CLI. Settings are
739
+ org-scoped (one row per `customer_id`); each list entry can optionally
740
+ pin to a specific source via `--source <routingKey>`:
741
+
742
+ ```bash
743
+ # Enable global workflows for the org
744
+ kici-admin org-settings global-workflows set-enabled true \
745
+ --customer-id <orgId>
746
+
747
+ # Allow authors from any source in the org
748
+ kici-admin org-settings global-workflows allow-add \
749
+ 'forgejo.example.com/ci-workflows/*' \
750
+ --customer-id <orgId>
751
+
752
+ # Allow authors only when the workflow lives on a specific source
753
+ kici-admin org-settings global-workflows allow-add \
754
+ 'forgejo.example.com/ci-workflows/*' \
755
+ --customer-id <orgId> \
756
+ --source "generic:<orgId>:<sourceId>"
757
+
758
+ # Forbid events from a specific source from firing any global workflow
759
+ kici-admin org-settings global-workflows deny-add \
760
+ 'forgejo.example.com/untrusted/*' \
761
+ --customer-id <orgId> \
762
+ --source "generic:<orgId>:<sourceId>"
763
+ ```
764
+
765
+ See [Global workflows](https://docs.kici.dev/architecture/global-workflows/) for the
766
+ policy model (`isWorkflowRepoAllowed` + `isSourceRepoAllowed` +
767
+ `isElevatedAccessAllowed`) and the cross-provider dispatch contract.
768
+
769
+ ## Routing-key collisions
770
+
771
+ When a user has both a GitHub App source and a universal-git source
772
+ targeting the same `owner/repo`, each creates its own registration and
773
+ each fires its own run on a matching push. This is intentional: the two
774
+ sources are independently authenticated and may resolve different lock
775
+ files. If you want deduplication, either:
776
+
777
+ - constrain one side via `global_workflow_denied_repos`, or
778
+ - don't create both sources.
779
+
780
+ ## Troubleshooting
781
+
782
+ **The webhook hits the orchestrator but no run fires.** Check the
783
+ orchestrator log for `Skipping global workflow dispatch` or
784
+ `no registrations for event`. Most common cause: the webhook event
785
+ header doesn't match the preset's `eventMapping`. For `custom` sources,
786
+ make sure the `eventMapping` array includes every value the forge
787
+ actually sends (they can vary by event type).
788
+
789
+ **Clone fails with 401.** The source-scoped secret is missing or
790
+ wrong. Verify with:
791
+
792
+ ```bash
793
+ kici-admin secret list <orgId> "__source__/<sourceId>"
794
+ ```
795
+
796
+ **Clone fails with 403 `default branch` fetch.** The PAT lacks
797
+ read-access to the repo or the SSH deploy key isn't registered on it.
798
+
799
+ **SSH clone fails with host-key rejection.** If you set
800
+ `sshHostKeyPolicy: pinned`, verify the known-hosts PEM matches the
801
+ forge's current key. If you're still using `accept-new`, the orch's
802
+ `~/.ssh/known_hosts` has a stale entry — clear it or flip to `pinned`
803
+ with the right PEM.
804
+
805
+ ---