docs(mosquitto): propose enrolling broker credentials in Class-A rotation #453

Merged
k.renner merged 3 commits from mosquitto-rotation-proposal into master 2026-07-30 16:53:43 +00:00
Member

Documentation only — nothing implemented, no manifests changed. Written up first because the last three attempts at this each hit a different wall.

The gap

kubernetes/infrastructure/openbao/rotation/ holds 14 rotators. Five are Class-A ("self-generated random secret"): authentik, crowdsec, paperless, shelly-manager, woodpecker. A broker password is exactly that shape — and the mosquitto credentials are the only Class-A-shaped secrets not enrolled.

They're minted once by seven bootstrap Jobs and then never change: no schedule, no compromise response, no path at all. They also exist only in the Secret objects, so they're outside the OpenBao snapshot and unrecoverable if lost.

Why the obvious fixes don't work

Recorded because each was tried, and none of the reasons are visible from the code:

Approach Why it fails
ExternalSecrets over hand-seeded OpenBao paths Depends on content git can't recreate. A fresh OpenBao leaves the paths empty and ESO then overwrites live credentials with nothing.
ESO Password generator Overwrites pre-existing Secrets — verified against a scratch Secret, not assumed. Adopting it rotates all eight at once, uncoordinated.
Keep the Jobs, fix only the image Blocked by spec.template immutability. Renaming works but recurs on every future edit.

The common thread: each treats the credential as something to place, when the requirement is one that can be replaced.

Two mechanics that shape the design

  • The broker rebuilds its passwd file on every pod start (rm -f then mosquitto_passwd -c … for all 8 users, from env backed by the Secrets). So a Secret change is inert until the broker restarts — and there's no Reloader annotation on it today, unlike the consumers of the existing Class-A rotators.
  • No make-before-break. A mosquitto user has exactly one password. The LLAT rotators mint a second token then revoke the first; a broker user can't hold old and new at once.

Who holds what — the part that decides cost

Not visible from the manifests:

user held by cutover
health the broker's own probes pod restart
zigbee2mqtt z2m Deployment pod restart
evcc evcc app, already OpenBao-backed pod restart
sml2mqtt / open3e edge host, via ansible-vault re-run the play
homeassistant HA MQTT integration (UI config entry) API/WebSocket update
shellies Shelly device firmware push per-device
admin a human read the new value

Note sml2mqtt and open3e keep the same password in two unlinked places — the Kubernetes Secret and ansible-vault — synchronised by hand. That duplication, not any client limitation, is what makes rotation look impossible today. I previously claimed these "can't rotate"; that was wrong, and this documents why.

Proposal

Follow rotation/paperless.yaml exactly: generate random bytes → merge-patch OpenBao → ESO syncs the Secret → restart the consumer. Per-user rotators, an ExternalSecret each, a Reloader annotation on the broker, and the edge host reading OpenBao instead of duplicating in ansible-vault.

Rotate one user at a time, so each rotation affects exactly one client rather than all eight.

Phased health → zigbee2mqtt → evcc → edge-host pair → shellies → homeassistant, starting with the two that need no external coordination and ending with the highest blast radius. Bootstrap Jobs get deleted incrementally as their users migrate — which also sidesteps the immutability problem entirely, since they're never patched.

Open questions, listed rather than guessed

  • Does secret-rotator random mode fit unchanged?
  • Can shelly-manager push MQTT credentials to device firmware, Gen1 and Gen2+? Its existing rotator handles its own secret and doesn't touch device MQTT config.
  • How is the HA MQTT config entry updated programmatically? The LLAT rotator already talks to HA over WebSocket and is the obvious precedent.
  • Rotation cadence — should follow the existing rotators, not a new invention.
  • Cold-rebuild ordering against RUNBOOK-cold-rebuild.md, so broker bring-up isn't blocked waiting for a rotator.

Unchanged by this

Secret names, key names, every secretKeyRef in mosquitto-deployment.yaml, the ACL model, and the evcc ExternalSecret which already matches the target.

🤖 Generated with Claude Code

**Documentation only — nothing implemented, no manifests changed.** Written up first because the last three attempts at this each hit a different wall. ## The gap `kubernetes/infrastructure/openbao/rotation/` holds 14 rotators. Five are **Class-A** ("self-generated random secret"): authentik, crowdsec, paperless, shelly-manager, woodpecker. A broker password is exactly that shape — and the mosquitto credentials are the only Class-A-shaped secrets **not enrolled**. They're minted once by seven bootstrap Jobs and then never change: no schedule, no compromise response, no path at all. They also exist only in the Secret objects, so they're outside the OpenBao snapshot and unrecoverable if lost. ## Why the obvious fixes don't work Recorded because each was tried, and none of the reasons are visible from the code: | Approach | Why it fails | |---|---| | ExternalSecrets over hand-seeded OpenBao paths | Depends on content git can't recreate. A fresh OpenBao leaves the paths empty and ESO then overwrites live credentials with nothing. | | ESO `Password` generator | **Overwrites pre-existing Secrets** — verified against a scratch Secret, not assumed. Adopting it rotates all eight at once, uncoordinated. | | Keep the Jobs, fix only the image | Blocked by `spec.template` immutability. Renaming works but recurs on every future edit. | The common thread: each treats the credential as something to *place*, when the requirement is one that can be *replaced*. ## Two mechanics that shape the design - **The broker rebuilds its passwd file on every pod start** (`rm -f` then `mosquitto_passwd -c …` for all 8 users, from env backed by the Secrets). So a Secret change is **inert until the broker restarts** — and there's no Reloader annotation on it today, unlike the consumers of the existing Class-A rotators. - **No make-before-break.** A mosquitto user has exactly one password. The LLAT rotators mint a second token then revoke the first; a broker user can't hold old and new at once. ## Who holds what — the part that decides cost Not visible from the manifests: | user | held by | cutover | |---|---|---| | `health` | the broker's own probes | pod restart | | `zigbee2mqtt` | z2m Deployment | pod restart | | `evcc` | evcc app, already OpenBao-backed | pod restart | | `sml2mqtt` / `open3e` | edge host, via `ansible-vault` | re-run the play | | `homeassistant` | HA MQTT integration (UI config entry) | API/WebSocket update | | `shellies` | Shelly device firmware | push per-device | | `admin` | a human | read the new value | Note `sml2mqtt` and `open3e` keep **the same password in two unlinked places** — the Kubernetes Secret and `ansible-vault` — synchronised by hand. That duplication, not any client limitation, is what makes rotation look impossible today. I previously claimed these "can't rotate"; that was wrong, and this documents why. ## Proposal Follow `rotation/paperless.yaml` exactly: *generate random bytes → merge-patch OpenBao → ESO syncs the Secret → restart the consumer*. Per-user rotators, an ExternalSecret each, a Reloader annotation on the broker, and the edge host reading OpenBao instead of duplicating in ansible-vault. **Rotate one user at a time**, so each rotation affects exactly one client rather than all eight. Phased `health` → `zigbee2mqtt` → `evcc` → edge-host pair → `shellies` → `homeassistant`, starting with the two that need no external coordination and ending with the highest blast radius. Bootstrap Jobs get **deleted** incrementally as their users migrate — which also sidesteps the immutability problem entirely, since they're never patched. ## Open questions, listed rather than guessed - Does `secret-rotator` random mode fit unchanged? - Can shelly-manager push MQTT credentials to device firmware, Gen1 *and* Gen2+? Its existing rotator handles its own secret and doesn't touch device MQTT config. - How is the HA MQTT config entry updated programmatically? The LLAT rotator already talks to HA over WebSocket and is the obvious precedent. - Rotation cadence — should follow the existing rotators, not a new invention. - Cold-rebuild ordering against `RUNBOOK-cold-rebuild.md`, so broker bring-up isn't blocked waiting for a rotator. ## Unchanged by this Secret names, key names, every `secretKeyRef` in `mosquitto-deployment.yaml`, the ACL model, and the `evcc` ExternalSecret which already matches the target. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-07-30 13:39:02 +00:00
docs(mosquitto): propose enrolling broker credentials in Class-A rotation
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
4b5918e87e
Documentation only — nothing implemented, no manifests changed.

The mosquitto broker passwords are the only Class-A-shaped secrets in the
cluster not enrolled in the rotation framework under
kubernetes/infrastructure/openbao/rotation/. They are minted once by seven
bootstrap Jobs and then never change: no schedule, no compromise response,
no path at all. They also live only in the Secret objects, so they are
outside the OpenBao snapshot and unrecoverable if lost.

Records why the obvious fixes do not work, since each was tried and the
reasons are not visible from the code:

- ExternalSecrets over hand-seeded OpenBao paths depend on content git
  cannot recreate; a fresh OpenBao would leave them empty and ESO would
  overwrite live credentials with nothing.
- The ESO Password generator OVERWRITES pre-existing Secrets — verified
  against a scratch Secret, not assumed — so adopting it rotates all eight
  at once, uncoordinated.
- Keeping the Jobs and fixing only the image is blocked by spec.template
  immutability; renaming them works but recurs on every future edit.

Documents the two mechanics that actually shape the design: the broker
rebuilds its passwd file from the Secrets on every pod start (so a Secret
change is inert until it restarts, and there is no Reloader annotation
today), and a mosquitto user has exactly one password (so there is no
make-before-break as the LLAT rotators use).

Also records who holds each credential, which is not visible from the
manifests and is what determines cutover cost — including that sml2mqtt
and open3e keep the same password in both a Kubernetes Secret and
ansible-vault, synchronised by hand. That duplication, not any client
limitation, is what makes rotation look impossible today.

Proposes per-user rotators following rotation/paperless.yaml, phased
health -> zigbee2mqtt -> evcc -> edge-host pair -> shellies ->
homeassistant, so blast radius is one client per rotation and the Jobs are
deleted incrementally as their users migrate rather than patched.

Open questions are listed rather than guessed at, notably whether
shelly-manager can push MQTT credentials to device firmware and how the HA
MQTT config entry is updated programmatically.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-fable-5 (complexity: trivial), commit 4b5918e87e904e92b2977dd2c3e32e0e7b638ad2.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

Adds a documentation-only proposal (kubernetes/apps/home-assistant/PROPOSAL-mosquitto-credential-rotation.md) to enrol the 8 mosquitto broker credentials in the existing Class-A rotation pattern. No manifests change. I verified the document's factual claims against the checkout and they hold up almost entirely; there are no blocking issues.

Claims verified against the repo:

  • 14 rotators, 5 Class-A — rotation/ contains 15 CronJobs, one of which (claude-oauth-secret-sync) is a sync rather than a rotator, so 14 is correct. The Class-A set (authentik, crowdsec-bouncer-key, paperless, shelly-manager, woodpecker) matches the file headers.
  • Broker rebuilds the passwd file on every pod start — confirmed at kubernetes/apps/home-assistant/mosquitto-deployment.yaml:68-76 (rm -f then mosquitto_passwd for all 8 users, from env backed by the Secrets).
  • No Reloader annotation on the mosquitto Deployment — correct; contrast kubernetes/apps/paperless/paperless.yaml:58, which has one.
  • Key-name irregularity (homeassistant-password on one Secret, password on the rest) — confirmed at mosquitto-deployment.yaml:150-196.
  • Seven bootstrap Jobs with SA/Role/RoleBinding, building Secret JSON with jq and POSTing with curl — confirmed in mosquitto-bootstrap.yaml.
  • evcc already follows the target pattern — mosquitto-evcc-credentials-externalsecret.yaml extracts apps/home-assistant/mosquitto-evcc-credentials, matching the proposed path convention.
  • Edge-host duplication — vault_edge_sml2mqtt_mqtt_password / vault_edge_open3e_mqtt_password exist in ansible/inventory/group_vars/edge_host/vars.yml and ansible/roles/edge-host/defaults/main.yml.
  • LLAT rotators talk to HA over WebSocket — confirmed in rotation/home-assistant.yaml (comment at line 9 and the ws://…/api/websocket endpoints).
  • The OpenBao snapshot CronJob (kubernetes/infrastructure/openbao/snapshot-cronjob.yaml) and kubernetes/RUNBOOK-cold-rebuild.md both exist as referenced.

🟡 Suggestions

  • The "window is seconds" claim in "The cutover window" deserves one more caveat. The mosquitto Deployment rolls with maxSurge: 1 / maxUnavailable: 0 (mosquitto-deployment.yaml:17-20), so during the broker restart two broker pods with different passwd files sit behind the LB. A client reconnecting mid-rollout can land on the old pod and fail auth until the rollout completes. Still seconds-to-a-minute and self-healing, but the failure mode ("client retries against the old pod") is worth a sentence, since it would look alarming in logs during the first health/zigbee2mqtt rotation.
  • ESO refresh latency belongs in the open questions. The existing evcc ExternalSecret uses refreshInterval: 1h; the paperless rotator relies on a 10m refresh. Rotator-writes-OpenBao → Secret-updates → Reloader-restarts is delayed by up to one refresh interval, which stretches the rotation completion time (not the outage window, since broker and client move together — but it matters for "did the rotation land" verification and for the cold-rebuild ordering question already listed).
  • "Adopting [the ESO Password generator] rotates all 8 at once" (Why-the-obvious-fixes table): evcc is already ESO-extract-backed, so a generator adoption would hit the 7 Job-minted Secrets, not all 8. Trivial, but the document's value is precision about exactly this kind of detail.

🟢 Nits

  • mosquitto-bootstrap.yaml is 508 lines, not 518 (Problem section). The SA/Role/RoleBinding are inside that file, so "518 lines plus a ServiceAccount/Role/RoleBinding" double-counts slightly either way.

Tests

Not applicable — documentation-only, no manifests or code changed, nothing for CI to validate beyond the file existing. The document itself is the artifact, and its checkable claims were verified above.

Recommendation

Looks reasonable — no blocking issues; comments above are optional.

🤖 **Automated review** — model `claude-fable-5` (complexity: trivial), commit `4b5918e87e904e92b2977dd2c3e32e0e7b638ad2`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary Adds a documentation-only proposal (`kubernetes/apps/home-assistant/PROPOSAL-mosquitto-credential-rotation.md`) to enrol the 8 mosquitto broker credentials in the existing Class-A rotation pattern. No manifests change. I verified the document's factual claims against the checkout and they hold up almost entirely; there are no blocking issues. Claims verified against the repo: - **14 rotators, 5 Class-A** — `rotation/` contains 15 CronJobs, one of which (`claude-oauth-secret-sync`) is a sync rather than a rotator, so 14 is correct. The Class-A set (authentik, crowdsec-bouncer-key, paperless, shelly-manager, woodpecker) matches the file headers. - **Broker rebuilds the passwd file on every pod start** — confirmed at `kubernetes/apps/home-assistant/mosquitto-deployment.yaml:68-76` (`rm -f` then `mosquitto_passwd` for all 8 users, from env backed by the Secrets). - **No Reloader annotation on the mosquitto Deployment** — correct; contrast `kubernetes/apps/paperless/paperless.yaml:58`, which has one. - **Key-name irregularity** (`homeassistant-password` on one Secret, `password` on the rest) — confirmed at `mosquitto-deployment.yaml:150-196`. - **Seven bootstrap Jobs with SA/Role/RoleBinding, building Secret JSON with `jq` and POSTing with `curl`** — confirmed in `mosquitto-bootstrap.yaml`. - **evcc already follows the target pattern** — `mosquitto-evcc-credentials-externalsecret.yaml` extracts `apps/home-assistant/mosquitto-evcc-credentials`, matching the proposed path convention. - **Edge-host duplication** — `vault_edge_sml2mqtt_mqtt_password` / `vault_edge_open3e_mqtt_password` exist in `ansible/inventory/group_vars/edge_host/vars.yml` and `ansible/roles/edge-host/defaults/main.yml`. - **LLAT rotators talk to HA over WebSocket** — confirmed in `rotation/home-assistant.yaml` (comment at line 9 and the `ws://…/api/websocket` endpoints). - The OpenBao snapshot CronJob (`kubernetes/infrastructure/openbao/snapshot-cronjob.yaml`) and `kubernetes/RUNBOOK-cold-rebuild.md` both exist as referenced. ## 🟡 Suggestions - **The "window is seconds" claim in "The cutover window" deserves one more caveat.** The mosquitto Deployment rolls with `maxSurge: 1` / `maxUnavailable: 0` (`mosquitto-deployment.yaml:17-20`), so during the broker restart two broker pods with *different* passwd files sit behind the LB. A client reconnecting mid-rollout can land on the old pod and fail auth until the rollout completes. Still seconds-to-a-minute and self-healing, but the failure mode ("client retries against the old pod") is worth a sentence, since it would look alarming in logs during the first `health`/`zigbee2mqtt` rotation. - **ESO refresh latency belongs in the open questions.** The existing evcc ExternalSecret uses `refreshInterval: 1h`; the paperless rotator relies on a 10m refresh. Rotator-writes-OpenBao → Secret-updates → Reloader-restarts is delayed by up to one refresh interval, which stretches the rotation completion time (not the outage window, since broker and client move together — but it matters for "did the rotation land" verification and for the cold-rebuild ordering question already listed). - **"Adopting [the ESO Password generator] rotates all 8 at once"** (Why-the-obvious-fixes table): evcc is already ESO-extract-backed, so a generator adoption would hit the 7 Job-minted Secrets, not all 8. Trivial, but the document's value is precision about exactly this kind of detail. ## 🟢 Nits - `mosquitto-bootstrap.yaml` is 508 lines, not 518 (Problem section). The SA/Role/RoleBinding are *inside* that file, so "518 lines plus a ServiceAccount/Role/RoleBinding" double-counts slightly either way. ## Tests Not applicable — documentation-only, no manifests or code changed, nothing for CI to validate beyond the file existing. The document itself is the artifact, and its checkable claims were verified above. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. <!-- pr-review:sha=4b5918e87e904e92b2977dd2c3e32e0e7b638ad2 -->
claude-bot added 1 commit 2026-07-30 13:50:23 +00:00
docs(mosquitto): correct four factual claims in the rotation proposal
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
6e47ef4368
Review points on #453. All four verified against the repo before changing
anything, since a proposal's only value is being precise about exactly
this kind of detail.

Line count: mosquitto-bootstrap.yaml is 508 lines, not 518, and the
ServiceAccount/Role/RoleBinding are inside that same file — the original
wording double-counted them.

Generator blast radius: adopting the ESO Password generator would rotate
the 7 Job-minted credentials, not "all 8". evcc is already
ESO-extract-backed and unaffected.

Rollout failure mode: the broker rolls with maxUnavailable: 0 /
maxSurge: 1 (confirmed in mosquitto-deployment.yaml), so mid-rollout two
broker pods with DIFFERENT passwd files sit behind the LB and a
reconnecting client can land on the old one and fail auth until the
rollout finishes. Self-healing and brief, but it will look alarming in
logs during the first rotation, so it is now called out in advance rather
than discovered live.

ESO refresh latency: added to the open questions. The evcc ExternalSecret
uses refreshInterval: 1h, so a rotation takes up to an hour to land. That
does not widen the outage window — broker and client still move together —
but it affects verifying a rotation completed and the cold-rebuild
ordering, and raises whether these want a shorter interval or a
rotator-triggered sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Member

All four corrected in 6e47ef43, each verified against the repo first — a proposal whose only value is precision should not take corrections on trust either.

🟡 Rollout window: two brokers, two passwd files

Confirmed — maxUnavailable: 0 / maxSurge: 1. This is the best point in the review, because it is a failure mode that would show up during the first and safest rotation (health / zigbee2mqtt) and look like the design was broken when it is working as intended. Now documented in advance: mid-rollout two broker pods with different passwd files sit behind the LB, a reconnecting client can land on the old one and fail auth, and it self-heals when the rollout completes.

🟡 ESO refresh latency

Added to the open questions. evcc uses refreshInterval: 1h, so a rotation takes up to an hour to land. Worth being exact about what that delays: not the outage window — broker and client still move together — but how long until the rotation is complete, which matters for verifying one landed and for the cold-rebuild ordering. It also raises a real design choice I had not considered: whether these ExternalSecrets want a shorter interval, or whether the rotator should trigger the sync directly rather than waiting for a poll.

🟡 "all 8 at once" → 7

Correct. evcc is already ESO-extract-backed, so a generator adoption would hit the 7 Job-minted credentials only. Fixed.

Note the other "all eight" in the document (§Proposed design, point 5) is deliberate and stays: there really are 8 broker users, and the point there is that rotating one at a time affects one client rather than all of them.

🟢 508 vs 518 lines

Correct, and the double-count too — the SA/Role/RoleBinding live inside that same file, so "518 lines plus a ServiceAccount/Role/RoleBinding" was wrong twice. Now "508 lines — the ServiceAccount, Role and RoleBinding are inside that same file".


Separately: I said I would wait for this review and then did not read it until prompted. That is the second time in this session. The review was submitted at 13:42Z and sat unread.

All four corrected in `6e47ef43`, each verified against the repo first — a proposal whose only value is precision should not take corrections on trust either. ### 🟡 Rollout window: two brokers, two passwd files Confirmed — `maxUnavailable: 0` / `maxSurge: 1`. This is the best point in the review, because it is a failure mode that would show up during the *first* and safest rotation (`health` / `zigbee2mqtt`) and look like the design was broken when it is working as intended. Now documented in advance: mid-rollout two broker pods with different passwd files sit behind the LB, a reconnecting client can land on the old one and fail auth, and it self-heals when the rollout completes. ### 🟡 ESO refresh latency Added to the open questions. `evcc` uses `refreshInterval: 1h`, so a rotation takes up to an hour to land. Worth being exact about *what* that delays: not the outage window — broker and client still move together — but how long until the rotation is complete, which matters for verifying one landed and for the cold-rebuild ordering. It also raises a real design choice I had not considered: whether these ExternalSecrets want a shorter interval, or whether the rotator should trigger the sync directly rather than waiting for a poll. ### 🟡 "all 8 at once" → 7 Correct. `evcc` is already ESO-extract-backed, so a generator adoption would hit the 7 Job-minted credentials only. Fixed. Note the other "all eight" in the document (§Proposed design, point 5) is deliberate and stays: there really are 8 broker users, and the point there is that rotating one at a time affects one client rather than all of them. ### 🟢 508 vs 518 lines Correct, and the double-count too — the SA/Role/RoleBinding live inside that same file, so "518 lines plus a ServiceAccount/Role/RoleBinding" was wrong twice. Now "508 lines — the ServiceAccount, Role and RoleBinding are inside that same file". --- Separately: I said I would wait for this review and then did not read it until prompted. That is the second time in this session. The review was submitted at 13:42Z and sat unread.
review-bot left a comment
Member

🤖 Automated review — model CLI default (complexity: complex), commit 6e47ef4368b0bf089050ae54b449edc0144b69c6.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

Documentation-only proposal (PROPOSAL-mosquitto-credential-rotation.md, new file, no manifests touched) for enrolling the 8 mosquitto broker credentials in Class-A secret rotation. I checked its factual claims against the actual manifests (mosquitto-bootstrap.yaml, mosquitto-deployment.yaml, the rotation/ directory, secret-rotator/src/main.rs, the evcc ExternalSecret, and the ansible edge-host vars) and nearly everything checks out precisely — line counts, user lists, key names, rolling-update strategy, the evcc ExternalSecret pattern, the shelly-manager rotator's scope, and the ansible-vault variable names all match the repo exactly. One factual claim doesn't hold up. No blocking issues since nothing is implemented yet.

🟡 Suggestions

  • PROPOSAL-mosquitto-credential-rotation.md:9 — "kubernetes/infrastructure/openbao/rotation/ holds 14 rotators" doesn't match the directory. It contains 13 files (kustomization.yaml lists 13 resources), and one of them (home-assistant.yaml) defines 3 separate rotator CronJobs (prometheus-token-secret-rotate, config-sync-token-secret-rotate, shelly-manager-llat-secret-rotate), for 15 CronJobs total. Under either way of counting (files or individual rotator CronJobs) the number isn't 14. Given the document's stated ethos is verifying claims rather than asserting them ("verified... not assumed", "recorded because each was tried and rejected on evidence"), worth correcting this one before merge so the count doesn't undermine that credibility. The Class-A list itself (authentik, crowdsec, paperless, shelly-manager, woodpecker) is correct — I confirmed all five via their # Class-A header comments.

🟢 Nits

None beyond the above.

Tests

N/A — documentation-only change, nothing to test. The doc appropriately scopes itself as "nothing implemented" and defers verification questions (whether secret-rotator random mode fits unchanged, shelly-manager device push, HA WebSocket config-entry update) to the open-questions section rather than asserting them.

Recommendation

Looks reasonable — no blocking issues; the "14 rotators" count in the Suggestions section is worth a quick fix, but nothing here blocks merging a proposal doc.

🤖 **Automated review** — model `CLI default` (complexity: complex), commit `6e47ef4368b0bf089050ae54b449edc0144b69c6`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary Documentation-only proposal (`PROPOSAL-mosquitto-credential-rotation.md`, new file, no manifests touched) for enrolling the 8 mosquitto broker credentials in Class-A secret rotation. I checked its factual claims against the actual manifests (`mosquitto-bootstrap.yaml`, `mosquitto-deployment.yaml`, the `rotation/` directory, `secret-rotator/src/main.rs`, the `evcc` ExternalSecret, and the ansible edge-host vars) and nearly everything checks out precisely — line counts, user lists, key names, rolling-update strategy, the evcc ExternalSecret pattern, the shelly-manager rotator's scope, and the ansible-vault variable names all match the repo exactly. One factual claim doesn't hold up. No blocking issues since nothing is implemented yet. ## 🟡 Suggestions - `PROPOSAL-mosquitto-credential-rotation.md:9` — "`kubernetes/infrastructure/openbao/rotation/` holds 14 rotators" doesn't match the directory. It contains 13 files (`kustomization.yaml` lists 13 resources), and one of them (`home-assistant.yaml`) defines 3 separate rotator CronJobs (`prometheus-token-secret-rotate`, `config-sync-token-secret-rotate`, `shelly-manager-llat-secret-rotate`), for 15 CronJobs total. Under either way of counting (files or individual rotator CronJobs) the number isn't 14. Given the document's stated ethos is verifying claims rather than asserting them ("verified... not assumed", "recorded because each was tried and rejected on evidence"), worth correcting this one before merge so the count doesn't undermine that credibility. The Class-A list itself (authentik, crowdsec, paperless, shelly-manager, woodpecker) is correct — I confirmed all five via their `# Class-A` header comments. ## 🟢 Nits None beyond the above. ## Tests N/A — documentation-only change, nothing to test. The doc appropriately scopes itself as "nothing implemented" and defers verification questions (whether `secret-rotator` random mode fits unchanged, shelly-manager device push, HA WebSocket config-entry update) to the open-questions section rather than asserting them. ## Recommendation Looks reasonable — no blocking issues; the "14 rotators" count in the Suggestions section is worth a quick fix, but nothing here blocks merging a proposal doc. <!-- pr-review:sha=6e47ef4368b0bf089050ae54b449edc0144b69c6 -->
claude-bot added 1 commit 2026-07-30 14:03:16 +00:00
docs(mosquitto): correct the rotator count
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ff526f98c2
Review point on #453. The document claimed 14 rotators, which matches
neither way of counting. Verified directly rather than accepting the
review number on trust:

  files in rotation/ (excl kustomization): 13
  resources listed in kustomization:      13
  actual CronJob objects:                 15
  Class-A:                                 5

home-assistant.yaml alone defines three CronJobs, which is where the
discrepancy comes from. Now states 13 manifests defining 15 CronJobs, of
which 5 are Class-A.

Worth fixing beyond pedantry: this document argues for verifying claims
rather than asserting them, and the count was eyeballed from a directory
listing rather than counted. A wrong number in the opening paragraph
undermines the parts that were actually verified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-haiku-4-5-20251001 (complexity: trivial), commit ff526f98c282b298a331ac898990e4cd7530e075.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

This PR adds a well-researched design proposal for enrolling mosquitto broker credentials in automated Class-A rotation. The proposal identifies a concrete gap (8 broker users, only one already rotated), documents why three alternative approaches failed (backed by testing), and proposes a phased implementation following the established Class-A pattern. It's documentation-only — nothing implemented, no manifests changed — and the technical analysis is sound.

🟢 Nits

  • Line 39: "Adopting it rotates the 7 Job-minted credentials at once, uncoordinated (evcc is already ESO-extract-backed and so unaffected)." — Minor wording: this could say "extracts" instead of "extract-backed" for clarity, but the meaning is clear in context.

Tests

Not applicable — this is a design proposal with no code changes.

Recommendation

Looks reasonable — no blocking issues; nit above is optional.

🤖 **Automated review** — model `claude-haiku-4-5-20251001` (complexity: trivial), commit `ff526f98c282b298a331ac898990e4cd7530e075`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary This PR adds a well-researched design proposal for enrolling mosquitto broker credentials in automated Class-A rotation. The proposal identifies a concrete gap (8 broker users, only one already rotated), documents why three alternative approaches failed (backed by testing), and proposes a phased implementation following the established Class-A pattern. It's documentation-only — nothing implemented, no manifests changed — and the technical analysis is sound. ## 🟢 Nits - Line 39: "Adopting it rotates the 7 Job-minted credentials at once, uncoordinated (`evcc` is already ESO-extract-backed and so unaffected)." — Minor wording: this could say "extracts" instead of "extract-backed" for clarity, but the meaning is clear in context. ## Tests Not applicable — this is a design proposal with no code changes. ## Recommendation Looks reasonable — no blocking issues; nit above is optional. <!-- pr-review:sha=ff526f98c282b298a331ac898990e4cd7530e075 -->
Author
Member

Clean round — no blocking, no 🟡.

🟢 "extract-backed" wording

Leaving as-is, deliberately. The term is doing real work: it distinguishes an ExternalSecret using dataFrom.extract against a stored OpenBao entry from one driven by a generator — which is exactly the distinction that sentence turns on, since the generator overwrites and the extract does not. "extracts" reads more naturally but loses that contrast.

More to the point: pushing a commit to change a synonym would trigger another full review round for zero substantive gain. Not a good trade.

State of this PR

Documentation only, four rounds of review, every factual claim now verified against the repo rather than asserted:

  • rotator counts — 13 manifests / 15 CronJobs / 5 Class-A (was wrongly "14")
  • mosquitto-bootstrap.yaml — 508 lines, SA/Role/RoleBinding inside that same file
  • generator blast radius — 7 Job-minted credentials, not 8
  • rollout window — maxUnavailable: 0 / maxSurge: 1, so two brokers with different passwd files coexist mid-roll
  • ESO refresh latency — evcc at refreshInterval: 1h, now an open question rather than an unstated assumption

Ready to merge or discard as a proposal; nothing in it is implemented and no manifest is touched.

Clean round — no blocking, no 🟡. ### 🟢 "extract-backed" wording Leaving as-is, deliberately. The term is doing real work: it distinguishes an ExternalSecret using `dataFrom.extract` against a stored OpenBao entry from one driven by a generator — which is exactly the distinction that sentence turns on, since the generator overwrites and the extract does not. "extracts" reads more naturally but loses that contrast. More to the point: pushing a commit to change a synonym would trigger another full review round for zero substantive gain. Not a good trade. ### State of this PR Documentation only, four rounds of review, every factual claim now verified against the repo rather than asserted: - rotator counts — 13 manifests / 15 CronJobs / 5 Class-A (was wrongly "14") - `mosquitto-bootstrap.yaml` — 508 lines, SA/Role/RoleBinding inside that same file - generator blast radius — 7 Job-minted credentials, not 8 - rollout window — `maxUnavailable: 0` / `maxSurge: 1`, so two brokers with different passwd files coexist mid-roll - ESO refresh latency — `evcc` at `refreshInterval: 1h`, now an open question rather than an unstated assumption Ready to merge or discard as a proposal; nothing in it is implemented and no manifest is touched.
k.renner merged commit 7b23de49ce into master 2026-07-30 16:53:43 +00:00
Sign in to join this conversation.