apiVersion: v2 name: stalwart description: A Helm chart to deploy a cluster-capable Stalwart mail server (SMTP, IMAP, JMAP, ManageSieve) on Kubernetes icon: https://avatars.githubusercontent.com/u/76623657 type: application # appVersion is the exact image tag, and the "v" is part of it - so it is part # of appVersion and of the "+up" suffix. Precedent: calibre-web-automated # (+upv4.0.6), matrix-synapse (+upv1.159.0), ytdlp-web-player (+upv1.2.0). # # The glibc image, not "-alpine". Both flavours are published for every release # from the same source and carry the same uid/gid 2000 and the same two volumes; # the choice is a deployment decision, and keeping it out of the tag is what # lets renovate.json5 anchor the version with a plain three-part regex instead # of maintaining a compatibility suffix. The moving tags "latest", "v0.16" and # their -alpine twins are excluded by that same anchor: measured on 2026-10-02 # all three of latest / v0.16 / v0.16.24 resolved to one digest pushed seconds # apart, so either floating name would change the running image with no commit # in this repository and nothing to report it. version: 1.0.0+upv0.16.24 appVersion: "v0.16.24" maintainers: - name: jklein # The second chart in this repo with a dependency (after ytdlp-web-player). # README, "Chart dependencies", holds the bar it has to clear and the price; # both are restated here because the price is paid by whoever reads this file # next, not by whoever added the line. # # WHY ONE HERE. Stalwart's in-memory store is not a cache that can be dropped: # from v0.16 it also carries the cluster coordinator ("the Default variant # reuses the Redis connection already configured on the in-memory store"), so a # multi-node install does not work without it. Redis/Valkey is admissible as # the in-memory store ONLY - never as the data or blob store. Offering it inside # the chart means the wiring (host, port, egress rule) is in one place instead # of split across two releases that have to agree. # # WHY IT IS OFF BY DEFAULT: an installation that already runs a Valkey points # externalServices.valkey.host at it and this dependency renders nothing. The # subchart is the path for an installation that has none. # # THE SUBCHART IS OUR OWN valkey CHART, resolved from the published index - # NOT upstream's. So guideline 14.2 ("we consume their chart as it comes") does # not apply here and there is nothing of someone else's to leave unpatched: the # subchart follows this repo's conventions because it IS this repo's chart. What # does apply is everything about the subchart being invisible downstream, plus # one hazard that a self-referencing dependency adds and an external one does # not - see the PR-ordering warning below. dependencies: - name: valkey # KEEP THIS VERSION MOVING - it is the only path a Valkey update has. # # A subchart is invisible to everything downstream that watches Helm release # versions: what this file declares is baked into our packaged .tgz, and # Fleet only ever sees the version of "stalwart". So a new valkey chart - a # security fix included - reaches a cluster ONLY when this line is raised # and a new stalwart version is published. Nothing reports the gap. # # Renovate's BUILT-IN helmv3 manager reads this block and opens the PR (no # customManagers entry exists or would help - repository and version sit # together in one file, which is the case that manager is for), and # ci/renovate-bump-chart.sh bumps this chart's patch version, which is what # actually ships it. That chain is the whole safety net; switched off, this # line needs a human on a calendar instead. # # MEASURED, not assumed, because the registry is self-hosted and the version # carries build metadata - two things that could each have broken the chain # silently. Renovate 44.132.2, dry run on 2026-10-02 with this dependency # pinned at a lower version: manager helmv3 matched the file, took # registryUrls from the line below, and offered # newVersion: "1.0.0+up9.1.2.alpine", versioning: "helm" # with the release timestamp read from our index. So both the self-hosted # index and the "+up..." suffix are handled. # # Independent of appVersion: raising it is a patch or minor bump of this # chart and is never tied to the Stalwart image. version: "1.0.0+up9.1.2.alpine" # Our own published index, and the READ path only. It is anonymously # readable - measured 2026-10-02: GET .../index.yaml returns 200 without # credentials - so ci/helm-dependencies.sh needs no secret and was not # touched. Verified with an empty HELM_REPOSITORY_CONFIG, i.e. the state a # fresh runner is in: helm reports "Getting updates for unmanaged Helm # repositories" and resolves the exact build-metadata version from the URL # alone, with no "helm repo add" anywhere. Both "helm dependency update" and # "helm dependency build" were tried. # # The PUBLISH target and its credentials still live in 1Password and appear # nowhere in this repository - see README, "CI/CD". repository: "https://git.jk-dev.de/api/packages/jlab-cluster/helm" # Off by default, so a plain install renders none of its objects. # # NOT "valkey.enabled", AND THAT IS NOT A STYLE CHOICE. Everything under the # "valkey:" key is passed to the subchart as its values, and our valkey # chart ships a values.schema.json with additionalProperties: false and no # "enabled" property - so "valkey.enabled" is rejected by the SUBCHART: # Error: values don't meet the specifications of the schema(s) in the # following chart(s): valkey: - at '': additional properties 'enabled' # not allowed # Measured. Worse, Helm skips a disabled subchart's schema validation, so # the error appears only for whoever first sets it to true - the default # path renders clean. # # The condition is therefore a key of OUR OWN, outside the subchart's value # subtree, which is also the honest shape: "enabled" is not a valkey value. # It only ever looked like one because upstream's schema was permissive. # The alternative - adding "enabled" to valkey/values.schema.json - would # have to be its own PR and be published first (see the warning below), for # a key that chart does not read. condition: valkeySubchart.enabled # # !!! NEVER CHANGE valkey/ AND THIS CHART IN THE SAME PULL REQUEST !!! # # The hazard a self-referencing dependency adds. This dependency resolves out of # the PUBLISHED index, not out of the working tree, and valkey/ is only # published by a push to main that touches it. So in one PR carrying both: # # - every helm command here - lint, package, render, AND the install-test - # pulls the OLD valkey .tgz, because the new one does not exist yet; # - the PR therefore goes green against a version of the subchart that is not # the one in the diff, and the diff to valkey/ is reviewed while nothing # actually exercised it; # - after the merge both charts publish, and the first install of the new # stalwart is the first time the new valkey is used anywhere. # # Two consecutive pull requests instead: change valkey/, merge, let it publish, # then raise the version here. The ordering is not a nicety - the green check on # the combined PR is a statement about the wrong artifact.