# M6.1 — CNPG `agent-manager-db` manifest | Field | Value | |---|---| | Phase | M6 — agent-manager migration | | Size | M — 1–3 days | | Status | ⬜ Not started | | Flags | homelab | | Spec | inlined below | | Blocks | — | ## Goal A dedicated Postgres for agent-manager's session store, provisioned the way everything else in the cluster is: through git, with no manual `psql`. Same pattern this project already used for `memory-db` (M2.2), applied to a different, unrelated app. ## Facts (inlined — no spec read needed) **agent-manager is a separate repo**, not part of this Rust workspace: `github.com/Riotpiaole/agent-manager` (fork of `github.com/YoanWai/ agent-manager`), local checkout `~/workplace/agent-manager`, branch `add-headless-spawn`. Its session store is `internal/store/store.go` — see M6.2/M6.3 for the schema and query port. **Existing CNPG pattern, verified on the live cluster: 3 Clusters today, one per app, each ClusterIP-only (not LAN-reachable):** | namespace/name | app | |---|---| | `cicd/forgejo-db` | Forgejo | | `iam/authentik-db` | Authentik | | `temporal/temporal-db` | Temporal | **No shared/multi-tenant DB cluster** — every app gets its own dedicated CNPG `Cluster`. `agent-manager-db` follows the same rule; it does not join `memory-db` (M2.2's cluster) even though both are new Postgres instances touched by the same person around the same time. Different app, different cluster. Follow `k8s/infra/databases/temporal-db.yaml` exactly, same as M2.2 did: `imageName` pinned, `enableSuperuserAccess: false`, `storageClass: longhorn-cnpg`, `enablePodMonitor: true`, control-plane tolerations, `podAntiAffinityType: preferred`. **Instance count — open question, default to convention.** Motivation for this whole migration is durability-of-location, not HA (single-machine usage, not a multi-host shared-session requirement). The 3 existing clusters are all 3-instance. Default to 3 instances for consistency with every other app in the cluster rather than special-casing this one to 1; revisit only if resource pressure on the homelab nodes makes it a real tradeoff. Storage: session rows are tiny (`sessions`, `groups`, `settings`, `review_*` — no blobs beyond a `snapshot TEXT` pane capture per session). 1Gi is generous; no need for `memory-db`'s 10Gi (that one holds 768-dim vectors). ## Steps 1. `k8s/infra/databases/agent-manager-db.yaml` — `Cluster` + `Database`, namespace `agent-manager`, no extensions (plain relational, no pgvector). 2. Namespace `agent-manager`, created by the ArgoCD app that owns it. 3. Add to the owning kustomization's explicit resource list — an unlisted file is silently dropped with no error and no drift shown (the M2.2 task file names this exact trap). 4. Commit, push to **both** Forgejo origin and the GitHub mirror — verify which `repoURL` the eventual ArgoCD `Application` for this app actually watches before assuming either push is the one that matters (`kong` app, for example, tracks the GitHub mirror specifically, not Forgejo). 5. Let ArgoCD sync. **No `kubectl apply`.** 6. Verify the app user can create tables (schema arrives in M6.2, but a throwaway `CREATE TABLE t(id text); DROP TABLE t;` proves connectivity here). ## Acceptance - `Cluster` reaches `Cluster in healthy state`. - ArgoCD shows the app `Synced/Healthy`. - No manual `psql` was run to get there. - Service is ClusterIP-only — not reachable from the LAN directly (M6.4's nginx route is the only path in). ## Verify **Harness:** `kubectl` and `psql` read-only checks after sync. **Integration test** — `verify/m6.1.sh`, output diffed against `expected/m6.1.txt`: 1. `a1_cluster_healthy` — `kubectl get cluster -n agent-manager agent-manager-db` reports all instances ready. 2. `a2_clusterip_only` — `kubectl get svc -n agent-manager -o jsonpath='{.items[*].spec.type}'` contains no `LoadBalancer` or `NodePort`. 3. `a3_argocd_synced` — the owning app is `Synced/Healthy`. 4. `a4_app_user_can_ddl` — as `app`, `CREATE TABLE t(id text); DROP TABLE t;` succeeds. 5. `a5_no_lan_route_yet` — connection attempt from outside the cluster network fails at this point in the plan (M6.4 hasn't landed). **Command:** `bash verify/m6.1.sh | diff - expected/m6.1.txt` **False pass:** - Confirming sync without checking service type. A `Cluster` can be healthy and `Synced` while someone fat-fingered a `LoadBalancer` type into the manifest, silently violating the "dedicated ingress, not raw LAN IP" network-path decision this whole migration made. Assertion 2 is the guard. ## Traps - Forgetting the kustomization resource list (same trap M2.2 already named) — file sits in git, ArgoCD reports Synced, objects never exist. - Adding `prune: true` without accounting for CNPG-operator-created children (Services, Secrets, PVCs). M2.2's Traps section already hit this on `llm-serving`; same fix applies here (`prune: false`). --- Background: `k8s/infra/databases/temporal-db.yaml` · [M2.2](M2.2-memory-db-manifest.md) (same pattern, different app)