Two agent-oriented runbooks under vibe/runbooks/ with [AGENT]/[HUMAN] step markers, grounded in real diffs: - new-tool.md : add a platform component to the tools repo so ArgoCD deploys it into the tools namespace (wrapper Chart.yaml + the tool library + a row in chart/values.yaml; optional iac/ for secrets). Mirrors the prometheus/crowdsec additions. - new-app.md : stand up a brand-new application across THREE repos (app + factory + tools) with the strict ordering dependency and the TERRAFORM_SSH_KEY pitfall. Phase-by-phase mapped to the dance-lessons-coach onboarding PRs (#89/#97/#98/#99/#100), factory #1/#2, tools #1; the FR doc/runbooks/new-web-app is linked as the detailed companion. 2 mermaid diagrams MCP-validated; zero dead links across the vibe tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15 KiB
vibe > Runbooks > Set up a new tool
Set up a new tool
Status: ✅ Active Audience: platform operator + agents (English). For the application-onboarding equivalent see Set up a new app. Last Updated: 2026-06-23
TL;DR
Tip
Adding a platform component means dropping a small wrapper chart into the
toolsrepo and registering it in the app-of-apps. An agent can do the bulk of it: scaffoldtools/<tool>/(a wrapperChart.yamlthat depends on the upstream chart + the localtoollibrary chart, the twohelm-chart*.yamltemplates, and avalues.yaml), add one key undertools:intools/chart/values.yaml, and lint it locally. The human approval gate sits at two places: (1) any Vault/database wiring undertools/<tool>/iac/and (2) opening + merging the PR — ArgoCD auto-syncs the new Application the moment it lands onmain.
Scope
This runbook covers adding a new platform component (monitoring, cache, security engine, connection pooler, analytics, …) to the tools repo so the factory ArgoCD tools project renders an Application for it and deploys it into the tools namespace.
Systems touched: Gitea (tools repo), ArgoCD (the tools AppProject), k3s (the helm-controller that materialises each HelmChart CR), and — only for secret-backed tools — Vault + the Vault Secrets Operator (VSO).
This runbook does not cover standing up a brand-new business application (its own repo, chart, CI/CD, database). That is the Set up a new app runbook. It also does not cover the underlying app-of-apps wiring of the tools project itself — read the tools guidebook for how that works.
Preconditions
- Working in a worktree under
.claude/worktrees/<slug>/of atoolsrepo clone (never the trunk). - The tool deploys into the
toolsnamespace (thetoolsAppProject only permits that destination). - You know the upstream Helm chart (chart name + repo URL) and a pinned version, OR you have decided this tool needs Kustomize + helm inflation (charts that require post-render patching, like
clickhouse/plausible). helm(with the upstream repo reachable) and, for the Kustomize path,kustomizeavailable locally for the lint step.- If the tool needs secrets or a database: confidence with the Vault
app_rolesmodule pattern and thetofu-applyCI flow — see the tools secrets & VSO page and the tofu CI apply flow.
Procedure
-
[HUMAN] Choose the tool name
<tool>(kebab-case) and the deployment shape.Decide between the two supported shapes:
- Wrapper chart (default). A thin Helm chart that depends on the upstream chart at a pinned version and lets the local
toollibrary chart emit a k3sHelmChartcustom resource. Used byprometheusandcrowdsec. - Kustomize + helm inflation. For charts that need post-render JSON6902 patches or extra
resources/. Used byclickhouseandplausible.
Pin the upstream chart version now — it goes verbatim into the next step.
- Wrapper chart (default). A thin Helm chart that depends on the upstream chart at a pinned version and lets the local
-
[AGENT] Scaffold
tools/<tool>/(wrapper-chart shape).Create four files. The
Chart.yamldeclares two dependencies — the localtoollibrary chart (served from the Gitea Helm package registry) and the upstream chart pinned to your chosen version:# tools/<tool>/Chart.yaml apiVersion: v2 name: <tool> description: A Helm chart for Kubernetes dependencies: - name: tool version: 0.1.0 repository: https://gitea.arcodange.lab/api/packages/arcodange-org/helm - name: <upstream-chart> version: <pinned-version> repository: https://<upstream-helm-repo> type: application version: 0.1.0The two template files are one-liners that delegate to the
toollibrary (they only render whentool.kindisHelmChart; underSubChartthey are inert and the upstream chart is pulled as a normal dependency):# tools/<tool>/templates/helm-chart.yaml {{- if eq .Values.tool.kind "HelmChart" -}} {{- include "tool.helm-chart.tpl" . -}} {{- end -}}# tools/<tool>/templates/helm-chart-config.yaml {{- if eq .Values.tool.kind "HelmChart" -}} {{- include "tool.helm-chart-config.tpl" . -}} {{- end -}}The
values.yamlcarries the upstream values under a YAML anchor and re-references it from thetool:block. Web-facing tools set an ingress host<tool>.arcodange.lab; stateful tools set persistence with the longhorn storage class and resource requests/limits. The shape, taken fromprometheus/values.yaml:# tools/<tool>/values.yaml <upstream-chart>: &<tool>_config # ── upstream values go here ── # web-facing tools: expose an ingress host ingress: enabled: true hosts: - <tool>.arcodange.lab # stateful tools: pin storage class + size persistence: enabled: true storageClass: longhorn size: 8Gi resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi tool: # kind 'SubChart': pull the upstream chart as a dependency and pass it the values below. # kind 'HelmChart': let the tool library emit a k3s HelmChart CR instead. kind: 'SubChart' repo: https://<upstream-helm-repo> chart: <upstream-chart> version: <pinned-version> values: *<tool>_configNote
Under
tool.kind: 'HelmChart'the localtoollibrary chart emits ahelm.cattle.io/v1HelmChartCR (and an optionalHelmChartConfig) pinned tonamespace: tools/targetNamespace: tools, and the k3s helm-controller installs the upstream chart. Under'SubChart'(the default that prometheus and crowdsec use) the upstream chart is just a Helm dependency rendered in-line. PickSubChartunless you specifically need the helm-controller to own the release.For the Kustomize shape instead, skip the wrapper
Chart.yaml/templates and create akustomization.yamlthat inflates the upstream chart plus anyresources/, mirroringplausible/kustomization.yaml:# tools/<tool>/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: tools helmCharts: - name: <upstream-chart> repo: https://<upstream-helm-repo> version: <pinned-version> releaseName: <tool> valuesFile: <tool>Values.yaml namespace: tools resources: - resources/ingressroute.yaml # patches: / patchesJson6902: ← post-render tweaks, see plausible for a worked example -
[AGENT] Register the tool in the app-of-apps.
Add a single key for
<tool>undertools:intools/chart/values.yaml:# tools/chart/values.yaml tools: pgbouncer: {} hashicorp-vault: {} crowdsec: {} # …existing entries… <tool>: {}The
chart/templates/apps.yamltemplate ranges over.Values.toolsand renders one ArgoCDApplicationper key, withpath: <tool>anddestination.namespace: toolsunder thetoolsAppProject. The key must match the directory name you created in step 2. See the tools guidebook for how the app-of-apps meta-chart drives this. -
[HUMAN] If the tool needs secrets or a database, wire Vault + VSO and a tofu-apply workflow.
This step mutates Vault (creates roles/secrets) and so is gated. Use
crowdsec(dynamic Postgres role) andplausible(kvv2 static secrets) as the worked examples, and read the tools secrets & VSO page.a. Add
tools/<tool>/iac/— OpenTofu that configures Vault. For a dynamic Postgres role, reuse the sharedapp_rolesmodule exactly as crowdsec does:# tools/<tool>/iac/main.tf module "app_roles" { source = "git::ssh://git@192.168.1.202:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main" name = "<tool>" service_account_namespaces = ["tools"] } # for kvv2 static config, add vault_kv_secret_v2 resources (see plausible/iac/main.tf)Pair it with a
backend.tf(GCS state atprefix = "tools/<tool>/main") and aproviders.tfwhoseauth_login_jwtrole isgitea_cicd_<tool>— both copied from crowdsec.b. Add the VSO CRDs to the chart templates so VSO mints a k8s Secret the workload consumes. A
serviceaccount.yaml, aVaultAuthbound to a Vaultkubernetesrole named<tool>, and aVaultDynamicSecret(orVaultStaticSecretfor kvv2) pointing at the Vault path:# tools/<tool>/templates/vaultauth.yaml apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultAuth metadata: name: <tool> namespace: {{ .Release.Namespace }} spec: vaultConnectionRef: default method: kubernetes mount: kubernetes kubernetes: role: <tool> serviceAccount: <tool> audiences: - vault# tools/<tool>/templates/vaultdynamicsecret.yaml apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultDynamicSecret metadata: name: <tool>-db-credentials namespace: {{ .Release.Namespace }} spec: mount: postgres path: creds/<tool> destination: create: true name: <tool>-db-credentials rolloutRestartTargets: - kind: Deployment name: <tool> vaultAuthRef: <tool>Then reference the VSO-created secret from the workload (env
valueFrom.secretKeyRef), as crowdsec'svalues.yamldoes forDB_USER/DB_PASSWORD. For the Kustomize shape, add these CRDs as files underresources/and list them inkustomization.yamlinstead oftemplates/.c. Add a
.gitea/workflows/<tool>.yamlthat tofu-applies<tool>/iacon changes, mirroringcrowdsec.yaml: a path filter on'<tool>/**/*.tf', a Gitea→Vault JWT auth job, and adflook/terraform-applystep withpath: <tool>/iac. See the tofu CI apply flow for what that pipeline does end to end. -
[AGENT] Lint and render locally before opening the PR.
For the wrapper-chart shape:
helm dependency update tools/<tool> helm lint tools/<tool> helm template <tool> tools/<tool> | head -n 60 # render the app-of-apps Application for <tool>: helm template tools-apps tools/chart | grep -A12 "name: <tool>"For the Kustomize shape:
kustomize build --enable-helm tools/<tool> | head -n 60 -
[HUMAN] Open a PR on the
toolsrepo, get it reviewed, and merge.git checkout -b arcodange/<slug> git add tools/<tool> tools/chart/values.yaml git commit -m "declare <tool>" git push -u origin arcodange/<slug>Important
The
toolsrepo is on Gitea, not GitHub — open the PR with themcp__gitea__*tools (loadselect:mcp__gitea__pull_request_writeviaToolSearch), notgh. Once the PR merges tomain, ArgoCD detects the new key inchart/values.yaml, renders the<tool>Application, and syncs it automatically.
Verification
All read-only — an agent can run these after the PR merges and ArgoCD has reconciled.
# 1. The ArgoCD Application for <tool> is Synced + Healthy
kubectl --context <ctx> -n argocd get application <tool> \
-o jsonpath='{.status.sync.status}/{.status.health.status}{"\n"}'
# expected: Synced/Healthy
# 2. The pod is Running in the tools namespace
kubectl --context <ctx> -n tools get pods -l app.kubernetes.io/name=<tool>
# expected: <tool>-… 1/1 Running
# 3. Web-facing tools: the ingress is admitted and the host resolves
kubectl --context <ctx> -n tools get ingress | grep <tool>
curl -sI https://<tool>.arcodange.lab | head -n1 # expected: HTTP/2 200 (or app login redirect)
# 4. Secret-backed tools: VSO created the k8s Secret
kubectl --context <ctx> -n tools get secret <tool>-db-credentials
# expected: the Secret exists with the keys the workload mounts
Rollback
- [HUMAN] Revert the
tools/chart/values.yamlentry (remove the<tool>:key). On the next sync ArgoCD prunes the<tool>Application —prune: trueis set inapps.yaml— which removes the deployed workload from thetoolsnamespace. - [HUMAN] In a follow-up PR, delete the
tools/<tool>/directory to remove the wrapper chart / Kustomize source. - [HUMAN] For secret-backed tools, the Vault role/secret created by
tools/<tool>/iac/is not removed by ArgoCD. Destroy it explicitly (tofu -chdir=tools/<tool>/iac destroy) or remove the IaC and let the workflow reconcile, and drop the.gitea/workflows/<tool>.yamlfile. - For a full cluster-level recovery (power cut, lost quorum) follow CLUSTER_RECOVERY.md.
References
- Tools guidebook — how the app-of-apps meta-chart turns each
tools:key into an ArgoCD Application. - Tools components — the catalogue of platform components and what each provides.
- Tools secrets & VSO — the Vault
app_roles+ VaultAuth/VaultDynamicSecret pattern used in step 4. - Tofu CI apply flow — what the
<tool>/iactofu-apply workflow does end to end. - Real examples in the
toolsrepo:prometheusandcrowdsec(wrapper-chart shape), the sharedtoollibrary chart, andclickhouse/plausible(Kustomize shape). - Set up a new app — the sibling runbook for onboarding a business application (not a platform component).