Files
SRCmail/VNCMAIL-SETUP.md
T
Bernd RodlerandClaude Opus 4.8 a3d551b640 feat(deploy): k8s manifests for microk8s (vncmail.sandbox.vnc.de)
Bulwark is stateful (local /app/data) — Vercel serverless (read-only fs)
crashes it. Deploy as a container with 4 persistent volumes on microk8s,
alongside bulwark.sandbox.vnc.de. Adds deploy/k8s/ (namespace, pvc, deployment,
service, ingress, secret template, runbook) + rewrites setup doc off Vercel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 17:09:54 +02:00

3.8 KiB

VNCmail+ — setup & deploy runbook

VNCmail+ is VNC's fork of Bulwark, a Next.js (App Router) JMAP webmail client for Stalwart. Stalwart is the source of truth; VNCmail+ is the UI. It deploys as a container on Kubernetes (microk8s) at vncmail.sandbox.vnc.de — see deploy/k8s/.

License: AGPL-3.0. Serving a modified VNCmail+ to users over the network obligates VNC to offer those users the corresponding source. Keeping this fork public (with a "Source" link in the imprint/UI) satisfies that. Loop in legal before a public/customer-facing launch if a closed fork is ever desired.

Architecture — why a container, not Vercel

  • Bulwark is a stateful, long-lived server: it persists settings-sync, admin config/state, and telemetry to a local data directory (/app/data/*).
  • Vercel serverless was tried and dropped — its filesystem is read-only except /tmp, so Bulwark's mkdir ./data crashes (ENOENT /var/task/data). You cannot point its data dirs at a remote host either (they're POSIX paths, not URLs). Bulwark's native model is a container + persistent volumes.
  • So VNCmail+ runs as a Docker image (ghcr.io/brvncde-dotcom/vncmail-plus-*) with 4 persistent volumes, exactly like the existing bulwark.sandbox.vnc.de.
  • JMAP calls go through server-side /api/* routes (proxy.ts) → server-to- server to Stalwart, no browser CORS. Config is runtime-read.

Branches (dev-first)

Branch Role
main Production — CI builds …/vncmail-plus-beta. Only updated by an explicit promote.
dev Integration + QA — CI builds …/vncmail-plus-dev on push. Default working branch.
vnc/* Feature branches for UI work (branch off dev, PR into dev).

All VNC customization lives under vnc/ (see vnc/VNC-CHANGES.md).

Deploy (Kubernetes / microk8s)

Full runbook: deploy/k8s/README.md. In short:

  1. CI builds the image on push to dev/mainghcr.io/brvncde-dotcom/vncmail-plus-dev (.github/workflows/docker-publish.yml).
  2. kubectl apply the manifests in deploy/k8s/ (namespace, 4 PVCs, deployment, service, ingress) + a secret.yaml (from secret.example.yaml) + a ghcr-pull image-pull secret.
  3. Point vncmail.sandbox.vnc.de DNS at the ingress; cert-manager issues TLS.

Runs alongside the existing bulwark.sandbox.vnc.de. Match your cluster's StorageClass / IngressClass / cert issuer to bulwark's (see the runbook).

Deploy workflow (dev-first — ALWAYS)

Same flow as every other VNC/SRC repo:

  1. Work on dev (or vnc/* → PR into dev). Push to dev → CI builds the -dev image → kubectl -n vncmail rollout restart deploy/vncmail-plus to pull it. QA at vncmail.sandbox.vnc.de.
  2. Promote to production only on explicit go-live — merge devmain:
    git log dev..main            # MUST be empty — main must have nothing dev lacks (else prod would revert)
    git checkout main && git merge --ff-only dev
    git push origin main         # CI builds the production image
    git checkout dev
    
    Then roll the production deployment to the new image (pin its digest — see deploy/k8s/README.md). Never push straight to main. Never let a dev→main merge silently revert prod.

Syncing upstream (Bulwark releases)

Bring upstream into dev (NOT main), integrate + QA on the dev image, then promote as above:

git fetch upstream
git checkout dev && git merge upstream/main    # resolve conflicts via vnc/VNC-CHANGES.md; QA on preview

Auth

Basic auth via Stalwart is the default — users sign in with their @sandbox.vnc.de address + password; VNCmail+ authenticates them over JMAP. No extra config. (SSO via vncdirectory/OIDC is a later option — see vnc/vercel.env.template.)