Skip to content

feat: add Helm chart for standalone AI Gateway#27256

Draft
pawbana wants to merge 2 commits into
mainfrom
pawel/aigov-318-create-helm-chart-for-standalone-ai-gateway
Draft

feat: add Helm chart for standalone AI Gateway#27256
pawbana wants to merge 2 commits into
mainfrom
pawel/aigov-318-create-helm-chart-for-standalone-ai-gateway

Conversation

@pawbana

@pawbana pawbana commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

This PR introduces a new coder-ai-gateway Helm chart.

It deploys the Coder AI Gateway as a standalone Kubernetes workload, and wires it into the existing build and release pipelines.

New helm/ai-gateway chart:

  • Deploys the AI Gateway as a Deployment using the shared libcoder library chart
  • Reads an AI Gateway key from an existing Kubernetes Secret mounted at /etc/coder/ai-gateway-auth/key
  • Connects to Coder via an explicit aigateway.coderURL or a constructed in-cluster Service URL from aigateway.coderService
  • Exposes the data-plane listener on port 4001 and a Prometheus metrics endpoint on port 2112
  • Supports optional listener TLS (aigateway.listenerTLS) and Coder-facing mTLS/CA (aigateway.coderTLS)
  • Includes optional Service (ClusterIP/NodePort/LoadBalancer), Ingress, and Gateway API HTTPRoute resources
  • Validates required fields and rejects conflicting configurations at render time (e.g. missing key secret, invalid URL scheme, Ingress without a host, HTTPRoute without the required CRD)
  • Prevents user-supplied coder.env from overriding chart-owned environment variables
  • Supports extraTemplates for additional manifests rendered within the release

Release pipeline integration:

  • build/ai-gateway_helm_<version>.tgz is now signed and uploaded as a release artifact in both release.yaml and tag-and-release.yaml
  • The chart is published to gs://helm.coder.com/v2, merged into the Helm repo index, and pushed to oci://ghcr.io/coder/chart

Build and tooling:

  • scripts/helm.sh now accepts ai-gateway as a valid --chart value
  • Makefile includes ai-gateway in the charts list, golden file generation, and golden file cleanup targets
  • helm/Makefile adds a lint/helm/ai-gateway target with the required lint flags

Tests:

  • A full golden-file test suite covers default values, cross-namespace service URLs, TLS, networking (Ingress + HTTPRoute), NodePort, custom workload configuration, and all validation error cases

@linear-code

linear-code Bot commented Jul 15, 2026

Copy link
Copy Markdown

AIGOV-318

pawbana commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@pawbana pawbana force-pushed the pawel/aigov-318-create-helm-chart-for-standalone-ai-gateway branch 4 times, most recently from 4e23a5a to d65696c Compare July 15, 2026 10:09
@pawbana pawbana changed the title tmp-commit feat: add heml chart for standalone AI Gateway Jul 15, 2026
@pawbana pawbana force-pushed the pawel/aigov-318-create-helm-chart-for-standalone-ai-gateway branch from d65696c to 531f6b8 Compare July 15, 2026 10:40
@pawbana

pawbana commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-15 17:56 UTC by @pawbana
Spend: $51.54 / $100.00

Review history
  • R1 (2026-07-15): 16 reviewers, 4 Nit, 6 Note, 1 P2, 10 P3, COMMENT. Review
  • R2 (2026-07-15): 13 reviewers, 8 Nit, 6 Note, 1 P2, 12 P3, 1 P4, COMMENT. Review

deep-review v0.9.0 | Round 2 | cc11c8a..2f1347a

Last posted: Round 2, 28 findings (1 P2, 12 P3, 1 P4, 8 Nit, 6 Note), COMMENT. Review

Finding inventory

Finding inventory - PR #27256

Law analysis

R1: Effective +2469 -7 (40 files), head 531f6b8. R2: Effective +3054 -7 (51 files, 1001 production / 2053 test), head 2f1347a. Verdict: Don't split (advisory), unchanged both rounds. Growth is schema + docs + golden fixtures within the single concern.

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 Nit Dropped by orchestrator (empirically disproven; copy is load-bearing) tests/chart_test.go:75 Redundant tc := tc loop-variable copy R1 Netero No
CRF-2 P3 Author fixed (2f1347a) tests/chart_test.go:23 Six of 13 fail validation branches have no test case R1 Netero, Mafu-san Yes
CRF-3 P3 Author fixed (2f1347a) tests/chart_test.go:123 HTTPRoute CRD-missing guard structurally untestable (harness hardcodes --api-versions) R1 Netero Yes
CRF-4 P2 Author fixed (2f1347a) templates/_aigateway.tpl:125 Liveness probe with no startupProbe crashloops pod while coderd unreachable at startup R1 Mafuuu P2, Pariston P2, Kite P2 Yes
CRF-5 P3 Author fixed (2f1347a) templates/_helpers.tpl:19 renderTemplate narrows extraTemplates to strings only; diverges from libcoder + sibling charts R1 Robin P3, Zoro P3 Yes
CRF-6 P3 Author fixed (2f1347a) templates/_helpers.tpl:71 Chart-owned env $owned list is hand-maintained duplicate of containerspec; fails open on drift R1 Meruem P3, Hisoka Note, Chopper Note Yes
CRF-7 P3 Author fixed (2f1347a) templates/_aigateway.tpl:87 CODER_AI_GATEWAY_KEY unguarded; user coder.env value crashes container ("mutually exclusive") R1 Mafuuu P3 Yes
CRF-8 P3 Author fixed (2f1347a) templates/NOTES.txt:5 NOTES/README print in-cluster Service URL unconditionally even when service.enabled=false R1 Mafuuu P3, Chopper P3, Hisoka Note, Bisky Note Yes
CRF-9 P3 Author contested R2 (docs added, validation rejected) templates/ingress.yaml:32 listenerTLS + ingress/httproute renders plaintext-to-TLS backend; no validation/warning R1 Hisoka P3, Mafuuu Note, Meruem Note Yes
CRF-10 P3 Author fixed (2f1347a) templates/_aigateway.tpl:30 automountServiceAccountToken suppression only on created SA; disableCreate path mounts unused token R1 Kurapika P3 Yes
CRF-11 P3 Author fixed (2f1347a) templates/_helpers.tpl:10 coderURL namespace substitution untested under non-default release namespace R1 Bisky P3 Yes
CRF-12 P3 Author fixed (2f1347a) values.yaml:188 service.enabled diverges from chart's own enable convention and sibling coder.service.enable R1 Gon P2, Zoro Nit, Luffy Nit Yes
CRF-13 Nit Author fixed (2f1347a) templates/_aigateway.tpl:27 Inline pod-template component annotation duplicates coder.componentAnnotation override R1 Gon P3, Zoro Nit Yes
CRF-14 Nit Author fixed (2f1347a) templates/_aigateway.tpl:125 Probes hardcoded, no values surface; coder chart exposes probe knobs R1 Pariston Nit Yes
CRF-15 Nit Author fixed (2f1347a) templates/_helpers.tpl:29 0.1.0 install-from-Git sentinel duplicates libcoder's coder.image tag check R1 Meruem Nit Yes
CRF-16 Nit Author fixed (2f1347a) commit/PR title Title misspells "helm" as "heml"; lands in main on squash R1 Leorio Nit, Mafu-san Nit Body
CRF-17 Nit Author fixed (2f1347a) values.yaml:186 Comment "for port 4001" contradicts default service.port 80 R1 Gon Nit Yes
CRF-18 Note Author accepted R2 (conscious choice, matches sibling charts) templates/_aigateway.tpl:91 Metrics listener always-on, unauthenticated on 0.0.0.0:2112, no toggle, no NetworkPolicy R1 Kurapika Note, Luffy Note Yes
CRF-19 Note Author fixed (2f1347a) templates/_aigateway.tpl:31 terminationGracePeriodSeconds (300) equals binary shutdown timeout; SIGKILL cuts in-flight streams R1 Mafuuu Note Yes
CRF-20 Note Author fixed (2f1347a) templates/service.yaml:32 nodePort rendered regardless of service.type; conflict only caught at apply, not render R1 Kite Note Yes
CRF-21 Note Author fixed (2f1347a) values.yaml:113 Default resources inherited from libcoder (2000m/4096Mi) heavy for a forwarding gateway R1 Kite Note Yes
CRF-22 Note Author accepted R2 (conscious choice) templates/_aigateway.tpl:130 Readiness probe ties Service membership to coderd DRPC; coderd blip drops all replicas at once R1 Pariston Note Yes
CRF-23 Note Author fixed (2f1347a) README.md:75 README hardcodes http:// proxy target; scheme is https when listenerTLS set R1 Leorio Note Yes
CRF-24 Note Open (informational, not posted) tests/chart_test.go:121 Test harness is a third near-identical copy of coder/provisioner harnesses R1 Robin Note, Zoro Note No
CRF-25 Note Open (informational, not posted) tests/testdata/owned_env.yaml:1 Chart-owned env guard tested for only 1 of 10 protected variables R1 Bisky Note No
CRF-26 Note Open (informational, not posted) templates/_aigateway.tpl:82 Ports 4001/2112 hardcoded and repeated across templates/NOTES/README R1 Gon Note No
CRF-27 P3 Open templates/_helpers.tpl:101 Env override guard scans only coder.env, not coder.envFrom; CRF-7 class left open, "cannot be overridden" promise false for envFrom R2 Hisoka P3 Yes
CRF-28 P3 Open values.yaml:119 startupProbe/livenessProbe comment misdirects: following it while enabling liveness reintroduces CRF-4 crashloop; liveness-off also removes restart-on-hang R2 Mafuuu P3, Pariston Note, Meruem Note Yes
CRF-29 P4 Open templates/_aigateway.tpl:112 livenessProbe render branch has zero golden coverage (only case sets enabled:false) R2 Bisky P4 Yes
CRF-30 Nit Open values.yaml:194 Secret refs inconsistent: keySecret/caSecret/clientSecret use .name, listenerTLS uses .secretName R2 Gon Nit Yes
CRF-31 Nit Open values.yaml:197 terminationGracePeriodSeconds 330 coupled to binary shutdown timeout with no comment (CRF-19 fix undocumented) R2 Gon P3 (downgraded) Yes
CRF-32 Nit Open README.md:14 README kubectl create secret lacks -n; verbatim use into non-default namespace misplaces the Secret R2 Leorio Nit Yes
CRF-33 Note Open (informational, not posted) templates/service.yaml:28 Service port named http carries HTTPS when listenerTLS set R2 Gon Note No
CRF-34 Nit Open templates/_aigateway.tpl:106 Probe field-projection block triplicated verbatim across startup/liveness/readiness R2 Meruem Nit, Robin Note Yes
CRF-35 Note Open (informational, not posted) tests/testdata/custom.golden:52 Goldens hardcode 0.1.0 version; release pipeline must regenerate goldens in the same pass R2 Komugi Note No

Contested and acknowledged

CRF-9 (P3, templates/ingress.yaml:32) - listenerTLS + ingress plaintext-to-TLS backend

  • Finding (R1): Enabling listenerTLS together with ingress/httproute renders a backend that speaks plaintext HTTP to the HTTPS listener on 4001, with no backend-protocol hint. Hisoka rendered the combination and got TLS-handshake 502s at request time. Proposed a validate rejection or documented backend-protocol requirement.
  • Author defense (R2): "Ingress or HTTPRoute + HTTPS on backend is possible configuration. Check was not added. Extended documentation." Author rejects a hard validation failure (the combination is legitimate for controllers that support backend TLS) and instead added a README section on controller-specific backend TLS annotations.
  • Status: contested; not yet evaluated by the panel. Reviewers judge whether documentation is a sufficient response or a validation/warning is still warranted.

CRF-9 (P3, templates/ingress.yaml:32) - listenerTLS + ingress plaintext-to-TLS backend

  • Finding (R1): Enabling listenerTLS together with ingress/httproute renders a backend that targets the Service port with no backend-protocol hint, so a controller such as ingress-nginx speaks plaintext HTTP to the HTTPS listener on 4001 and requests fail at runtime (TLS-handshake 502s). The chart validates several other cross-field combinations but not this one. Proposed a validate rejection or documented backend-protocol requirement.
  • Author defense (R2): "Ingress or HTTPRoute + HTTPS on backend is possible configuration. Check was not added. Extended documentation." Declined a hard validate reject (the combination is legitimate for controllers that re-originate TLS at the backend) and added a README section on controller-specific backend TLS.
  • Panel disposition (R2, 7 reviewers): Unanimous that a hard validate fail is wrong (Hisoka, Mafuuu, Kurapika, Meruem, Knov, Pariston, Leorio all agree the combination is legitimate and controller-specific backend TLS cannot be validated generically). Kurapika confirmed it fails closed (no security exposure). 5 reviewers (Hisoka, Mafuuu, Kurapika, Meruem, Knov) judged the README documentation a sufficient ceiling and voted close. 2 reviewers (Pariston P3, Leorio P3) re-raised on doc/UX completeness: the README teaches the fix but never names the failure symptom (502/handshake) so an operator has nothing to grep for (Leorio), and there is no install-time signal though NOTES.txt already computes the exact condition (Pariston). Disposition: hard-validation question closed (author correct); residual kept open at P3 for the two cheap, non-blocking doc/UX additions. Re-raised as a reply on the existing thread.

CRF-18 (Note, templates/_aigateway.tpl:91) - metrics listener always-on

  • Finding (R1): Metrics listener is hardcoded on, unauthenticated on 0.0.0.0:2112, with no disable toggle. The note asked whether the absent toggle was a conscious decision worth stating.
  • Author accepted (R2): "Yes, it is conscious choice." Matches sibling coder/provisioner charts. Note answered as intended.

CRF-22 (Note, templates/_aigateway.tpl:130) - readiness probe coupled to coderd DRPC

  • Finding (R1): Readiness probe ties data-plane Service membership to the coderd DRPC connection, so a coderd blip drops all replicas from endpoints at once. Flagged to confirm intent.
  • Author accepted (R2): "Yes, it is conscious choice." Readiness is now gated behind readinessProbe.enabled (default true). Intentional coupling confirmed.

CRF-1 (Nit, tests/chart_test.go:75) - tc := tc copy

  • Finding: Netero recommended deleting the tc := tc copy as redundant under Go 1.22+ per-iteration semantics, matching sibling helm tests.
  • Empirical disproof (Komugi): The outer tc is shared across the inner for _, namespace := range testNamespaces loop, which mutates tc.namespace and launches t.Parallel() subtests closing over it. Only default_values has two namespaces. Deleting line 75 and running GOMAXPROCS=2 go test -race -run TestRenderChart -count=8 fails deterministically (default/default_values reads the coder golden while templating namespace default). The copy is load-bearing. Empirical result wins; finding dropped, not posted.

Round log

Round 2 update

Churn guard: PROCEED. 19 addressed (CRF-2 through CRF-8, CRF-10 through CRF-17, CRF-19, CRF-20, CRF-21, CRF-23), 2 acknowledged (CRF-18, CRF-22), 1 contested (CRF-9). "Author fixed" = author claims fixed; panel verifies on encounter. New commit 531f6b8 -> 2f1347a, +585 lines. CRF-24/25/26 remain informational (never posted).

Round 2 panel

13-reviewer panel (bisky, hisoka, mafu-san, mafuuu, pariston, gon, leorio, kurapika, komugi, meruem, robin, ging-go + wildcard knov) plus Netero (no findings) and Law (Don't split). All 19 author-claimed fixes independently verified against the tree; CRF-2/3/5/6/10/11/15 confirmed structural, not symptom patches. CRF-9 disposed (hard validation rejected by consensus; residual doc gap re-raised P3). New: 2 P3 (CRF-27 envFrom guard gap, CRF-28 misleading probe comment), 1 P4, 4 Nit, informational notes. Highest new severity P3, so COMMENT. Reviewed against cc11c8a..2f1347a.

Round 1

Netero + Law (infrastructure), then 16-reviewer panel (bisky, hisoka, mafu-san, mafuuu, pariston, gon, leorio, kurapika, chopper, ging-go, komugi, luffy, robin, zoro + wildcards meruem, kite). Law: Don't split (advisory). Highest severity P2 (liveness/startup crashloop, 3-reviewer convergence). No P0/P1, so COMMENT event. CRF-1 dropped (Komugi empirical disproof). Reviewed against cc11c8a..531f6b8.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-built chart. It reuses the libcoder library the way the coder and provisioner charts do, keeps the imperative surface to a few template helpers, ships render-time validation with precise field-level error messages, and backs it with a deterministic golden-file suite (63% test density) that the panel ran green under -race and a GOMAXPROCS sweep. Bisky's take: "this test suite is lovely... These are real gems." No scope drift: every one of the 40 files belongs to the chart or its mechanical build/release wiring, which mirrors the existing chart entries line-for-line.

Severity count: 1 P2, 10 P3, 4 Nit, 6 Note. No P0/P1, so this is a COMMENT, not a block. The one finding to fix before shipping is the liveness/startup race (CRF-4): three reviewers independently traced that the process does not open its /healthz listener until it has connected to coderd and loaded providers, so on a fresh co-install or during a coderd outage the gateway crashloops. The rest cluster into contract-narrowing (CRF-5, CRF-6, CRF-7), operator-signal correctness (CRF-8, CRF-9, CRF-23), and convention drift (CRF-12, CRF-13).

Process notes, not inline findings:

  • The commit subject / PR title misspells "helm" as "heml" (CRF-16). It squash-merges into main permanently. Suggest feat(helm/ai-gateway): add Helm chart for standalone AI Gateway.
  • The PR description claims the suite covers "all validation error cases," but 6 of the 13 fail branches have no test (this is CRF-2). Mafu-san flagged this as fake completeness: a reviewer trusting the description approves ~46% of the validation logic as tested when it is not. Either add the cases or scope the wording.
  • Netero's round-1 tc := tc nit was dropped: Komugi empirically proved the copy is load-bearing for the parallel multi-namespace subtests (deleting it fails default_values deterministically). Do not remove it.

No findings against the build/release plumbing: the charts list drives the build/ai-gateway_helm_$(VERSION).tgz target before signing, and the attestation ./build/*_helm_*.tgz glob already covers the artifact.

🤖 This review was automatically generated with Coder Agents.

Comment thread helm/ai-gateway/templates/_aigateway.tpl
Comment thread helm/ai-gateway/templates/_aigateway.tpl
Comment thread helm/ai-gateway/templates/_helpers.tpl Outdated
Comment thread helm/ai-gateway/templates/_helpers.tpl Outdated
Comment thread helm/ai-gateway/templates/_aigateway.tpl Outdated
Comment thread helm/ai-gateway/values.yaml
Comment thread helm/ai-gateway/templates/_aigateway.tpl
Comment thread helm/ai-gateway/README.md Outdated
Comment thread helm/ai-gateway/tests/chart_test.go
Comment thread helm/ai-gateway/tests/chart_test.go Outdated
@pawbana pawbana changed the title feat: add heml chart for standalone AI Gateway feat(helm/ai-gateway): add Helm chart for standalone AI Gateway Jul 15, 2026
@pawbana pawbana changed the title feat(helm/ai-gateway): add Helm chart for standalone AI Gateway feat: add Helm chart for standalone AI Gateway Jul 15, 2026
@pawbana

pawbana commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong round-2 turnaround. Every one of the 19 round-1 findings the author marked fixed was independently verified against the tree, and the important ones were fixed at the root rather than patched at the symptom: the chart-owned env guard now derives its protected list from the rendered ownedEnv (no more hand-maintained duplicate), automountServiceAccountToken: false moved to the pod spec so it holds on the disableCreate path, extraTemplates delegates to libcoder's coder.renderTemplate again, and the HTTPRoute CRD guard is now actually testable via per-case apiVersions. Bisky: "this test suite is a genuine gem, and I do not say that often" - all 13 validation branches now have a verbatim-message test. TestRenderChart passes.

Contested CRF-9 (listenerTLS behind ingress/httproute): the panel agrees unanimously with the author that a hard validate reject would be wrong, because backend-TLS to the HTTPS listener is legitimate for controllers that re-originate TLS and the chart cannot know the controller's annotation dialect. Kurapika confirmed it fails closed, so there is no security exposure. Five reviewers accept the new README section as a sufficient ceiling. Two (Pariston, Leorio) re-raised a cheap residual: the docs teach the fix but never name the symptom (a 502 / TLS handshake error), and there is no install-time signal even though NOTES.txt already computes the exact condition. Kept open at P3 as a non-blocking doc/UX ask, not a request for validation. See the reply on that thread.

Severity count this round: 2 P3, 1 P4, 4 Nit (all new; no regressions of prior fixes). Two are follow-on consequences of round-1 fixes rather than the original issues re-opening (CRF-27 is the sibling of CRF-7; CRF-28 is guidance that can undo CRF-4).

Acknowledged as intentional by the author and accepted: CRF-18 (metrics listener always-on, matches sibling charts) and CRF-22 (readiness coupled to the coderd DRPC connection).

Process notes:

  • The CI title check is red (✗ title). The current PR title parses clean against the conventional-commit validator, so this may be a stale run after the "heml" -> "Helm" rename, but reviewers could not read the failing run (no GitHub credentials in the worktree). Please confirm it is green before merge rather than assume the rename cleared it; a failing check is a bug until proven stale.
  • The branch commits still read feat: add heml chart for standalone AI Gateway and agentic review 1. The PR title is corrected, so a squash merge lands a clean subject and this is cosmetic, but if the branch is rebase- or merge-committed the "heml" typo reaches main history. Squash, or reword both subjects.

🤖 This review was automatically generated with Coder Agents.

{{- range (include "coder-ai-gateway.ownedEnv" . | fromYamlArray) }}
{{- $owned = append $owned .name }}
{{- end }}
{{- range .Values.coder.env }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 [CRF-27] The env override guard scans only coder.env, so the collision CRF-7 fixed still crashes the container through coder.envFrom. (Hisoka)

The CRF-7 fix loops over .Values.coder.env and rejects any owned name, including CODER_AI_GATEWAY_KEY. But the container also gets .Values.coder.envFrom (_aigateway.tpl:78-81, verified), and the guard never looks there. envFrom keys are opaque at render time, so the loop cannot inspect them, which means this is not fixable by extending the loop.

Two consequences, both verified from the code + CLI:

  • The chart always emits CODER_AI_GATEWAY_KEY_FILE, and resolveAIGatewayKey (enterprise/cli/aigatewaystart.go:366) aborts when both CODER_AI_GATEWAY_KEY and CODER_AI_GATEWAY_KEY_FILE are set. An operator who ships CODER_AI_GATEWAY_KEY via a ConfigMap/Secret referenced by coder.envFrom bypasses the guard and lands in CrashLoopBackOff on the same confusing "mutually exclusive" error.
  • For the other owned names, Kubernetes gives env precedence over envFrom, so an envFrom override is silently ignored, which contradicts the values.yaml:4 promise "Chart-owned variables cannot be overridden."

The guarantee is only true for coder.env. Fix: scope the values.yaml comment and README to coder.env explicitly and warn that envFrom is not guarded (and that CODER_AI_GATEWAY_KEY there is a startup crash), or drop the absolute wording. Related: the derived owned-set is also dynamic (Mafuuu, Kurapika) so the TLS file-path vars are unguarded when their secret is unset; both reviewers judged that self-inflicted (the referenced file just does not exist), not an attack surface.

🤖

memory: 1Gi

# coder.startupProbe -- Startup probe configuration for the AI Gateway.
# Keep this disabled until /healthz is independent of initial provider load.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 [CRF-28] The startupProbe comment tells operators to keep it disabled "until /healthz is independent of initial provider load," a condition the architecture never satisfies, and following it while enabling liveness reintroduces the CRF-4 cold-start crashloop. (Mafuuu P3, Pariston Note, Meruem Note)

Both the startupProbe (line 119) and livenessProbe (line 125) blocks carry the identical comment. ai-gateway start never opens its /healthz listener until loadProviders has connected to coderd (aigatewaystart.go: loadProviders blocks, net.Listen runs afterward), so /healthz is coupled to provider load by design and the condition is never met. For liveness the disable is sound and matches the sibling coder chart. For the startup probe the reasoning is inverted: a startupProbe with a generous failureThreshold is precisely the mechanism that makes liveness safe under that ordering. The chart's own custom.yaml fixture proves it (startupProbe enabled, failureThreshold: 12, liveness off).

Consequence: an operator who wants liveness in production reads this comment, keeps startupProbe off, enables livenessProbe, and gets back the ~30s CrashLoopBackOff that CRF-4 removed from the defaults. Meruem adds a second angle: with liveness off there is no restart-on-hang, so a deadlocked (not crashed) replica lingers forever under replicaCount > 1, silently shrinking capacity. Reword the startup-probe comment: enable startupProbe with a generous failureThreshold first, then livenessProbe is safe; /healthz depends on provider load by design, so the startupProbe is required, not deferred. Note that liveness-off means no automatic recovery from a post-startup hang.

🤖

{{- end }}
{{- end }}
{{- end }}
{{- if .Values.coder.livenessProbe.enabled }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4 [CRF-29] The livenessProbe render branch is the only one of the three probe blocks with zero golden coverage. (Bisky)

readinessProbe renders in all 9 goldens and startupProbe in 2 (custom, custom_coder), but livenessProbe renders in none: the sole case that sets it, custom.yaml, sets enabled: false, and every other case leaves the default off, so lines 112-124 never execute in the suite. The block is a copy of the startup/readiness blocks with a different values key and path; it reads correctly today, but copy-paste blocks are exactly where a wrong key or a /readyz typo hides, and the startup golden would not catch it. This is a user-enableable surface CRF-14 added. Add coder.livenessProbe.enabled: true to one values case and snapshot it so the block is pinned like its two siblings. P4 because the code is verified correct by reading.

🤖

keyKey: tls.key
# The certificate must cover every hostname clients use.
listenerTLS:
secretName: ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-30] Secret references in the aigateway block are inconsistent: three use .name, one uses .secretName. (Gon)

keySecret.name, coderTLS.caSecret.name, and coderTLS.clientSecret.name all use name, but listenerTLS.secretName uses secretName for the same concept (the name of a Kubernetes Secret). listenerTLS.secretName does mirror the coder chart's coder.tls.secretName, so the outlier has a cross-chart rationale, but the block is internally inconsistent. additionalProperties: false caps the harm (a wrong guess fails loudly). Cheapest to settle now, before this new chart's values API stabilizes. (ingress.tls.secretName mirrors the native Ingress field and should stay.)

🤖

secretName: ""
certKey: tls.crt
keyKey: tls.key
terminationGracePeriodSeconds: 330

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-31] terminationGracePeriodSeconds: 330 is coupled to the binary's shutdown timeout with nothing recording the coupling. (Gon)

CRF-19 raised that the previous value (300) equaled the binary's 5m shutdown timeout, so SIGKILL cut in-flight streams; the fix bumped it to 330 for headroom, which is correct. But no comment states that 330 is "binary shutdown timeout (~300s) plus headroom." A maintainer tuning this later sees only 330, cannot know it is anchored to an external timeout, and can lower it below the shutdown timeout, silently reintroducing CRF-19. Add a one-line comment tying the number to the binary's shutdown timeout. (Gon rated this P3; recorded as Nit since the consequence is regressing a Note-level issue and the fix is a comment.)

🤖

Comment thread helm/ai-gateway/README.md
1. An AI Gateway key created in Coder.
2. A Kubernetes Secret containing that key in the chart namespace:

```console

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-32] The prerequisite says the Secret must live "in the chart namespace," but the kubectl create secret command has no -n. (Leorio)

README.md:12 states the Secret must be "in the chart namespace," then the command runs kubectl create secret generic coder-ai-gateway-key ... with no namespace flag, and helm install later also has no -n. On the happy path (current context namespace) this works. An operator who installs the release into a dedicated namespace but runs the secret command verbatim creates the Secret in default; the pod then fails to mount secretName: coder-ai-gateway-key and never starts, with only a Kubernetes mount error to debug. Add -n <release-namespace> to the command, or state that the Secret and the release must share a namespace.

🤖

port: http
scheme: {{ $scheme }}
initialDelaySeconds: {{ .Values.coder.startupProbe.initialDelaySeconds }}
{{- range $field := list "periodSeconds" "timeoutSeconds" "successThreshold" "failureThreshold" }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-34] The probe field-projection block is triplicated verbatim across startupProbe, livenessProbe, and readinessProbe. (Meruem Nit, Robin Note)

The same range $field := list "periodSeconds" "timeoutSeconds" "successThreshold" "failureThreshold" / hasKey loop is copied three times (lines 106-110, 119-123, 132-136), differing only in the probe key and path. A define "coder-ai-gateway.probe" taking (probeName, path, scheme) would collapse the three and remove the drift risk when a field is added. The sibling coder chart duplicates the same logic (and less cleanly), so this extends a pre-existing pattern rather than introducing one; purely a maintainability nit.

🤖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant