{{- $replicas := include "searxng.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.searxng.replicas) -}}
{{- $settings := fromYaml (include "searxng.effectiveSettings" .Values.settings) -}}
{{- $httpRoute := .Values.httpRoute | default dict -}}
{{- $hostnames := $httpRoute.hostnames | default list -}}
{{- if not $hostnames -}}
{{- $hostnames = list .Values.ingress.domain -}}
{{- end -}}
SearXNG {{ .Chart.AppVersion }} — release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.

{{- if .Values.ingress.enabled }}

ACCESS — INGRESS MODE (ingress.enabled: true, httpRoute.enabled: false)
  https://{{ .Values.ingress.domain }}

  The certificate is requested by cert-manager through the "{{ .Values.ingress.clusterIssuer }}"
  cluster issuer. Until it has been issued the browser shows a warning; follow it with

    kubectl -n {{ .Release.Namespace }} get certificate tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress
{{- else if $httpRoute.enabled }}

ACCESS — GATEWAY MODE (httpRoute.enabled: true, ingress.enabled: false)
{{- range $hostnames }}
  https://{{ . }}
{{- end }}

  This release renders an HTTPRoute ({{ .Release.Name }}-{{ .Chart.Name }}-httproute) and NOTHING else
  of the routing path — no Gateway, no class, no Certificate. Three things
  therefore belong to the cluster and not to this chart:

    1. The Gateway-API CRDs. Without them this install would have failed.
    2. The Gateway named in httpRoute.parentRefs
{{- range $httpRoute.parentRefs }}
         {{ .name }}{{ if .namespace }} in namespace {{ .namespace }}{{ end }}{{ if .sectionName }}, listener {{ .sectionName }}{{ end }}
{{- end }}
       must exist AND its listener must allow routes from this namespace
       ({{ .Release.Namespace }}) in allowedRoutes. A rejected attachment shows
       up only in the route's status:

         kubectl -n {{ .Release.Namespace }} get httproute {{ .Release.Name }}-{{ .Chart.Name }}-httproute -o yaml

       Read status.parents[].conditions — "Accepted: True" and
       "ResolvedRefs: True" is a working attachment. An empty status means no
       controller has looked at the route at all.
    3. The TLS certificate for the hostname, on the Gateway's listener.
       ingress.clusterIssuer is NOT used in this mode and is not required: the
       chart renders no Certificate, so there is no
       tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress secret and nothing for cert-manager to do.

  SEARXNG_BASE_URL still follows ingress.domain ("https://{{ .Values.ingress.domain }}/"), not
  httpRoute.hostnames: signed image-proxy URLs and the OpenSearch description
  keep naming that host whatever the route publishes.

  AND THE THING THIS MODE TAKES AWAY: the forward-auth escape hatch. The
  Gateway API has no annotation channel, so the Authentik-style auth
  annotations documented under ingress.annotations in values.yaml have NO
  counterpart here, and the chart renders no implementation-specific policy
  object on purpose. SearXNG has no login of its own, so "who may use this
  instance" is entirely the Gateway's question in this mode — a listener that
  is not publicly reachable, or that implementation's own auth filter,
  configured next to the Gateway and not here.
{{- else }}

ACCESS — NEITHER INGRESS NOR HTTPROUTE
  ingress.enabled and httpRoute.enabled are both false, so this release has no
  Ingress, no HTTPRoute and no certificate.
  ingress.domain is still required: it builds SEARXNG_BASE_URL, which SearXNG
  puts into its OpenSearch description, into the signed image-proxy URLs and
  into every absolute link. Reach the instance inside the cluster with

    kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-{{ .Chart.Name }}-service 8080:80

  and open http://localhost:8080. Note that links on the page will still point
  at https://{{ .Values.ingress.domain }}/.
{{- end }}
{{ if eq $replicas "0" }}
SCALED TO ZERO
  {{ if .Values.scaleDown }}scaleDown: true{{ else }}searxng.replicas: 0{{ end }} — the deployment runs with replicas: 0, so the endpoint
  above does not answer until you set it back and upgrade again. Service{{ if .Values.ingress.enabled }} and
  Ingress stay in place, so the certificate is not re-issued.{{ else if $httpRoute.enabled }} and
  HTTPRoute stay in place, so the route keeps pointing at a Service with no
  endpoints — the Gateway answers with an error of its own rather than with
  nothing.{{ else }} stays in
  place.{{ end }}
{{ end }}
CONFIGURATION
  Instance name: {{ $settings.instanceName }}
  settings.yml is rendered from the "settings" values into
  {{ .Release.Name }}-{{ .Chart.Name }}-configmap and mounted at /etc/searxng/settings.yml.
  It starts with use_default_settings: true and names only the deviations, so
  every engine the image ships stays available and upstream engine changes
  arrive with the app version instead of with a chart release.

  Anything the chart does not model first-class goes into settings.extra in
  SearXNG's own key names; it is merged over the generated block. Changing any
  of it rolls the pods — the deployment hashes the ConfigMap, because a subPath
  mount would otherwise never reach the running container.

WHAT THE GREEN CHECKMARK DOES AND DOES NOT MEAN
  All three probes hit /healthz, the only path SearXNG's bot detection lets
  through unconditionally. Its handler returns a fixed "OK". So a Ready pod
  means the worker answers — not that a search works, not that any engine is
  reachable, and not that a login placed in front of it is up (probes go
  straight to the pod and never pass the {{ if $httpRoute.enabled }}Gateway{{ else }}Ingress{{ end }}).
{{- if not $settings.limiter }}

BOT DETECTION IS OFF
{{- if $httpRoute.enabled }}
  settings.limiter is false and no Valkey is configured. The instance relies on
  whatever sits in front of it — and in GATEWAY MODE that cannot be configured
  from this chart: ingress.annotations has no counterpart, so a login has to be
  put on the Gateway itself (a listener that is not publicly reachable, or that
  implementation's own auth filter). The limiter itself is unaffected by the
  mode; it runs inside SearXNG, not at the proxy.
{{- else }}
  settings.limiter is false and no Valkey is configured. The instance relies on
  whatever sits in front of it — put authentication there via
  ingress.annotations if it must not be open to everyone. Mind that this
  escape hatch exists in Ingress mode only: httpRoute.enabled has no
  annotation channel at all.
{{- end }}
{{- else if not .Values.secrets.valkeySecretRef }}

BOT DETECTION IS ON WITHOUT A VALKEY
  settings.limiter is true but secrets.valkeySecretRef is unset. The limiter
  needs a Valkey to keep its counters, so it does nothing at all: SearXNG logs

    ERROR:searx.limiter: The limiter requires Valkey, please consult the
    documentation: https://docs.searxng.org/admin/searx.limiter.html

  once at worker start, the pod becomes Ready, and every request is served
  unlimited. This release is therefore NOT rate limited, and the only trace of
  that is one line at the top of the log. Set secrets.valkeySecretRef, or set
  settings.limiter back to false.
{{- end }}

ROTATING THE SIGNING KEY
  secrets.searxngSecretRef carries the key that signs the settings cookie and
  the image-proxy URLs. Pointing it at a DIFFERENT 1Password item rolls the
  pods; changing the value INSIDE the same item does not — Helm never sees that
  content. After such a rotation:

    kubectl -n {{ .Release.Namespace }} rollout restart deployment/{{ .Release.Name }}-{{ .Chart.Name }}-deployment

  A new key invalidates every user's saved preferences cookie, which is a
  logout-shaped event for the people using it.

NOTHING TO BACK UP
  The pod holds no state worth keeping: settings.yml comes from the ConfigMap,
  the signing key from the Secret, and the SQLite caches under /tmp plus
  /var/cache/searxng are emptyDirs that refill on their own. App-version
  upgrades and rollbacks therefore carry no data risk.
{{- if .Values.networkPolicy.enabled }}

NETWORKPOLICY IS ACTIVE — WHAT IT CAPS, AND WHAT IT DOES NOT
  {{ .Release.Name }}-{{ .Chart.Name }}-networkpolicy selects this workload's pods.
  Inbound: TCP 8080 only. Outbound: DNS, TCP 443 (the search engines) and 6379
  (external Valkey){{ with .Values.networkPolicy.egress }}{{ with .extraPorts }}, plus TCP {{ join ", " . }}{{ end }}{{ end }}.

  443 GOES TO ANY DESTINATION, because querying search engines is the
  application and this object cannot name a host. 6379 is the STANDARD Valkey
  port: the URL sits in a 1Password item the chart never parses, so a Valkey on
  another port is blocked — put it in networkPolicy.egress.extraPorts.

  Three limits belong in the same breath, because none of them is visible in
  the object itself:

  - EGRESS IS BY PORT, NEVER BY DESTINATION. A NetworkPolicy matches CIDRs and
    selectors and cannot express a hostname, so every egress rule permits its
    port to ANY address. That is a limit of the object, not a shortcut — this
    cluster's CNI (Canal: Flannel plus Calico) has no FQDN rule either. An
    outbound port the chart does not know about is BLOCKED: add it to
    networkPolicy.egress.extraPorts, and expect to find out through an
    application-level connection error rather than through this object.
{{- if .Values.networkPolicy.probeSources }}
  - THE PROBE PATH IS RESTRICTED in this release, because
    networkPolicy.probeSources is set to
      {{ join ", " .Values.networkPolicy.probeSources }}
    Probes come from the IP of the WORKER NODE running the pod. If none of those
    CIDRs covers the node this pod lands on, the kubelet's probes are dropped
    and THE POD NEVER BECOMES READY — the rollout then times out and the symptom
    looks like a broken application. Compare

      kubectl -n {{ .Release.Namespace }} describe pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment

    against `kubectl get node -o wide`.
{{- else }}
  - THE PROBED PORT IS OPEN INSIDE THE CLUSTER. networkPolicy.probeSources is
    empty, so that rule carries no source selector at all and any pod the CNI
    routes to can reach it. Not from the internet — but this is NOT pod-level
    isolation. Narrowing it means listing the worker node IPs in
    networkPolicy.probeSources, because the kubelet probes from the node's own
    address and the chart cannot know it; a wrong guess there leaves the pod
    permanently un-Ready.
{{- end }}
  - IT IS ONLY A BOUNDARY WHERE THE CNI ENFORCES POLICIES. Where it does not,
    this object is accepted and filters nothing, so everything reads as
    configured while nothing is filtered. `kubectl -n {{ .Release.Namespace }} get networkpolicy`
    shows the object exists; only a connection attempt from an unrelated
    namespace shows that it bites.

  Switch it off with networkPolicy.enabled: false.
{{- end }}
