# VNCmail+ — setup & deploy runbook VNCmail+ is VNC's fork of [Bulwark](https://github.com/bulwarkmail/webmail), 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/](deploy/k8s/README.md)**. > **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](deploy/k8s/README.md)**. In short: 1. CI builds the image on push to `dev`/`main` → `ghcr.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 `dev` → `main`: ```bash 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: ```bash 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`.)