Repository navigation
Releases
v0.2.1
Shoalcheck 0.2.1: small fixes in the app.
A copy of 0.2.0 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the release key first.
- Settings now has its own icon, a gear. It used to show the same sun as the theme switch.
- Every place the app names in bold now opens when clicked: Readings, Handoff, Clusters, Team, and the licence a feature needs.
getandlistare shown as verbs (monospace), not as bold text that looks clickable. - The GitHub Action is out:
Shoalcheck/shoalcheck-action@v2runs the gate as one step. On a public repository it
publishes nothing of the reading (no log, summary, annotations or comment) unless the workflow sets
public-details: 'true', because a reading names namespaces, workloads and nodes. v1 is unchanged.
The engine is unchanged: 0.24.0, with 89 detection rules and 15 stack analyzers.
v0.2.0
Shoalcheck 0.2.0: a read-only Kubernetes audit you can check, a team that stays in sync by itself, and a CI gate a team can live with.
A copy of 0.1.x offers this one in Settings → Updates (or shoalcheck upgrade), checked against the release key first.
Trust you can check
- Every reading states the never-read guard's count. The guard refuses Secrets, ConfigMaps, logs, exec and
ServiceAccount tokens inside the binary, and the count is in the report, the CLI, the app and the CI summary. - Operator objects (Argo CD, Flux, …) are kept only to the fields a rule reads. Helm values and annotations are
dropped on arrival and never reach a capture. shoalcheck audit --show-permissionsends with five commands to run with the published read-only role and
nothing else: a ServiceAccount bound to it, and a kubeconfig holding only its one-hour token. Your API server
then enforces read-only, not our code.- Every action we run or hand you in a CI template is pinned to a commit.
- The open scanner, github.com/Shoalcheck/shoalcheck-scan, now ships signed releases that rebuild byte for byte.
A team space that keeps itself in sync (Team)
- Keep the space in one private GitHub repository. Shoalcheck keeps every member's copy in step by itself, over
GitHub's API and without running git. A file changed on both sides is shown and left alone, and a repository
that lost what it held stops the sync rather than removing anything. - In the app, Team → Set up takes five steps, about five minutes:
- the repository and a token, with GitHub's pages linked and filled in;
- create the space, or join it with an invite code;
- the Team licence;
- an invite message to copy;
- keep in sync — on open, every minute, after every change, and with the app closed too.
- From a terminal:
shoalcheck team init --github org/repo,team join --github … --invite …,team autosync on. - The token lives in the macOS keychain, the Linux secret service or Windows data protection. It never goes
on a command line.
The CI gate
- Acceptances: a YAML file of findings the team has decided to carry, each with an owner and an end date (a
UTC date) (gate --accept). - The verdict as a pull-request comment (
-o md), and on the lines of the pull request (--annotations,
--source-root). --namespacefor manifests that name none, and GitLab Code Quality output (findings -o codequality).- In the app, a CI gate screen shows what the pipelines decided over time, and a seven-step wizard connects a
pipeline.
Rule versions are unchanged. The engine is still 0.24.0 with 89 detection rules and 15 stack analyzers, so a reading
from 0.1.x compares with one from 0.2.0 without any rule held back.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck and kubectl krew upgrade pick this release up.
Shoalcheck is a read-only Kubernetes audit; preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.6
Shoalcheck 0.1.6: the same audit, built with a patched Go.
A copy of 0.1.0 to 0.1.5 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the
release key first.
What changed
- Built with Go 1.26.11, which fixes GO-2026-6859: a crash in Go's HTTP/1 server caused by a header race in
MaxBytesReader. The desktop app serves its window from a local HTTP server, which is where this applied. The
audit itself runs no server. - govulncheck finds no reachable vulnerability in this build.
Rule versions are unchanged. The engine is still 0.24.0 with 89 detection rules and 15 stack analyzers, so a
reading from 0.1.5 compares with one from 0.1.6 without any rule held back.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Shoalcheck is a read-only Kubernetes audit; preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.5
Shoalcheck 0.1.5: the binary refuses Secrets before it can ask for them.
A copy of 0.1.0 to 0.1.4 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the
release key first.
Read-only, enforced inside the binary
- Every client Shoalcheck builds goes through one transport. That transport now refuses any request for
secrets,configmaps,pods/log,pods/exec,pods/attachorserviceaccounts/tokenbefore it is sent,
in any API group, and also when you run with a cluster-admin kubeconfig. Shoalcheck never asked for these;
now it cannot. The published ClusterRole has no rule for them either, so with that role your API server
refuses them as well. - A refused request is not counted in a reading's request total, which still matches your API server's audit
log. - The code that talks to your cluster is public under Apache-2.0: https://github.com/Shoalcheck/shoalcheck-scan.
The guard isinternal/trace/guard.go; its test counts what actually reaches a real HTTP server.
Rule versions are unchanged. The engine is still 0.24.0 with 89 detection rules and 15 stack analyzers, so a
reading from 0.1.4 compares with one from 0.1.5 without any rule held back.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Shoalcheck is a read-only Kubernetes audit; preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.4
Shoalcheck 0.1.4: a weekly schedule set in the window works.
A copy of 0.1.0 to 0.1.3 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the
release key first.
Readings on a schedule
- "Every Monday at 03:00" and "every Sunday at 20:00" now write their binding. In 0.1.3 the window sent the
cadence as "Mon 03:00", while the schedule reads "mon@03:00", so both weekly choices failed with
--weekly wants <mon..sun>@HH:MM, got "Mon 03:00"and only "every day at 03:00" worked. The command line
(shoalcheck schedule install --weekly mon@03:00) was not affected. - A test now replays the window's own click handler on every cadence the panel offers and asks the server to
accept each one.
Rule versions are unchanged. The engine is still 0.24.0 with 104 rules, so a reading from 0.1.3 compares with one
from 0.1.4 without any rule held back.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Preflight is free on every plan. Plans and prices: https://shoalcheck.com
See it work: a 74-second walkthrough filmed on the real product, every screen a capture: https://youtu.be/eyYNvSTE65E
v0.1.3
Shoalcheck 0.1.3: preflight reads host ports.
A copy of 0.1.0, 0.1.1 or 0.1.2 offers this one in Settings → Updates (or shoalcheck upgrade), checked against
the release key first.
Preflight and feasibility
- A host port is checked the way the scheduler checks it. The scheduler places a pod only on a node where no
pod already bound there holds a host port the pod asks for. The model did not read ports. An ingress controller
run as a Deployment with one replica per worker had somewhere to go when a worker was drained, while every other
worker held the port:preflight drainanswered that nothing it checks stops the drain, and the evicted replica
sat Pending on "didn't have free ports for the requested pod ports". - A hostNetwork pod holds its container ports. The API server defaults
hostPorttocontainerPortfor it,
and the model reads it the same way. - Ports are keyed as the scheduler keys them: address, protocol and number. An empty address is every address and
an empty protocol is TCP. Two different specific addresses do not conflict. - The finding names the host ports the stranded pod asks for. The list of what the model does not check now names
two leaving pods that ask for the same host port, against each other.
Redaction
- A finding no longer prints an IP address. The idle load balancer finding cited the private ingress IP that
makes a load balancer internal. Redaction replaces names, not addresses, so a redacted reading still carried
it. The finding now says the ingress IP is private, without the address. A host port bound to one address is
said to be, also without the address.
Rule versions: node-loss and zone-loss feasibility 1.3.0, idle load balancer 1.3.0. The engine is still 0.24.0
with 104 rules. A reading compared with one from 0.1.2 says these three rules changed, and holds their findings back from "new" and "fixed"
rather than reporting the difference as the cluster's.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.2
Shoalcheck 0.1.2: preflight reads a disk's zone in whatever key its CSI driver wrote it.
A copy of 0.1.0 or 0.1.1 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the
release key first.
Preflight and feasibility
- A volume's node affinity is checked the way the scheduler checks it. A pod is placed only on a node whose
labels satisfy the required nodeAffinity of every PersistentVolume it has bound, whatever key the affinity is
written in. Before, only a hostname pin and a zone pin intopology.kubernetes.io/zone(or its beta key) were
read. - A disk a CSI driver provisioned is read as zonal. Azure Disk (
topology.disk.csi.azure.com/zone), Amazon EBS
(topology.ebs.csi.aws.com/zone) and Compute Engine persistent disk (topology.gke.io/zone) write the zone in
their own key. Before, such a disk read as no pin at all:preflight zonecould answer "nothing stops it" for a
zone that holds a database's only disk, andpreflight draincould place that pod on a node in another zone. - A pod is not placed in a zone its own disk refuses. When the nodes left in its zone refuse it (a taint, a
selector, no room), the pod now has nowhere to go, as it does for the scheduler. Before, it was placed in the
other zone, whichever key the disk used. - A local volume that names two nodes pins to neither. A hostname list of two is satisfied by either node, so
draining one leaves the pod the other. Before, the drain was reported blocked by that volume. - The zone note (
A volume ties … to zone …) reads the driver keys too, and counts the nodes the volume admits.
Rule versions: node-loss and zone-loss feasibility 1.2.0, zone-pinned volume 1.1.0, node-pinned volume 2.1.0. The
engine is still 0.24.0 with 104 rules. A reading compared with one from 0.1.1 says these four rules changed, and
holds their findings back from "new" and "fixed" rather than reporting the difference as the cluster's.
Command line
shoalcheck team add-member <public key>accepts a key that begins with a dash. One key in 64 does, and the
commandteam ci keygenprints failed for it with "flag provided but not defined".- After
--, every word is taken as an argument, including one that begins with a dash.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.1
Shoalcheck 0.1.1: cluster-admin is caught wherever it is granted, and help is per command.
A copy of 0.1.0 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the release key
first.
Security findings
- A User or Group bound to cluster-admin is a finding. It is reported by the new rule
k8s.rbac.cluster-admin-user-or-groupat high, and at critical forsystem:serviceaccounts, which is every pod's
token. Before, only a ServiceAccount bound to cluster-admin was reported, andshoalcheck gatelet the others pass. - cluster-admin bound to
system:authenticated,system:unauthenticatedorsystem:anonymousis critical.
Before, it was medium. - A RoleBinding that grants cluster-admin inside kube-system is reported. Before, system namespaces were skipped.
gate --manifests-onlyjudges a binding to cluster-admin. It reads the role's upstream body, so the binding
fails the job as it would against a live cluster. Before, it passed as "role not found".- kubeadm's own
kubeadm:cluster-adminsbinding is not reported only when the live read shows kubeadm built
the cluster. A pull request's manifests or a capture cannot claim that. - CIS 5.1.1 ("cluster-admin only where required") now rests on both cluster-admin rules.
The engine is 0.24.0 and has 104 rules. After you upgrade, the first reading compared with an older one may list the
new rule's findings as new.
Command line
shoalcheck audit --help, andrender,fleetandhistorywith--help, print that command's own usage and
flags. Help is free on every command.
The app
- Clusters brings back a finished reading after you open it and go elsewhere, until you start a new run.
- Check access says what it is doing while it waits.
- The preflight node list says when listing the nodes timed out, and offers Try again.
Downloads and verification: as for 0.1.0. See the files below, checksums.txt and its cosign bundle.
brew upgrade shoalcheck picks this release up.
Preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.0
The first stable release of Shoalcheck: read-only Kubernetes preflight and audit, from your laptop. It uses
get and list only and installs nothing in your cluster.
A copy of any release candidate offers this one in Settings → Updates (or shoalcheck upgrade), checked against
the release key first.
shoalcheck preflight drain node-1 node-2 # can these nodes be evacuated together? exit 0 · 2 · 1
shoalcheck preflight upgrade 1.33 # removed APIs still served, node pools that cannot roll
shoalcheck audit # the full reading: reliability, security and cost findings
shoalcheck activate <code> # turn the code from your order into a licence key
shoalcheck team join ./team --name Alice --invite <code> # join a team space you share
Since rc.12
- The shop is open. Pro, Team and Business are on sale at https://shop.shoalcheck.com/buy. An order sends an
activation code. Paste it into the app (Plans) or runshoalcheck activate <code>. The code becomes a key bound
to your cluster, or to your team space on Team and Business. - Team. A team space is a folder your team already shares. It holds the roster, decisions, readings and
baselines, and it now holds the Team licence and its member cap. An admin invites; an invite admits at once and
an admin's install settles it. A withdrawn or expired invite is signed and dated. The app's Team screen creates
a space, activates its code, invites and joins. - Tickets. File a finding or a blocked preflight answer as one ticket in Jira or Linear. Each
ticket's link is built from the tracker you configured, never read back from a record.shoalcheck tickets listand
tickets statusread them back;preflight --ticket jira|linearfiles a blocked answer. - Windows and Linux get a real window. On Windows the app is a GUI program with a per-user installer
(Shoalcheck_<v>_windows_<arch>_setup.exe); on Linux the app archive adds a menu entry. The local server stops
when the window closes. - Fleets keep history.
shoalcheck fleet --save-historykeeps every cluster's reading, and each one is
compared with that cluster's own earlier readings. - Each finding shows its own evidence. The inspector, Markdown copy and tickets show the why, the evidence and
the limits of that finding, not of the first finding under the same rule. - The app, screen by screen. A first run with three first steps and a tour of the rail; a verdict that is one
line, one row of facts and a Why; "Fix first" as a list to act from; where the spend is, as a share bar over a
table of pools; empty cells that say why they are empty; layouts that work at phone width and on touch screens. - Large clusters. A live run of a large cluster is drawn in one pass per answer, and survives navigating away.
- Homebrew.
brew install shoalcheck/tap/shoalcheckinstalls the command line on macOS and Linux.
Changed behaviour
gate --baseline <file>with no file at that path now exits 1 and says nothing was compared. Before, it passed.
Record a baseline with--update-baseline, or fix the path.- A redacted document no longer prints the kubeconfig context name.
Downloads
- The command line:
shoalcheck_<v>_<os>_<arch>(.zipon Windows), orbrew install shoalcheck/tap/shoalcheck. - The desktop app:
Shoalcheck_<v>_macos_universal.dmgfor macOS,Shoalcheck_<v>_windows_<arch>_setup.exefor
Windows, andshoalcheck-app_<v>_<os>_<arch>for Linux and for Windows without the installer. - Also
kubectl-shoalcheck(a kubectl plugin) andshoalcheck-mcp(an MCP server).
Verify
- Check the signature:
cosign verify-blob --key cosign.pub --bundle checksums.txt.sigstore.json checksums.txt. - Check the files:
shasum -a 256 --ignore-missing -c checksums.txt.
The public key is cosign.pub. The update
manifest, shoalcheck-update.json, is signed with a separate release key compiled into the binaries.
macOS. The disk image and the macOS command-line binaries are signed with a Developer ID and notarized by Apple.
Not yet. Windows files are not code-signed. SmartScreen asks once: More info → Run anyway.
Preflight is free on every plan. Plans and prices: https://shoalcheck.com
v0.1.0-rc.12
The twelfth release candidate of Shoalcheck: read-only Kubernetes preflight and audit, from your laptop. This
release brings:
- a security pass over everything rc.11 shipped;
- fairer licence charging;
- the CI gate for pull requests, with or without access to the cluster;
- jobs for six CI systems;
- a team space;
- an engine that reads a 2,000-node cluster in seconds and keeps 174 of 175 promises on a planted benchmark;
- macOS downloads signed with a Developer ID and checked by Apple's notary service.
A copy of rc.11 offers this one in Settings → Updates (or shoalcheck upgrade), checked against the release key
first.
shoalcheck preflight drain node-1 node-2 # can these nodes be evacuated together? exit 0 · 2 · 1
shoalcheck gate --with ./rendered/ # what would this pull request's manifests change on the cluster?
shoalcheck findings -o junit # the reading as a test report your CI already renders
shoalcheck team init ./team --name Alice # a shared team space: a folder you share, no service of ours
shoalcheck upgrade # replace this binary with the newest release, signature checked
Security
-
Toolchain and modules.
- rc.11 was built with Go 1.26.2. govulncheck found 29 known vulnerabilities its code could reach, among them
html/templatefixes on the report path. - This release is built with Go 1.26.10 and patched modules, and govulncheck reports none.
- Every build is now checked with govulncheck.
- rc.11 was built with Go 1.26.2. govulncheck found 29 known vulnerabilities its code could reach, among them
-
Release files no longer name the machine that built them. Release binaries carry no build-machine paths,
and every file in an archive is owned by root, dated by its commit. -
Pod environment values are dropped as they arrive. A pod list carries them, and before this release they
sat in memory for the whole run. The product never reads them, and now never holds them either. -
Text from your cluster is treated as data everywhere. An object name or status message can no longer:
- drive your terminal with escape sequences;
- inject Markdown into a ticket;
- forge the marker that stops a duplicate ticket. The marker is now
shoalcheck-dedup:, and tickets an older
build filed are still found.
The MCP server tells the assistant that quoted cluster text is data.
-
The desktop window is locked down further.
- It binds to a loopback address only.
- Every route answers one method.
- It opens only readings kept on this machine.
- It prints a stopped run's message as text, not markup.
-
Evidence packs are written owner-only.
-
Vulnerability reports go to GitHub's private vulnerability reporting on this repository.
Licence charging, corrected
- A cluster that does not answer is not a reading. Nothing is kept and no licence slot is spent, on the command
line and in the app. Before, three runs over a dead VPN could fill a Pro machine's clusters for 30 days. --redactno longer charges a second slot. A redacted reading is charged as the cluster it is.- The app checks the licence before it streams anything. It checks that the licence covers this cluster before
any finding is streamed; before, a key bound to another cluster saw the findings first and the refusal after.
CI/CD — new
gate --with <rendered manifests>. Lays a pull request's manifests over the live reading and judges them
before they land: the findings the change adds or removes, the dollar change, and what was not simulated.
Nothing is applied.audit --capture+gate --capture. Judges a pull request from a CI runner with no cluster access, against
a capture that is signed and states its age. Only a capture from a pinned key is judged.gate --manifests-only. Needs no cluster at all, and fails on what the change carries and cannot judge.--fail-on cost:+N. Fails when a change adds more than $N/month of identified cost.--update-baseline. Records only a run that passed, and writes the baseline whole.- New outputs.
findings -o junit,-o codequality(GitLab's merge-request widget) and-o md-summary(a
pull-request comment). - Ready-made jobs for GitHub Actions, GitLab CI, Azure DevOps, Jenkins, Bitbucket Pipelines and CircleCI. Each
judges a pull request's manifests, says when it cannot gate, and renders its CI's own report formats. One page
explains which job fits which scenario. - In the app.
- Integrations hands out the job for each of the six CIs, pinned to this build.
- Handoff writes JUnit, Code Quality and the pull-request summary from a kept reading.
- A kept reading becomes the CI baseline, shown with the commands to commit it.
Team — new (Team plan, command line)
shoalcheck team keeps a team space in a folder your team already shares: a private git repository, or a synced
or network folder. There is no service of ours.
- What it holds.
- The roster is the members' install keys. A join request is approved by its whole fingerprint, never by a name.
- Each member writes a signed log of decisions: acknowledge, accept a risk, known issue, ticketed, assign,
snooze, note. The logs merge per field, and two members changing the same field are shown as a conflict
rather than one silently winning. - Published readings and CI run records, both signed.
- A shared gate baseline, with the name of who set it.
- Commands.
team mineis what is yours to look at.team logis the feed.team synclists what changed;
Shoalcheck runs no version control in the folder. - What does not count. A record that does not verify is counted by reason, and never trusted.
- The gate. It says who set the team baseline it reads, and records the one it writes.
- Not yet in the app. The desktop app's Team screen follows in a later release.
A large cluster
On a synthetic 2,000-node, ~33,000-pod cluster served over HTTP, rc.11's audit did not finish in ten minutes:
its node-loss model grew as nodes × nodes × pods. This release reads that cluster, judges it and writes both
documents in about 5–6 seconds, with a peak of about 900 MB, on a 12-core Mac. At 4,000 nodes and 66,000 pods it
takes 9.5 seconds and 1.8 GB.
- Memory. Plan on roughly 1 GB for each 35,000 pods.
- What the figures leave out. They are measured against a local server; a real API server adds its own
response time for every page. --redactis about 13 times faster on a large reading.- How a list is read.
- Pages of 500, one request at a time, at most 50 a second; never a watch.
- A throttled list is retried with backoff.
- An expired continue token restarts the list.
- A list cut short is said ("cut short"), and its partial pages are not used.
- Each page has 30 seconds, and a list has 10 minutes.
The engine (0.23.0)
A ground-truth benchmark plants 175 known problems in a live kind cluster and checks each rule against them. Engine
0.22.0 kept 162 and raised 12 false positives; 0.23.0 keeps 174, with 1 known false positive and its reason. Every
one of the 104 rules has ground truth. The method and the per-rule results are in docs/engine/benchmark.md.
- Pod security on bare Pods, Jobs and CronJobs. These were read but not judged before.
- The sample reading now has 153 findings (was 141); its dollar figure is unchanged.
Also fixed
- Preflight.
preflight drain A Bassesses the nodes drained together as one change. Before, it answered for each node
alone, and could pass a pair that leaned on each other.- Preflight says a cluster did not answer, rather than calling it empty or forbidden.
- Connections.
- Every request is bounded. A server that never answers is not asked thirty more times.
- Rejected credentials, a failed credential plugin and an untrusted certificate are each named for what they
are.
- The command line.
- Every output path is checked before the cluster is read, and nothing is overwritten without
--force. --helpis free on every command.findings --namespaceincludes the findings about the namespace itself.- A rightsizing recommendation is never zero.
--discountapplies to node hours only, and says so.- A reading taken through the current context is named after it.
- Every output path is checked before the cluster is read, and nothing is overwritten without
- Documents. The linear HTML document is bounded at 2,000 full cards and says where the rest are.
- The app.
- A fleet draws one podium per cluster.
- "Copy the command" copies the command.
- Notices replace dialogs that the macOS window dropped.
- A second reading is refused while one runs.
- The update check retries within the hour.
- A reading signed by another key says so.
Downloads.
- The command line:
shoalcheck_<v>_<os>_<arch>(.zipon Windows). - The desktop app:
Shoalcheck_<v>_macos_universal.dmgfor macOS, andshoalcheck-app_<v>_<os>_<arch>for Windows
and Linux (it opens in your browser). - Also
kubectl-shoalcheck(a kubectl plugin) andshoalcheck-mcp(an MCP server).
Verify.
- Check the signature:
cosign verify-blob --key cosign.pub --bundle checksums.txt.sigstore.json checksums.txt. - Check the files:
shasum -a 256 --ignore-missing -c checksums.txt.
The public key is cosign.pub. cosign publishes no
Windows arm64 build; on Windows on Arm, its x64 build runs under emulation. The update manifest,
shoalcheck-update.json, is signed with a separate release key compiled into the binaries.
Not yet.
- Code-signed for Windows. SmartScreen asks once: More info → Run anyway.
- Licence keys on sale. The shop, https://shop.shoalcheck.com/buy, opens when the payment provider finishes
reviewing it. Until then the free line is preflight in full and the size of any reading. The plans are Free,
Pro, Team and Business; the one-off audit offers are withdrawn.
macOS. The app's disk image and the macOS command-line binarie...