> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quintai.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The Quint admission webhook

> What the quint-admission webhook refuses in a governed namespace, how to run it, and how it signs and delivers the record that makes a run contained.

> [!NOTE]
>
> Quint on Kubernetes is early access. The Quint container images, the Helm charts and the admission webhook are not available for download. Your Quint contact gives you each of them, with the image digests for your release, so talk to your Quint contact before you plan an install.

`quint-admission` is a validating admission webhook. It checks every pod in a namespace labelled `quintai.dev/governed=true` before Kubernetes creates or changes it, and refuses any pod that would let the agent get around Quint. Both the [standard](https://docs.quintai.dev/install/kubernetes-standard.md) and the [restricted](https://docs.quintai.dev/install/kubernetes-restricted.md) setup need it. On a cluster you run yourself it also signs a record of each pod it admits, which is what lets Quint record a run as **contained**.

## What the webhook refuses

In a governed namespace, the webhook refuses a pod when:

* Quint's containers are missing, in the wrong order, or use images that are not pinned by digest or not on the webhook's list.
* The agent, or any other container that is not Quint's, runs as root or as a user other than `agentUID`, can gain privileges, or adds any capability.
* The pod shares the node's process, network or IPC namespace, or shares one process namespace between its containers.
* The pod mounts the service account token, a `hostPath` volume or an inline `csi` volume, or the agent can read Quint's own volumes, Secrets or ConfigMaps.
* The pod sets any `fsGroup`, or pod-level `supplementalGroups` in the standard setup.
* A Quint container has extra mounts, environment variables outside the webhook's list, a Quint API or ingest address the webhook was not given, or CPU and memory requests below the webhook's floors.
* The pod uses the older single-container Quint layout (`qhelper.mode=inprocess`).
* The pod has a field the webhook does not know.
* The pod's name contains a dot and it sets no `spec.hostname`, or its hostname names the loopback, such as `localhost`.

On a running pod it refuses any change to a Quint container, a resize of a Quint container, `kubectl exec` and `kubectl attach` into a Quint container, and `kubectl port-forward` to the pod. An ephemeral container from `kubectl debug` is admitted only if the whole pod still passes. Your agent container may still be resized, and you can still `kubectl exec` into it.

Pod status updates are checked by the `quint-status-annotations` ValidatingAdmissionPolicy instead, which refuses a status update that adds, changes or removes a `quintai.dev/*` annotation or adds a container runtime annotation. Install it once per cluster; see [Install the status policy](https://docs.quintai.dev/install/kubernetes-standard.md).

## Run the webhook

Get the `quint-admission` webhook from your Quint contact, and ask how to run it in your cluster. It is not available for download. The Quint images you name in `--sidecar-image` and `--qhelper-image`, and their digests, come from your Quint contact too.

`serve` refuses to start without digest-pinned Quint images. Run it in the `quint-system` namespace behind a Service named `quint-admission`, then register it with the API server:

```bash theme={"dark"}
quint-admission serve --tls-cert tls.crt --tls-key tls.key \
  --sidecar-image REG/quint-sidecar@sha256:<digest> --qhelper-image REG/quint-qhelper@sha256:<digest> \
  --signing-key /signing/ed25519.key --org-id <your organisation ID> \
  --client-ca /client-ca/ca.crt --client-identity kube-apiserver-admission-client
quint-admission webhook-config --service-namespace quint-system --ca-bundle ca.crt | kubectl apply -f -
```

Send the output of `quint-admission key-entry --public-key pub.pem`, run on the signing key's public half, to your Quint contact so Quint can check your webhook's records. Until Quint has registered that key, every run is recorded as **unverified**.

## Signed records need the API server's client certificate

The webhook signs a record only when the request reached it over the API server's own client certificate. Give the API server that certificate with `kube-apiserver --admission-control-config-file`, and start the webhook with `--client-ca` set to a CA that signs that one certificate only. Never use the cluster CA as `--client-ca`. It signs every kubelet's and user's certificate, so any of them could get a record signed.

Amazon EKS and Google GKE managed clusters do not let you set `--admission-control-config-file`. There the webhook still checks every pod and refuses those that break the rules, but signs no record, so runs stay **unverified**. The webhook also signs no record for pods in the restricted setup.

A record says the pod met the rules when it was created. If someone later removes the namespace's `quintai.dev/governed` label, the webhook stops checking that namespace. Treat the right to change namespace labels as cluster-admin.

## Record delivery

Without delivery, every run is **unverified**. With it, the webhook keeps each record it signs and hands it to the pod it names, and the Quint sidecar presents it when the run registers. Start the webhook with:

```bash theme={"dark"}
quint-admission serve ... \
  --record-url https://quint-admission.quint-system.svc/v1/record \
  --record-addr <the Service's ClusterIP>:443 --record-ca-file serving-ca.crt
```

Then install the `quint-sidecar` chart, which comes from your Quint contact, with the same values: `admissionRecord.enabled=true`, `admissionRecord.url`, `admissionRecord.addr`, and `admissionRecord.caBundle` set to the CA that signed the webhook's serving certificate (`--set-file admissionRecord.caBundle=ca.crt`). Only the Quint sidecar sees these; the agent never does. Delivery works in the standard setup only.

The webhook's service account needs:

```yaml theme={"dark"}
# ClusterRole, bound to the webhook's service account
- {apiGroups: [authentication.k8s.io], resources: [tokenreviews], verbs: [create]}
- {apiGroups: [""], resources: [pods], verbs: [get]}
# Role in the webhook's namespace
- {apiGroups: [""], resources: [secrets], verbs: [create, get, list, delete]}
```

Keep these in mind:

* The webhook serves a record only to a connection from the pod's own address. On a network that rewrites pod source addresses to a Service backend, every fetch is refused and runs stay **unverified**.
* If you let Quint reach private ranges (`--allowed-private-cidrs` on the webhook, `QUINT_ALLOW_PRIVATE_CIDRS` on the sidecar), also set `--record-refuse-cidrs` to the ranges the webhook's pods get their addresses from, and the same value as `admissionRecord.refuseCIDRs`.
* A record is kept for 15 minutes and served once. If storing it fails, the pod is still admitted and that run is **unverified**.

## Rotate the record CA

`--record-ca-file` can be given more than once, and each pod's CA must match one of them.

1. Issue the new CA and restart the webhook with `--record-ca-file old.crt --record-ca-file new.crt`. It keeps serving with its old certificate.
2. Switch the webhook's serving certificate to one signed by the new CA.
3. Set the new `admissionRecord.caBundle` on your workloads and roll the pods. Do this promptly: until then, a new pod with the old CA fails its TLS check and registers **unverified**.
4. Once no pod carries the old CA, restart the webhook with `--record-ca-file new.crt` only.

## Next

* [Standard setup](https://docs.quintai.dev/install/kubernetes-standard.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.