# Helm charts Three example charts for deploying [zddc-server](../zddc/) on Kubernetes. All compile zddc-server from source via an init container — no container image needs to be pulled from a registry, and no binary needs to be built ahead of time. The init container clones the repo at a configured git ref and runs `go build`; the main container is plain alpine + the freshly built static binary. ## Charts | Chart | When to use | |---|---| | **`zddc-server-prod/`** | Production **master**. Pin `zddc.gitRef` to a stable tag (`zddc-server-vX.Y.Z`). Slower probe cadence; image-pull policy `IfNotPresent`. Mounts the data PVC directly RW at `ZDDC_ROOT`. The token system is enabled automatically (tokens persist on the data PVC at `/.zddc.d/tokens/`); operators visit `/.tokens` to issue them. | | **`zddc-server-dev/`** | Development / soak **master**. Tracks `main` by default; `helm upgrade` triggers a pod recreate so each rollout pulls the latest commit. Faster probes; debug-level logging (request headers logged — sensitive). Wraps the data PVC in **OverlayFS** (lower = PVC mounted RO, upper = ephemeral `emptyDir`) so dev-side writes never mutate the underlying store. Use this shape when the dev replica points at the same data as prod. | | **`zddc-server-cache/`** | Downstream **client** (proxy / cache / mirror) of an upstream master. Set `zddc.upstream.url` + `zddc.upstream.mode`; the binary skips master-side machinery and forwards all requests to the master, persisting responses under the cache PVC (in cache or mirror modes). Bearer auth via a separately-created Kubernetes Secret. Use cases: corporate-master → DR-mirror, vendor-scoped mirror in a vendor's own cluster, regional edge cache, dev environment that mirrors prod read-only. Mirror mode adds an access-triggered subtree walker. | The prod and dev chart values are nearly identical; the differences are encoded as defaults in each chart's `values.yaml.example`. The dev chart's overlay-isolation layer is a structural difference, not a values-level toggle — see `zddc-server-dev/templates/deployment.yaml` for the privileged init container and the `data-readonly` / `overlay-scratch` / `data` volume sandwich. The cache chart shares the same source-build pattern but adds client-mode env wiring (`ZDDC_UPSTREAM`, `ZDDC_MODE`, `ZDDC_BEARER_FILE`, `ZDDC_NO_AUTH`, `ZDDC_SKIP_TLS_VERIFY`, mirror-mode subtree config), a Recreate strategy (single-instance — multiple replicas would race the cache directory), and TCP-socket probes (HTTP probes against `/` would fail when both upstream is down AND the cache is empty). ## Quick start ```sh # Pre-requisite: a PersistentVolumeClaim for ZDDC_ROOT data kubectl apply -f - <<'EOF' apiVersion: v1 kind: PersistentVolumeClaim metadata: name: zddc-root spec: accessModes: [ReadWriteMany] # or RWO if single replica is fine resources: { requests: { storage: 100Gi } } storageClassName: your-shared-fs # NFS, CephFS, SMB, etc. EOF # Production install cp helm/zddc-server-prod/values.yaml.example my-prod-values.yaml $EDITOR my-prod-values.yaml # set zddc.gitRef, hostnames, etc. helm install zddc-server-prod helm/zddc-server-prod/ -f my-prod-values.yaml # Dev install (tracks main HEAD) cp helm/zddc-server-dev/values.yaml.example my-dev-values.yaml $EDITOR my-dev-values.yaml helm install zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml # Trigger a rebuild from latest main HEAD (dev chart) helm upgrade zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml # Cache install (downstream client of an upstream master) # # 1) Issue a bearer token on the master at https:///.tokens # 2) Create the Secret (do NOT put the token in values.yaml): kubectl create secret generic zddc-cache-bearer \ --from-literal=token= # 3) Create a cache PVC (separate from the master's data PVC; can # be smaller — sized to the working set you expect to mirror): kubectl apply -f - <<'PVC' apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: zddc-cache } spec: accessModes: [ReadWriteOnce] resources: { requests: { storage: 50Gi } } storageClassName: your-block-storage PVC # 4) Install the chart, pointing at your master: cp helm/zddc-server-cache/values.yaml.example my-cache-values.yaml $EDITOR my-cache-values.yaml # set zddc.upstream.url, mode, etc. helm install zddc-server-cache helm/zddc-server-cache/ -f my-cache-values.yaml ``` ## What the chart does and doesn't do **Does:** - Clones the configured `zddc.gitRepo` at `zddc.gitRef` in an init container, builds the Go binary, copies it to a shared `emptyDir`, and starts the main container against that binary. - Wires the `ZDDC_*` environment-variable contract (root path, addr, email header, CORS allowlist, log level, index path). - Mounts a caller-supplied PersistentVolumeClaim at `ZDDC_ROOT` (prod chart) or as the OverlayFS lowerdir behind a merged `ZDDC_ROOT` (dev chart). - Optionally creates an Ingress (`ingress.enabled: true`). **Does not:** - Create the PVC. Operators provision storage themselves; the chart only references it by name. - Manage TLS for the pod. zddc-server runs in plain HTTP mode behind whatever ingress / authenticating reverse proxy the cluster already has. `ZDDC_TLS_CERT=none` and `ZDDC_INSECURE_DIRECT=1` are hardcoded in the templates because the chart is opinionated about the TLS-terminated-upstream deployment shape. - Authenticate users. zddc-server reads the user's email from a header set by the upstream proxy (`X-Auth-Request-Email` by default). The chart does not deploy oauth2-proxy / nginx-auth-request / Pomerium / etc. — bring your own. - Manage secrets. `values.yaml.example` contains no secrets and never should. ACL email lists belong in `.zddc` files inside the data volume; image-pull credentials and TLS certs (if you enable ingress TLS) reference Kubernetes secrets you've created separately. ## Why build from source instead of using a registry image Three reasons: 1. **Reproducibility.** The init container's logs show exactly which git ref was built. There's no opaque "what did I deploy" question that a registry tag can introduce. 2. **One distribution channel.** Codeberg release-asset binaries already exist for direct downloads; the chart compiles its own binary from the same source git ref so there's nothing extra to maintain (no separate image registry, no image-promotion pipeline). 3. **Smaller blast radius.** A compromised build image affects only pods that pull during the compromise window. A compromised registry image stays compromised across rollbacks until the digest is rotated. The cost: every pod start takes 30-60s to clone + `go build` instead of pulling a pre-baked image. Acceptable for both chart audiences (production rollouts are infrequent; dev rollouts trade build time for tracking-main convenience). ## Linting ```sh helm lint helm/zddc-server-prod/ helm lint helm/zddc-server-dev/ helm lint helm/zddc-server-cache/ # Render to inspect (uses default values from values.yaml.example): helm template test-prod helm/zddc-server-prod/ \ --values helm/zddc-server-prod/values.yaml.example helm template test-cache helm/zddc-server-cache/ \ --values helm/zddc-server-cache/values.yaml.example ```