Skip to content

feat(quoter-bot): add helm chart for kubernetes self-hosting - #164

Draft
julien-devatom wants to merge 3 commits into
mainfrom
quoter-bot-helm-chart-0f5a5f
Draft

feat(quoter-bot): add helm chart for kubernetes self-hosting#164
julien-devatom wants to merge 3 commits into
mainfrom
quoter-bot-helm-chart-0f5a5f

Conversation

@julien-devatom

@julien-devatom julien-devatom commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Why

TIB-2026-08-14-quoter-bot-dockerhub-publishing gave third-party operators a public image (morphoorg/quoter) but no deployment recipe: a Kubernetes operator has to hand-write manifests and rediscover the bot's non-obvious runtime constraints — a config loader that rejects symlinked files, state files checked against the process uid, an image entrypoint that requires root, singleton nonce semantics, and a shutdown sequence that legitimately takes minutes. Each fails loud but late. This PR ships a package-owned Helm chart that encodes those constraints as defaults, so one values.yaml fully describes a correct deployment.

What

  • bots/quoter-bot/helm/quoter-bot — chart targeting morphoorg/quoter:latest (pinnable to immutable commit tags). One values file carries classic workload parameters (image, cpu/memory resources, persistence, scheduling, security contexts) next to a config mapping in the bot's native YAML schema, rendered verbatim into a Secret and passed via --config; the bot's own startup validation stays the single source of truth (no values.schema.json).
  • Bot README — promotes Helm as the recommended Kubernetes self-hosting path, installed from the repo checkout (the chart is deliberately not published to any registry yet); chart README carries the full parameter reference and a complete runnable quickstart.
  • Chart regression testsbots/quoter-bot/test/helm/chart.test.ts (vitest) locks in the review-driven invariants below.
  • TIBTIB-2026-08-14-quoter-bot-helm-chart records the decisions and rejected alternatives (StatefulSet, ConfigMap, registry publishing, in-cluster root entrypoint, fsGroup).
  • .oxfmtrc.json — ignores **/helm/**/templates/**: oxfmt cannot parse Go-templated YAML (pnpm format errored before this). Chart.yaml/values.yaml/chart README stay formatter-covered.
  • CLAUDE.md — operator-surface enumeration now mentions the chart (AGENTS.md/.cursorrules are symlinks, so they follow).

Design notes for review

  • subPath config mount at /repo/bots/quoter-bot/quoter-bot.yaml: the loader opens config with O_RDONLY|O_NONBLOCK|O_NOFOLLOW (config-source.utils.ts), so the symlinks a plain Secret volume exposes would fail startup; subPath bind-mounts a regular file, under a parent directory that exists in the image (also the bot's default-discovery filename in its workdir). Since subPath mounts never see Secret updates, a reserved checksum/config pod annotation (protected from podAnnotations collisions) rolls the pod on upgrade; existingConfigSecret users get a documented kubectl rollout restart.
  • Non-root instead of railway-entrypoint.sh: the image CMD needs root (chown + setpriv). The chart execs node dist/src/index.js directly as uid/gid 1000, with a chown-only root init container (default-on, mirrors the Railway entrypoint) fixing state-volume ownership. Deliberately no fsGroup: kubelet's fsGroup handling can chmod restored ownership-state files beyond 0600, which the bot's state readers correctly reject (mode & 0o077 check) — a restored claim would crashloop before cleanup. Hardened defaults otherwise (read-only rootfs + emptyDir /tmp, no capabilities, no SA token).
  • Singleton semantics: replicas: 1 hardcoded + Recreate strategy (nonce cursor, serialized mutation queue, and ownership state are per-instance); state PVC kept on uninstall by default because deleting ownership state fails readiness for previously bot-issued groups; terminationGracePeriodSeconds: 600 so SIGTERM cleanup (invalidate owned groups, wait for receipts) is not SIGKILLed at the k8s 30s default.
  • Secrets story: env overrides YAML, so env/envFrom carry MAKER_PRIVATE_KEY and the env-only BETTERSTACK_* values; existingConfigSecret keeps the whole file out of Helm release storage. Selector labels and the config checksum are reserved against podLabels/podAnnotations overrides.

Verification

  • helm lint clean; renders verified for default values, a fully-populated values file, existingConfigSecret, persistence.enabled=false, and persistence.existingClaim variants.
  • The chart-rendered Secret's quoter-bot.yaml was base64-decoded and loaded through the bot's own ConfigService.load in read-only and write mode (env-injected signer resolved to private-key) — proving the values→Secret→--config round trip, including quoted big-integer preservation through Helm.
  • Chart regression tests: 7/7 pass; break-one-assertion check confirmed they fail when violated.
  • pnpm format, pnpm lint, and full CI (Lint, Typecheck, Test, Dead-Code) green on the latest commit.
  • Reviews: repo reviewer agent (pre-commit) — no blocking issues, suggestions applied; carapulse APPROVED; codex — two rounds of findings (config mount parent, quickstart completeness, namespace ordering, podLabels/podAnnotations overrides, fsGroup-vs-state-file-modes) all fixed in follow-up commits, final verdict "Didn't find any major issues"; all 7 review threads resolved.

🤖 Generated with Claude Code

Ship a package-owned Helm chart at bots/quoter-bot/helm/quoter-bot so
Kubernetes operators can self-host the public morphoorg/quoter:latest
image from one values file: classic workload parameters (image,
resources, persistence, scheduling, security contexts) next to a
config mapping in the bot's native YAML schema, rendered verbatim into
a Secret and passed via --config.

The chart encodes the bot's runtime constraints as defaults: subPath
config mount (the loader opens with O_NOFOLLOW and rejects the
symlinks a plain Secret volume exposes) with a checksum annotation to
roll the pod on config change; direct non-root node execution with
fsGroup-managed volume ownership instead of the root-only Railway
entrypoint (state files are checked against the process uid); one
replica with a Recreate strategy for the singleton writer; a state PVC
kept on uninstall because losing offer-group ownership fails
readiness; and a 600s termination grace period so shutdown cleanup can
invalidate owned groups and wait for receipts.

Promote Helm as the recommended Kubernetes path in the bot README
(local chart install; the chart is not published to a registry yet),
document parameters and secret-handling options in the chart README,
record the decisions in a TIB, extend the CLAUDE.md operator-surface
enumeration, and ignore Go-templated YAML in oxfmt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@julien-devatom julien-devatom self-assigned this Aug 14, 2026
@julien-devatom

Copy link
Copy Markdown
Collaborator Author

@prd-carapulse[bot] can you review and approve if all good

@julien-devatom

Copy link
Copy Markdown
Collaborator Author

@codex review

@prd-carapulse

prd-carapulse Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

✅ Approved: #164 (review)

Review: no blocking findings
Local verification: helm lint plus five render variants passed
CI: Test, Typecheck, Dead-Code, Lint, Notify all green

@prd-carapulse prd-carapulse 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.

Reviewed chart templates, runtime/security assumptions, persistence/config variants, and docs. helm lint passed; default, managed-config, existing-secret, existing-claim, and ephemeral-state renders all parsed successfully. CI is green (Test, Typecheck, Dead-Code, Lint, Notify). No blocking findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 43afb4049d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread bots/quoter-bot/helm/quoter-bot/templates/deployment.yaml
Comment thread bots/quoter-bot/helm/quoter-bot/README.md Outdated
Comment thread bots/quoter-bot/helm/quoter-bot/README.md
Comment thread bots/quoter-bot/helm/quoter-bot/templates/deployment.yaml
Fix the config mount path, make the quickstart runnable, preserve selector labels, and add regression coverage.
@prd-carapulse

prd-carapulse Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dedd0c45dc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread bots/quoter-bot/helm/quoter-bot/templates/NOTES.txt Outdated
Comment thread bots/quoter-bot/helm/quoter-bot/templates/deployment.yaml Outdated
Comment thread bots/quoter-bot/helm/quoter-bot/values.yaml Outdated
Keep install notes aligned with the mounted config path, reserve the config checksum annotation, and preserve strict restored state-file modes with a chown-only init container.
@prd-carapulse

prd-carapulse Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: eff7196f5b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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