Ports paperless, woodpecker, shelly-manager, authentik and crowdsec-bouncer-key from inline shell to the secret-rotator binary's random mode. This is the payoff the binary was built for — five copies of the same bao kv patch boilerplate collapse to one tested, digest-pinned image.
Net -49 lines. Removes the OpenBao-login shell (duplicated in all five) and the busybox/openbao runtime image.
Behaviour preserved exactly
preserved
output bytes
STANDARD base64 w/ padding, RFC 4648 unit-tested; 50 bytes for the Django keys (paperless, authentik), 32 for the rest
admin-password + api-key still land in one patch, no split state
auth
same k8s-auth roles / SAs / schedule; roles already grant read+patch
New safety property the shell lacked
assert_fields_exist refuses to run if a target field is absent — a typo becomes a loud failure with the old secret untouched, instead of a silent no-op that adds a stray field.
Testing
The random mode + assert_fields_exist are new code (only ha-token had run live). Tested against real OpenBao on a throwaway path + role — zero blast radius, no live consumer touched:
happy path: two fields rotated to 44-char values (32-byte base64), the co-located keep-me field UNTOUCHED, multi-field single patch (the shelly-manager shape)
fail-safe: a nonexistent target field → job Failed, error names the missing field and lists what's present, and the real field is unchanged (whole patch aborted, no partial write)
Static-verified that all five real paths currently hold their target fields, and all five pass server-side admission dry-run under the restricted PSA.
I did not trigger a live rotation of any of the five — each has real user-facing effects (paperless/authentik log users out; woodpecker/crowdsec restart) and there was an active CI pipeline. The scratch test proves the mechanism; the quarterly schedule (next: Oct 1, 04:00) exercises them naturally. Image digest sha256:159e5888….
Ports paperless, woodpecker, shelly-manager, authentik and crowdsec-bouncer-key from inline shell to the `secret-rotator` binary's `random` mode. This is the payoff the binary was built for — five copies of the same `bao kv patch` boilerplate collapse to one tested, digest-pinned image.
**Net -49 lines.** Removes the OpenBao-login shell (duplicated in all five) and the busybox/openbao runtime image.
## Behaviour preserved exactly
| | preserved |
|---|---|
| output bytes | STANDARD base64 w/ padding, RFC 4648 unit-tested; 50 bytes for the Django keys (paperless, authentik), 32 for the rest |
| co-located fields | merge-patch keeps `WOODPECKER_GITEA_*`, authentik `smtp-password` |
| shelly-manager | `admin-password` + `api-key` still land in **one** patch, no split state |
| auth | same k8s-auth roles / SAs / schedule; roles already grant read+patch |
## New safety property the shell lacked
`assert_fields_exist` refuses to run if a target field is absent — a typo becomes a **loud failure with the old secret untouched**, instead of a silent no-op that adds a stray field.
## Testing
The `random` mode + `assert_fields_exist` are new code (only `ha-token` had run live). Tested against **real OpenBao on a throwaway path + role — zero blast radius, no live consumer touched**:
- **happy path**: two fields rotated to 44-char values (32-byte base64), the co-located `keep-me` field **UNTOUCHED**, multi-field single patch (the shelly-manager shape)
- **fail-safe**: a nonexistent target field → job **Failed**, error names the missing field and lists what's present, and the real field is **unchanged** (whole patch aborted, no partial write)
Static-verified that all five real paths currently hold their target fields, and all five pass **server-side admission dry-run** under the restricted PSA.
I did **not** trigger a live rotation of any of the five — each has real user-facing effects (paperless/authentik log users out; woodpecker/crowdsec restart) and there was an active CI pipeline. The scratch test proves the mechanism; the quarterly schedule (next: Oct 1, 04:00) exercises them naturally. Image digest `sha256:159e5888…`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Replaces the inline `head -c N /dev/urandom | base64 | tr -d '\n'` +
`bao kv patch` shell in each of paperless, woodpecker, shelly-manager,
authentik and crowdsec-bouncer-key with the secret-rotator binary's `random`
mode. Net -49 lines; the OpenBao login boilerplate (duplicated in all five)
and the busybox/openbao runtime image go away.
Behaviour preserved exactly:
- byte-compatible output — N raw bytes, STANDARD base64 with padding (RFC
4648 unit-tested); 50 bytes for the Django keys (paperless, authentik),
32 for the rest
- merge-patch, so co-located fields are untouched (WOODPECKER_GITEA_*,
authentik smtp-password)
- shelly-manager's two fields (admin-password, api-key) still land in ONE
patch, no split state
- same k8s-auth roles, SAs, schedule, quarterly timing — the roles already
grant read+patch, which the binary's assert_fields_exist needs
Plus a new safety property the shell lacked: assert_fields_exist refuses to
run if a target field is absent, turning a typo into a loud failure with the
old secret untouched, instead of a silent no-op that adds a stray field.
Image pinned by digest sha256:159e5888… (all three ha-token fixes; the shared
OpenBao code path is the same one `random` uses).
Tested against real OpenBao on a throwaway path+role (zero blast radius, no
live consumer touched):
- happy path: two fields rotated to 44-char values (32-byte base64),
co-located field preserved, multi-field single patch
- fail-safe: a nonexistent target field -> job Failed, clear error, the real
field left unchanged (whole patch aborted, no partial write)
All five pass server-side admission dry-run under the restricted PSA.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Ports paperless, woodpecker, shelly-manager, authentik and crowdsec-bouncer-key from inline shell to the
secret-rotatorbinary'srandommode. This is the payoff the binary was built for — five copies of the samebao kv patchboilerplate collapse to one tested, digest-pinned image.Net -49 lines. Removes the OpenBao-login shell (duplicated in all five) and the busybox/openbao runtime image.
Behaviour preserved exactly
WOODPECKER_GITEA_*, authentiksmtp-passwordadmin-password+api-keystill land in one patch, no split stateNew safety property the shell lacked
assert_fields_existrefuses to run if a target field is absent — a typo becomes a loud failure with the old secret untouched, instead of a silent no-op that adds a stray field.Testing
The
randommode +assert_fields_existare new code (onlyha-tokenhad run live). Tested against real OpenBao on a throwaway path + role — zero blast radius, no live consumer touched:keep-mefield UNTOUCHED, multi-field single patch (the shelly-manager shape)Static-verified that all five real paths currently hold their target fields, and all five pass server-side admission dry-run under the restricted PSA.
I did not trigger a live rotation of any of the five — each has real user-facing effects (paperless/authentik log users out; woodpecker/crowdsec restart) and there was an active CI pipeline. The scratch test proves the mechanism; the quarterly schedule (next: Oct 1, 04:00) exercises them naturally. Image digest
sha256:159e5888….🤖 Generated with Claude Code
Replaces the inline `head -c N /dev/urandom | base64 | tr -d '\n'` + `bao kv patch` shell in each of paperless, woodpecker, shelly-manager, authentik and crowdsec-bouncer-key with the secret-rotator binary's `random` mode. Net -49 lines; the OpenBao login boilerplate (duplicated in all five) and the busybox/openbao runtime image go away. Behaviour preserved exactly: - byte-compatible output — N raw bytes, STANDARD base64 with padding (RFC 4648 unit-tested); 50 bytes for the Django keys (paperless, authentik), 32 for the rest - merge-patch, so co-located fields are untouched (WOODPECKER_GITEA_*, authentik smtp-password) - shelly-manager's two fields (admin-password, api-key) still land in ONE patch, no split state - same k8s-auth roles, SAs, schedule, quarterly timing — the roles already grant read+patch, which the binary's assert_fields_exist needs Plus a new safety property the shell lacked: assert_fields_exist refuses to run if a target field is absent, turning a typo into a loud failure with the old secret untouched, instead of a silent no-op that adds a stray field. Image pinned by digest sha256:159e5888… (all three ha-token fixes; the shared OpenBao code path is the same one `random` uses). Tested against real OpenBao on a throwaway path+role (zero blast radius, no live consumer touched): - happy path: two fields rotated to 44-char values (32-byte base64), co-located field preserved, multi-field single patch - fail-safe: a nonexistent target field -> job Failed, clear error, the real field left unchanged (whole patch aborted, no partial write) All five pass server-side admission dry-run under the restricted PSA. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>