9f9620431f9d12f0988896a7d13bd6d46e1e0861
657
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9f9620431f
|
fix(stalwart): let the mode conflict, not the MTA-STS entry, name the defect
Install-test / Install-test stalwart (pull_request) Successful in 1m55s
Validate Helm Charts / Install-test (pull_request) Successful in 1m55s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 6s
Validate Helm Charts / Resource quantities (pull_request) Successful in 17s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 15s
Validate Helm Charts / Build stalwart (pull_request) Successful in 18s
Validate Helm Charts / Trivy config stalwart (pull_request) Successful in 23s
With BOTH routing modes on and an mtaSts entry missing its tlsSecretName, the render reported mtaSts.hosts[0] (mta-sts.example.com) has no tlsSecretName, and in Ingress mode every entry needs one which is true and is the wrong diagnosis: the values file's defect is that ingress.enabled and httpRoute.enabled are both true, and "in Ingress mode" is a claim about a configuration that is not coherent at all. Whoever met it would add a tlsSecretName, re-run, and reach the real message one round later - or conclude the MTA-STS feature was the problem. Measured on this chart, not reasoned about. And it is the second occurrence of a defect the parallel keycloak round of Gitea #122 found on its own realm list, independently and in the same round, which is what makes it a rule rather than one chart's story: a specific validation must not be reachable in a state that a structural validation already rejects. Ordering is not the fix and could not be - Helm renders templates in an order a chart cannot pin, so this template may not assume the httproute template's abort runs first. Scope is: the Ingress-mode MTA-STS block is now gated on ingress.enabled AND NOT httpRoute.enabled, so in the incoherent combination it validates nothing and the only fail left is the one that names the real problem. The per-entry guard is NOT dead by this - verified reachable with ingress.enabled alone. Defaults, the unchanged CI fixture and the overlay all still render byte-identically against origin/main, and the HTTPRoute render test still holds 58 assertions over 21 charts. |
||
|
|
a54bceee23
|
feat(stalwart): MTA-STS routes as a maintainable list, in both routing modes
Validate Helm Charts / Detect changed charts (pull_request) Successful in 5s
Validate Helm Charts / Resource quantities (pull_request) Successful in 17s
Validate Helm Charts / Trivy config stalwart (pull_request) Successful in 22s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Install-test / Install-test stalwart (pull_request) Successful in 1m44s
Validate Helm Charts / Install-test (pull_request) Successful in 1m44s
Validate Helm Charts / Build stalwart (pull_request) Successful in 8s
Validate Helm Charts / Selector match (pull_request) Successful in 16s
The policy route for mta-sts.<domain> belonged to another repository
(jk-cluster-config) while pointing at this chart's own Service. It moves
here as mtaSts.hosts, a list, so several domains - subdomains included -
are maintainable in one place and in whichever routing mode is active.
THE LIST IS THE SWITCH. No mtaSts.enabled: "enabled with an empty list"
would be a state with no meaning that the chart would then have to catch.
The schema rejects the key outright, so the rule is enforced rather than
documented. Default [] renders nothing in either mode - the defaults and
the unchanged CI fixture both produce a byte-identical helm template
against origin/main.
Entry form {host, tlsSecretName, clusterIssuer?}. The two TLS fields are
read ONLY in Ingress mode; in Gateway mode TLS sits on the listener, so
requiring them there would demand a value that does nothing.
THE PATH IS HARDWIRED. RFC 8461 fixes /.well-known/mta-sts.txt, rendered
with pathType Exact (type: Exact in the route). A values key for a
protocol-mandated path can only ever be set wrong, and wrong is invisible
from this side. Exact also matters in itself: a prefix on these hostnames
would publish the mail server's whole management surface under them.
ONE INGRESS PER CLUSTER ISSUER, NOT ONE OBJECT - and the plan's own three
requirements forced that. cert-manager reads the two fields at different
scopes: cluster-issuer is an ANNOTATION (object-scoped, and absent means
nothing is issued for that object at all), while secretName is
tls-entry-scoped and yields one Certificate per entry. So an object cannot
both request a certificate and leave another host's secret alone, which is
what an empty per-entry issuer has to mean. Measured in jk-cluster:
ingress-shim names the Certificate AFTER THE SECRET and owns it from the
Ingress, so a shared secret must sit in exactly one object (hence grouping
the tls list by secret, reusing rss-bridge.tlsGroups' shape) and a secret
may not span two issuer groups (hence the abort). Objects are named by
issuer, not by index: an index is only free where the object holds no
certificate, which is the precondition homeassistant's route naming states
for itself.
Gateway mode renders ONE HTTPRoute with every hostname - nothing differs
per host there - attached to the same Gateway(s) as the WebUI route.
WHAT THE CHART CANNOT PROMISE, AND THEREFORE NAMES: it registers a route,
it does not create a domain. The domains live in Stalwart's data store, so
an entry whose domain is missing there answers 404 at a correctly routed
endpoint with every object green - and MTA-STS failures are silent on the
SENDER's side, which delivers in the clear and logs nothing here. Same for
the _mta-sts.<domain> TXT record and the policy's content. The subdomain
trap is documented too: mta-sts.sub.<domain> is one label below
*.<domain>, so a parent wildcard certificate does not cover it.
Not folded into certificate.extraDnsNames deliberately - that would hang
the MAIL certificate on this list, letting a bad policy entry take STARTTLS
and IMAPS with it.
Minor, additive, empty default: 1.2.0+upv0.16.25 -> 1.3.0+upv0.16.25.
CI: ci/httproute/stalwart.yaml enters all three shapes of the entry form
(with issuer, without, and a subdomain), taking the HTTPRoute render test
from 56 assertions over 21 charts to 58 over 21 - stalwart from 2 to 4.
The Ingress half cannot be covered there, since an overlay's whole job is
to switch the mode off, so ci/stalwart/test-values.yaml carries two
entries and the install-test has a real API server accept them.
Fixes #123
|
||
|
|
6517643cd6
|
Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.17.0 | ||
|
|
96dd1adbda
|
Update l4rm4nd/vouchervault Docker tag to v1.30.3
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Resource quantities (pull_request) Successful in 19s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 17s
Install-test / Install-test voucher-vault (pull_request) Successful in 2m25s
Publish VoucherVault Helm Chart / Publish VoucherVault Helm Chart (push) Successful in 23s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 10s
Validate Helm Charts / Selector match (pull_request) Successful in 18s
Validate Helm Charts / Build voucher-vault (pull_request) Successful in 6s
Validate Helm Charts / Trivy config voucher-vault (pull_request) Successful in 19s
Validate Helm Charts / Install-test (pull_request) Successful in 2m25s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 23s
SonarQube Analysis / SonarQube Analysis (push) Successful in 1m4s
|
||
|
|
6d79d8f270
|
docs(README): group 3 of the Gateway-API sweep, and four corrections it forced
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 15s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 13s
Publish Keycloak Helm Chart / Publish Keycloak Helm Chart (push) Successful in 12s
Publish Matrix Synapse Helm Chart / Publish Matrix Synapse Helm Chart (push) Successful in 13s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 13s
Publish Stalwart Helm Chart / Publish Stalwart Helm Chart (push) Successful in 13s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 14s
Publish YTDLP Web Player Helm Chart / Publish YTDLP Web Player Helm Chart (push) Successful in 14s
Publish Bulwark Helm Chart / Publish Bulwark Helm Chart (push) Successful in 15s
Publish Homeassistant Helm Chart / Publish Homeassistant Helm Chart (push) Successful in 13s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 13s
SonarQube Analysis / SonarQube Analysis (push) Successful in 53s
The figures: the HTTPRoute render test goes from 37 assertions over 15 charts to **56 over 21**, with the per-chart breakdown, and the sweep is finished except for the frozen rss-bridge - so #114 still cannot be closed while #51 is open. FOUR CORRECTIONS, because three of #114's descriptions of the group-3 shapes and one of this file's own forward-looking claims did not survive contact with the charts. The corrected list is a table in section 4, since that is what somebody will copy from: - bulwark has ONE Ingress, not two. keycloak has two. - bulwark has NO rewrite-target and cannot have one: NEXT_PUBLIC_BASE_PATH is compiled into its bundle. #114's table reached "bulwark, freesailarr" by grepping for the string, and every occurrence in that chart is an explanation of why the annotation does not help. freesailarr is therefore still the only chart the #118 prefix/redirect pairing applies to - the HTTPRoute-test section said bulwark would meet it "when group 3 lands", and now says why it does not. - matrix-synapse does NOT render in a loop over several apps (that is freesailarr): one Ingress object with two HOST rules, which has to become two HTTPRoute objects because spec.hostnames applies to the whole object. - stalwart does not render a routing object per node group - its Services and StatefulSets do; one route targets ingress.nodeGroup's own http Service. THREE NEW CONVENTIONS, written where the next sweep will read them: - SCREEN THE LIVE OBJECT, not only the chart. Four of group 3's six charts carried an annotation the chart never sets and the Gateway mode drops - keycloak's proxy buffers on BOTH its Ingresses, matrix-synapse's body size and snippet, stalwart's whole CORS block, bulwark's body size. None of them appears in a git grep of this repo. At #118 this was found after shipping; here it was measured beforehand with read-only kubectl, and that measurement is now part of the procedure. - READ THE DIRECTIVE before calling an annotation load-bearing. matrix-synapse's configuration-snippet uses more_set_headers, which is the headers-more module's OUTPUT-header directive - so those headers go on the response and Synapse never sees them. The scariest-looking loss in the group is not a loss, and its NOTES.txt says not to re-create it. - "NO COUNTERPART" IS A CLAIM TO CHECK AGAINST THE PINNED CRDs, and for CORS it is false. Grepped out of gateway-api v1.6.3 standard channel: HTTPRoute filters carry a full cors field (GEP-1767), Support: Extended, and responseHeaderModifier is Extended too rather than Core. stalwart still renders neither, and the three reasons are recorded - no CORS values to render from, Extended support is not support (Cilium accepts the filter and forwards the preflight anyway), and the preflight is the half a bare header filter cannot do while Authorization in the allowed headers makes every request preflighted. Also: pathType ImplementationSpecific has no counterpart and is a chart DEFAULT in homeassistant - PathPrefix on "/", render abort on any other path, and RegularExpression deliberately not offered. The ingress.enabled-derived-value screen is closed: ytdlp-web-player was the third and last find, and its guard is the one that is needed MORE in Gateway mode, because the ingress-nginx admission webhook never sees an HTTPRoute and the silent outcome is an authentication bypass that depends on DNS. A subchart does not come along: oauth2-proxy has no Gateway support, so in the recommended configuration the subchart's Ingress publishes the release in both of the chart's modes. And stalwart is the only chart in the sweep still rendering a Certificate in Gateway mode, for its mail listeners. The homeassistant documented-exception bullet now covers its Gateway mode: no httpRoute.hostnames key (the second chart without it), one route per ingress.hosts entry, index-based route names and what reordering the list costs. Part of #114 |
||
|
|
e4b41577ae
|
feat(ytdlp-web-player): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep, last chart - and the one that carried the group-2 pattern this sweep was told to look for. It reads ingress.enabled OUTSIDE its Ingress template, in ytdlp-web-player.validate, and in Gateway mode ingress.enabled is false by construction, so that guard would have gone silent. Fixed with ytdlp-web-player.published = ingress.enabled OR httpRoute.enabled - the freesailarr.published / sure.publishedOverTls shape, identical to ingress.enabled while httpRoute.enabled is false, which is why it changes no existing render. AND THE GUARD IS NEEDED MORE IN GATEWAY MODE, NOT LESS. It refuses to publish the application and the oauth2-proxy on the same host: - In INGRESS mode the consequence of ignoring it is loud: ingress-nginx's admission webhook rejects the second Ingress and the Fleet bundle goes to ErrApplied. - In GATEWAY mode THE WEBHOOK NEVER FIRES. It compares Ingress objects with Ingress objects, and an HTTPRoute is a different kind - so nothing rejects anything, both objects stay valid, and one hostname is served over two data paths that are NOT equivalent: the proxy's Ingress goes through AUTHENTICATION and this chart's HTTPRoute goes straight to the application. Which one a client reaches depends on what DNS resolves to. That is an authentication bypass that comes and goes with a DNS record and that no Kubernetes object reports. So the Gateway-mode abort has its own message saying exactly that. Both messages exercised, plus the collision reached through httpRoute.hostnames rather than through the ingress.domain fallback, and the guard still firing when only the Deployment is rendered. THE SUBCHART HAS NO GATEWAY SUPPORT, and this chart cannot give it one - upstream's chart is consumed as it comes (README, "Chart dependencies"). It renders an Ingress or nothing. So in the RECOMMENDED configuration, where the proxy sits in the data path and owns the host, the publishing object is the SUBCHART'S INGRESS in both of this chart's modes, and turning httpRoute on buys nothing. The route is for the proxy-disabled case, or for the application on a host of its own - which is the documented unauthenticated bypass. Said in the template header, in values.yaml, in the schema and in the proxy branch of NOTES.txt, because someone migrating hosts will otherwise set the value and wonder why nothing moved. The 16k proxy-buffer-size this chart recommends is NOT lost by this switch, and that needed stating rather than assuming: it belongs on the PROXY's Ingress (oauth2-proxy.ingress.annotations) as the second half of the 4kB session-cookie trap, and the subchart keeps rendering an Ingress. This chart's own ingress.annotations default is {} and it sets nothing but the cert-manager annotation, so there is no chart-side annotation loss to name. CORRECTED CLAIM: values.yaml and the schema both said that everything the Ingress needs is read only inside its template, so switching it off freed BOTH ingress.domain and ingress.clusterIssuer. clusterIssuer is still freed; domain is now read by the HTTPRoute, so it is freed only with both modes off. The reason the claim was made still holds and is kept - the application has no base-URL setting and builds no absolute links. NOTHING WAS MEASURED ON A LIVE INGRESS OF THIS CHART, because there is none. Measured 2026-10-12: the release runs in namespace jlab-player with both Deployments Ready, and NOT ONE of the cluster's 23 Ingress objects is in that namespace - neither this chart's nor the subchart's. What publishes it instead could not be determined: this workspace's identity may not list httproutes or secrets, so neither a hand-written HTTPRoute nor the release's Helm values are readable. Recorded as the open question it is, in values.yaml and in the overlay, rather than guessed at. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/ytdlp-web-player/test-values.yaml. Part of #114 |
||
|
|
b10800da2a
|
feat(homeassistant): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep, and the one chart where the convention had to bend rather than the chart. homeassistant is the DOCUMENTED INGRESS EXCEPTION (README): upstream's flexible values, a list of hosts each with its own list of paths, instead of the repo's required domain/clusterIssuer pair. The exception stays - nothing here unifies it. NO httpRoute.hostnames KEY, which is the freesailarr shape from group 2 and for the same reason: there is no ingress.domain to fall back to, and a chart-global hostname list could not say which host entry - and therefore which set of paths - it belonged to. So httpRoute carries only enabled and parentRefs, the hostnames come from ingress.hosts, and the chart renders ONE HTTPRoute PER ENTRY with one rule per path of that entry. One object with several hostnames would be wrong, not merely different: an HTTPRoute's rules apply to every hostname of the object, and per-host paths are this chart's whole point. The schema rejects httpRoute.hostnames outright, so the absence is enforced rather than merely intended. ingress.hosts is therefore read in BOTH modes and is not an Ingress-only value. ingress.className, ingress.tls and ingress.annotations are not read in Gateway mode: class and certificate belong to the Gateway's listener, and there is no annotation channel. pathType IS THE SHARP EDGE, AND THE CHART'S DEFAULT IS THE AWKWARD VALUE. Ingress has Exact/Prefix/ImplementationSpecific; the Gateway API has Exact/PathPrefix/RegularExpression. Exact and Prefix map cleanly. ImplementationSpecific - the default in values.yaml - means "ask the controller" and has NO counterpart, so: - on "/" it renders as PathPrefix. Every controller treats a root ImplementationSpecific path as a prefix match, so the chart's own default works in this mode unchanged (verified: the untouched default renders a valid route). - on ANY OTHER path the render ABORTS, naming the value and asking for Prefix or Exact. In ingress-nginx - this chart's default class - an ImplementationSpecific sub-path is a REGEX, and PathPrefix is not one. Mapping it silently would change how the path matches while the render stayed green, which is exactly the class of defect #118 was. RegularExpression is deliberately not offered as an escape hatch: its support level is Implementation-specific, which decision 1 of #114 forbids. NOTHING WAS MEASURED ON THE LIVE CLUSTER FOR THIS CHART, because there is nothing to measure: homeassistant is NOT DEPLOYED in jk-cluster (checked 2026-10-12 - 23 Ingress objects cluster-wide, none this chart's, and no workload of it in any namespace). So it is the one chart of the six with no operator-supplied annotation to lose. values.yaml and NOTES.txt say that as an absence of evidence about this installation rather than as a property of the chart. Route NAMES carry the entry's INDEX, not its hostname - a hostname is not a dependable object-name fragment. Consequence named in values.yaml and in the template: reordering ingress.hosts renames routes and therefore recreates rather than edits them. Harmless, since a route holds no state and no certificate. ci/httproute/homeassistant.yaml has to SUPPLY HOSTS, which no other overlay in the sweep does, and it adds coverage the install-test never had: this chart's ingress.enabled defaults to FALSE and the fixture does not switch it on, so no routing object was exercised at all before. Two entries on purpose - a plain one, and one with two paths including an Exact - because that second entry is what proves the chart renders one object per host rather than one object with several hostnames. 5 assertions over 2 routes and 3 rules. NOTES.txt asks $published where it asked ingress.enabled, and the trusted_proxies section now warns that moving from an Ingress to a Gateway CHANGES the proxy's pod network: a list carried over unchanged turns every request into a 400 if the CIDR differs, or silently keeps attributing every client to one address if it does not. Minor: additive, defaults unchanged. Empty helm template diff against origin/main, both with the fixture as it stands and with ingress.enabled=true. Part of #114 |
||
|
|
3e969c5659
|
feat(stalwart): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep. ONE route - not one per node group. #114 describes this chart as rendering "per node group"; its SERVICES and StatefulSets are, the routing object is not and must not be. The route targets exactly ingress.nodeGroup's own http Service and keeps both of the Ingress's guards, because the WebUI's setup and management screens are not safe to spread over a group that may not run the management tasks. ingress.nodeGroup is therefore shared by both modes: it names where the management surface is published from, which is not a property of the Ingress. THE MAIL CERTIFICATE STAYS, and this is the only chart in the sweep where anything of the TLS path does. stalwart.certificate.yaml covers STARTTLS on 25 and implicit TLS on 465/993; those connections pass through neither an ingress controller nor a Gateway. So in Gateway mode ingress.domain is still required (the certificate's dnsNames read it), ingress.clusterIssuer is still required unless certificate.clusterIssuer is set, and the Certificate must NOT be removed as part of a migration - deleting it drops SMTP and IMAP back to Stalwart's self-signed certificate, with clients seeing a name mismatch while every Kubernetes object stays green. Verified in the render: Gateway mode emits Certificate + HTTPRoute and no Ingress. WHAT THIS MODE DROPS - AND HERE IT BREAKS A SECOND CHART OF THE SAME GROUP. Measured on 2026-10-12, the live Ingress on mail.bekemeyer-klein.de carried, all of it operator-supplied through ingress.annotations (this chart sets none of it): enable-cors: true cors-allow-origin: https://webmail.bekemeyer-klein.de cors-allow-methods: GET, POST, OPTIONS cors-allow-headers: Authorization, Content-Type That origin is BULWARK, the webmail chart in this same group. Without the CORS configuration the browser-side webmail cannot call this server's JMAP endpoint at all, and the failure is entirely client-side: a console error, nothing in the server's logs. AND "no counterpart" WOULD HAVE BEEN WRONG. Measured by grepping the CRDs this repo pins (gateway-api v1.6.3 standard channel, ci/gateway-api-crds.sh) rather than inferred: HTTPRoute's filters carry a full 'cors' field (GEP-1767) with allowOrigins/allowMethods/allowHeaders/allowCredentials/maxAge, and its documented level - verbatim from that CRD - is "Support: Extended". responseHeaderModifier is in the same CRD and is also Extended, not Core. The chart still does not render it, for three reasons in order: 1. The chart has NO CORS values. The live configuration is the operator's, and the chart sets no CORS annotations in Ingress mode either - so rendering a filter means inventing a values surface that would exist in Gateway mode only. That asymmetry is its own defect and its own work. 2. Extended support is not support. A rejected route is rejected WHOLE, which would take the WebUI and JMAP down together (the reasoning #119 reached for statusCode); and the observed failure is quieter - Cilium accepts filters.type: CORS and forwards the preflight upstream anyway (cilium/cilium#45272), i.e. Accepted: True with the behaviour absent. 3. Half a CORS is worse than none, and the translatable half is the useless one HERE. A responseHeaderModifier can add the Access-Control-Allow-* headers; the PREFLIGHT has to be answered BY THE GATEWAY, which the CORS filter's own contract states and a bare header filter cannot do. Because the allowed headers include Authorization, EVERY webmail request is preflighted - so the expressible half is exactly the half that does not help. Named, not rebuilt (decision 2 of #114), in values.yaml, in the schema and in the Gateway branch of NOTES.txt, with the operational instruction: configure CORS at the Gateway with that implementation's own support BEFORE moving this host, and test it from the webmail in a browser - curl sends no preflight, so it passes while the webmail fails. NOTES.txt now asks $published (ingress.enabled OR httpRoute.enabled) where it asked ingress.enabled, and two paragraphs that named the ingress controller specifically now name the Gateway in that mode: the auto-ban step (switching API does not avoid or reset it - one proxy address instead of another) and the NetworkPolicy paragraph, where the namespace to allow is the Gateway's data plane rather than an ingress controller's. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/stalwart/test-values.yaml. Part of #114 |
||
|
|
e54cfe5fdd
|
feat(matrix-synapse): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep. TWO HTTPRoutes - and the shape is not the one the issue described. #114 says this chart renders its Ingresses in a LOOP OVER SEVERAL APPS. It does not; that is freesailarr. It renders ONE Ingress object with TWO HOST rules: the homeserver on ingress.domain at "/", and the delegation nginx on matrix.delegation.serverUrl at "/.well-known/matrix". An HTTPRoute's spec.hostnames applies to the WHOLE object, so two hostnames that want different paths and different backends have to be two objects. That is the only faithful translation, not a stylistic choice. httpRoute.delegation.hostnames deliberately does NOT fall back to httpRoute.hostnames. The server name is what REMOTE homeservers resolve and is a different thing from the homeserver's own hostname; the migration order - homeserver host first, server name last - depends on telling them apart, and a chart-global hostname list covering both would erase the distinction. httpRoute.delegation.parentRefs falls back to httpRoute.parentRefs, which matters because the server name is usually an apex domain behind a different Gateway. ONE THING THIS MODE IMPROVES, and it is the awkward part of Matrix delegation. Under ingress-nginx two Ingress objects cannot both claim one hostname - the admission webhook rejects the second - which is why the delegation rule has to live inside this chart's single Ingress object even though the apex domain usually belongs to a website. HTTPRoutes attached to the same listener for the same hostname are merged by the implementation, longest match winning, so the /.well-known/matrix route can sit beside whatever owns that apex. WHAT THIS MODE DROPS, MEASURED ON THE LIVE OBJECT ON 2026-10-12 - and ONE OF THE TWO IS NOT WHAT IT LOOKS LIKE. The live Ingress carried, all of it operator-supplied through ingress.annotations (this chart sets none of it): nginx.ingress.kubernetes.io/proxy-body-size: 50m nginx.org/client-max-body-size: 50m nginx.ingress.kubernetes.io/configuration-snippet: | more_set_headers "X-Forwarded-For $remote_addr"; more_set_headers "X-Forwarded-Proto $scheme"; more_set_headers "Host $host:$server_port"; 1. THE BODY SIZE IS THE REAL LOSS. Synapse's media repository takes uploads; its own ceiling is max_upload_size in homeserver.yaml, which this chart cannot see because the whole config arrives from 1Password. The effective limit is the smaller of the two, and in this mode the proxy half becomes the GATEWAY'S default - neither number anyone can point at is necessarily the one that decides. No counterpart in the Gateway API, so per decision 2 of #114 it is documented, not rebuilt. nginx.org/client-max-body-size is the same limit spelled for NGINX Inc's controller, a hedge across both implementations, and it goes for the same reason. (#114's own measured table did not list matrix-synapse under proxy-body-size at all; the live object has it.) 2. THE SNIPPET IS NOT LOAD-BEARING, which took checking rather than assuming. It looks like what Synapse's x_forwarded setting depends on - client IP for rate limiting, and federation. It is not: more_set_headers is the headers-more module's OUTPUT-header directive ("Replaces (if any) or adds (if not any) the specified output headers"), the request-side directive being more_set_input_headers. So those three headers are added to the RESPONSE TO THE CLIENT; Synapse never sees them, and what it reads with x_forwarded: true is the REQUEST's X-Forwarded-For, which ingress-nginx supplies itself. The Gateway API's ResponseHeaderModifier could not rebuild it anyway (it knows no nginx variables), and should not: NOTES.txt says in as many words not to re-create it and not to read its absence as the cause of a rate-limiting or federation problem. Stated as what it is - a reading of the directive's documented semantics. Nothing here observed a Synapse instance with and without the snippet. NOTES.txt now asks $published (ingress.enabled OR httpRoute.enabled) wherever it used to ask ingress.enabled - the server-name/serverUrl mismatch warning, the "clients work fine" aside and the scaled-to-zero paragraph were all really asking whether anything publishes the release. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/matrix-synapse/test-values.yaml. Part of #114 |
||
|
|
9625d8b182
|
feat(bulwark): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep. One route, one rule, at the root - and THREE EXPECTATIONS ABOUT THIS CHART DID NOT SURVIVE CONTACT WITH IT: - #114 says bulwark has TWO Ingress objects. It has ONE. keycloak has two. - #114's measured table lists bulwark under rewrite-target. It has no rewrite-target annotation and CANNOT have one: NEXT_PUBLIC_BASE_PATH is compiled into the bundle, so the application cannot be served under a sub-path, and stripping a prefix at the proxy would yield a page that loads and then fetches everything from the wrong place. Every occurrence of the string "rewrite-target" in this chart - values.yaml, the schema, the Ingress template - is an explanation of exactly that. A grep for the string finds the refutations and counts them as occurrences. - ci/run-httproute-match-test.sh said in its header that bulwark was the second rewrite-target chart and would meet the #118 prefix/redirect assurance when group 3 landed. Group 3 landed and it does not: with no URLRewrite on a prefix other than "/", the assurance's precondition is never met here. The header is corrected rather than left standing, and it now says freesailarr is the only chart the pairing applies to. So no redirect rule is added, and none is needed. WHAT THIS MODE DROPS, MEASURED ON THE LIVE OBJECT rather than inferred. On 2026-10-12 the live Ingress on webmail.bekemeyer-klein.de carried nginx.ingress.kubernetes.io/proxy-body-size: 25m supplied by the OPERATOR through ingress.annotations - values.yaml recommends exactly that value and explains why the chart cannot pick it, so the chart sets nothing and a reader of this repository alone would not find it. It is the single annotation this installation depends on, and the Gateway API has no body-size counterpart: implementation policy, not routing. Per decisions 1 and 2 of #114 it is NAMED, not rebuilt - in values.yaml, in the schema and in the Gateway branch of NOTES.txt, in this application's own terms: an attachment above the Gateway's default is rejected before Bulwark sees a byte, the 413 comes from the proxy, and it arrives after the user has written the mail and attached the file. CORRECTED CLAIM, the same one garage and calibre-web-automated needed: the Ingress template, the bulwark.publicUrl helper and the schema all stated that nothing outside the Ingress template FUNCTIONALLY reads ingress.domain. The HTTPRoute reads it, so the claim held only while both modes were off. All three now say so, and the route carries a required() of its own. bulwark.publicUrl follows the ACTIVE mode instead of always printing ingress.domain. The two differ exactly when httpRoute.hostnames is set, which is how a migration off the Ingress runs - and printing the old host there would hand the operator an OIDC redirect URI for the hostname being migrated away from, a failure that surfaces as a rejected login at the identity provider and says nothing about this chart. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/bulwark/test-values.yaml. Part of #114 |
||
|
|
d0f0d49a93
|
feat(keycloak): Gateway-API as an alternative to the two Ingresses
Group 3 of the #114 sweep, first chart. keycloak has TWO Ingress objects - the public hostname and the admin hostname - so it gets TWO HTTPRoutes, with httpRoute.admin.parentRefs falling back to httpRoute.parentRefs exactly as ingress.admin.className falls back to ingress.className: "which data path" is a property of the Gateway in this API, not of the route, so an internal Gateway here is what an internal ingress class is in the other mode. The admin route stays gated on ingress.admin.enabled, because that value is not an Ingress switch - it also decides whether KC_HOSTNAME_ADMIN is set on the Deployment, i.e. whether the admin hostname exists at all. Switching to Gateway mode therefore must not require turning it off. FIXED ALONG THE WAY: the admin Ingress did not hang off ingress.enabled. Measured on origin/main, `helm template -f ci/keycloak/test-values.yaml --set ingress.enabled=false` emitted kc-keycloak-admin-ingress - so "ingress.enabled: false" did not mean "no Ingress", and ingress.clusterIssuer stayed required for an object nobody asked for. It also made Gateway mode impossible: the admin Ingress would have gone on claiming the admin host next to the admin HTTPRoute, one host over two data paths, which is the exact conflict the mutual exclusion exists to prevent. The default render is unchanged; what changed is a combination that could not be coherent. WHAT THIS MODE DROPS, MEASURED ON THE LIVE CLUSTER rather than inferred. On 2026-10-12 BOTH of this chart's Ingress objects in jk-cluster carried nginx.ingress.kubernetes.io/proxy-buffer-size: 16k nginx.ingress.kubernetes.io/proxy-buffers-number: 4 set by the OPERATOR through ingress.annotations and ingress.admin.annotations - the chart sets neither, which is why reading the chart alone does not reveal them. The Gateway API has no annotation channel and no counterpart for proxy buffer sizing; it is implementation policy, not routing. Per decision 1 of #114 the chart renders no policy object, so this is NAMED and not rebuilt, in values.yaml, in the schema and in the Gateway branch of NOTES.txt, with the consequence spelled out in Keycloak's own terms: large headers (JWTs, SAML assertions, long Set-Cookie chains on the login flow) make a too-small buffer answer 502 at the PROXY while Keycloak logs the request as successful. Named and deliberately not touched: four further Ingress objects in the same namespace (keycloak-realm-*-ingress, with app-root) belong to the Helm release jk-cluster-config-fleet-cd-keycloak-realm-ingress, i.e. to another repository. They point at this release's Service under four further hostnames. This chart renders exactly the two routes its own two Ingresses cover. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/keycloak/test-values.yaml. Part of #114 |
||
|
|
d36112aaf6
|
chore(renovate): group the freesailarr stack into one PR per cycle
Validate Helm Charts / Detect changed charts (pull_request) Successful in 6s
Validate Helm Charts / Resource quantities (pull_request) Successful in 30s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 24s
Install-test / Install-test freesailarr (pull_request) Successful in 4m9s
Validate Helm Charts / Install-test (pull_request) Successful in 4m9s
SonarQube Analysis / SonarQube Analysis (push) Successful in 50s
Validate Helm Charts / Selector match (pull_request) Successful in 17s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 7s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 12s
The first groupName in this config, and a deliberate reversal for one chart of the stance the file records above its app-version managers: that the helm-values manager finds freesailarr's ten images and proposes one update per image. That described what Renovate does, not what the chart wants. For every other chart here one PR per image is already one PR per chart. What it fixes, measured against the Gitea API on 2026-10-12: eight of the ten images had an open PR at the same moment (#102, #103, #104, #105, #106, #107, #108, #111). Each edits freesailarr/values.yaml and, through the postUpgradeTasks hook, freesailarr/Chart.yaml - so the API reported mergeable: false for all eight, since they rewrite the same version: line. One can merge; the other seven then need a rebase each, and every rebase restarts the most expensive install-test in this repo. The second symptom is sharper: all eight carried version: 3.1.1+up1.0.0 -> 3.1.2+up1.0.0 while main already stood at 3.3.0+up1.0.0, because the hook derives everything from the base commit and those branches predated the Gateway-API rounds. A rebase repairs it, which is the point - nothing fails if the rebase does not happen first. Two rules rather than one, and not for tidiness: renovate rejects the combination outright with "packageRules cannot combine both matchUpdateTypes and separateMinorPatch", a config-validation error. That is consistent, since separateMinorPatch decides the split a rule would then be selecting on. Without the second rule the group still arrives as two PRs - verified by dry-run against a checkout holding those eight updates, they split onto renovate/freesailarr-stack (the three minors) and renovate/patch-freesailarr-stack (the five patches); note that the setting prefixes the branch rather than suffixing it. With both rules, one branch carries all eight. Decisions, with their reasons in the rules themselves: matched by file name so that no second list of image names has to be kept current (an image added later would silently never join the group); mediathekarr included, since its digest PR blocks the others identically - checked with a stale digest, because a current pin makes a working and a broken rule look the same; gluetun and busybox included, as neither's role has anything to do with the blocking; and majors kept out via matchUpdateTypes, so the stepwise app-major procedure and separateMultipleMajor keep working. What it cannot show from here: Renovate runs as the shared instance outside this repository, so the evidence available is a config that validates and a local lookup that groups as intended - not the next real run. |
||
|
|
7003f3674c
|
feat(freesailarr)!: eight image updates and a trustedNetworks value for the *arr apps
Collapses the eight open Renovate PRs for this stack into one change, and answers what two of them turned out to carry. The eight tags: qbittorrent 5.2.3 -> 5.2.4, bazarr 1.6.0 -> 1.6.2, sonarr 4.0.19 -> 4.0.20, flaresolverr v3.5.0 -> v3.5.2, recyclarr 8.7.1 -> 8.7.3, prowlarr 2.5.2 -> 2.6.5, radarr 6.3.0 -> 6.4.4, seerr v3.4.1 -> v3.5.0. gluetun, the busybox init image and the digest-pinned mediathekarr have no update pending and are untouched. Major rather than patch, because prowlarr 2.6.3 and radarr 6.4.3 are not plain bumps: they removed the hard-coded trusted proxy networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, fe80::/10, read in Startup.cs at the previously pinned tags) and replaced them with a TrustedNetworks setting whose own default is string.Empty - ForwardedHeadersConfigurator returns before registering a single KnownNetwork while it is empty. Behind an in-cluster ingress controller that means the forwarded headers are ignored: the client address the app reports is the controller's pod IP, the scheme reads as http behind TLS termination, and under "Disabled for Local Addresses" a proxied request stops counting as local. So a <app>.trustedNetworks value is added for prowlarr, radarr AND sonarr, rendered as <APP>__SERVER__TRUSTEDNETWORKS and only when non-empty - an empty variable cannot be told apart from an unset one by the app, but looks handled to a reader of the manifest. The env form was verified per app rather than inferred: all three bind ServerOptions to <App>:Server next to a plain AddEnvironmentVariables(). A helper suppresses the chart's entry when the app's own env block already carries the variable, for the reason the UrlBase helper states - two identically named variables in one container are not a defined state. sonarr is included because it is already in the empty state: 4.0.19, the version this chart shipped before, has no hard-coded networks either. It arrived in what the convention classes as a plain app-version bump, so nothing reviewed or documented it. Repairing two and knowingly leaving the third would be the same silent pattern. The default is empty, and this therefore does NOT restore the behaviour from before the upgrade. That is deliberate and follows upstream's reason - trusting X-Forwarded-For from any private network is a spoofing vector - and it is made visible instead of reinstated: NOTES.txt warns in exactly the combination that costs something, the app published through an Ingress or an HTTPRoute and the value empty, and stays quiet otherwise. AllowedHosts, added by the same upstream releases, is deliberately not exposed. Its default is string.Empty as well and the app then accepts any host, so there is nothing to repair; the trap is documented instead, because if it is ever set the app appends only localhost, 127.0.0.1, [::1] and its own hostname - neither the pod IP nor the Service DNS name - and the kubelet probes the pod by IP, which an incomplete list answers with HTTP 400. At default values the rendered manifest changes in exactly eight lines, the image tags; the new value renders nothing until it is set. |
||
|
|
f202fdc3fc
|
fix(freesailarr): redirect the bare /qbittorrent prefix in Gateway mode
Validate Helm Charts / Detect changed charts (pull_request) Successful in 5s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Install-test / Install-test freesailarr (pull_request) Successful in 4m19s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
SonarQube Analysis / SonarQube Analysis (push) Successful in 48s
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 8s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 22s
Validate Helm Charts / Install-test (pull_request) Successful in 4m19s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 12s
qBittorrent has no base-path setting, so the chart serves it under a prefix and strips that prefix before the request reaches it. For the slashed form that is correct. For the BARE form - /qbittorrent, no trailing slash - the request is rewritten to "/" and qBittorrent answers 200 with its login page, while the browser's document URL keeps the prefixless form. Every relative link in that page therefore resolves against the host root, and on the shared host the root is seerr: the login form comes up unstyled and does nothing while a different application answers its assets and XHRs. Nothing reports an error at any level. The shared-domain HTTPRoute now carries a second rule for qBittorrent: an Exact match on qbittorrent.ingress.path with a RequestRedirect filter that replaces the full path with the slashed form, statusCode 301. The Gateway API ranks an Exact path match above every prefix match, so it wins over both qBittorrent's own PathPrefix rule and seerr's "/" wherever it is emitted - the rules' length-descending order stays a rendering convention. 301 rather than 308 because only 301 and 302 are Core in that API; an Extended value an implementation declines makes it set Accepted=False on the WHOLE route, which would take the four neighbouring apps' routing down with it, and that would also contradict decision 1 of Gitea #114. statusCode is written out because it defaults to 302. The rule is guarded on the app being qbittorrent and its path not being "/", and it reads the path from the value, never a literal. The INGRESS BRANCH IS UNCHANGED, deliberately. The remedy there is an nginx configuration-snippet annotation, and the chart cannot ship it: snippets are ignored unless the controller runs with allow-snippet-annotations=true, off by default since ingress-nginx 1.9 and only switched on by accepting annotation risk level "Critical". A shipped annotation would be a silent no-op on every cluster keeping the default. The working snippet, that price, and why a third Ingress object with pathType: Exact is not a substitute - buildLocation in ingress-nginx controller-v1.14.5 returns a regex location in its enforceRegex branch before reaching the PathTypeExact branch, and enforceRegex is computed once per nginx server over every location of the hostname, one of which carries rewrite-target - are all written out in values.yaml instead. ci/run-httproute-match-test.sh gains a third assertion, per HTTPRoute object: a rule stripping a prefix with ReplacePrefixMatch requires a rule in the same route matching that path Exactly and carrying a RequestRedirect filter. This is what stops the gap reappearing; bulwark is the second rewrite-target chart and meets the same check when group 3 of Gitea #114 lands. 37 assertions over 15 charts, up from 36. Both fail paths were exercised: the rule deleted, and the redirect pointed at a path other than the one stripped. Minor and not major: with the Ingress branch untouched and httpRoute.enabled defaulting to false, no existing release's render changes by a byte and no value becomes required. Shown by an empty helm template diff against origin/main in both Ingress shapes, shared-domain and per-app hosts. Fixes #118 |
||
|
|
471ea5ba5d
|
docs(README): the remaining-charts count, without double-counting rss-bridge
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Selector match (pull_request) Successful in 13s
Validate Helm Charts / Build calibre-web-automated (pull_request) Successful in 7s
Validate Helm Charts / Build zipline (pull_request) Successful in 8s
Validate Helm Charts / Trivy config zipline (pull_request) Successful in 22s
Validate Helm Charts / Build outline (pull_request) Successful in 8s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 8s
Validate Helm Charts / Trivy config outline (pull_request) Successful in 16s
Install-test / Install-test outline (pull_request) Successful in 2m50s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 14s
Publish Calibre Web Automated Helm Chart / Publish Calibre Web Automated Helm Chart (push) Successful in 14s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 14s
Publish Outline Helm Chart / Publish Outline Helm Chart (push) Successful in 12s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Trivy config calibre-web-automated (pull_request) Successful in 20s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 8s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 20s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 23s
Install-test / Install-test calibre-web-automated (pull_request) Successful in 2m22s
Install-test / Install-test freesailarr (pull_request) Successful in 4m2s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m45s
Install-test / Install-test zipline (pull_request) Successful in 2m23s
Validate Helm Charts / Install-test (pull_request) Successful in 15m22s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 14s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 11s
Publish Paperless-NGX Helm Chart / Publish Paperless-NGX Helm Chart (push) Successful in 11s
Publish Zipline Helm Chart / Publish Zipline Helm Chart (push) Successful in 10s
SonarQube Analysis / SonarQube Analysis (push) Successful in 44s
Group 2's sweep paragraph said "seven charts plus rss-bridge" while rss-bridge is one of those seven. Six charts in group 3 plus the frozen one is seven, which is also what the twelve before this PR was counting. Part of #114 |
||
|
|
bd1fdc09a0
|
feat: Gateway-API as an alternative to the Ingress in five annotation charts
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 7s
Validate Helm Charts / Trivy config outline (pull_request) Successful in 17s
Validate Helm Charts / Trivy config zipline (pull_request) Successful in 19s
Install-test / Install-test calibre-web-automated (pull_request) Successful in 2m11s
Install-test / Install-test freesailarr (pull_request) Successful in 3m55s
Install-test / Install-test outline (pull_request) Successful in 2m44s
Install-test / Install-test zipline (pull_request) Successful in 2m6s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build outline (pull_request) Successful in 7s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 8s
Validate Helm Charts / Build zipline (pull_request) Successful in 8s
Validate Helm Charts / Build calibre-web-automated (pull_request) Successful in 7s
Validate Helm Charts / Trivy config calibre-web-automated (pull_request) Successful in 19s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 24s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 19s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m52s
Validate Helm Charts / Install-test (pull_request) Successful in 14m50s
Group 2 of the #114 sweep: the charts whose behaviour hangs off an nginx annotation - calibre-web-automated, outline, paperless-ngx, zipline (proxy-body-size) and freesailarr (rewrite-target). Each gets the form the pilot (cyberchef, PR #115) fixed and group 1 (PR #116) repeated: an httpRoute block off by default, a template rendering ONLY an HTTPRoute with parentRefs - no Gateway, no gatewayClassName, no Certificate, no implementation-specific policy object - render aborts on both modes at once (naming both value paths), on an empty parentRefs and on a missing hostname, empty hostnames meaning [ingress.domain], a schema entry closed against unknown keys with an open parentRef definition, and a NOTES.txt that names the active mode and what the mode costs. Minor, additive, defaults unchanged: `helm template` with each chart's CI values is byte-identical to origin/main for all five. The upload limit is DOCUMENTED, not rebuilt (decision 2 of the issue). The Gateway API knows no body size; a policy object would bind the chart to one implementation. So the Gateway branch of NOTES.txt says, per chart and in the chart's own terms, that the value is NOT ENFORCED and names where to set it: an e-book library (calibre), an upload service (zipline), the web-UI half of the document intake while the consume directory keeps no limit at all (paperless), and the open #12 pair (outline). Four findings that were not in the plan, each argued in the file it changes: freesailarr gated freesailarr.sharedIngressApps on ingress.enabled, and that list is read by the <APP>__SERVER__URLBASE variables as well as by the routing objects. In Gateway mode it would have come out EMPTY while the route served radarr, sonarr and prowlarr under their prefixes: UrlBase unset, assets and API calls linked at the host root, broken UI behind a Ready pod. The helper's two guards (seerr/mediathekarr only at "/", no duplicate paths) would have gone unenforced there too. Fixed with freesailarr.published (ingress.enabled OR httpRoute.enabled) - the same shape as sure.publishedOverTls in group 1, and identical to ingress.enabled while httpRoute.enabled is false. Measured: on origin/main with ingress.enabled: false the render emits ZERO URLBASE variables; with this change in Gateway mode it emits three. paperless-ngx was the last chart in the repository rendering its Ingress UNCONDITIONALLY. Without an ingress.enabled switch there is no second value path for the mutual-exclusion abort to name, and the Ingress plus its unconditional required() on clusterIssuer would have kept rendering in Gateway mode - a value demanded for nothing, which is issue #114's "Folge 1" and README guideline 12. The switch was added, defaulting to true, so the render is unchanged. paperless-ngx PAPERLESS_URL had no required() of its own: it relied on the Ingress template's, which stops firing the moment the Ingress is switchable. With httpRoute.hostnames set and ingress.domain empty it would have rendered "https://" and refused every login POST as a CSRF failure. It now carries its own required(), naming what the value is for. calibre-web-automated claimed, in values.yaml and in its schema, that ingress.domain is read nowhere but in the Ingress template. The HTTPRoute reads it, so the claim now holds only while both modes are off. Corrected rather than left standing - the same correction garage needed in group 1. freesailarr is where the Gateway API is LESS machinery than what it replaces, not a translation of it. rewrite-target applies per Ingress OBJECT, so qBittorrent (no base path, prefix must be stripped) needed a second Ingress on the shared host, which then had to repeat the first object's TLS secret name, omit the cert-manager annotation conditionally so only one Certificate is requested, and match an ImplementationSpecific regex path for the rewrite's capture group. A URLRewrite filter applies per RULE: one route, one rule per app, the filter on qBittorrent's rule, a plain PathPrefix. All four crutches gone. It is also the only chart without httpRoute.hostnames - it has no ingress.domain either, and a chart-global hostname list could not say which of the ten apps it belonged to, so the hostnames stay in ingress.host and <app>.ingress.host. CI: one render overlay per chart under ci/httproute/, so the assertion is not vacuum-green - ci/run-httproute-match-test.sh goes from 20 held assertions over 10 charts to 36 over 15. freesailarr's overlay moves bazarr onto a host of its own, so the shared-host route AND the per-app route are both rendered; without that the per-app branch would be exercised by nothing. The install-test fixtures stay in Ingress mode: the install runs one configuration per chart, cyberchef already pays that price against the real Gateway-API CRDs, and for paperless-ngx that fixture is the only install of the valkey-subchart path anywhere. README also carries a stale figure fixed here: the HTTPRoute test was still documented at the pilot's 4 assertions over 2 charts, although group 1 had taken it to 20 over 10. Not proven, by construction: that traffic flows. No Gateway controller runs in CI. Not run locally either: k3d, Docker, Trivy. The Gateway branch of NOTES.txt is EXECUTED by the render test (a template error in it fails the job, verified) but its text is printed by no pipeline stage - helm template does not emit notes, and the install-test installs in Ingress mode. Part of #114 |
||
|
|
33d7cedbab
|
feat: Gateway-API as an alternative to the Ingress in eight charts
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / Build bentopdf (pull_request) Successful in 9s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 17s
Validate Helm Charts / Build garage (pull_request) Successful in 8s
Validate Helm Charts / Build rallly (pull_request) Successful in 7s
Validate Helm Charts / Build searxng (pull_request) Successful in 6s
Validate Helm Charts / Trivy config garage (pull_request) Successful in 18s
Validate Helm Charts / Trivy config rallly (pull_request) Successful in 18s
Install-test / Install-test bentopdf (pull_request) Successful in 1m10s
Validate Helm Charts / Trivy config sure (pull_request) Successful in 18s
Install-test / Install-test rallly (pull_request) Successful in 1m39s
Install-test / Install-test sure (pull_request) Successful in 2m39s
Install-test / Install-test voucher-vault (pull_request) Successful in 2m9s
Validate Helm Charts / Install-test (pull_request) Successful in 13m15s
Install-test / Install-test urlaubsverwaltung (pull_request) Successful in 1m50s
Publish BentoPDF Helm Chart / Publish BentoPDF Helm Chart (push) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish SearXNG Helm Chart / Publish SearXNG Helm Chart (push) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 9s
Publish Sure Helm Chart / Publish Sure Helm Chart (push) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish Patchmon Helm Chart / Publish Patchmon Helm Chart (push) Successful in 9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
SonarQube Analysis / SonarQube Analysis (push) Successful in 43s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 5s
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Trivy config bentopdf (pull_request) Successful in 19s
Validate Helm Charts / Build patchmon (pull_request) Successful in 7s
Validate Helm Charts / Build sure (pull_request) Successful in 7s
Validate Helm Charts / Build urlaubsverwaltung (pull_request) Successful in 6s
Validate Helm Charts / Build voucher-vault (pull_request) Successful in 7s
Validate Helm Charts / Trivy config patchmon (pull_request) Successful in 16s
Validate Helm Charts / Trivy config searxng (pull_request) Successful in 22s
Validate Helm Charts / Trivy config urlaubsverwaltung (pull_request) Successful in 17s
Validate Helm Charts / Trivy config voucher-vault (pull_request) Successful in 19s
Install-test / Install-test garage (pull_request) Successful in 1m17s
Install-test / Install-test patchmon (pull_request) Successful in 1m24s
Install-test / Install-test searxng (pull_request) Successful in 1m7s
Publish Garage Helm Chart / Publish Garage Helm Chart (push) Successful in 9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 9s
Publish Rallly Helm Chart / Publish Rallly Helm Chart (push) Successful in 10s
Publish Urlaubsverwaltung Helm Chart / Publish Urlaubsverwaltung Helm Chart (push) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish VoucherVault Helm Chart / Publish VoucherVault Helm Chart (push) Successful in 10s
Group 1 of the #114 sweep: the charts that render exactly one Ingress object and set no behaviour-bearing nginx annotation - bentopdf, garage, patchmon, rallly, searxng, sure, urlaubsverwaltung, voucher-vault. Each gets the form the pilot (cyberchef, PR #115) fixed and template-chart carries: an httpRoute block (enabled/parentRefs/hostnames, off by default), a template rendering ONLY an HTTPRoute with parentRefs - no Gateway, no gatewayClassName, no Certificate, no implementation-specific policy object - render aborts on both modes at once (naming both value paths), on an empty parentRefs and on a missing hostname, empty hostnames meaning [ingress.domain], a schema entry closed against unknown keys with an open parentRef definition, and a NOTES.txt that names the active mode and what the mode costs. Minor, additive, defaults unchanged: `helm template` with each chart's CI values is byte-identical to origin/main for seven of the eight, and for sure identical in every rendered value (the diff is two YAML comment lines). Three findings that were not in the plan, each argued in the file it changes: sure derived RAILS_ASSUME_SSL/RAILS_FORCE_SSL from ingress.enabled alone. Gateway mode sets that false by construction while TLS is still terminated at the Gateway, so both flags would have gone false: insecure cookies, no HSTS, http:// links, with a Ready pod and a working UI as the only symptom. They now read sure.publishedOverTls (ingress.enabled OR httpRoute.enabled), which is ingress.enabled while httpRoute.enabled is false - hence still Minor. garage claimed, in its Ingress template and its schema, that nothing outside that template reads ingress.domain. The HTTPRoute reads it, so the claim now holds only while both modes are off. Corrected rather than left standing. garage also depends on an upload limit it does not set: its annotations comment tells the operator to set proxy-body-size: "0" because ingress-nginx truncates S3 uploads at 1 MB. proxy-body-size has no Gateway-API counterpart, so its Gateway-mode NOTES prints that the limit is the Gateway's. README's list of affected charts named five and now names garage as a sixth. clusterIssuer needed no change in any of the eight: its only required() sits inside the Ingress template behind ingress.enabled, the same no-op the pilot found. Annotation guidance that stops applying in Gateway mode moved into the Gateway branch of NOTES.txt for bentopdf (X-Robots-Tag) and searxng (forward-auth). CI: one render overlay per chart under ci/httproute/, so the assertion is not vacuum-green - ci/run-httproute-match-test.sh goes from 4 held assertions over 2 charts to 20 over 10. The install-test fixtures stay in Ingress mode: the install runs one configuration per chart, cyberchef already pays that price against the real Gateway-API CRDs, and for garage the Ingress is the only place its Service port is referenced by NAME. Not proven, by construction: that traffic flows. No Gateway controller runs in CI. Not run locally either: k3d, Docker, Trivy. Part of #114 |
||
|
|
de92e59bb0
|
docs(README): the HTTPRoute test's figure from the CI run, not from the local subset
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 7s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 15s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 17s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 13s
Validate Helm Charts / Build template-chart (pull_request) Successful in 5s
Validate Helm Charts / Install-test (pull_request) Successful in 58s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 19s
SonarQube Analysis / SonarQube Analysis (push) Successful in 43s
Validate Helm Charts / Selector match (pull_request) Successful in 13s
Install-test / Install-test cyberchef (pull_request) Successful in 58s
Publish Cyberchef Helm Chart / Publish Cyberchef Helm Chart (push) Successful in 19s
The authoring environment cannot reach the private Helm registry, so the three charts with a dependency were missing from the local run and the number quoted 27 charts. CI run 719 renders all 30 and holds the same 4 assertions - that is the figure a later deviation should be compared against, so it is the one written down. |
||
|
|
50f683776e
|
feat(cyberchef): Gateway-API as an alternative to the Ingress (pilot)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 13s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 18s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 17s
Install-test / Install-test cyberchef (pull_request) Successful in 1m0s
Validate Helm Charts / Install-test (pull_request) Successful in 1m0s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Build template-chart (pull_request) Successful in 7s
Validate Helm Charts / Selector match (pull_request) Successful in 12s
The Gateway-API pilot of Gitea #114: one chart, the convention, the CI that holds it. The remaining 20 charts follow in their own PRs. The form, additive and off by default: httpRoute: enabled: false # counterpart to ingress.enabled parentRefs: [] # the Gateway - a platform object hostnames: [] # empty = [ingress.domain] Only HTTPRoute with parentRefs is rendered: no Gateway, no gatewayClassName, no Certificate, and no implementation-specific policy object. That subset is identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio, which is what lets the implementation be chosen later without touching the charts again. Class and certificate move to the platform. Four guards, all against silent failures: - mutually exclusive with ingress.enabled, with a render abort naming BOTH value paths. A migration runs over a second hostname, not in parallel. - parentRefs mandatory in Gateway mode, render abort on the empty list: a route without a parent is created, is schema-valid, and routes nothing. - hostnames empty means [ingress.domain], so the hostname has one source. - the mode is a VALUE, not auto-detection: .Capabilities.APIVersions is unreliable at template time and a missing CRD fails at install time. ingress.clusterIssuer was already only read inside the Ingress template, so the "conditionally required" half of #114 needed no change in this chart - checked rather than assumed, and it has to be re-checked per chart in the sweep. CI, both halves, because neither covers the other: - the install-test installs cyberchef in GATEWAY mode against the REAL Gateway-API CRDs (standard channel v1.6.3, pinned and Renovate-tracked, applied --server-side). A schema-less stub would accept a malformed route and prove only that something was created. The Ingress path of this one chart is no longer installed; it is a built-in type that never routed in k3d anyway, and helm lint renders this chart's defaults - Ingress mode - on every PR. - ci/run-httproute-match-test.sh, over every chart on every PR: every backendRefs entry must name a Service, on a port, that the SAME render creates, and parentRefs must not be empty. The CRDs accept both of those mistakes, so the schema cannot catch them, and the only report would be a route status no controller writes in CI. What stays unprovable, and is written down instead of implied: that traffic flows. No Gateway controller runs in k3d. Minor: additive with unchanged defaults, proven by an empty helm template diff against origin/main for the default values. Part of #114 |
||
|
|
b5b5d9a310
|
Update docker.io/stalwartlabs/stalwart Docker tag to v0.16.25
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 12s
Validate Helm Charts / Build stalwart (pull_request) Successful in 5s
Install-test / Install-test stalwart (pull_request) Successful in 1m9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 15s
Publish Stalwart Helm Chart / Publish Stalwart Helm Chart (push) Successful in 15s
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Selector match (pull_request) Successful in 9s
Validate Helm Charts / Trivy config stalwart (pull_request) Successful in 11s
Validate Helm Charts / Install-test (pull_request) Successful in 1m9s
SonarQube Analysis / SonarQube Analysis (push) Successful in 26s
|
||
|
|
6ade74db80
|
feat(stalwart): spread each node group over nodes and give each one a PDB
A two-replica node group was redundancy on paper: nothing kept both pods off one node, and no PodDisruptionBudget kept a drain from taking both at once. Both objects are now rendered per node group, gated on that group's COMPUTED replica count being >= 2. The distribution is a promise, not an effort: maxSkew 1 on kubernetes.io/hostname with whenUnsatisfiable DoNotSchedule, selecting only this group's own pods, so an "mx" and a "full" pod may share a node. The selector comes from the chart (stalwart.groupLabels), never from the operator - a spread constraint whose selector matches no pod is trivially satisfied, distributes nothing and reports nothing. nodeAffinityPolicy: Ignore is set explicitly, and that is the load-bearing detail. Its API default is Honor, under which a narrowing nodeSelector shrinks the set of counted domains until both replicas fit into one: skew 0, both pods co-located, no Pending, no event, and a selector that is correct - so no check can see it. With Ignore the same narrowing gives skew 2 and the second pod is Pending. A render abort was the alternative and would also have forbidden a nodeSelector spanning five nodes, which is legal. A user entry colliding with the chart's own on (topologyKey, whenUnsatisfiable) aborts the render naming both values, because the API rejects the duplicate with a message about a list index and an apply-based path merges the two over patchMergeKey: topologyKey silently, dropping one selector. Minor rather than major, proved with a diff rather than argued: with the shipped defaults (full: 1, mx: 0) and with ci/stalwart/test-values.yaml, helm template renders byte-for-byte what 1.0.1+upv0.16.24 rendered - both diffs empty. New in CI, over every chart on every PR: ci/run-selector-match-test.sh holds every rendered PodDisruptionBudget selector and every topologySpreadConstraints labelSelector against the labels of the pod templates in the same render. Helm itself is the YAML parser (ci/yaml2json), because the runners have neither pyyaml nor yq and a hand-written parser could read nothing and report nothing. podAffinity/podAntiAffinity are out of scope; matchExpressions-only selectors are reported as not checked rather than counted as passing. Two anti-vacuum guards: a chart with an overlay under ci/selector-match/ that renders no selector fails, and a run that checks no selector at all fails. Fixes #96 |
||
|
|
1698fc0aeb
|
Update itzg/mc-router Docker tag to v1.47.2
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Install-test / Install-test minecraft-router (pull_request) Successful in 5m9s
Validate Helm Charts / Install-test (pull_request) Successful in 5m9s
SonarQube Analysis / SonarQube Analysis (push) Successful in 27s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Resource quantities (pull_request) Successful in 10s
Validate Helm Charts / Build minecraft-router (pull_request) Successful in 4s
Validate Helm Charts / Trivy config minecraft-router (pull_request) Successful in 13s
Publish Minecraft-Router Helm Chart / Publish Minecraft-Router Helm Chart (push) Successful in 7s
|
||
|
|
6e196c26a5
|
test(ci): restore the theme mount - the A/B shows the restart is pre-existing
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 12s
Install-test / Install-test keycloak (pull_request) Successful in 3m10s
Validate Helm Charts / Install-test (pull_request) Successful in 3m10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Keycloak Helm Chart / Publish Keycloak Helm Chart (push) Successful in 7s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
SonarQube Analysis / SonarQube Analysis (push) Successful in 28s
|
||
|
|
8cd0c14e3f
|
test(ci): control run 598 recorded (81s, 0 restarts)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Build keycloak (pull_request) Successful in 9s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 16s
Validate Helm Charts / Resource quantities (pull_request) Successful in 13s
Install-test / Install-test keycloak (pull_request) Successful in 9m24s
Validate Helm Charts / Install-test (pull_request) Successful in 9m24s
|
||
|
|
71cc545277
|
test(ci): A/B control run - keycloak install-test without the theme mount (temporary)
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 10s
Install-test / Install-test keycloak (pull_request) Successful in 2m14s
Validate Helm Charts / Install-test (pull_request) Successful in 2m14s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
|
||
|
|
d7067abfab
|
docs(ci): the whole restart series, intermittent on an unchanged spec
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 9s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Install-test (pull_request) Successful in 2m45s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 10s
Install-test / Install-test keycloak (pull_request) Successful in 2m45s
|
||
|
|
5e47191252
|
docs(ci): three runs, and the load explanation does not hold
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 9s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 13s
Validate Helm Charts / Install-test (pull_request) Successful in 3m59s
Install-test / Install-test keycloak (pull_request) Successful in 3m59s
|
||
|
|
34c600f7a8
|
docs(ci): the second run settles it - the keycloak restart was the runner, not the pod spec
Validate Helm Charts / Resource quantities (pull_request) Successful in 10s
Validate Helm Charts / Install-test (pull_request) Successful in 4m2s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 10s
Install-test / Install-test keycloak (pull_request) Successful in 4m2s
|
||
|
|
1cf59f5448
|
docs(ci): record the keycloak install-test run that was slower and restarted once
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 11s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 9s
Install-test / Install-test keycloak (pull_request) Successful in 2m18s
Validate Helm Charts / Install-test (pull_request) Successful in 2m18s
|
||
|
|
d8b392bf64
|
feat(keycloak): extraVolumes/extraVolumeMounts for custom themes and providers (1.1.0+up26.8.0)
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 17s
Validate Helm Charts / Install-test (pull_request) Successful in 3m9s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 11s
Install-test / Install-test keycloak (pull_request) Successful in 3m9s
|
||
|
|
b39f36ff5c
|
feat(bulwark): new chart for Bulwark Webmail (1.0.0+up1.12.0)
A JMAP webmail client for the stalwart chart (Gitea #65): StatefulSet, one PVC at /app/data, Service plus headless Service, Ingress at a host root, two OnePasswordItems, three probes, computed limits, replicas plus scaleDown, strict values.schema.json. Everything below was measured against the source at tag 1.12.0 rather than read out of the documentation, which was wrong about three of them. THE DEFINING PROPERTY: THE VOLUME BEATS THE CHART, FOR EVERY VALUE, SILENTLY. configManager.get resolves "admin override > env var > default", the admin dashboard may write any key of CONFIG_ENV_MAP, and nothing reports that it just disconnected the chart - so a helm upgrade can succeed and change nothing. Upstream documents this for the server URL only; it is true of everything. Exactly three values escape it (SESSION_SECRET(_FILE), ADMIN_PASSWORD, BULWARK_TELEMETRY). The value that closes it, bulwark.adminConfigReadOnly, defaults to false because its price is threefold: it freezes the settings policy (and with it the only way to move Web Push off the vendor relay), it freezes an override that ALREADY exists instead of removing it, and on a fresh install it stops admin.json from being written at all - so the admin dashboard never comes up and its login answers HTTP 500 while the pod stays Ready. OIDC-only sign-in, with the real way back out of a lockout: not helm upgrade with OAUTH_ONLY=false (oauthOnly goes through the same precedence chain) but removing the override in the admin dashboard. That is why ADMIN_PASSWORD is set - the dashboard is the door. It generates no random password and logs nothing, so the #65 analogy does not apply. SESSION_SECRET_FILE and OAUTH_CLIENT_SECRET_FILE are both used - the better path garage (#81) and keycloak (#83) did not have - in one projected volume mounted WITHOUT subPath, because both are re-read on every use and a subPath mount is never refreshed, and at mode 0440 because secret files are owned by root and fsGroup only supplies the group. Two backup statements, both at the value: without the session secret the synchronized settings are unreadable out of an intact volume, and if the dashboard's OAuth setup was ever used the client secret sits in config.json on that volume in cleartext. Probes: all three a plain GET on /api/health, with the limit written at the readiness value - it carries process, HTTP acceptance, a free event loop and Next.js routing, and nothing about the reported state or about Stalwart. No ConfigMap, so the checksum/config rule (#82) has nothing to attach to, and values.yaml says so explicitly; the op:// paths are covered by checksum/secret-ref. Renovate: no versioningTemplate, measured rather than assumed. 315 tags in eight shapes (releases, a second v-prefixed family of the same images, an -always flavour, floating minors and names, commit SHAs, betas, attestations) and plain docker versioning excludes all six unwanted ones. Dry runs at 1.12.0, 1.11.0 and 1.10.0 are recorded in the block. Part of #89 - deliberately not "Fixes", because one binding decision of the issue was changed out of necessity and wants ratification rather than an automatic close: the plan specifies HEAD /api/health for all three probes, and a Kubernetes probe cannot send HEAD at all (HTTPGetAction has no method field; the kubelet always issues GET). The alternative, an exec probe, would mean starting a Node process every few seconds in an image that carries no curl and no wget. GET is used instead and is marginally more capable than HEAD, so every limit the issue argues is unchanged - but the decision is the maintainer's to confirm. |
||
|
|
40e29f7f72
|
Update lukevella/rallly Docker tag to v4.15.4
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Install-test / Install-test rallly (pull_request) Successful in 1m35s
Validate Helm Charts / Build rallly (pull_request) Successful in 4s
Publish Rallly Helm Chart / Publish Rallly Helm Chart (push) Successful in 7s
SonarQube Analysis / SonarQube Analysis (push) Successful in 26s
renovate/stability-days Updates have not met minimum release age requirement
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Install-test (pull_request) Successful in 1m35s
Validate Helm Charts / Trivy config rallly (pull_request) Successful in 9s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
|
||
|
|
28567842f3 |
fix(keycloak): group 1000 instead of the image's root group
The first Trivy run on this chart reported one finding out of 96 tests: AVD-KSV-0116, "Runs with a root primary or supplementary GID", because the pod context carried runAsGroup: 0 - copied from what the image expects. Checked instead of suppressed, and the check says the group is not needed. Counted across the image layer of quay.io/keycloak/keycloak:26.8.0: 521 of 522 entries are owned by 1000:0, and every mode present (drwxrwxr-x, -rwxrwxr-x, -rw-rw-r--) grants the OWNER full access. The one remaining entry is /opt itself, which needs only traverse. So uid 1000 gets everything it reads and executes through the owner bits, whatever group it is in. Group 0 is upstream's OpenShift arbitrary-uid pattern - root group plus a group-writable tree, so that ANY uid landing in group 0 can write. It buys nothing once the owning uid is pinned, which this chart does. Writes all go into the three emptyDirs, which fsGroup 1000 makes writable. So runAsGroup is 1000, the finding is gone rather than excepted, and no entry was added to ci/trivy/trivyignore.yaml - which matters more than one LOW finding: that file scopes by chart, so an AVD-KSV-0116 exception for keycloak would also have silenced it for the serving container, on a check that is making a fair point about an identity provider. The condition under which this goes back to 0 is written at the value: a path uid 1000 does not own. There is none in this image today. Part of #83 |
||
|
|
320257c742 |
fix(keycloak): readiness could not fail on the database, and the measured budget
Two corrections found while verifying the first CI run, not from the plan. THE DEFAULT THAT MADE THE READINESS PROBE TOOTHLESS. Keycloak's DATABASE health check is the one row of upstream's "Available Checks" table whose "Requires Metrics" column says yes; Cluster, Graceful shutdown and Keycloak Initialized say no. So with metrics-enabled=false - this chart's default until now - /health/ready answers 200 while the database is gone, and the pod keeps taking logins it cannot serve. The probe path was right and the aggregate behind it contained nothing external, which is the urlaubsverwaltung defect (#218) in a new hiding place: not a health group this time, but an option named for observability, where nobody looks when reviewing a probe. keycloak.metrics.enabled therefore defaults to TRUE, and its comment argues about readiness rather than about scraping. The price is stated at the value: /metrics then answers on the management port, unauthenticated, carrying realm names, client ids and login counters - on a port that is ClusterIP-only and in no Ingress. Switching it off is now documented as a decision about whether readiness can fail at all. The CI fixture keeps it on and says why, so a green run really does touch the database. THE STARTUP BUDGET NOW CARRIES ITS MEASUREMENT. From the install-test of PR #87 on the Gitea docker runner (k3s v1.35.5, two replicas, empty database, image pulled during the run): 71.8 seconds from `helm install` to BOTH pods Ready - first at ~70.8s, second one second later, 0 restarts. The 600s budget stays, with the reason for the headroom written down: everything in that run was the cheap case, and a first install against a real database serializes the second replica behind the first's Liquibase migration lock instead of letting it arrive a second later. AND AN OVERCLAIM OF MY OWN, REMOVED. ci/keycloak/test-values.yaml said a green run proves jdbc-ping found the second replica. It does not: readiness reports one node's own health, so a node that never found its peer is Ready, healthy and alone - and two such nodes are two islands that each believe they are the cluster. What the test does rule out is the louder half, a replica that cannot start or cannot share the database. The Infinispan view has to be read out of the log, which is what step 2 of NOTES.txt tells the operator to do, now with the note that no probe and no install-test replaces that step. Measurement point 4 of the plan therefore stays open. Part of #83 |
||
|
|
4c1cff1744 |
feat(keycloak): new chart for Keycloak (1.0.0+up26.8.0)
Two replicas with cache=ispn, clustered through Keycloak's default jdbc-ping stack: discovery runs over the PostgreSQL that is there anyway, so no headless Service and no JGroups wiring. Deployment rather than StatefulSet - Keycloak 26 keeps everything in the database, user sessions included. The chart's defining property is that it has TWO INIT CONTAINERS and needs both. db, health-enabled and metrics-enabled are Keycloak BUILD options (verified in the 26.8.0 option table), the published image is augmented with their defaults, and supplying different ones at runtime makes Keycloak rewrite /opt/keycloak/lib/quarkus at start - which readOnlyRootFilesystem: true forbids and which upstream describes as a start that "creates an image that is mutable". Not setting db is no way out: the probes alone force it, because health-enabled is a build option too. An empty emptyDir over that path fails as well, because QuarkusEntryPoint loads quarkus/quarkus-application.dat before it dispatches any command, `build` included. So one init container seeds the directory into a volume, the next runs `kc.sh build` into it, and the serving container runs `start --optimized` - every container keeping the full hardening baseline, with no documented deviation to carry. Build options therefore exist in exactly one place, and the asymmetry is enforced rather than trusted: the serving container passes no build option at all, and a build option's variable name in keycloak.env fails the render with a message naming it. Hostname handling is the security-relevant part. KC_HOSTNAME is derived from ingress.domain as a full URL (which upstream requires alongside hostname-admin), hostname-strict stays at its default true, and proxy-headers is set rather than optional. The admin hostname is NOT presented as an access control: upstream states that hostname-admin "does not prevent accessing the Administration REST API endpoints via the frontend URL specified by the hostname option", so the chart renders no /admin deny rule and values.yaml, NOTES.txt and the wiki all say in the same words that restricting that API is reverse-proxy work. The management port 9000 is a containerPort and a ClusterIP Service for in-cluster scraping, and is in no Ingress: it serves /health/* and optionally /metrics without authentication. Liveness uses /health/live and deliberately not /health or /health/ready - those include the database, and a liveness probe on them would restart the whole cluster for as long as the database is away. Also in this change: - First PodDisruptionBudget in this repository, which makes it a convention (README section 11): minAvailable as a value, and rendered only while the computed replica count is >= 2, because minAvailable: 1 with one replica blocks every node drain forever with a message that names the PDB rather than the replica count. - First quay.io image, so quay.io joins the trusted registries in ci/trivy/data/ksv0125.yaml - configured rather than suppressed, so AVD-KSV-0125 keeps being able to say something. - Renovate block with NO version constraint, which is the counter-case to garage and stalwart and is measured rather than assumed: docker versioning already excludes the -N rebuild tags, the floating minors and latest/nightly. Three dry-run fixtures are recorded at the block, and against the committed config 26.7.4 offers exactly 26.7.5 (patch) and 26.8.0 (minor). - No automerge exception, by decision (Gitea #83): the global automerge applies, and the minor-upgrade procedure plus `kc.sh update-compatibility` is documented in NOTES.txt instead. - Two upstream defaults deviated from, each with its reason at the value: db-pool-max-size 20 instead of 100 (two replicas at 100 ask a default PostgreSQL for twice its max_connections), and uid 1000 / gid 0 instead of this repo's 20000/1000, because the image declares keycloak:x:1000:0 and chowns its install tree to that pair. Part of #83 |
||
|
|
a23d4977c5
|
Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.15.0
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Trivy config urlaubsverwaltung (pull_request) Successful in 10s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 8s
SonarQube Analysis / SonarQube Analysis (push) Successful in 28s
Validate Helm Charts / Resource quantities (pull_request) Successful in 9s
Validate Helm Charts / Build urlaubsverwaltung (pull_request) Successful in 4s
Install-test / Install-test urlaubsverwaltung (pull_request) Successful in 2m3s
Validate Helm Charts / Install-test (pull_request) Successful in 2m3s
Publish Urlaubsverwaltung Helm Chart / Publish Urlaubsverwaltung Helm Chart (push) Successful in 7s
|
||
|
|
639e0b5f57 | Update docker.io/rssbridge/rss-bridge Docker tag to v2026 | ||
|
|
236767070d |
Update dependency aquasecurity/trivy to v0.75.0
SonarQube Analysis / SonarQube Analysis (push) Successful in 28s
|
||
|
|
caea6e7353
|
Update Helm release oauth2-proxy to v10.7.1
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Resource quantities (pull_request) Successful in 25s
Validate Helm Charts / Build ytdlp-web-player (pull_request) Successful in 9s
Install-test / Install-test ytdlp-web-player (pull_request) Successful in 2m6s
Validate Helm Charts / Install-test (pull_request) Successful in 2m6s
Publish YTDLP Web Player Helm Chart / Publish YTDLP Web Player Helm Chart (push) Successful in 36s
SonarQube Analysis / SonarQube Analysis (push) Successful in 31s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 7s
Validate Helm Charts / Trivy config ytdlp-web-player (pull_request) Successful in 40s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 37s
|
||
|
|
b19da4e580
|
fix(garage): a comment in the ConfigMap tripped Trivy's KSV-0109
Validate Helm Charts / Resource quantities (pull_request) Successful in 11s
Validate Helm Charts / Build garage (pull_request) Successful in 5s
Validate Helm Charts / Install-test (pull_request) Successful in 1m20s
Publish Garage Helm Chart / Publish Garage Helm Chart (push) Successful in 10s
SonarQube Analysis / SonarQube Analysis (push) Successful in 36s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 2s
Install-test / Install-test garage (pull_request) Successful in 1m20s
Validate Helm Charts / Trivy config garage (pull_request) Successful in 16s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Run 446 reported one HIGH finding, and it was prose, not data: the line "metrics_require_token = true plus a token to close it" sits inside the ConfigMap's data block, and KSV-0109 reads a <key> = <value> assignment with a credential-ish key name as a stored secret - comments included. The ConfigMap holds no credential at all; the RPC secret arrives as an environment variable precisely so that it cannot. Reworded rather than excepted: an entry in ci/trivy/trivyignore.yaml would carry a permanent exception for one sentence, and the next reader of the report would have to re-establish that the ConfigMap really is secret-free. The replacement names the mechanism at the line, so the shape does not come back. |
||
|
|
01336af953
|
docs(garage): state the admin surface precisely, including open /metrics
Validate Helm Charts / Build garage (pull_request) Successful in 7s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 5s
Validate Helm Charts / Trivy config garage (pull_request) Successful in 15s
Install-test / Install-test garage (pull_request) Successful in 1m22s
Validate Helm Charts / Install-test (pull_request) Successful in 1m23s
Validate Helm Charts / Resource quantities (pull_request) Successful in 11s
"No admin_token is set" read like "nothing is open", and two parts of that
were wrong. /metrics needs no authentication either, because
metrics_require_token defaults to false - internal metrics on a ClusterIP port.
And a config token is not the only way to reach the authenticated API: Garage
2.x keeps admin tokens in the cluster as well ("garage admin-token create"),
which work without any entry in garage.toml. Both now stated at the ConfigMap
and the Service, plus the way to close /metrics.
|
||
|
|
5d10b02458
|
fix(garage): compressionLevel disables with "none", never with 0
Validate Helm Charts / Trivy config garage (pull_request) Successful in 16s
Install-test / Install-test garage (pull_request) Successful in 1m17s
Validate Helm Charts / Resource quantities (pull_request) Successful in 12s
Validate Helm Charts / Build garage (pull_request) Successful in 7s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Install-test (pull_request) Successful in 1m17s
My own values comment claimed 0 switches compression off. It does not: Garage's deserializer takes an integer OR the literal string "none" (src/util/config.rs, deserialize_compression), and 0 is a valid integer that zstd reads as its default level - 3, i.e. MORE compression than the chart's 1, not none. A comment that invites an operator to write 0 for "off" is the values-that-must-not-lie shape with the lie in the documentation instead of in the template. The schema now accepts an integer (negative included, those are zstd's fast modes) or exactly "none", and the ConfigMap quotes the string form so the rendered TOML stays valid. Verified: 1, 7, -3 render unquoted, "none" renders quoted and the result still parses as TOML, and any other string is rejected by the schema. No version bump: the chart is not published yet. |
||
|
|
79b7e44fdc
|
feat(garage): new chart for the Garage object store
Install-test / Install-test garage (pull_request) Successful in 1m15s
Validate Helm Charts / Install-test (pull_request) Successful in 1m15s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 11s
Validate Helm Charts / Build garage (pull_request) Successful in 6s
Validate Helm Charts / Trivy config garage (pull_request) Successful in 16s
One Garage node with replication_factor = 1, built from template-chart to the style guide rather than copied from upstream's in-tree chart (Gitea #81). The single-node trade is deliberate and its price is named at the value: the factor applies to the metadata as well, where upstream calls a corrupted database "generally not recoverable" and a replicated volume replicates the corruption faithfully. The three countermeasures that work at factor 1 are therefore mandatory, not optional - metadata_fsync = true (off in Garage's defaults), the automatic metadata snapshots, and those snapshots on a second PVC. Storage: PVC 1 carries metadata_dir AND data_dir as two subdirectories, because volume snapshots are atomic per volume and that is what keeps index and blocks consistent with each other; PVC 2 carries metadata_snapshots_dir, because a recovery point must not die with the volume whose corruption it should repair. Garage creates all three directories itself, which matters because the image is FROM scratch and has no mkdir. Two corrections to the approved plan, both verified against the source at tag v2.4.1 and decided on the issue before implementation: - The RPC secret arrives as GARAGE_RPC_SECRET via secretKeyRef, not through rpc_secret_file. The file form cannot work in a non-root pod: read_secret_file rejects any mode with a group or other bit set (src/garage/secrets.rs:144, mask 0o077) while the kubelet OR-s the group-read bit onto every file of a secret volume once fsGroup is set (pkg/volume/volume_linux.go), and Kubernetes can set the group of such a file but never its owner. The mechanism is documented at the value so the next round does not "fix" it back. - The first start is done by the server, not by hand: garage server --single-node creates the layout in-process before any listener opens, is idempotent on the layout version and has no --version argument that could be reused, and --default-access-key/--default-bucket import key and bucket FROM the vault item. Both premises of the original "manual first start" decision fell with that. Probes follow from the same source reading: readiness is GET /health on the admin port, which answers 503 while no layout exists; liveness and startup are a TCP connect, because a probe that can only fail would restart a node waiting for its layout forever (same lesson as zipline, README 12.3). The S3 listener binds independently of the layout, so a TCP probe on 3900 would have been the lie the plan warned about. CI: fixtures under ci/garage/ rather than an exclusion - with --single-node the pod reaches Ready on its own, which makes the install-test an assertion about the layout, the one-PVC subdirectory layout and readOnlyRootFilesystem instead of a formality. The Renovate block carries a regex versioning anchored to ^v<major>.<minor>.<patch>$, because the registry receives 40-character commit-SHA tags almost daily (measured 2026-10-03: the newest 22 tags are all of that kind) and the four v1.99.x-internal builds sort above the v1.3.x line. Part of #81 |
||
|
|
bf2e1009e4 | Update docker.io/library/busybox Docker tag to v1.38.0 | ||
|
|
a9ca5302de
|
feat(paperless-ngx): optional valkey subchart, password from the redis item
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 12s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 22s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m31s
Validate Helm Charts / Install-test (pull_request) Successful in 3m31s
SonarQube Analysis / SonarQube Analysis (push) Successful in 33s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish Paperless-NGX Helm Chart / Publish Paperless-NGX Helm Chart (push) Successful in 10s
paperless-ngx cannot run without a Redis - PAPERLESS_REDIS is the Celery broker - so every installation needed a second Helm release whose address lived in a 1Password item and in neither chart. This adds our own published valkey chart as an optional dependency, off by default and gated on valkeySubchart.enabled (not valkey.enabled, which the subchart's strict schema rejects and which Helm would only have complained about for whoever first switched it on). Additive, default behaviour unchanged: minor, 12.1.3+up3.2.1 -> 12.2.0+up3.2.1. Configured as a QUEUE (PVC + noeviction, the subchart's defaults), unlike stalwart's cache. Read off the 3.2.1 source rather than assumed: one PAPERLESS_REDIS url serves five consumers - the Celery broker, the result backend (Redis in 3.x, the database before), the channels pub/sub layer and two Django caches. allkeys-lru would not be a trade here but actively wrong, because LRU evicts typeless across the whole keyspace and a backlog of waiting consume tasks is exactly what it reads as cold; a dropped task is a document silently never processed. The price, named in values.yaml rather than smoothed over: two of the five consumers are caches, so a full instance refuses writes - new tasks included - and the answer is memory sizing, not a policy change. All three cache-like consumers are TTL-bounded, which is what keeps that price small. The password comes from the SAME item as the Redis connection, in a third field auth-conf holding the complete line "requirepass <password>". It cannot DEFAULT to secrets.redisSecretRef, and that is a property of Helm, not an omission: a values.yaml is never templated and a parent cannot compute a subchart's value. Measured - a template expression there lands verbatim in the subchart's itemPath and fails as a YAML parse error naming neither the value nor the cause. Forgetting it is therefore caught in values.schema.json, by a conditional rule rather than a fail() in a template. Also measured, and the reason: Helm renders a subchart's templates BEFORE the parent's, so a guard in this chart loses the race to the subchart's own required(), which reports "secrets.authSecretRef" and cannot know the path belongs to the redis item. A check that cannot fire is the mistake found while testing #77, so the first draft of that guard was removed rather than shipped. The schema runs before any rendering and names /valkey/secrets/authSecretRef. One trap found while testing and closed here: "valkeySubchart: null" DELETES the key, Helm then cannot resolve the condition path and treats the dependency as ENABLED - so erasing the switch turned the subchart on with no password set. Unlike every other block in this chart (issue #99), that one is rejected by the schema instead of defaulting. The install-test now runs with the subchart ON and the throwaway redis Deployment removed: the url in the fixtures names ci-valkey-service, so the bundled Valkey is the broker the release actually uses. #65 covered this path by a render only - deliberately, since an unused Valkey would have made that test look like it covered clustering. This is the first real installation of the valkey-subchart path. Documented, not glossed: the password now sits in that item twice and a rotation has to touch both fields; with the subchart on the vault url is a mandatory step, not a hint; and a OnePasswordItem materializes every field of its item, so both Secrets carry all three keys - untidy, harmless, and written down so nobody "tidies it up". Fixes #78 |
||
|
|
a288ee231a |
Pin dependencies
SonarQube Analysis / SonarQube Analysis (push) Failing after 20s
|
||
|
|
07c75e80c1 |
feat(stalwart): take our own published valkey chart as the subchart
Replaces the dependency on upstream's valkey chart with this repo's own, resolved from the published index: version: "1.0.0+up9.1.2.alpine" repository: "https://git.jk-dev.de/api/packages/jlab-cluster/helm" The condition is "valkeySubchart.enabled", NOT "valkey.enabled", and that is forced rather than stylistic. Everything under "valkey:" is handed to the subchart as its values and validated against ITS schema, and our valkey chart ships additionalProperties: false with no "enabled" property - so "valkey.enabled" is rejected by the subchart: valkey: - at '': additional properties 'enabled' not allowed Measured. And because Helm skips a DISABLED subchart's schema validation, that error would have surfaced only for whoever first set it to true: every render, lint and install-test in CI would have stayed green. Adding "enabled" to valkey/values.schema.json was rejected as the repair - that chart does not read the key, and it would have needed its own PR and a publish first. The host helper was also wrong for our chart and is fixed: our Service is "<release>-valkey-service", not upstream's bare "<release>-valkey". The values block is rewritten for our chart's schema, with the pair chosen rather than inherited: CACHE (persistence off + allkeys-lru), because everything Stalwart keeps in the in-memory store - rate limiters, auth tokens, sessions, auto-ban entries, coordination pub/sub - is reconstructible, and the node-id lease lives in the data store. The cost is named where it is set: with allkeys-lru the rate limiters and the auto-ban FAIL OPEN under memory pressure, so the answer is to size memory, not to switch to noeviction (which would make writes error and take out authentication instead). secrets.authSecretRef wants an item whose single field "auth-conf" holds the complete "requirepass <pw>" line, not a bare password. Chart.yaml's comments are corrected where they became false: nothing is "consumed as it comes" any more and there is no shortfall of someone else's to document. The Renovate reasoning stays and is now backed by measurement. New trap, in Chart.yaml and in three places in the wiki: NEVER change valkey/ and a chart depending on it in the same PR. The dependency resolves from the published index, so a combined PR lints, packages, renders AND install-tests against the OLD subchart - green on an artifact that is not the one in the diff. Two consecutive PRs. Two things measured, not assumed, because either could have broken the update path with no symptom but "no updates ever": - helm dependency update AND build resolve the exact build-metadata version from the URL alone with an EMPTY HELM_REPOSITORY_CONFIG - a fresh runner's state - so the anonymous index needs no credential and ci/helm-dependencies.sh is untouched; - Renovate 44.132.2 dry run with the dependency pinned low: manager helmv3 matched the file, took registryUrls from Chart.yaml and offered newVersion "1.0.0+up9.1.2.alpine", versioning "helm". The self-hosted index and the "+up..." suffix are both handled. README: the claim "no registry address appears in this repository" is narrowed to what still holds - the publish target and its credentials come from 1Password and are not in the repo. The read path is now in Chart.yaml by necessity, carries no credential, and the host was always in CLAUDE.md. Done here rather than later so the paperless PR finds it already corrected. Re-verified after the change: helm lint --strict (0 failed), helm package (the subchart is embedded in the .tgz), helm template with the subchart OFF and ON (ON was previously impossible), against CI test-values, the rendered config.json, ci/run-quantity-render-test.sh (0 failed), NOTES.txt through the scratch-ConfigMap trick, and the fail paths including the reworded abort. The rendered subchart Service name matches what the helper emits, the cache pairing really renders (maxmemory-policy allkeys-lru, appendonly no, save "", emptyDir) and no PVC appears. Part of #65 |
||
|
|
780cb8eafd |
feat(stalwart): new chart for the Stalwart mail server
Cluster-capable from the start, with roles rather than symmetry: one StatefulSet per entry of the "nodes" map, the key passed in STALWART_ROLE. Since v0.16 Stalwart keeps its whole configuration inside the data store and manages it over JMAP, so the chart's scope is deliberately narrow: it renders config.json (exactly the DataStore object), the workloads, the Services, the NetworkPolicies, the Ingress and a cert-manager Certificate. Listeners, blob store, in-memory store, TLS object and cluster roles are operator work in the WebUI, which is why NOTES.txt carries an eight-step first install. config.json stays secret-free: authSecret uses the EnvironmentVariable variant (verified against the serde definition of PostgreSqlStore at v0.16.24), so the password arrives through secretKeyRef and checksum/config stays meaningful. The username cannot follow it - authUsername is a plain String with no secret form - so it is a public value and says so. Services are per PORT, not per group: a NodePort is cluster-unique, so "full" and "mx" can only accept SMTP behind one Service, selected through a per-port pod label. The http port gets a per-group ClusterIP Service instead, so the Ingress reaches exactly ingress.nodeGroup. Four traps, documented rather than explained away: - The role lookup FAILS OPEN. A node whose ClusterRole is missing from the database enables every listener and every task while staying Running with a green probe, and the log line arrives after the listeners are bound. Hence mx at replicas 0, per-port Services, and a NetworkPolicy that is a ceiling independent of what was bound. - NET_BIND_SERVICE is a documented deviation needed to EXEC, not to bind. The image sets setcap cap_net_bind_service=+ep, which makes the kernel fail execve when the bounding set is empty. The values comment says both why it is there and that it is never used, because the second half is what stops the next hardening pass from removing it. - externalTrafficPolicy must be Cluster - no pod runs on a control-plane node while the HA proxy delivers to them, so Local would drop every packet - and the source address that costs is restored only by the HA proxy's PROXY v2 header. - /healthz is rate-limited to 100 requests/60s per source IP, which is a node IP for kubelet probes, so all pods on a node share one bucket. The probe intervals are calculated (6 req/min/pod, ~16 pods per node) and the sum is a comment in values.yaml. A first install needs bootstrap.enabled: true: the setup wizard appears only when config.json is ABSENT, and this chart always renders one, so recovery mode is upstream's documented path for a templated config.json. Second chart in the repo with a dependency - the Valkey project's own chart, off by default behind condition:, because Stalwart's cluster coordinator rides on the in-memory store connection. The render aborts when more than one pod runs with no in-memory store address anywhere. Also wired up: publish-stalwart.yml through the shared block in workflow-util/pipelines, the change-app-version.yml choice list, a Renovate customManagers block anchored on the v prefix (which excludes the moving latest / v0.16 / -alpine tags), CI fixtures, and the README and CLAUDE.md lines that claimed ytdlp-web-player was the only chart with a dependency. Verified locally: ci/helm-dependencies.sh, helm lint --strict (0 failed), helm package, helm template against chart defaults and against ci/stalwart/test-values.yaml, the rendered config.json parsed as JSON with the field types the serde definition requires, ci/run-quantity-render-test.sh (0 failed, both nodes.*.resources paths exercised), NOTES.txt rendered through a scratch ConfigMap, and the null-safety and fail paths (securityContext=null keeps NET_BIND_SERVICE; a cluster without an in-memory store, an unknown port name, a group without http and an unknown ingress.nodeGroup all abort with a named message; the schema rejects timeout, a string timeoutMs and a list-shaped nodes). NO runtime verification was possible - there is no k3d or Docker daemon in this workspace. Part of #65 |
||
|
|
ee43c33ef4
|
Update dependency kubernetes/kubernetes to v1.35.9
renovate/stability-days Updates have met minimum release age requirement
validate-charts.yml / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Blocked by required conditions
validate-charts.yml / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Blocked by required conditions
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Install-test (pull_request) Skipped
Validate Helm Charts / Resource quantities (pull_request) Successful in 10s
SonarQube Analysis / SonarQube Analysis (push) Failing after 24s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Skipped
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Skipped
|
||
|
|
9ae7eef99f
|
Update lukevella/rallly Docker tag to v4.15.3
Validate Helm Charts / Detect changed charts (pull_request) Successful in 49s
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Successful in 16s
Install-test / Install-test ${{ fromJSON(inputs.charts) }} (pull_request) Successful in 2m26s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 58s
Publish Rallly Helm Chart / Publish Rallly Helm Chart (push) Successful in 58s
renovate/stability-days Updates have met minimum release age requirement
Validate Helm Charts / Resource quantities (pull_request) Successful in 11s
Validate Helm Charts / Install-test (pull_request) Successful in 2m26s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Successful in 6s
SonarQube Analysis / SonarQube Analysis (push) Failing after 15s
|