Six days ago I posted SOC 2, CRA, NIS2: they all ask your cluster the same five questions. I expected a few reactions. What I got was three people reading the mechanism instead of the claims, and two of them found a hole that mattered. Today the tool behind that post reached 1.0. Most of what changed between the post and the release came out of that thread, and it would be strange to write a release post that pretends otherwise. What it is, in one paragraph Attestkeep is a validating admission webhook for Kubernetes with a scanner behind it. Every pod CREATE and UPDATE outside a short list of bypass namespaces is reviewed against a policy: the :latest tag, a registry allow-list, digest pinning, vulnerability thresholds from the last scan, cosign signatures and attestations, and fifteen workload hardening checks (privileged containers, hostPath mounts and their relatives). Every decision, allowed or refused, is written to a ledger under the content hash of the policy that made it. When a period has gone by, you pick a framework and the ledger becomes a signed evidence package with citations, statuses and named gaps. That last part is what the previous post was about. This one is about what shipped. What the comments changed Vinh Nguyen pointed out that a ledger of admission decisions is evidence about boundary crossings, not about the population. Whatever entered while the webhook was down, or before it was installed, is still running and has no row. So the control grew a second half. An hourly sweep reads every running container's observed imageID, not the spec's image reference, which under a mutable tag still matches its original admission event while the bytes underneath have changed, and reconciles the digest against the ledger. A digest with no admission behind it lands in the evidence package as its own population section. The policy decides what a finding does: recorded, announced, or the pod is stopped so its controller recreates it through the webhook, onto the ledger this time. Then he read the mechanism again and found the destructive case. A pod applied by hand has no controller, so stopping it closes the finding by deleting its subject: no denial, no admission, an absence where an unverified workload used to be. Static pods fail from the other end, because the kubelet rebuilds the mirror without going near admission, so enforcement loops without ever producing an event. Both are now reported and left running, in the same bucket as side-loaded images, marked unmanaged, with one line per sweep saying the sweep found something only a person can decide. That was 0.3.3 and it is in 1.0. Tobias asked whether ran-and-found-nothing is recorded as evidence, or only findings. Admission is the easy half: every review writes a row, including "allowed, nothing wrong". The quiet week is the hard half, and the answer is that the operator keeps re-verifying its own webhook endpoint against the CA bundle it published to the API server, records any window in which that check fails, and prints those windows in the evidence package as outages against the period. What I conceded then still holds today: the absence alert, an absent() over the attestkeep_* series, is a few lines in your Prometheus and not a toggle in ours. Kane Lim listed what a decision record should reference. The row today carries the namespace, the pod, the image reference and its digest, the verdict and its reason, whether the scan result was cached, the policy name and the policy's content hash, and whether break-glass was active. The workload's service account is still not on it. That is a fair gap and it is still open. What 1.0 means 1.0.0 is the 0.3.6 line declared stable. Nothing about an installed policy, licence or evidence document changes on upgrade. Since the first post the ledger became tamper-evident. Every decision is content-hashed when it is written, and the operator periodically seals the ledger with signed checkpoints: each carries the Merkle root of the records in its range and chains to the previous checkpoint's hash. The signing key is generated into a Kubernetes Secret and never stored in PostgreSQL, so rewriting ledger rows and re-signing the seals require two different accesses. The evidence package states whether the record set verifies and names the exact records and seals that do not. Where the guarantees end is written down on the evidence trust model page, including the part where generated_at is still an assertion by the signer and not a trusted clock. Two decisions in 1.0 itself are about posture rather than features, so they get a paragraph each. admissionTiming. A digest nobody has scanned has no result yet. Scanning takes minutes and the API server waits seconds, so the gate never scans inline; the scanner fills a cache in the background and admission reads the cache. The policy chooses what happens to the unknown: AllowAndScan, the default, admits it, queues the scan and decides on every later admission of that digest; DenyUntilScanned refuses it with "a scan has been queued, retry shortly" until the report exists. In 0.3.x a Community policy that set the second one was silently treated as the first. From 1.0 it is honoured in every edition, because the rule is now written down: policy behaviour is never gated by licence, only capacity is. failurePolicy. The webhook ships with Ignore. While it is unreachable, pods are admitted unreviewed. The console used to paint that amber, which nagged everyone towards a fail-closed posture they had not chosen. Now it explains the trade: the window is recorded for your evidence, the hourly sweep reviews what came in, and webhook.failurePolicy: Fail is one Helm value away if you want a gate that holds while the operator is down. Neither value is universally right. The point is that the choice is yours and the consequence is on the record either way. And one thing that was plainly a bug. Every operator replica used to run the daily licence check two minutes after its own start, so a rolling restart or two spent the day's allowance and the pods logged a refusal they could not act on. It is now one call a day per cluster, however many pods run and however often they restart; the check time is kept in the operator's database and shared by all replicas. Enforcement never depended on it: the certificate a cluster holds keeps working for its thirty-day term. What a key buys, and what it does not Every security feature is in the free Community edition: scanning, signature and attestation verification, admission enforcement, the sweep, the sealed ledger, evidence packages, deny-until-scanned, fail-closed. A community edition with the security removed would deserve to lose to the free tools. What a paid key buys is capacity, and single sign-on. Community Team Business Enterprise Clusters per key 1 1 3 5 Compliance frameworks 1 1 3 all eight Image names a month 15 100 200 sized to your estate Single sign-on (OIDC) no yes yes yes Price free, forever $480 a month, or $4,800 a year $1,440 a month, or $14,400 a year $3,000 a month, or $30,000 a year Prices exclude VAT. Checkout runs on Polar as merchant of record, so the card never reaches our servers. There is no time-limited trial because there is no need for one: Community never expires, and the question a trial usually answers, does this work on my cluster, is one you can answer on your own cluster for as long as you like. Any charge can be refunded within 14 days, for any reason. For the launch, the code LAUNCH20 takes 20% off the first invoice until 6 October 2026: a month if you pay monthly, the whole first year if you pay yearly. What leaves your cluster: once a day, a licence identifier, a hash of the kube-system namespace UID, and the operator version. Nothing about your images. The scanner reads your registry, the operator writes to your PostgreSQL, and no agent registers with us. Try it, then watch it refuse something One Helm command. The chart pulls from the registry, there is no repository to add. helm install attestkeep oci://ghcr.io/attestkeep/charts/attestkeep \ --namespace attestkeep --create-namespace \ --set clusterName=trial \ --wait kubectl -n attestkeep port-forward svc/attestkeep 8080:8080 The console asks for a licence key on first run; the free one comes from an account at attestkeep.com and never expires. Then create the owner account and you land on the overview. An admission webhook that is silently not running looks exactly like a compliant cluster, so the first thing to do after installing any gate is to watch it refuse something. With the default policy the smallest violation is the :latest tag: $ kubectl run enforcement-check --image=nginx:latest Error from server: admission webhook "image-security.attestkeep.com" denied the request: image security policy: nginx:latest: the :latest tag is blocked by policy $ kubectl get pod enforcement-check Error from server (NotFound): pods "enforcement-check" not found The error comes from Error from server, meaning the deny happened inside the API server's admission chain and not in an agent that might have been asleep. If the pod was created instead, check in order: a bypassed namespace, a policy in audit mode, a webhook that is failing open, an installation that was never activated. Kubernetes 1.27 or later, amd64 and arm64. Every release image is signed twice on the same digest: keyless through GitHub's OIDC, which proves the digest was built by the release workflow on that tag, and keyed against cosign.pub at docs.attestkeep.com/cosign.pub, which verifies with nothing beyond your registry and exists for air-gapped clusters. Verify the digest, not the tag. It is the same argument the product makes to your own cluster. What it still does not do Being straight about the limits, again, since the whole pitch is honest gaps. The compliance mappings have not been through legal or audit review. They are a careful reading of the published texts, with citations included so a professional can check the work rather than trust it. Nothing binds a report to a trusted clock. It answers admission time and rescan time. It does not watch behaviour at runtime, and it does not do eBPF. If a container starts doing something odd at three in the morning, that is somebody else's product. One installation is one cluster's view. Five clusters on Enterprise are five consoles. A fleet view is on the roadmap without a date attached, because I would rather say that than name a release I might miss. It is self-hosted and closed source. Every claim above is checkable from inside your cluster, and the issue tracker is public, but you cannot read the code. If you read the last post and left a comment, some of this is yours. If you install it and it refuses something it should not have, or admits something it should not have, that is the comment I want. I build Attestkeep, a Kubernetes admission controller with compliance evidence built in. The release notes for 1.0.0 and 1.0.1 are on the releases page.