docs(openbao): correct the handover README, and prove the Retain guarantee #754

Merged
binjovi-bot merged 1 commit from fix/openbao-platform-readme into trunk 2026-09-14 16:46:21 +00:00
Owner

What

Corrects two factual errors in the openbao-platform README from PR #750, and replaces its one open assumption with a proof.

Error 1: the wrong dependents

The README said that deleting the OpenBaoConnection would mean "every other CR here loses the connection it points at."

That is wrong. The other nine CRs carry an inline openbaoRef.connection.address and never name that record:

openbaoRef:
  connection:
    address: http://openbao.openbao.svc.cluster.local:8200

The real dependents sit outside this directory, in a different API group, and reach it by name over two hops:

OpenBaoDynamicSecret --openbaoAuthRef--> OpenBaoAuth --openbaoConnectionRef--> openbao/openbao

Five live pipelines across four tenant namespaces: forgejo-db-user, n8n-secrets, hydra-secrets, kratos-secrets, outline-database. The hazard is real, but it is larger than described and in a different place. platform-seanfarm #684 fixes the cause by setting that Object to Orphan.

Error 2: the wrong GC mechanism

The README said Crossplane tracks the composed resources "through spec.resourceRefs, not through ownerReferences." The live objects say otherwise:

pki-root-platform:  ownerRefs=XPlatform/seanfarm-s66ww blockOwnerDeletion=true
transit-platform:   ownerRefs=XPlatform/seanfarm-s66ww blockOwnerDeletion=true

They are collected by ordinary Kubernetes GC. That is better news than what was written, because it means --cascade=orphan or stripping the ownerReferences is enough to decouple them.

The assumption, now proven

The README previously said to confirm what the operator does to OpenBao-side state on CR deletion, and not to assume. Confirmed by reading the deployed code rather than testing against production.

openbao-operator v0.12.32 pins libseanfarm-operator v0.4.13. In reconcile/harness.go:401:

if policy != DeletionPolicyRetain && !dryRun {
    ... h.Delete(ctx, obj, c) ...
}

Retain skips the entire remote-delete block. The UnmountSecrets call that destroys the transit mount and the cosign signing keys lives inside it (openbaotransitbackend_impl.go:152), so it is unreachable under Retain. The accessor returns spec.deletionPolicy with no defaulting, and all nine live CRs carry Retain.

Consequence: retiring the XR removes those nine Kubernetes objects and Flux recreates them, while both PKI mounts, the intermediate CA, the three issuing roles, the transit mount and its three keys are untouched. No functional outage, only a window with no drift correction.

The README now records the file and line, and says to re-verify if the operator version changes, because the guarantee is a property of that code path and not of the CRD.

Tests

tests/check.sh exits 0.

https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA

## What Corrects two factual errors in the `openbao-platform` README from PR #750, and replaces its one open assumption with a proof. ## Error 1: the wrong dependents The README said that deleting the `OpenBaoConnection` would mean "every other CR here loses the connection it points at." That is wrong. The other nine CRs carry an inline `openbaoRef.connection.address` and never name that record: ```yaml openbaoRef: connection: address: http://openbao.openbao.svc.cluster.local:8200 ``` The real dependents sit outside this directory, in a different API group, and reach it by name over two hops: ``` OpenBaoDynamicSecret --openbaoAuthRef--> OpenBaoAuth --openbaoConnectionRef--> openbao/openbao ``` Five live pipelines across four tenant namespaces: `forgejo-db-user`, `n8n-secrets`, `hydra-secrets`, `kratos-secrets`, `outline-database`. The hazard is real, but it is larger than described and in a different place. platform-seanfarm #684 fixes the cause by setting that `Object` to `Orphan`. ## Error 2: the wrong GC mechanism The README said Crossplane tracks the composed resources "through `spec.resourceRefs`, not through `ownerReferences`." The live objects say otherwise: ``` pki-root-platform: ownerRefs=XPlatform/seanfarm-s66ww blockOwnerDeletion=true transit-platform: ownerRefs=XPlatform/seanfarm-s66ww blockOwnerDeletion=true ``` They are collected by ordinary Kubernetes GC. That is better news than what was written, because it means `--cascade=orphan` or stripping the `ownerReferences` is enough to decouple them. ## The assumption, now proven The README previously said to confirm what the operator does to OpenBao-side state on CR deletion, and not to assume. Confirmed by reading the deployed code rather than testing against production. `openbao-operator` v0.12.32 pins `libseanfarm-operator` v0.4.13. In `reconcile/harness.go:401`: ```go if policy != DeletionPolicyRetain && !dryRun { ... h.Delete(ctx, obj, c) ... } ``` `Retain` skips the entire remote-delete block. The `UnmountSecrets` call that destroys the transit mount and the cosign signing keys lives inside it (`openbaotransitbackend_impl.go:152`), so it is unreachable under `Retain`. The accessor returns `spec.deletionPolicy` with no defaulting, and all nine live CRs carry `Retain`. Consequence: retiring the XR removes those nine Kubernetes objects and Flux recreates them, while both PKI mounts, the intermediate CA, the three issuing roles, the transit mount and its three keys are untouched. No functional outage, only a window with no drift correction. The README now records the file and line, and says to re-verify if the operator version changes, because the guarantee is a property of that code path and not of the CRD. ## Tests `tests/check.sh` exits 0. https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA
docs(openbao): correct the handover README, and prove the Retain guarantee
All checks were successful
binjovi/ci Binjovi completed the frozen plan
b3dae68881
Two errors from PR #750, and one assumption now closed.

First, the README said deleting the OpenBaoConnection would make every other
CR here lose the connection it points at. It would not. The other nine carry
an inline openbaoRef.connection.address and never name that record. The real
dependents are five OpenBaoDynamicSecrets in a different API group, which
reach it by name over two hops: OpenBaoDynamicSecret -> openbaoAuthRef ->
OpenBaoAuth -> openbaoConnectionRef openbao/openbao. They are forgejo-db-user,
n8n-secrets, hydra-secrets, kratos-secrets and outline-database, in four
tenant namespaces. The hazard is real, larger, and elsewhere.

Second, the README said Crossplane tracks the composed resources through
spec.resourceRefs rather than ownerReferences. The live objects carry
ownerReferences to XPlatform/seanfarm-s66ww with blockOwnerDeletion true, so
ordinary Kubernetes garbage collection applies. That is better news: it means
--cascade=orphan, or stripping the references, decouples them.

Third, the README said to confirm what the operator does to OpenBao-side state
on CR deletion, and not to assume. Confirmed by reading the deployed code.
openbao-operator v0.12.32 pins libseanfarm-operator v0.4.13, whose
reconcile/harness.go:401 skips the whole remote-delete block when the policy
is Retain. The UnmountSecrets call that would destroy the transit mount and
the cosign keys is inside that block. It is unreachable under Retain, and all
nine live CRs set Retain.

Claude-Session: https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA
binjovi-bot deleted branch fix/openbao-platform-readme 2026-09-14 16:46:21 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/seanfarm!754
No description provided.