Skip to content

feat: Helm chart, production dashboard image, and Kubernetes CI/CD - #1

Open
macjayz wants to merge 3 commits into
mainfrom
feat/kubernetes-helm
Open

macjayz wants to merge 3 commits into
mainfrom
feat/kubernetes-helm

Conversation

@macjayz

@macjayz macjayz commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Helm chart (deploy/helm/heightproof): API, worker, dashboard; optional bundled Postgres/Redis StatefulSets or external URLs; secrets generated once and persisted across upgrades; advisory-locked migration init containers sourced from infra/migrations; hardened pod security; optional ingress/HPA/PDB; Prometheus scrape annotations; helm test smoke test.
  • Production dashboard image (infra/dashboard.prod.Dockerfile): static Vite build on unprivileged nginx, proxying /v1, /ready, /health and the /live WebSocket on the same origin. An empty VITE_HEIGHTPROOF_API_URL now means same-origin; unset keeps the local-dev default.
  • Workflows: kubernetes.yml (chart lint/render + kind e2e install) and release.yml (multi-arch GHCR images with SBOM/provenance; chart pushed as OCI on v* tags).

Fixes found while deploying

  • Workers defaulted to consumer name worker-<pid>, which is worker-1 in every container, so replicas collided in the Redis consumer group. The chart sets it to the pod name.
  • Images declare USER node, which runAsNonRoot can't verify; the chart sets UID 1000 explicitly.

Known limitation

The /live WebSocket hub is in-process, so with more than one API replica a dashboard only receives pushes for events ingested by its own pod. API defaults to 1 replica; documented in the chart README.

Test plan

  • helm lint --strict, render with default and production-shaped values
  • kind (k8s v1.37) via deploy/kind/e2e.sh: all pods ready, helm test passed, 138 seeded events persisted API → Redis → worker → Postgres, 2 workers with distinct consumer names
  • Migrations re-run idempotently on upgrade
  • Kubernetes workflow green on this PR
  • Release workflow publishes images after merge

🤖 Generated with Claude Code

Helm chart (deploy/helm/heightproof)
- API, worker and dashboard Deployments; optional bundled Postgres and
  Redis StatefulSets with PVCs, or external URLs.
- Secrets generated once and reused across upgrades (lookup), or an
  existingSecret.
- Migrations from infra/migrations (symlinked, single source) applied by
  an init container under a Postgres advisory lock.
- Workers use the pod name as Redis consumer name: the default
  worker-<pid> is worker-1 in every container, so replicas collided.
- Non-root (explicit UID: images use USER node), RuntimeDefault seccomp,
  read-only root fs, dropped caps, no SA token.
- Optional ingress, HPA, PDB; Prometheus scrape annotations; helm test.

Production dashboard image: static Vite build on unprivileged nginx that
proxies /v1, /ready, /health and the /live WebSocket on the same origin.
An empty VITE_HEIGHTPROOF_API_URL now means same-origin.

Workflows
- kubernetes.yml: chart lint/render, then a kind e2e install that runs
  helm test and checks events flow API -> Redis -> worker -> Postgres.
- release.yml: multi-arch GHCR images with SBOM/provenance on main and
  tags; chart pushed as an OCI artifact on v* tags.

Verified locally on kind (k8s v1.37): all pods ready, helm test passed,
138 seeded events persisted by 2 workers with distinct consumer names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macjayz and others added 2 commits October 29, 2026 18:54
Resolve the main.tsx conflict by taking main's module split and moving
the same-origin apiBase handling into apps/dashboard/src/api.ts.

Align the chart with main's readiness rework: /ready now answers 503 and
ignores worker backlog, so the API readiness probe uses it; helm test
also checks /health/pipeline, which the dashboard nginx now proxies.

Verified: typecheck, tests, lint, build, helm lint --strict, and kind
e2e (helm test passed, 138 events persisted by 2 workers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main already carries this chart via #6. Resolve every conflict to main's
version, keeping only the dashboard nginx route for /health/pipeline so
the pipeline check is reachable on the dashboard's origin too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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