---
id: w4/m6/t001
title: "App CRD env plumbing: spec.env + secret-ref support in the kubernetes runtime"
worker: worker4
status: done
estimate: 40m
depends_on: []
---

## Objective

Give Apps a way to receive configuration at all: add `spec.env` (plain name/value pairs) and per-app secret consumption (`envFrom` a named k8s Secret) to the `App` CRD and build the Deployment's env from it — today `app_controller.go` injects only `PORT` (operator/internal/controller/app_controller.go:206). This is the plumbing both plain env vars and the OpenBao-backed path (t004) share.

## Context

- `operator/api/v1alpha1/app_types.go` — no env field exists today.
- `operator/internal/controller/app_controller.go:206` — `Env: []corev1.EnvVar{{Name: "PORT", ...}}`.
- Read `operator/CLAUDE.md` before editing; new Go files need the Apache-2.0 boilerplate header.
- `PORT` stays operator-owned; user env must not be able to shadow it.
- `scripts/app-apply.sh` maps `bex.yml` → App CR; extend the mapping for env.

## Steps

1. Add `Env []corev1.EnvVar` (or a minimal name/value struct) + optional `EnvFromSecret string` to `AppSpec`; `make manifests generate`.
2. Merge into the Deployment env in the kubernetes runtime, `PORT` last (wins).
3. Extend `app-apply.sh`'s `bex.yml` mapping (Render's `envVars` shape).
4. envtest: env vars appear in the Deployment; `PORT` cannot be shadowed.

## Files

- `operator/api/v1alpha1/app_types.go`
- `operator/internal/controller/app_controller.go`
- `scripts/app-apply.sh`
- generated CRD manifests

## Acceptance criteria

- [ ] An App with `spec.env` runs with those variables set (mock cluster or envtest).
- [ ] `spec.envFromSecret` wires `envFrom` to the named Secret.
- [ ] `PORT` remains operator-controlled; `make test` green.

## Out of scope

- Where secret values come from (t002–t004); opensandbox runtime env support.
