# M6.5 — Postgres credentials for the Mac client | Field | Value | |---|---| | Phase | M6 — agent-manager migration | | Size | S — <1 day | | Status | ⬜ Not started | | Flags | homelab | | Spec | inlined below | | Blocks | M6.1 | ## Goal The `agent-manager` process running on the Mac gets a Postgres connection string/credentials, managed the same ksops way the rest of this homelab handles secrets — not a password pasted into a local config file or env var by hand. ## Facts (inlined — no spec read needed) **Existing pattern to follow: ksops-managed secret**, same as `model-invoke-apikey` elsewhere in this homelab. CNPG itself already generates an in-cluster Secret for the `app` user (`agent-manager-db-app` by its usual naming convention) — the work here is getting that credential (or a dedicated read-write user, if reusing the CNPG-generated superuser-adjacent `app` credential isn't desired) to a process running outside the cluster, on the Mac. **Two credentials touch two different trust boundaries:** - Cluster-internal: CNPG's own generated Secret, already ksops-free (CNPG manages it, not this repo). - Mac client: needs that same username/password (or a separate, narrower-scoped user) delivered to `~/workplace/agent-manager`'s runtime config, via a ksops-encrypted file in git rather than a manually-copied value. ## Steps 1. Decide: reuse CNPG's auto-generated `app` user, or create a dedicated `agent_manager_client` role scoped to only the `sessions`/`groups`/ `settings`/`review_*` tables (narrower blast radius if the credential ever leaks from a Mac laptop, which is a meaningfully different threat model than a credential that only ever lives inside the cluster). 2. If a dedicated role: add it via CNPG's declarative `Database` / `postInitSQL` (or a one-time migration in M6.2's schema setup) rather than a manual `psql` grant. 3. Encrypt the resulting connection string (or user/password pair) with ksops, following the exact file layout `model-invoke-apikey` uses. 4. Commit the encrypted secret to git (safe — that's the point of ksops), push to both remotes. 5. Wire agent-manager's config loading (check `cmd/` / existing config file handling on the `add-headless-spawn` branch) to read the decrypted value at runtime — decide whether decryption happens via a `sops exec-env`-style wrapper the Mac invokes, or a decrypted file materialized once locally and gitignored, matching whatever `model-invoke-apikey`'s consumers already do. 6. Verify agent-manager connects using only the ksops-sourced credential — no plaintext password anywhere in the repo or in shell history. ## Acceptance - No Postgres password appears in plaintext in git, in `~/workplace/agent-manager`'s tracked config, or was typed directly into a `kubectl`/`psql` command during setup. - The credential is encrypted with ksops in the same repo location/ pattern as `model-invoke-apikey`. - agent-manager on the Mac successfully authenticates through M6.4's ingress route using this credential. ## Verify **Harness:** manual — this is a secret-handling task, not one to automate a fake credential through. **Checklist** (no `verify/*.sh`, since scripting a real credential check means committing something that either leaks a real secret or asserts nothing): 1. `git grep -i "password"` in both the homelab repo and `~/workplace/agent-manager` shows only ksops-encrypted blobs or references to environment/config loading, never a literal value. 2. The ksops secret file's structure matches `model-invoke-apikey`'s (same encryption provider, same key layout) — a side-by-side diff of the YAML structure (not values) confirms this. 3. `psql` from the Mac using the decrypted credential (via M6.4's route) succeeds. 4. Revoking/rotating the credential (delete the CNPG Secret or drop the dedicated role, if one was created) and re-running step 3 fails cleanly — confirms the client isn't caching or falling back to something else. **False pass:** - Confirming connectivity once with a credential typed in manually during setup, then wiring the ksops path afterward without re-testing that the *ksops-sourced* value is what actually authenticates. Checklist item 4 (rotate and confirm the old path is really gone) is the guard against "it worked, but only because of leftover state." ## Traps - Granting the dedicated role (if created) superuser or database-owner-equivalent privileges "to avoid permission errors during setup" and never narrowing it afterward — defeats the reason to create a dedicated role at all. - Storing the decrypted credential in a file the Mac client reads that isn't gitignored, recreating the plaintext-secret problem one directory away from the ksops-encrypted source of truth. --- Background: [M6.1](M6.1-agent-manager-db-manifest.md) · `model-invoke-apikey` (ksops pattern reference, this homelab)