{{- /*
NOTES.txt — printed after `helm install` / `helm upgrade`.
Use it to highlight post-install checklist items the operator MUST review.
*/ -}}
Thank you for installing {{ .Chart.Name }} {{ .Chart.AppVersion }}.

The ChatCLI AIOps operator is now reconciling Anomaly / Issue / RemediationPlan /
PostMortem CRs in your cluster.
{{- if .Values.crdUpgrade.enabled }}

A pre-install/pre-upgrade Helm hook already re-applied every CRD shipped with
this chart. You don't need to run any manual `kubectl apply -f`
to refresh the CRD schema — that is handled automatically on every
`helm upgrade`. Disable via `--set crdUpgrade.enabled=false` if you manage
CRDs out of band.
{{- else }}

⚠️  CRD UPGRADE HOOK DISABLED — you manage the CRDs yourself.
   Helm does not refresh CRDs on `helm upgrade` by design. If you bump
   the chart version, run this BEFORE upgrading the binary so the API
   server accepts the new fields and enum values:

       helm pull oci://ghcr.io/diillson/charts/chatcli-operator --version <new-version> --untar
       kubectl apply --server-side -f chatcli-operator/crds/

   Otherwise the new controller logic will be silently rejected at runtime
   (Issue.status.state=Contained, PostMortemSpec.RequiresHumanAction, etc.).
{{- end }}

==================================================================================
 Before the first Instance
==================================================================================

A. EVERY INSTANCE NEEDS A CREDENTIAL AND TLS
   - Credential: spec.server.token (Secret ref),
     spec.server.security.jwtSecretRef (or jwtPublicKeyRef plus
     operatorTokenRef / operatorClientCertSecretName), or mTLS with
     spec.server.tls.clientCASecretName. Without one the operator does not
     create the Deployment and sets AuthenticationConfigured=False.
   - TLS: the operator always dials the server over TLS 1.3. Set
     spec.server.tls.enabled: true and spec.server.tls.secretName to a Secret
     with tls.crt, tls.key and (for a private/self-signed CA) ca.crt, valid
     for <instance>.<namespace>.svc.cluster.local. Without TLS the server runs
     but ServerReachable=False and the AIOps pipeline cannot reach it.
   Encryption at rest is on by default: without features.encryptionKeyRef the
   operator creates Secret <instance>-atrest-key (never overwritten, kept when
   the Instance is deleted). Deleting it makes the encrypted data unreadable:
   back up the key with the PVC data, or opt out with
   features.encryptionAtRest: false. Back it up with:

       kubectl -n <namespace> get secret <instance>-atrest-key \
         -o jsonpath='{.data.encryption-key}' | base64 -d > <instance>-atrest-key.txt

   and restore it with:

       kubectl -n <namespace> create secret generic <instance>-atrest-key \
         --from-file=encryption-key=<instance>-atrest-key.txt
   ServerReachable is re-probed every 5 minutes. Rotating a referenced Secret
   or editing a mounted ConfigMap rolls the Instance pods.
   Check the conditions with:

       kubectl get instance <name> -n <namespace> -o jsonpath='{range .status.conditions[*]}{.type}={.status} {.reason}{"\n"}{end}'

{{- if not (and .Values.aiops.instance.namespace .Values.aiops.instance.name) }}

   AIOPS INSTANCE NOT PINNED: the AIOps bridge stays disconnected (no alerts,
   analyses or remediations) until you name the Instance it may trust:

       --set aiops.instance.namespace=<namespace> --set aiops.instance.name=<instance>
{{- end }}

   Remediation mode: {{ .Values.remediationMode | default "approve" }}.
{{- if eq (.Values.remediationMode | default "approve") "auto" }}
   Every plan no ApprovalPolicy, cluster tier or decision engine parks runs
   without a human, high-risk actions included.
{{- end }}

B. DASHBOARD / REST API KEYS
{{- if .Values.security.devMode }}
   security.devMode=true: the REST API accepts unauthenticated calls as admin
   when no keys are configured. Never use this in production.
{{- else if .Values.apiKeys.create }}
   The chart created Secret chatcli-operator-secrets with {{ len .Values.apiKeys.entries }} key(s).
{{- else }}
   The REST API and dashboard reject every /api/ call (401) until keys exist.
   The operator reads them from the Secret chatcli-operator-secrets (key
   api-keys) in {{ .Release.Namespace }}, re-read every 30s. Create it, e.g.:

       kubectl -n {{ .Release.Namespace }} create secret generic chatcli-operator-secrets \
         --from-literal=api-keys="$(printf -- '- key: "%s"\n  role: admin\n  name: ops\n' "$(openssl rand -hex 32)")"

   or install with --set apiKeys.create=true and apiKeys.entries (roles:
   viewer, operator, admin; give each approver a key and a name).
{{- end }}
   /api/ calls are rate limited: 30/min per client host without a valid key,
   600/min per key (HTTP 429 beyond that). Every replica serves the API.
   Open the dashboard with:

       kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "chatcli-operator.fullname" . }} {{ .Values.api.port }}:{{ .Values.api.port }}

==================================================================================
 Post-install checklist (GAP-05 fix, chaos test report 2026-05-23)
==================================================================================

1. REVIEW THE EXECDIAGNOSTIC ALLOWLIST
   The agentic remediation loop can only execute diagnostic commands that exactly
   match an entry in the ExecDiagnostic allowlist. The default list ships with
   100 read-only commands targeting health/metrics/pprof endpoints on the
   standard ports (8080, 9090, 9091, 15000, 15020, 15021, 6060); the startup
   log below prints the exact count of the running version.

   Workloads using non-standard ports (e.g. localhost:5678/health) will have
   their diagnostics REJECTED, which wastes remediation attempts and can leave
   real issues unremediated. See chaos test Cycle 1 evidence in the 2026-05-23
   report for the regression this guards against.

   The allowlist is read by the OPERATOR process (not by Instances). Extend it
   with this chart's security.allowedDiagnosticCommands value (comma-separated).
   Keep your overrides in a values file and pass it on every upgrade, or use
   --reset-then-reuse-values (Helm 3.14+). Plain --reuse-values skips the
   defaults of keys added by newer chart versions and can fail the render:

       helm upgrade {{ .Release.Name }} oci://ghcr.io/diillson/charts/chatcli-operator \
         --version {{ .Chart.Version }} -n {{ .Release.Namespace }} --reset-then-reuse-values \
         --set security.allowedDiagnosticCommands="curl -s localhost:5678/health\,curl -s localhost:7000/healthz"

   Inspect the EFFECTIVE allowlist after the operator starts:

       kubectl -n {{ .Release.Namespace }} logs deploy/{{ include "chatcli-operator.fullname" . }} \
         | grep "Effective ExecDiagnostic allowlist loaded"

   The startup log line includes total/default/custom counts and lists every
   custom entry — no need to dump every CR to confirm.

2. CONFIRM DEDUP TTL FOR YOUR DEPLOYMENT CADENCE
   The default Instance.spec.aiops.dedupTTLMinutes is 30. If you re-deploy the
   same resource (GitOps rollouts, rollbacks) more often than that and rely on
   the AIOps platform to re-detect issues, lower it further. UID-aware hashing
   (GAP-02 fix) handles the recreate-with-same-name case automatically; TTL is
   only the secondary safety net for genuinely persistent incidents.

3. CONFIGURE NotificationPolicy FOR THE Contained STATE
   A new IssueState `Contained` is emitted when a remediation silenced the
   workload (e.g., scaled it to 0) without truly fixing the root cause — a
   human must intervene. Add a NotificationRule that matches state `Contained`
   so on-call gets paged when the platform contains an incident but doesn't
   resolve it:

       rules:
         - name: contained-needs-human
           severities: [critical, high]
           states: [Contained]
           channels: [pagerduty-primary]

4. (Optional) PAUSE NOTIFICATIONS DURING CHAOS DRILLS
   Issues that correlate with an active ChaosExperiment are labeled
   `platform.chatcli.io/source=chaos-experiment`: they are not escalated and
   are left out of MTTD/MTTR, but remediation still runs and
   NotificationPolicy rules still match them (rules filter by severity,
   state, signal type, namespace and resource kind, not by label). To keep
   drills out of regular channels, run them in namespaces your rules do not
   list, or disable the policy for the duration of the drill. Only pod_kill,
   pod_failure, cpu_stress, memory_stress and disk_stress run; network_delay,
   network_loss and any spec.schedule fail at once without injecting.

Operator guide: https://chatcli.edilsonfreitas.com/kubernetes/k8s-operator
Production setup: https://chatcli.edilsonfreitas.com/cookbook/aiops-production-setup
