Skip to content

Changelog

All notable changes to this project are documented here.

Release sections are generated by git-cliff from Conventional Commits. Breaking changes and migration notes are written by hand above them, because a commit subject cannot tell a consumer what to change.

[Unreleased]

[0.1.2] - 2026-09-06

Features

  • (helm) Add a values schema and finish the Artifact Hub listing (#40)

Bug Fixes

  • (release) Check the chart changelog before writing anything (#41)

[0.1.1] - 2026-09-05

Changed

Release archives are verified with a cosign bundle

v0.1.0 published an image and a chart but no archives and no signatures: the signing step failed on the tag. cosign v3 deprecated the flags that produced a detached .sig and certificate, and now requires a bundle, so the release errored before it could upload anything.

Verification is one file and one flag instead of two of each:

cosign verify-blob \
  --bundle checksums.txt.bundle \
  --certificate-identity-regexp '^https://github\.com/ethan-kane-ops/k8s-pod-oom-oracle/\.github/workflows/release\.yml@refs/tags/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt

The bundle carries the signature, the signing certificate and the transparency log entry together. Anything scripted against the old two-file form has to be updated, though nothing was ever published in that form.

The image and chart signatures are unchanged, and the v0.1.0 image and chart remain signed and verifiable.

Bug Fixes

  • (release) Sign with a cosign bundle and pin the cosign version (#39)

[0.1.0] - 2026-09-05

Nothing has been tagged yet, so everything below landed before any release. It is recorded anyway: the "pre-1.0 and pre-public, so no shim" reasoning that allowed these to ship stops applying the moment someone can depend on the JSON.

Breaking

hogs is now processes on GET /v1/events

The report's process listing was renamed, and its meaning changed with it.

Before After
hogs (JSON) processes (JSON)
HogProcess (Go) ProcessSnapshot (Go)

This is silent at the HTTP layer. A consumer reading hogs gets undefined rather than an error, so a dashboard built on it goes blank without failing. Anything parsing /v1/events must be updated by hand.

hogs was documented as the processes still alive after the kill. That was wrong. containerd sets memory.oom.group=1 on the container scope, so the kernel kills every process in the cgroup and there are no survivors to list; reading /proc mid-teardown returned a partial set with resident sizes already collapsing towards zero. processes is what it always actually was: a snapshot taken just after the kill, heaviest first, with the victim removed.

groupKill added to the report, and it is nullable

groupKill records memory.oom.group on the cgroup the report is attributed to, so a reader can tell which of the two things the listing is.

It has three states. true means the kernel killed every process in the cgroup, false means it killed only the process it selected, and null means the setting could not be read, which is what group kill usually leaves behind: the cgroup holding the answer is destroyed along with the container.

Both true and false now come from a real read, taken at detection time rather than when the report is assembled. Under the eBPF detector that read happens before SIGKILL is delivered, while the cgroup still exists.

A consumer must not treat null as false. Doing so reports that a container survived a kill that nothing observed. Anything typed against this field needs a nullable boolean, not a boolean.

Changed

  • The DaemonSet no longer requests privileged: true. It asks for CAP_BPF and CAP_PERFMON with drop: [ALL], plus allowPrivilegeEscalation: false, a read-only root filesystem and the RuntimeDefault seccomp profile. It still runs as UID 0: a non-root UID cannot raise a capability into its effective set without ambient capabilities, which a pod spec cannot request.

The namespace still needs the privileged Pod Security level. hostPID, hostPath volumes and non-default capabilities are each outside baseline.

  • Cgroup membership is read from the kernel's cgroup.procs rather than by matching /proc/<pid>/cgroup. The latter is written relative to the reading process's cgroup namespace, and containerd puts an unprivileged pod in a private one, so the daemon matched nothing and produced reports with an empty process list. No error was logged, because none occurred.

Added

  • oom-oracle watch, a live terminal dashboard. The left pane lists kills newest first, the right renders the selected one through the same function inspect prints, and the header states which detector is active because an inferred victim and a traced one differ in one boolean and nothing else. It reads the HTTP API and needs no privileges of its own.
  • examples/workloads/gradual-leak.yaml, a slow leak. Every other sample allocates faster than the sampler runs, so their trajectories are flat and peakBytes is the only honest number. Nothing demonstrated the case the trajectory and trend exist for until this.
  • A Helm chart, published to oci://ghcr.io/ethan-kane-ops/charts/oom-oracle on each tag, signed and attested like the image. What the agent cannot work without is deliberately not configurable: a chart that let you turn off hostPID would produce a daemon that installs, runs, reports nothing and explains nothing.
  • A release pipeline. A v* tag builds CLI archives for linux, darwin and windows on amd64 and arm64, plus a multi-arch container image. Everything is signed with cosign in keyless mode and carries an SBOM, and the image is attested through GitHub. Verification commands are in SECURITY.md.

Release notes are extracted from this file rather than generated from commit subjects, and the release fails if the section for the tag is empty. - victimMatch on the report: hostPid, nsPid or none, naming which identifier removed the victim from processes. A none alongside a known victim means the listing may still name the process the kernel killed. It also says whether the daemon and the kernel share a PID namespace, which is what decides whether victim.pid is comparable to the PIDs in processes. - unattributed on GET /v1/status, beside the existing skipped. Additive, but it is the counter to alert on: skipped climbs on any busy node because the probes see every kill on the machine, while unattributed counts kills that came from inside the kubepods tree and still could not be placed. See the API reference.

Features

  • (helm) Add a chart, and record the README demo (#34)
  • (tui) Add a live terminal dashboard (#32)
  • (daemon) Expose the unattributed counter and cover pod-level kills (#19)
  • (k8s) Resolve reports to pod names via a node-scoped informer (#10)
  • (detector) EBPF kernel tracer for oom_kill_process (#6)
  • (daemon) Assemble the pipeline into a runnable daemon and CLI (#5)
  • (detector) Add Detector interface with polling and fake implementations (#4)
  • (correlate) Process reader and cgroup-path to pod mapper (#3)
  • (cgroup) Cgroup v1/v2 memory reader and pressure trend sampler (#2)
  • (cmd) Add internal/cmd tree and version command
  • Initial commit

Bug Fixes

  • (release) Merge the changelog instead of prepending it (#37)
  • (deploy) Drop privileged and read cgroup membership from the kernel (#31)
  • (oom) Read groupKill in the kill window and make it a tri-state (#29)
  • (ci) Unbreak just check on darwin and pin one linter version (#27)
  • (oom) Filter the victim across PID namespaces (#20)
  • (correlate) Report pod-level OOM kills (#18)
  • (deps) Bump Go to 1.26.6 for five stdlib advisories (#13)
  • (ci) Lint both target platforms and bump Go to 1.26.5 (#9)

Documentation

  • Add a docs badge, and let CodeQL run on demand (#38)
  • Correct stale launch claims, and pin the timezone in TUI goldens (#35)
  • Record the breaking /v1/events rename and stop the changelog eating it (#28)
  • Add an mkdocs-material site on GitHub Pages (#21)
  • Add runnable examples and correct the OOM survival claim (#17)
  • Add community health files and issue templates (#16)
  • Correct README and CONTRIBUTING against shipped code (#11)

CI

  • Publish signed multi-arch releases from a tag (#33)
  • Split workflows, pin actions by SHA, add fuzzing (#14)
  • (build) Fix Docker toolchain mismatch and harden the pipeline

Dependencies

  • (deps) Bump the gomod-minor-patch group across 1 directory with 2 updates (#25)
  • (deps) Bump the actions-minor-patch group with 5 updates (#24)