---
id: w7/m1/t002
title: "Workspace identity labels: projector stamps App CRs, operator propagates to pods"
worker: worker7
status: done
estimate: 45m
depends_on: [w7/m1/t001]
---

## Objective

Give every tenant workload a workspace identity label (e.g. `app.bex.co/workspace: tea-<xid>`) so NetworkPolicies can select by tenant: the control-plane projector stamps it on projected App CRs, and the operator propagates it to the Deployment pod template.

## Context

- The projector (`lego/backend/internal/store/reconciler.go`) builds App CRs from control-plane `apps` rows, which carry the tenant since w1/m2; w1/m9 makes tenant binding real for new signups.
- The operator builds Deployments in `lego/operator/internal/controller/app_controller.go`; pod-template labels are where policy selectors bite.
- Workspace ids (`tea-<xid>`, docs/ADR020-identifiers.md) are DNS/label-safe by design — hyphen, never underscore.
- Managed datastores must carry the same label for t003's own-datastore allow rule: check whether `Database`/`KeyValue` reconcilers already propagate CR labels to CNPG/Valkey pods; extend if not.

## Steps

1. Projector: set the workspace label in projected App CR metadata (and keep it on update).
2. Operator: propagate the label into the Deployment `spec.template.metadata.labels` (and the app's Service selector must remain unchanged).
3. Verify/extend the `Database` and `KeyValue` reconcilers so datastore pods carry the owning workspace label too.

## Files

- `lego/backend/internal/store/reconciler.go`
- `lego/operator/internal/controller/app_controller.go` (+ database/keyvalue reconcilers under `lego/operator/internal/` if label propagation is missing)

## Acceptance criteria

- [ ] Unit/envtest: a projected App CR carries the workspace label; the reconciled Deployment pod template carries it
- [ ] Datastore pods (CNPG, Valkey) carry the owning workspace label (or a documented pointer to where CNPG makes this impossible + the fallback selector)
- [ ] Existing apps without a workspace row still reconcile (label absent ⇒ treated as legacy/default, noted for t003)

## Out of scope

- The NetworkPolicies themselves (t003)
