ZDDC/helm/zddc-server-prod/values.yaml.example
ZDDC da4754b6ef feat(convert): bwrap engine as production default
Replaces the always-spawn-an-OCI-container model with a per-call
bubblewrap sandbox. Pandoc and chromium binaries are baked into the
zddc-server runtime image; each conversion runs them under bwrap's
Linux-namespace isolation. No daemon, no socket, no privileged outer
container, no OCI image pull at conversion time.

Why: the OCI engine paid ≈ 350 MB image pulls + 400 MB persistent
storage + ~300 ms per-conversion startup, plus required either an
on-host daemon socket (zddc-RCE → host-RCE in one hop) or nested
container privileges. bwrap gets the same sandbox properties
(--unshare-all, ro-bind /usr, tmpfs /tmp, clearenv, no-network) at
~5 ms per call and zero external dependencies. This is the same
primitive Flatpak uses for every app launch — battle-tested at scale
for "untrusted-input, short-lived, isolated."

Runner abstraction:
- `Runner.Run` signature: image string → ToolSpec{Image, Binary}.
  Both fields populated by entry points; whichever engine is
  installed reads the one it needs.
- `bwrapRunner` (new): assembles bwrap argv via `buildBwrapArgs`
  helper (testable in isolation), spawns bwrap with the binary.
- `containerRunner` (renamed conceptually to "legacy fallback"):
  unchanged behavior, still reachable for hosts that prefer OCI
  containers per conversion.

Probe order in health.Probe: bwrap → podman → docker. First hit wins.
Engine kinds in Capabilities: "bwrap" | "podman" | "docker". The
no-engine error message now lists all three.

Config (cmd/zddc-server):
- new --convert-pandoc-binary  / ZDDC_CONVERT_PANDOC_BINARY  (default "pandoc")
- new --convert-chromium-binary / ZDDC_CONVERT_CHROMIUM_BINARY (default "chromium-browser")
- existing --convert-pandoc-image / --convert-chromium-image kept
  for the OCI engine, doc updated to clarify they only apply there.
- --convert-engine helptext lists bwrap first.

Images:
- New `zddc/runtime.Containerfile` — alpine + bubblewrap + pandoc-cli +
  chromium + font-noto. Documents build/publish workflow.
- helm/zddc-server-prod/values.yaml.example: runtimeImage default
  switched to a placeholder for the new bundled runtime image; bare
  alpine NO LONGER works for /.convert (clearly called out in the
  comment).
- bitnest dev: /var/lib/zddc-dev-build/Containerfile mirrors the
  production runtime image. Quadlet at /etc/containers/systemd/
  zddc.container drops the podman-socket mount (no longer needed)
  and sets ZDDC_CONVERT_ENGINE=bwrap explicitly to avoid silent
  downgrades if a stray podman ends up on PATH.

Tests:
- convert_test.go: fakeRunner / recordingRunner now record ToolSpec.
- New TestToolSpecPopulation pins that both Image and Binary are
  filled by every entry point.
- New TestBwrapArgs_SandboxFlagsPresent / MountTranslation /
  RejectsBadMountSpec lock in the bwrap argv shape — a refactor that
  drops a hardening flag or misroutes a mount fails this loud.

Docs:
- AGENTS.md § "Server-side document conversion" rewritten around
  the bwrap-first model with podman/docker as legacy fallbacks.
- ARCHITECTURE.md convert reference updated.
- internal/convert package doc reflects the two-engine probe order.

Verified end-to-end on bitnest: probe reports
  engine=bwrap pandoc_binary=pandoc chromium_binary=chromium-browser
on startup. All 15 Go test packages green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 17:42:28 -05:00

126 lines
5.1 KiB
Text

# values.yaml.example — zddc-server-prod
#
# Copy to values.yaml (or pass via --values) and customize for your
# environment. Contains NO secrets — secrets like the .zddc admin email
# list, TLS certs (if used), and image-pull credentials must be
# materialised from your secret-management system (sealed-secrets,
# external-secrets, kubectl create secret, etc.) and referenced by name
# below.
# Source-build configuration. The init container clones the repo at
# `gitRef` and compiles cmd/zddc-server. Pin gitRef to a stable tag
# (zddc-server-vX.Y.Z) for production; trying main HEAD risks pulling
# unreleased changes.
zddc:
gitRepo: https://codeberg.org/VARASYS/ZDDC.git
gitRef: zddc-server-v0.0.7 # pin to a stable tag
# ZDDC environment-variable contract — see zddc/README.md
env:
# Path inside the container where ZDDC_ROOT data is mounted.
# The chart wires the data PVC to this path automatically.
rootPath: /srv
# Listening address (plain HTTP — ingress terminates TLS).
addr: ":8080"
# Email-header convention from your authenticating reverse proxy.
emailHeader: X-Auth-Request-Email
# Comma-separated CORS allowlist. Empty (default) disables CORS —
# appropriate for the embedded-tools install path where tools are
# served same-origin by zddc-server itself. Set to a specific origin
# only if browser-loaded pages from a different host call back into
# this server (e.g. self-hosted tools at https://tools.acme.com,
# or the CDN-bootstrap pattern from https://zddc.varasys.io).
corsOrigin: ""
# info / warn / error / debug. Production stays on info; debug logs
# every request's full header map (includes cookies/auth tokens).
logLevel: info
# Index URL segment for the virtual archive index. Default fits
# most deployments; only change if you have a tracking-number
# collision with a real directory named ".archive".
indexPath: ".archive"
# Skip ACL enforcement entirely on this instance. Anyone hitting
# the port reads everything in scope. Only enable for genuinely-
# public archives (and even then, only behind an authenticating
# ingress that doesn't gate on identity for /). Distinct from
# --insecure (which gates the startup check requiring a root .zddc).
# Default false.
noAuth: false
# Bearer-token system. Master automatically self-issues tokens via
# /.tokens (browser) and /.api/tokens (JSON). The token store lives
# at <ZDDC_ROOT>/.zddc.d/tokens/<sha256> on the data PVC; no Helm
# configuration required. Operators sign in via the upstream auth
# proxy, visit /.tokens, copy the displayed token into a 0600 file,
# and pass --bearer-file to any CLI / cache / mirror that needs to
# authenticate against this master. See zddc/README.md "Bearer
# tokens" for the full lifecycle.
# Persistent storage for ZDDC_ROOT. Operators provide their own PVC,
# typically backed by a shared filesystem (NFS, CephFS, SMB) so multiple
# replicas of zddc-server (and your sync tooling) see the same tree.
# This chart does NOT create the PVC — it only references it by name.
data:
pvcName: zddc-root # name of an existing PersistentVolumeClaim
subPath: "" # optional subPath within the PVC
# Service exposure. zddc-server listens on a plain HTTP port; ingress
# (or whatever reverse proxy you put in front) terminates TLS and
# enforces authentication, then forwards to this service.
service:
type: ClusterIP
port: 8080
# Ingress is optional — disabled by default since most deployments wire
# zddc-server into an existing ingress / auth-proxy stack. Enable here
# only if this chart is the only thing in front of the pod.
ingress:
enabled: false
className: ""
host: zddc.example.com
tls:
enabled: false
secretName: zddc-tls # secret you create separately
# Pod resource limits. Sized for a small/medium archive (~10k files).
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Replicas. zddc-server is read-only stateless given a shared filesystem
# behind it, so multiple replicas are safe.
replicaCount: 1
# Build-stage Go image (init container). Pinned digest is recommended
# in production for reproducibility; using a tag means upstream changes
# break your deploy.
buildImage:
repository: docker.io/golang
tag: 1.24-alpine
# digest: sha256:...
# Runtime image (main container). Hosts the zddc-server binary copied
# in by the init container, plus the conversion toolchain (pandoc,
# chromium, bubblewrap) used by the /.convert endpoint. Build from
# `zddc/runtime.Containerfile` and publish to your registry; the
# Containerfile documents the build/publish commands. Plain alpine
# does NOT have the conversion tools — the /.convert endpoint will
# serve 503 until you swap in a runtime image that bundles them.
runtimeImage:
repository: codeberg.org/varasys/zddc-server-runtime
tag: "latest"
# digest: sha256:...
# Image pull credentials, if your registry requires them. Reference a
# secret you've created separately; do not put credentials in values.
imagePullSecrets: []
# - name: regcred