This file tells AI coding agents how to work correctly in this repository. Read it before making any changes.
apiservice-audit-proxy is a Go pass-through aggregated API server that sits
in front of a real Kubernetes aggregated backend and emits synthetic
audit.k8s.io/v1 events for mutating requests. See docs/ARCHITECTURE.md
for a full description and component diagram.
Module: github.com/ConfigButler/apiservice-audit-proxy
All development tasks use Task. Never invoke go,
golangci-lint, helm, or docker directly when an equivalent task target
exists — the Taskfile sets the correct flags and environment.
task --list # see all available targetstask fmt # auto-format Go code with gofmt
task fmt:check # verify formatting without modifying (used in CI)
task lint # run golangci-lint (config: .golangci.yml)
task lint-fix # run golangci-lint with --fix
task test # unit tests with coverage (produces coverage.out)task build # compile the server binary → bin/apiservice-audit-proxy
task docker-build # build the container image (local tag)task helm:lint # lint charts/apiservice-audit-proxy
task helm:template # render chart to stdout (useful for review)
task helm:package # package chart into dist/
task dist # build all release artefacts in dist/task ci # fmt:check → lint → test → build → helm:lint → distThe e2e suite requires a running k3d cluster. Tasks are composable — each higher-level task calls its dependencies automatically.
task e2e:doctor # verify docker, k3d, flux, helm, kubectl are available# Full smoke test from scratch (creates cluster if needed, deploys everything,
# runs the Go test suite)
task e2e:test-smoke
# Same but with explicit backend CA validation enabled
task e2e:test-smoke-backend-ca
# Impersonation-mode scenarios with operator-supplied RBAC
task e2e:test-impersonation
# Impersonation-mode missing-RBAC scenario
task e2e:test-impersonation-no-rbac
# Aggregated API audit-gap regression guard
task e2e:test-audit-gap
# Bring the cluster up without running tests
task e2e:cluster-up
# Tear the cluster down
task e2e:cluster-down
# Rebuild and reload the proxy image without rerunning tests
task e2e:load-image
# Re-deploy the proxy only (faster than a full smoke run when iterating on
# chart or proxy code changes)
task e2e:deploy-proxye2e:test-smoke
└─ e2e:prepare
└─ e2e:deploy-proxy
├─ e2e:load-image
│ └─ e2e:build-image (builds apiservice-audit-proxy:e2e-local)
├─ e2e:_services-ready
│ └─ e2e:flux-bootstrap
│ └─ e2e:cluster-up
└─ Helm backend.testApiserver.enabled=true + audit.testWebhookReceiver.enabled=true
└─ chart-generated webhook kubeconfig Secret
Inbound requestheader trust is sourced live from the cluster's
kube-system/extension-apiserver-authentication ConfigMap, so there is no
CA-copy step: the chart's kube-system RoleBinding grants the proxy
ServiceAccount read access.
The Flux bootstrap step installs the following into the cluster, then waits for all resources to become Ready before continuing:
| Component | Purpose |
|---|---|
| cert-manager | Issues TLS certificates for the proxy serving endpoint |
| traefik | Ingress controller (also provides ServiceMonitor targets for Prometheus) |
| reflector | Mirrors Secrets and ConfigMaps across namespaces |
| prometheus-operator | Installs monitoring.coreos.com/v1 CRDs and the operator |
After the Flux resources are ready, the bootstrap step waits for the Prometheus
CRDs to be established and then applies test/e2e/setup/manifests/, which
creates the Prometheus instance, RBAC, and the traefik ServiceMonitor.
The default cluster is k3d-audit-pass-through-e2e (kubectl context name).
All kubectl and helm calls in tasks use --context {{.CTX}} so they
target only this cluster even when other clusters are present.
If gitops-reverser is open in the same devcontainer, both clusters share the
same Docker daemon and host kernel. The start-cluster.sh script automatically
bumps fs.inotify.max_user_instances to 512 before creating the cluster.
See test/e2e/cluster/README.md for details.
- Go version: see
go.mod(go 1.26.3) - No generated files in version control;
controller-genoutputs are committed but regenerated withcontroller-genwhen CRD types change - Imports: grouped as stdlib / external / internal, formatted with
goimports— runtask fmtto apply - Error handling: always wrap errors with context (
fmt.Errorf("... : %w", err)); never discard errors silently - Comments: only add a comment when the why is non-obvious; do not describe what the code does
Config is in .golangci.yml. Key linters enabled:
errcheck— no unchecked errorsgovet— standard vet checksstaticcheck— SA* and S* checksgocritic— code qualitygosec— security anti-patternsrevive— stylegoimports/gofmt— formatting
Run task lint to check, task lint-fix to auto-fix where possible.
The chart lives in charts/apiservice-audit-proxy/. Key notes:
- Integer values passed as CLI arguments must use
| intin templates to prevent Helm rendering them as scientific notation (e.g.1.048576e+06). - TLS is mandatory; the chart supports three modes:
cert-manager,self-signed, andexisting-secret. - The
APIServiceresource is only created whenserver.apiService.enabled: true.
- Never run
git commit,git push, or open PRs yourself. The human reviews every change before it lands; stage files at most, and leave the commit to them. This applies even when a task seems obviously done. - Run
task ciand confirm it passes before asking for a commit - For e2e, chart, proxy request handling, audit, TLS, or identity changes, run
the live e2e lanes before asking for a commit:
task e2e:test-smoke,task e2e:test-audit-gap,task e2e:test-impersonation, andtask e2e:test-impersonation-no-rbac. Confirm the relevant--- PASS:lines, includingTestSmoke,TestAggregatedAPIAuditGap, the three impersonation happy-path tests, andTestImpersonationRBACMissing. Also runtask e2e:test-smoke-backend-cawhen backend CA validation or its Taskfile wiring changes. - You may freely run
kubectl,task,helm,docker, andgh(read-only, e.g.gh run list,gh run view) to investigate and iterate locally - Keep commits focused; reference the issue or context in the commit body
- Do not commit
.stamps/directories (they are gitignored build artefacts)
The external-resources/ directory is gitignored and contains local checkouts
of sibling projects that this proxy interacts with. Read them freely for
context — they often answer "why does the proxy do X" better than this repo
alone. Do not edit files inside external-resources/ from this repository (unless asked explicitly by user).
| Path | Repo | Why it matters here |
|---|---|---|
| external-resources/gitops-reverser/ | ConfigButler/gitops-reverser |
Downstream consumer of the audit events this proxy emits; shares the same devcontainer and may run a second k3d cluster concurrently. See its AGENTS.md and docs/ for the reverse-GitOps flow. |
| external-resources/webhook-tester/ | tarampampam/webhook-tester |
Reference webhook server used during e2e to receive and inspect the emitted audit events. Useful for understanding the wire format the proxy produces. |
| File | Purpose |
|---|---|
| docs/ARCHITECTURE.md | Component diagram, request flow, trust model |
| docs/E2E_TESTS.md | E2E suite summary: what each test proves, coverage strength, and known gaps |
| test/e2e/cluster/README.md | inotify limits and two-cluster DooD behaviour |
| Taskfile.yml | All non-e2e tasks |
| Taskfile.e2e.yml | All e2e tasks and variables |
| .golangci.yml | Linter configuration |
| charts/apiservice-audit-proxy/values.yaml | Helm chart defaults |
| test/e2e/setup/flux/ | Flux resources applied during bootstrap |
| test/e2e/setup/manifests/ | Prometheus instance and ServiceMonitors |