---
id: w4/m6/t004
title: "Materialize on write: sync OpenBao values into a per-app k8s Secret via envFrom; change triggers rollout"
worker: worker4
status: done
estimate: 40m
depends_on: [w4/m6/t003]
---

## Objective

Connect the store to the runtime: on env-vars write, the Core materializes the service's OpenBao values into a per-app k8s Secret (e.g. `<app>-env` in the app namespace) and sets the App's `spec.envFromSecret` (t001) so the Deployment consumes it; a value change rolls the pods (hash annotation or revision bump) so the DoD's "app serves the new value" is automatic. OpenBao stays the source of truth; the k8s Secret is a projection.

## Context

- t001 already gives the CRD/runtime half (`envFromSecret` → `envFrom`).
- Rollout trigger: pod-template hash annotation over the Secret data, consistent with how the operator already handles revisions (docs/ADR004-deployment.md).
- Record the accepted trade-off in docs (t007): a projection copy exists in etcd; OpenBao adds durability/versioning/audit/policy, not etcd-avoidance. Sidecar injection is the future alternative.

## Steps

1. Core: after a successful store write, upsert the k8s Secret and patch the App (`envFromSecret` + hash annotation).
2. Reconcile path: hash change ⇒ new rollout/revision.
3. Handle deletes (key removed ⇒ Secret updated ⇒ rollout).
4. envtest/unit coverage for upsert + rollout trigger.

## Files

- `operator/internal/api/` (Core write path)
- `operator/internal/controller/app_controller.go` (hash → rollout, if not free via pod template)

## Acceptance criteria

- [ ] PUT env-vars ⇒ k8s Secret updated ⇒ pods roll ⇒ new value visible in the container env.
- [ ] Delete propagates the same way; `make test` green.

## Out of scope

- Sidecar/agent injection (recorded as future work in the ADR).
