Charts
Justin Klein edited this page 2026-10-14 23:21:15 +00:00
Clone

Wiki Page Revisions

15 Commits

Author SHA1 Message Date
jklein f700f74a4c Charts: rss-bridge is not the Gateway sweep's exception any more
The page still named 21 charts with one exception, rss-bridge, "frozen for
changes". Both halves are stale: the freeze from Gitea #51 was lifted
deliberately, the chart was converted in Gitea #124, and #114 closed with
it. Measured on main instead of counted from memory: 22 charts carry an
Ingress template and 22 carry an HTTPRoute template, so no exception is
left.

Two agents reported this while working on other issues and correctly left
it alone rather than editing another issue's documentation inside theirs.
2026-10-14 23:21:15 +00:00
jklein 81980279c4 NetworkPolicies in every chart (Gitea #5): the convention, the gate, and what it does not do
29 chart pages plus Chart-Style-Guide, CI-CD, Charts and Lessons-Learned.

Chart-Style-Guide gains section 15, and its core is the FOUR PORT CLASSES -
the part that is actually hard to get right. A port a network probe addresses
stays open until probeSources is set, because the kubelet probes from the
worker node's IP and no chart can know it. A NodePort-published port stays open
because the source is already masqueraded. Everything else can be and is
restricted by source, which is why the charts with a real inbound restriction
are exactly those whose probes use a different port. A replica-to-replica bus
gets a bare podSelector - and, since policyTypes lists Egress, its outbound
side needs a rule of its own or the cluster never forms while every node looks
healthy.

The exec-probe distinction is written down as load-bearing rather than
pedantic: it is the whole reason valkey and minecraft-server are the tightest
policies in the repository and every HTTP-probed web app is looser.

CI-CD gains the networkpolicy job as section 8 (the later two renumbered), and
it ends with the measurement rather than a claim. Two experiments ran on the PR
and were reverted: cyberchef's probe port restricted to an address no probe can
come from stayed GREEN at 1m9s, and outline with its DNS egress rule removed
went RED at 5m59s. Together those say k3s exempts the kubelet's probe traffic
while enforcing everything else - so the install-test's net is an EGRESS net,
and a wrong ingress port is caught by the render assertion or not at all.

Every chart page gets a networkPolicy values table and an operating note naming
that chart's own trap, not a boilerplate one:

- keycloak carries the blast radius: it is the cluster's identity provider, its
  http rule is the only source-restricted inbound rule in the repo, and nothing
  in CI can catch a wrong controller - so the page asks for a real login and a
  look at consumers' issuer URLs after the first upgrade.
- garage asks the reader to check their S3 consumers, because anything in
  another namespace is blocked until it is in allowedNamespaces.
- bulwark names the quietest failure in the sweep: its probes test its own port,
  so a jmapServerUrl on a blocked port yields a Ready pod serving an empty
  webmail.
- gotenberg explains what its DNS-only cap COSTS - HTML conversion silently
  loses remote assets - rather than only what it buys.
- satisfactory and homeassistant each argue their own Ingress-only policy;
  satisfactory's deciding half is that it is the one chart with no install-test
  at all, so an egress cap there would be the only one with no empirical check
  behind it.
- paperless-ngx names the two pods of its release that the object does NOT
  cover, including the consume CronJob whose pod template carries no labels.
- freesailarr explains why ten containers behind a VPN need only three egress
  rules, and that extraPorts cannot help with an alternative WireGuard port
  because it is TCP-only.

valkey and stalwart are corrected rather than extended. valkey's page now says
why having no probeSources is what makes it the tightest policy here and not an
incomplete one. stalwart's carries a dated correction: this page and the chart
both advised putting the ingress controller's namespace into allowedNamespaces
with ["ingress-nginx"] as the example, and both halves are wrong - ingress-nginx
is gone from the cluster entirely, and allowedNamespaces would have opened http
to every pod in that namespace anyway.

Charts drops the claim that valkey is "the first chart with a NetworkPolicy on
by default" and says instead that every chart has one, with the two shapes a
reader needs before opening any chart page.

Lessons-Learned gains four, all transferable:

- the net that existed for a different failure than the one it was built for,
  and why both experiments were needed before either could be read;
- a comment that could not describe its own syntax (a template comment must not
  spell out its own closing delimiter; the render then fails pointing at the
  template, not at the sentence);
- three values that reached no rule and would have read as controls - a
  convention about value NAMES is not a convention about which values exist;
- the subchart whose comments the parent could not fix, and the rule it
  produced: before making a check a gate, ask which failures the author of this
  diff can actually fix.
2026-10-14 14:57:00 +00:00
jklein 20d504722e keycloak: one hostname per realm, in both routing modes (#122)
The chart gains keycloak.realmHosts: a list rendering one routing object
per entry - an Ingress with app-root, or an HTTPRoute whose Exact match on
/ carries a RequestRedirect filter to /realms/<realm>/account/. Empty by
default, so nothing renders and no installation changes.

keycloak: the new Configurable-values row, and the section 'Four more
Ingress objects in this namespace, and they are not this chart's' is
replaced by 'One hostname per realm' - the four objects are still named
there, but now as the thing a migration would adopt rather than as a
permanent neighbour. With the per-mode object table, the two required
deviations (per-entry tlsSecretName, optional per-entry issuer), the six
render aborts and what each prevents, the proxy-buffer loss in Gateway
mode phrased for account-console traffic, and the migration order with
its one unchecked question.

Chart-Style-Guide: the third admissible Ingress shape, with both
deviations stated as required rather than tolerated, and keycloak added to
the index-naming rule beside homeassistant.

CI-CD: 56 assertions over 21 charts becomes 60, keycloak 4 -> 8 plus two
'not checked' lines; and why the overlay does not repeat the entries that
the install-test fixture already carries.

Charts: the Ingress cell for keycloak.

Lessons-Learned: 'The precise message that hid the structural one' - a
per-entry check that fired before the mode-conflict abort and answered the
wrong diagnosis, plus the note that a strict schema can make a template
guard unreachable.
2026-10-13 10:13:44 +00:00
jklein 6421d29824 Charts: the Ingress column describes the Ingress mode only
With the Gateway-API sweep finished (#114), 21 charts offer httpRoute.enabled
as the mutually exclusive alternative and only rss-bridge does not - so a
column reading 'yes' no longer means the Ingress is the only mode. One note
above the table rather than 21 rewritten rows, because the per-chart detail
belongs on the chart pages: the Gateway shapes differ more than the column
could carry (two objects for keycloak and matrix-synapse, one per host entry
for homeassistant, one per app or one shared for freesailarr).
2026-10-12 21:43:30 +00:00
jklein 63e18d4527 Gateway API as the second routing mode (pilot on cyberchef)
Documents Gitea #114's pilot, which is in helm-charts PR #115.

Chart-Style-Guide §4 becomes "Ingress and Gateway API" and carries the
binding form: HTTPRoute with parentRefs only - no Gateway, no
gatewayClassName, no certificate, no implementation-specific policy object,
because that subset is identical on nginx-gateway-fabric, Cilium, Envoy
Gateway and Istio. Plus the four guards (a value instead of auto-detection,
mutual exclusion with ingress.enabled, mandatory parentRefs, one source of
truth for the hostname) and what has no counterpart at all: proxy-body-size
and auth annotations. The §4 anchor changed, so the reference on the
homeassistant page moved with it.

CI-CD gains the httproute-match job as #5 (install-test renumbered to #7)
and a section on the Gateway-API CRD fixture - why it is real rather than a
stub, why --server-side (the CRD is 244792 bytes as JSON against a 262144
byte annotation limit), and why it is not in ci/common/.

cyberchef gains the httpRoute values, a "Routing modes" section, the
attachment check, and the honest version of its CI section: its install-test
now runs in Gateway mode, which gives up installing the Ingress and buys a
route validated against a real schema. What stays unprovable is named -
nothing routes in k3d.

template-chart carries the reference implementation; New-Chart gains the
overlay obligation; Renovate replaces the blanket "ci/** is not managed"
with the distinction that was always meant - a fixture image stays
unmanaged, a pinned tool is tracked wherever it lives.

Lessons-Learned: the stub CRD that would have proved nothing.
2026-10-12 15:23:46 +00:00
jklein 996f8c0dde bulwark: new chart page, index, sidebar, stalwart cross-reference, and four rules it bought
The chart page leads with the two things that are not visible from outside and
that govern everything else: Bulwark is a pure CLIENT, so with the mail server
gone the release is Ready and green and nothing in it can tell; and the VOLUME
BEATS THE CHART for every value, silently, because the application resolves
"admin override > env var > default" and its admin dashboard may write any key.
Upstream documents the second only for the server URL, which is what makes it a
trap rather than a footnote.

Also on the page, each measured against the source at tag 1.12.0 rather than
taken from the documentation: the real way out of an OIDC lockout (remove the
override in the dashboard - a helm upgrade with OAUTH_ONLY=false goes through the
same precedence chain and can do nothing), the three-part price of
adminConfigReadOnly, the redirect URI that carries the UI language across 27
locales, the two-part backup statement (the volume alone is not a backup, and it
may hold the OAuth secret in cleartext), why rotation needs no restart and which
mount detail that depends on, and what the probes do and do not carry.

Charts: new row, and the counter is now measured against */Chart.yaml instead of
carried forward - it had drifted. The table also still lists convertx, for which
there is no chart directory in the repository; flagged in a note rather than
deleted, because removing it is a decision about that chart and not a
documentation fix.

Lessons learned, four entries, all of them transferable beyond this chart:
a recovery instruction that ran through the fault it was meant to repair; a
read-only switch whose write barrier also blocked the one-time bootstrap write
that creates the admin password; a probe method the platform does not offer at
all (an HTTP probe is always GET); and a 0400 secret file that only works
because the kubelet silently widens it.
2026-10-05 21:27:25 +00:00
jklein 0852b72ee2 keycloak: new chart page, index, sidebar, and three rules it bought
The chart page carries the values table, the vault fields, the first-install
checklist with the permanent administrator as a mandatory step, the
minor-upgrade procedure, and the traps - of which two are the point of the
page: hostname-admin is not an access control, and a shared database merges two
clusters.

Chart index counter: 30 directories / 29 installable. That follows the index
TABLE, which already lists convertx - its page landed before its PR, and
convertx/ does not exist on origin/main yet. So the number is one ahead of the
repository on purpose, as it was before this change.

Renovate: the counter-case to garage, measured rather than assumed - four tag
shapes on quay.io/keycloak/keycloak and plain docker versioning already excludes
the three unwanted ones, so that block carries no versioningTemplate. Written
down because an unnecessary constraint is not free: it reads as load-bearing to
the next person and has to stay correct as upstream's tagging changes. Plus a
section on the automerge decision: switching it off for minor and major was
proposed and declined, so the upgrade procedure is documented instead of
prevented, with the obligation that puts on whoever reviews an app-version PR.

Chart style guide section 11: a chart defaulting to more than one replica ships
a PodDisruptionBudget, and it is rendered only while the computed replica count
is >= 2 - minAvailable 1 with a single replica blocks every drain forever with a
message that names the PDB rather than the replica count.

CI/CD: the trusted-registry list for AVD-KSV-0125 is a separate file from the
ignore file on purpose, so a new registry is an entry there rather than a
suppression. quay.io joined it with this chart.

Lessons learned, two entries and three lines in the recurring shape:
- "The application that rewrites its own installation at start" - three correct
  requirements that cannot start a pod together, and why the obvious repair
  (an empty volume over the directory) breaks the command meant to fill it.
- "The upgrade procedure the automerge did not know about" - the general form,
  which applies to any clustered application whose versions must not be mixed:
  a constraint living in documentation is invisible to the machinery that
  performs upgrades, and the better the pipeline, the less likely anybody is
  standing in the path to notice.
2026-10-04 17:09:56 +00:00
jklein c9c99757c1 garage: new chart page, index row, sidebar entry, two lessons
The page carries the values table, the first-install checklist for both paths
(self-configuring with --single-node, and the manual layout sequence) and a
traps section: the metadata gap in a volume backup, metadata_fsync being off
upstream, the two-snapshot restore window, the non-idempotency of
layout apply and why single-node mode does not have it, replication factor 1
applying to the metadata, the scratch image that rules out exec probes for
good, and why the RPC secret has to be an environment variable.

Two Lessons-Learned entries, both in their general form rather than as garage
notes: "the snapshot that was not a restore point" repeats at every embedded
database on a volume with snapshots, and "a permission check that assumes it
owns the filesystem" at every application offering a *_secret_file with a
0o077 mode test.

Chart index count 28 -> 29 directories.
2026-10-03 20:36:12 +00:00
jklein 43aef80b2e paperless-ngx: the optional valkey subchart, and two findings it produced
paperless-ngx (Gitea #78) takes our own valkey chart as an optional subchart,
off by default. Its page gets the two new value blocks, the five Redis
consumers that argue the queue-vs-cache choice, the three operator traps and
the CI section, which now describes the first real installation of the
valkey-subchart path anywhere here.

Two findings are general enough to leave the chart page:

- A rule about the relationship between a parent's and a subchart's values
  cannot live in a template. Helm renders a subchart's templates first, so the
  subchart's own required() always wins and reports the path as the subchart
  sees it. Schema validation runs before any rendering, so that is where such a
  rule goes. The first draft was a fail() in the parent that could never fire.
- Erasing a dependency switch turns the subchart ON: a null override deletes
  the key, an unresolvable condition counts as enabled. The one block in a
  chart that must not be null-safe, and the opposite of the rule for every
  other block.

Both in Chart-Style-Guide section 14 and Lessons-Learned; valkey's page names
its second consumer and the queue/cache pair of worked examples; Charts gets
the row.
2026-10-02 21:29:18 +00:00
jklein 079639225c stalwart: new chart page, index, sidebar and four lessons
The chart page carries the full values reference, the eight-step first
install (eight, not seven: the bootstrap wizard appears only when
config.json is ABSENT, and the chart always renders one, so a first
install goes through recovery mode) and a Traps section.

Four entries on Lessons-Learned, because none of them is specific to mail:

- The source address every NodePort quietly replaces. externalTrafficPolicy
  Cluster masquerades the client address onto the node IP, and every
  application that evaluates the client address meets this - bans, rate
  limits, geo rules, audit logs. Includes the three traps of the PROXY
  header remedy and the reason the trusted set and the probe set have to be
  disjoint in Kubernetes.
- The rate limit that counted a node instead of a client. A per-IP limit in
  an application is a per-node limit for probes; the arithmetic that picks
  the probe intervals, and why a tcpSocket probe is the wrong escape.
- The hardening that stopped the container from starting. setcap +ep makes
  the binary capability-dumb, so drop: [ALL] fails the execve itself. The
  lesson is that a deviation has to be documented with its mechanism, or the
  next hardening pass removes it as unused.
- The missing file that opened a database nobody meant to use. An absent
  config.json opens an ephemeral in-memory store with green probes; an
  unparsable one aborts. The lenient path is the one a broken mount takes.

Also on the fail-open: it covers the TASKS as well as the listeners, with
all ten enabled by default, and the error is logged after the listeners are
already bound.
2026-10-02 19:24:15 +00:00
jklein cb89a6476c convertx: new chart page, index row, sidebar entry, two lessons
The chart page follows outline.md: prerequisites, installation walkthrough,
the required 1Password field, the complete values reference and the operating
notes. No version rows, as on every chart page.

The notes it carries are the ones that cannot be read off the values:

- the chart bypasses the image entrypoint, which is what keeps the hardening
  baseline intact on an image that carries no USER - together with the
  obligation that creates, namely re-reading the upstream entrypoint at every
  app-version bump
- uid 1000 rather than the repo's 20000, because the image owns /home/convertx
  with it and the uid owns data on a PersistentVolume
- what has to be writable and why: /home/convertx is HOME, and LibreOffice is
  invoked without -env:UserInstallation, so its profile can only go there
- the probes use /healthcheck; "/" is never a 200
- whoever opens the page first becomes the administrator, and
  accountRegistration does not gate that
- the ingress annotation is the only upload ceiling, because the application
  enforces none
- WEBROOT, PUID/PGID/UMASK and PORT do not belong in the env block

Two entries in Lessons learned, both preventive rather than post-mortem, in
the shape the valkey entries already use:

- the probe that would have passed on a redirect and meant something else a
  week later - a 3xx makes an httpGet pass, so a redirecting path hides the
  question instead of answering it, and the answer has to hold for every
  state the application can be in
- the entrypoint we skipped, and the obligation that created - an override
  forks the entrypoint silently, and a fork with no diff is a fork nobody
  reviews, so the warning belongs at the line the next change will touch

Also corrected in CI-CD: the per-chart publish workflow count, which read 24
and was already one behind before this chart made it 26.
2026-09-30 15:31:06 +00:00
jklein d91927d2d0 valkey: chart page, index row, sidebar entry, two lessons
The new valkey chart (helm-charts #53, issue #46): one instance per
application instead of the shared cluster-redis, whose separation rested
on a single ACL file in a single 1Password field.

The page carries what an operator cannot read off the templates: that the
1Password field auth-conf holds a finished "requirepass <password>" line
rather than a password, that uid 999 / gid 1000 are the image's own user
and why fsGroup has to match them, that maxmemory is derived from the
computed memory limit and a value above it aborts the render, and that
persistence and the eviction policy are a pair rather than two switches.
Also what the install-test does not cover: one variant per chart, and
fsGroup itself is unproven on k3d's node-directory volumes.

Two Lessons-Learned entries, both from this chart's review rather than
from an outage, because both generalize: a CLI that prints an error and
exits 0 (valkey-cli without -e, so the natural ping probe cannot fail),
and a secret whose FORMAT is load-bearing - a present-but-malformed field
yields a running server with its protection off.
2026-09-27 11:08:43 +00:00
jklein 311293e164 chart pages carry no version numbers any more
Both version rows leave every chart page's at-a-glance table; the image
repository stays, because it does not rot. A plain app-version bump obliges
no wiki edit — that rule is deliberate and stays — so any number written
here is stale within a Renovate round, as urlaubsverwaltung's already was.
Chart.yaml is where the pair is read, exactly as the chart index has said
from the start.

Home and Workflow no longer promise version snapshots. Forge and tooling
described remotes named github and JLab-Git; the checkout has exactly one,
origin, pointing at Gitea. Workflow gains the rule the wiki never had:
fetch and rebase before every push, never force-push.
2026-09-26 21:42:15 +00:00
jklein e434810e33 sure: chart page for the new we-promise Sure chart
Adds the chart page following the outline reference (same sections, same
table shapes, the complete values reference), a row in the chart index and
the sidebar entry, and pulls the two publish-workflow counts up by one
where the new chart changes them.
2026-09-22 19:53:48 +00:00
jklein f62f2e5871 Fill the wiki as the repository's knowledge base
One page per chart - prerequisites, installation walkthrough, the required
1Password fields, the complete values reference and the chart's operating
traps - plus the long form of the workflow, the forge and its measured
differences, the pipelines, versioning, Renovate, the chart style guide, the
procedure for a new chart, local development, and the incidents each rule was
bought with.

Chart pages quote versions as snapshots; Chart.yaml stays authoritative,
because Renovate moves app versions continuously and never edits the wiki.
2026-09-18 08:48:01 +00:00