loki-mode 9.17.2 → 9.18.2

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.
@@ -0,0 +1,542 @@
1
+ # Deploying Loki Mode as a build service
2
+
3
+ You deployed it. Now what starts a build?
4
+
5
+ That is the question this document answers, because it is the one a fresh
6
+ deployment does not answer for you. The pods are running, `/health` returns
7
+ 200, and nothing is happening. Three things can start a build, and you have to
8
+ set up at least one of them deliberately.
9
+
10
+ - [The shape of a deployment](#the-shape-of-a-deployment)
11
+ - [Path 1: a GitHub webhook](#path-1-a-github-webhook)
12
+ - [Path 2: manual enqueue](#path-2-manual-enqueue)
13
+ - [Path 3: the API endpoint](#path-3-the-api-endpoint)
14
+ - [Triggers that do not exist yet](#triggers-that-do-not-exist-yet)
15
+ - [Kubernetes (Helm)](#kubernetes-helm)
16
+ - [Single node (Docker Compose)](#single-node-docker-compose)
17
+ - [Things that will bite you](#things-that-will-bite-you)
18
+
19
+ ---
20
+
21
+ ## The shape of a deployment
22
+
23
+ Two processes and a queue.
24
+
25
+ ```
26
+ GitHub --webhook--> receiver --enqueue--> Redis --pop--> worker --> loki start
27
+ (trigger-server.py) (queue-consumer.sh)
28
+ ```
29
+
30
+ The **receiver** (`autonomy/trigger-server.py`) validates an HMAC signature and
31
+ pushes an item onto a queue. It never runs a build, and it holds no provider
32
+ credential. If someone compromises the receiver, they cannot spend your
33
+ provider budget.
34
+
35
+ The **worker** (`autonomy/queue-consumer.sh`) pulls one item at a time and runs
36
+ `loki start <spec>`. This is where builds happen, where the money is spent, and
37
+ where your source code gets checked out. Worker replica count is the scaling
38
+ knob.
39
+
40
+ Separating them is what lets a webhook storm cost you a full queue instead of a
41
+ hundred concurrent builds.
42
+
43
+ ---
44
+
45
+ ## Path 1: a GitHub webhook
46
+
47
+ This is the default path and the one most deployments want.
48
+
49
+ ### 1. Get a secret into the deployment
50
+
51
+ The receiver requires a webhook secret. Without one it starts (so `/health`
52
+ stays up for your probes) and rejects **every** webhook with 503. It never
53
+ silently accepts unauthenticated builds. Generate one:
54
+
55
+ ```bash
56
+ openssl rand -hex 32
57
+ ```
58
+
59
+ Helm:
60
+
61
+ ```bash
62
+ helm upgrade --install loki helm/loki-mode \
63
+ --set secrets.githubWebhookSecret='<that value>' \
64
+ --set secrets.anthropicApiKey='<your key>'
65
+ ```
66
+
67
+ Better, for anything real: set `secrets.create=false` and
68
+ `secrets.existingSecret=<name>` and manage the Secret with sealed-secrets,
69
+ External Secrets Operator or a Vault injector. Values passed with `--set` land
70
+ in Helm release history and often in a git-tracked values file.
71
+
72
+ Compose: put both in `.env` next to `docker-compose.yml`.
73
+
74
+ ```
75
+ GITHUB_WEBHOOK_SECRET=<that value>
76
+ ANTHROPIC_API_KEY=<your key>
77
+ ```
78
+
79
+ ### 2. Expose the receiver to GitHub
80
+
81
+ GitHub has to reach `POST /webhook` from the internet. The chart ships an
82
+ Ingress, off by default:
83
+
84
+ ```bash
85
+ helm upgrade --install loki helm/loki-mode \
86
+ --set receiver.ingress.enabled=true \
87
+ --set receiver.ingress.host=loki.example.com \
88
+ --set receiver.ingress.className=nginx
89
+ ```
90
+
91
+ Terminate TLS there. On Compose the receiver binds to `127.0.0.1:7373`
92
+ deliberately: put a reverse proxy in front rather than publishing an
93
+ unencrypted webhook endpoint on every interface the host has.
94
+
95
+ ### 3. Create the webhook
96
+
97
+ In the repository: **Settings -> Webhooks -> Add webhook**.
98
+
99
+ | Field | Value |
100
+ |---|---|
101
+ | Payload URL | `https://loki.example.com/webhook` |
102
+ | Content type | `application/json` |
103
+ | Secret | the value from step 1 |
104
+ | SSL verification | Enable |
105
+ | Events | **Let me select individual events** |
106
+
107
+ Now the part that decides what your deployment costs. Tick:
108
+
109
+ - **Issues** -- an issue being opened starts a build.
110
+ - **Workflow runs** -- a failed CI run starts a repair build.
111
+
112
+ Leave **Pull requests** unticked unless you have decided you want it. See
113
+ below.
114
+
115
+ ### 4. What actually fires
116
+
117
+ Only these three combinations do anything. Every other event and action is
118
+ logged and ignored.
119
+
120
+ | Event | Action | What runs |
121
+ |---|---|---|
122
+ | `issues` | `opened` | `loki start owner/repo#N --pr --detach` |
123
+ | `pull_request` | `synchronize` | `loki start owner/repo#N --detach` |
124
+ | `workflow_run` | `completed` with `conclusion=failure` | `loki start --detach` |
125
+
126
+ Note what is **not** there. Labelling an issue does nothing. Commenting on an
127
+ issue does nothing. Opening a pull request does nothing -- for PRs it is
128
+ `synchronize`, meaning new commits pushed to an existing PR.
129
+
130
+ ### Why the PR trigger is off by default
131
+
132
+ `pull_request`/`synchronize` fires on **every push to every open PR**. On a
133
+ repository with a handful of active PRs and a normal rebase-and-force-push
134
+ habit, that is dozens of builds a day that nobody asked for. It is the easiest
135
+ way to surprise a team with a bill.
136
+
137
+ Turn it on when you have decided you want it:
138
+
139
+ ```bash
140
+ helm upgrade --install loki helm/loki-mode --set triggers.pullRequest=true
141
+ ```
142
+
143
+ **Read this carefully, because the value alone does not enforce anything.** The
144
+ receiver dispatches on whatever GitHub delivers; it does not consult an
145
+ allowlist before acting. `triggers.pullRequest` renders a ConfigMap that
146
+ records your intent and gives you something to diff when someone asks why a
147
+ build started. What actually keeps PR builds from firing is **not subscribing
148
+ the webhook to Pull request events** in step 3.
149
+
150
+ If you tick "Pull requests" in GitHub, PR builds will run no matter what
151
+ `triggers.pullRequest` says.
152
+
153
+ ### 5. Confirm it works
154
+
155
+ GitHub records every delivery. **Settings -> Webhooks -> your webhook ->
156
+ Recent Deliveries** shows request and response for each one.
157
+
158
+ | Response | Meaning |
159
+ |---|---|
160
+ | `202` `{"status":"queued"}` | Accepted and enqueued. This is success. |
161
+ | `401 invalid signature` | Secret mismatch between GitHub and the deployment. |
162
+ | `503 webhook secret not configured` | The receiver has no secret. Step 1. |
163
+ | `503 server busy, retry later` | Dispatch queue full; load shed on purpose. |
164
+ | `200 {"status":"duplicate"}` | Redelivery of a delivery ID already seen. |
165
+ | `200 skipped (action=...)` | Delivered fine, but not an action that fires. |
166
+
167
+ Then watch the worker:
168
+
169
+ ```bash
170
+ kubectl logs -l app.kubernetes.io/component=worker -f # Kubernetes
171
+ docker compose --profile service logs -f worker # Compose
172
+ ```
173
+
174
+ You are looking for `[queue-consumer] starting build: spec=owner/repo#N`.
175
+
176
+ ---
177
+
178
+ ## Path 2: manual enqueue
179
+
180
+ The queue is a plain Redis list. Pushing to it starts a build, with no GitHub
181
+ involved. This is the fastest way to prove a deployment works end to end, and
182
+ it is how you re-drive a build that failed.
183
+
184
+ Kubernetes:
185
+
186
+ ```bash
187
+ kubectl exec -it deploy/loki-redis-master -- \
188
+ redis-cli RPUSH loki-builds 'owner/repo#123'
189
+ ```
190
+
191
+ Compose:
192
+
193
+ ```bash
194
+ docker compose --profile service exec redis \
195
+ redis-cli RPUSH loki-builds 'owner/repo#123'
196
+ ```
197
+
198
+ A work item is anything `loki start` accepts: a GitHub issue ref
199
+ (`owner/repo#123`), a path to a PRD, a one-line brief, or a JSON object
200
+ `{"spec": "..."}`. An item beginning with `-` is rejected rather than being
201
+ parsed as a flag.
202
+
203
+ Check queue depth, which is also your "are the workers keeping up" metric:
204
+
205
+ ```bash
206
+ redis-cli LLEN loki-builds
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Path 3: the API endpoint
212
+
213
+ `POST /jobs` with a bearer token, for submitting a build from your own tooling
214
+ without involving GitHub at all.
215
+
216
+ Set `secrets.apiToken` (Helm) or `LOKI_API_TOKEN` (Compose). If it is unset the
217
+ receiver still starts, and rejects every `/jobs` request with 503 -- the same
218
+ fail-closed rule the webhook path follows.
219
+
220
+ ```bash
221
+ curl -X POST https://loki.example.com/jobs \
222
+ -H "Authorization: Bearer $LOKI_API_TOKEN" \
223
+ -H "Content-Type: application/json" \
224
+ -d '{"spec": "owner/repo#123"}'
225
+ # -> 202 {"id": "...", "status": "queued"}
226
+
227
+ curl -H "Authorization: Bearer $LOKI_API_TOKEN" \
228
+ https://loki.example.com/jobs/<id>
229
+ ```
230
+
231
+ It is a **separate credential from the webhook HMAC** on purpose. The HMAC
232
+ authenticates GitHub; the bearer token authenticates a human operator. Holding
233
+ one must never grant the other, so they rotate on different schedules and a
234
+ leaked API token does not let the holder forge GitHub webhooks.
235
+
236
+ The token can also be read from a mounted file via `LOKI_API_TOKEN_FILE`, which
237
+ is the better option if you inject secrets as files rather than env vars.
238
+
239
+ ---
240
+
241
+ ## Triggers that do not exist yet
242
+
243
+ Documented so you do not go looking for them:
244
+
245
+ - **Issue labelled** (add a `loki` label to start a build). Not implemented.
246
+ The `issues` handler acts on `opened` only.
247
+ - **Issue comment commands** (`/loki build`). Not implemented. The receiver has
248
+ no `issue_comment` handler; GitHub will deliver the event and get back
249
+ `unsupported event: issue_comment`.
250
+
251
+ Both are reasonable and neither exists in this release. Path 2 covers the same
252
+ ground manually today.
253
+
254
+ ---
255
+
256
+ ## Kubernetes (Helm)
257
+
258
+ No `helm dependency build` step: the chart has no subchart dependencies. Redis
259
+ is a plain Deployment on `redis:7-alpine`, the same image `docker-compose.yml`
260
+ uses.
261
+
262
+ The default image tag is the chart's `appVersion`, which tracks `VERSION` and so
263
+ names a tag that exists only once the release pipeline has published it. If you
264
+ install from a source tree whose `VERSION` is ahead of Docker Hub, pods fail on
265
+ `ImagePullBackOff` -- add `--set image.tag=<a published version>`.
266
+
267
+ ```bash
268
+ helm upgrade --install loki helm/loki-mode \
269
+ --namespace loki --create-namespace \
270
+ --set secrets.githubWebhookSecret='<hmac>' \
271
+ --set secrets.anthropicApiKey='<key>' \
272
+ --set worker.replicaCount=3
273
+ ```
274
+
275
+ Preview before applying:
276
+
277
+ ```bash
278
+ helm template loki helm/loki-mode | less
279
+ ```
280
+
281
+ ### The values that matter
282
+
283
+ | Value | Default | Why you would change it |
284
+ |---|---|---|
285
+ | `worker.replicaCount` | `2` | The scaling knob. One build per replica. |
286
+ | `worker.terminationGracePeriodSeconds` | `7200` | Must exceed your p99 build time. |
287
+ | `triggers.pullRequest` | `false` | Cost. See above -- and subscribe accordingly. |
288
+ | `queue.backend` | `redis` | `file` gives at-least-once, needs an RWX volume. |
289
+ | `networkPolicy.enabled` | `false` | Turn on if your CNI enforces policy. |
290
+ | `secrets.existingSecret` | `""` | Use a real secret manager. |
291
+
292
+ ### Verify your install
293
+
294
+ Rendering valid YAML is not evidence that a pod starts. Run these after every
295
+ install.
296
+
297
+ Everything in this section was executed against a real cluster (kind v0.31.0,
298
+ Kubernetes v1.35.0) on the `asklokesh/loki-mode:9.17.2` image, except the two
299
+ items explicitly marked UNVERIFIED.
300
+
301
+ **1. Everything reached Ready.** `--wait` already fails the install if not, but
302
+ check what is running:
303
+
304
+ ```bash
305
+ kubectl get pods -n loki-verify
306
+ # loki-loki-mode-receiver-... 1/1 Running
307
+ # loki-loki-mode-receiver-... 1/1 Running
308
+ # loki-loki-mode-redis-... 1/1 Running
309
+ # loki-loki-mode-worker-... 1/1 Running
310
+ ```
311
+
312
+ **2. The receiver is actually serving.** This is what `helm test` is for:
313
+
314
+ ```bash
315
+ helm test loki -n loki-verify --logs
316
+ # Phase: Succeeded
317
+ # GET http://loki-loki-mode-receiver:80/health -> 200 {"status": "ok", "service": "loki-trigger-server"}
318
+ # PASS: receiver is serving
319
+ ```
320
+
321
+ The test asserts on the response body, not just the status code, so a proxy or
322
+ a wrong backend answering 200 still fails it.
323
+
324
+ **3. The receiver picked up its secret.** `secret_configured: true` is the
325
+ difference between a working webhook and one that 503s every delivery:
326
+
327
+ ```bash
328
+ kubectl port-forward -n loki-verify svc/loki-loki-mode-receiver 18080:80 &
329
+ curl -s http://127.0.0.1:18080/status
330
+ # {"status": "running", "dry_run": false, "port": 7373,
331
+ # "enabled_events": [...], "secret_configured": true}
332
+ ```
333
+
334
+ **4. The worker can reach the queue.** A worker that cannot reach Redis sits in
335
+ its poll loop forever and looks perfectly healthy:
336
+
337
+ ```bash
338
+ W=$(kubectl get pod -n loki-verify -l app.kubernetes.io/component=worker \
339
+ --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}')
340
+ kubectl exec -n loki-verify "$W" -- sh -c 'redis-cli -u "$LOKI_QUEUE_URL" ping'
341
+ # PONG
342
+ ```
343
+
344
+ **5. A queued item actually gets picked up.** The end-to-end check:
345
+
346
+ ```bash
347
+ R=$(kubectl get pod -n loki-verify -l app.kubernetes.io/component=redis -o jsonpath='{.items[0].metadata.name}')
348
+ kubectl exec -n loki-verify "$R" -- redis-cli RPUSH loki-builds 'smoke-test-spec'
349
+ sleep 10
350
+ kubectl exec -n loki-verify "$R" -- redis-cli LLEN loki-builds # 0 == claimed
351
+ kubectl logs -n loki-verify "$W"
352
+ # [queue-consumer] starting build: spec=smoke-test-spec
353
+ ```
354
+
355
+ `starting build:` is the line that matters. With a placeholder API key the
356
+ build then fails, which is expected -- you are verifying that the item was
357
+ dequeued and `loki start` was invoked, not that the build succeeded.
358
+
359
+ Watch for one thing here: if the build exits 2, the consumer treats that as a
360
+ fatal configuration error and exits, and the pod restarts. `kubectl logs
361
+ --previous` shows what the previous container did.
362
+
363
+ **6. Confirm the hardening actually applied.** Cluster policy can override what
364
+ you set:
365
+
366
+ ```bash
367
+ kubectl exec -n loki-verify "$W" -- id
368
+ # uid=1000(loki) gid=1000(loki)
369
+
370
+ kubectl get deploy -n loki-verify loki-loki-mode-worker \
371
+ -o jsonpath='{.spec.template.spec.terminationGracePeriodSeconds}'
372
+ # 7200
373
+ ```
374
+
375
+ **7. NetworkPolicy enforcement -- test it, do not assume it.**
376
+
377
+ ```bash
378
+ kubectl exec -n loki-verify "$W" -- curl -s --max-time 5 http://169.254.169.254/
379
+ ```
380
+
381
+ **A timeout here does not prove the policy works.** On a laptop cluster there
382
+ is no metadata service to reach, so the request times out either way. Run the
383
+ control before believing it:
384
+
385
+ ```bash
386
+ kubectl delete networkpolicy -n loki-verify loki-loki-mode-worker
387
+ kubectl exec -n loki-verify "$W" -- curl -s --max-time 5 http://169.254.169.254/
388
+ # Same timeout with the policy deleted => the policy was never what blocked it.
389
+ helm upgrade loki helm/loki-mode -n loki-verify --reuse-values # restore
390
+ ```
391
+
392
+ That control was run on kind, whose default CNI (kindnet) does **not** enforce
393
+ NetworkPolicy: the result was identical with and without the policy. The object
394
+ applied cleanly and rendered the correct `except` for 169.254.169.254, and
395
+ nothing enforced it. This is the silent no-op described above, observed rather
396
+ than assumed.
397
+
398
+ **UNVERIFIED:** that the metadata endpoint is genuinely blocked on a
399
+ policy-enforcing CNI (Calico, Cilium). That needs a cluster with one, plus a
400
+ real metadata service. Run the delete-and-retry control there: on an enforcing
401
+ CNI the two results differ.
402
+
403
+ **UNVERIFIED:** the GitHub webhook path end to end. Delivery from GitHub to
404
+ `/webhook` with a real HMAC signature was not exercised, since it needs a
405
+ publicly reachable Ingress. The receiver's HMAC validation itself is covered by
406
+ the repository's test suite.
407
+
408
+ ### Worker scaling is a tenancy decision
409
+
410
+ Each worker pod runs exactly one build at a time onto an `emptyDir` that is
411
+ created with the pod and destroyed with it. That is deliberate: one submitter's
412
+ checkout is never readable by the next build.
413
+
414
+ Adding replicas adds parallel builds without breaking that. But **any worker
415
+ can claim any queue item**, so all your submitters share one trust boundary. If
416
+ they are not mutually trusting, replicas are not enough -- run a separate
417
+ release per tenant, each with its own `queue.key` and namespace.
418
+
419
+ This is the same reasoning behind one-user-per-runner guidance for self-hosted
420
+ CI runners. A shared build service without it leaks source between tenants.
421
+
422
+ ### Egress and the metadata endpoint
423
+
424
+ `networkPolicy.enabled=true` applies default-deny egress to the worker, with an
425
+ explicit exclusion for `169.254.169.254`.
426
+
427
+ That address needs naming specifically because it is **link-local**: it is not
428
+ in any pod CIDR, service CIDR or VPC subnet, so a policy written in terms of
429
+ subnets never touches it. Reaching it from inside a build returns the node's
430
+ instance credentials, which turns an ordinary build into access to your cloud
431
+ account.
432
+
433
+ ```bash
434
+ helm upgrade --install loki helm/loki-mode \
435
+ --set networkPolicy.enabled=true \
436
+ --set 'networkPolicy.allowedEgressCIDRs={0.0.0.0/0}'
437
+ ```
438
+
439
+ Every allow rule carves out `169.254.0.0/16`, so even the wide-open CIDR above
440
+ cannot reopen it by accident.
441
+
442
+ **Verify rather than assume.** A NetworkPolicy is enforced by your CNI. On a
443
+ cluster whose CNI has no policy support, the API server accepts the object and
444
+ nothing enforces it -- no error, no warning. `kubectl get networkpolicy`
445
+ showing your policy is not evidence it works:
446
+
447
+ ```bash
448
+ kubectl exec -it deploy/loki-worker -- \
449
+ curl -s --max-time 5 http://169.254.169.254/ && echo "NOT BLOCKED" || echo "blocked"
450
+ ```
451
+
452
+ It also stops at the cluster edge. Filtering traffic once it has left the
453
+ cluster is a NAT gateway, firewall or proxy you run.
454
+
455
+ ---
456
+
457
+ ## Single node (Docker Compose)
458
+
459
+ ```bash
460
+ cp .env.example .env
461
+ # set GITHUB_WEBHOOK_SECRET and ANTHROPIC_API_KEY
462
+
463
+ docker compose --profile service up -d
464
+ docker compose --profile service ps
465
+ ```
466
+
467
+ The `service` profile is what separates the build service from the one-shot
468
+ path. Without it, `docker compose run loki start prd.md` still does exactly
469
+ what it always did -- one build, right now, in the current directory.
470
+
471
+ Scale workers:
472
+
473
+ ```bash
474
+ docker compose --profile service up -d --scale worker=4
475
+ ```
476
+
477
+ Same tenancy caveat as Kubernetes: each worker gets its own named volume, and
478
+ any worker can claim any item. Scale within one trust boundary.
479
+
480
+ ---
481
+
482
+ ## Things that will bite you
483
+
484
+ **The grace period is a whole build, not a cleanup window.** On SIGTERM the
485
+ consumer lets the current build **run to completion** before exiting. At the
486
+ Kubernetes default of 30s (Docker's is 10s), every rolling update, node drain,
487
+ autoscaler scale-in and spot reclaim SIGKILLs a build mid-flight. The chart
488
+ sets `7200` and Compose sets `2h`. Set yours above your p99 build time.
489
+
490
+ **The Redis queue is at-most-once.** The shipped consumer pops an item and then
491
+ runs it. There is no visibility timeout and no dead-letter requeue, so if a
492
+ worker dies mid-build, that build is gone from the queue and **nothing retries
493
+ it** -- no error, no alert, just a build that never finishes. This is why the
494
+ grace period is measured in hours. If you need real at-least-once delivery, use
495
+ `queue.backend=file` (a crashed build leaves its item in `processing/` for a
496
+ human to re-drive) or bring a broker that has it and override `queue.command`
497
+ with your own consumer. SQS, Pub/Sub, RabbitMQ and Kafka are documented as
498
+ bring-your-own; they are not implemented here.
499
+
500
+ **No credentials in the image.** Every credential is injected at pod start from
501
+ a Secret or `.env`. A credential baked into a shared image is readable by every
502
+ job that image ever runs, including builds from submitters who should never
503
+ have had it, and rotating it means rebuilding and redeploying everything that
504
+ consumes it.
505
+
506
+ **A 200 response does not mean a build started.** `200 skipped (action=...)`
507
+ means the webhook was delivered and authenticated perfectly, and then did
508
+ nothing because it was not one of the three firing combinations. Only `202
509
+ queued` means a build is coming. Check worker logs, not just the delivery
510
+ response.
511
+
512
+ **Nothing here rate-limits spend.** The receiver's bounded queue caps
513
+ *in-flight dispatches*, not builds per day. Watch queue depth (`LLEN
514
+ loki-builds`) and worker logs, and turn `triggers.pullRequest` on only
515
+ deliberately.
516
+
517
+ ---
518
+
519
+ ## Reference
520
+
521
+ | Env var | Where | Meaning |
522
+ |---|---|---|
523
+ | `GITHUB_WEBHOOK_SECRET` | receiver | HMAC-SHA256 webhook secret. Required. |
524
+ | `LOKI_API_TOKEN` | receiver | Bearer token for `POST /jobs` (not yet live). |
525
+ | `ANTHROPIC_API_KEY` | worker | Provider credential. Worker only. |
526
+ | `GITHUB_TOKEN` | worker | Used by builds for push / PR creation. |
527
+ | `LOKI_QUEUE_BACKEND` | both | `redis` or `file`. |
528
+ | `LOKI_QUEUE_KEY` | both | Redis list key. Default `loki-builds`. |
529
+ | `LOKI_QUEUE_URL` | both | Redis connection URL. |
530
+ | `LOKI_QUEUE_DIR` | worker | File-backend root. `file` backend only. |
531
+ | `LOKI_QUEUE_ONESHOT` | worker | `1` processes one item and exits (KEDA). |
532
+ | `LOKI_QUEUE_POLL_SEC` | worker | File-backend empty-poll sleep. |
533
+ | `LOKI_QUEUE_BLOCK_SEC` | worker | Redis `BLPOP` block timeout. |
534
+
535
+ | Receiver route | Purpose |
536
+ |---|---|
537
+ | `GET /health` | Liveness and readiness. Does not touch the queue. |
538
+ | `GET /status` | Port, dry-run state, whether a secret is configured. |
539
+ | `POST /webhook` | The GitHub webhook endpoint. |
540
+
541
+ Source: `autonomy/trigger-server.py`, `autonomy/queue-consumer.sh`,
542
+ `helm/loki-mode/values.yaml`.