Two safe-by-default flips, both opt-out via explicit acknowledgement. 1. --insecure / ZDDC_INSECURE=1: zddc-server now refuses to start when no <ZDDC_ROOT>/.zddc exists. With no .zddc anywhere in the chain, AllowedWithChain falls through to "HasAnyFile=false → allow" and the tree is publicly accessible to anonymous callers — almost never what an operator wants on a fresh deployment, and previously a silent footgun. The flag is the escape hatch for deliberately- public archives (no .zddc anywhere by design). 2. ZDDC_CORS_ORIGIN now defaults to empty (CORS disabled) instead of the canonical "https://zddc.varasys.io". The embedded-tools install path serves tools and data same-origin, so the default never needed to permit cross-origin XHRs from a third-party host. Every deployment was implicitly trusting zddc.varasys.io to make authenticated XHRs on behalf of every logged-in user; if that origin were ever compromised, the blast radius extended to every customer server. Operators who deliberately use the CDN-bootstrap pattern or self- hosted tools at a different host now set the value explicitly. Helm chart values updated accordingly: prod default is empty; dev keeps localhost:8000 for tool-iteration workflows. Existing deployments that depended on the old defaults will need to either set the value explicitly or pass --insecure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| zddc-server-dev | ||
| zddc-server-prod | ||
| README.md | ||
Helm charts
Two example charts for deploying zddc-server on Kubernetes.
Both 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. Pin zddc.gitRef to a stable tag (zddc-server-vX.Y.Z). Slower probe cadence; image-pull policy IfNotPresent. |
zddc-server-dev/ |
Development / soak. 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). |
The chart values are nearly identical between the two; the differences
are encoded as defaults in each chart's values.yaml.example.
Quick start
# 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
What the chart does and doesn't do
Does:
- Clones the configured
zddc.gitRepoatzddc.gitRefin an init container, builds the Go binary, copies it to a sharedemptyDir, 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. - 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=noneandZDDC_INSECURE_DIRECT=1are 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-Emailby default). The chart does not deploy oauth2-proxy / nginx-auth-request / Pomerium / etc. — bring your own. - Manage secrets.
values.yaml.examplecontains no secrets and never should. ACL email lists belong in.zddcfiles 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:
- 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.
- 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).
- 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
helm lint helm/zddc-server-prod/
helm lint helm/zddc-server-dev/
# 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