Coding standards¶
Binding standards for Go code, tests, security, commits, architecture, and merge gates in Kollect. Operator-specific engineering principles (error taxonomy, robustness, definition of done) live in engineering guidelines. This page is the contributor-facing checklist enforced by lint, CI, and review.
Before you open a PR
task lint · task coverage · task coverage:race (recommended) · task verify ·
task scrub — see CONTRIBUTING.md and
Testing strategy.
License headers¶
Source files should include SPDX and copyright headers:
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Konrad Heimel
TypeScript/CSS under ui/src/ use the same license line with // or /* */ comment style.
CI enforces UI headers via task verify:headers (also run as part of task ui-ci).
Exception: Kubebuilder-generated api/*/zz_generated.deepcopy.go files are produced by
controller-gen and may omit copyright headers. Do not hand-edit them; regenerate with
make generate and task verify. If upstream tooling adds header support later, adopt it in
the generator config rather than patching generated output.
Go conventions¶
Short, actionable rules for Go code in this repo. Operator reconcile semantics and error taxonomy: guidelines § 1.
Error handling¶
- MUST wrap errors with context:
fmt.Errorf("export to %s: %w", sink, err). - MUST use
%w(not%v) so callers canerrors.Is/errors.As— required forErrTransient/ErrTerminal/ErrForbiddenclassification. - MUST NOT discard errors from fallible calls (
errcheckenforces this). - MUST NOT use
github.com/pkg/errors— blocked bygomodguard.
Formatting and style¶
- MUST format Go with
gofmt/goimports—task format:checkfails CI on drift (gofmt + goimports viagolangci-lint fmt --diff). - SHOULD follow the Google Go Style Guide for naming, simplicity, and readability; the Uber Go Style Guide is a useful secondary reference.
- MUST run golangci-lint v2 locally before every PR (
task lint); CI fails on lint errors.
Modules and dependencies¶
- MUST keep module path
github.com/konih/kollect(v0/v1); bump to…/v2only on a tagged major release per Go module path rules. - MUST commit
go.sum; rungo mod tidy— preflight CI checks for drift. - SHOULD NOT vendor — no
vendor/directory; rely on the module proxy and checked-ingo.sum. - MUST respect
depguard/gomodguardpolicy (see Dependency policy below).
Tests¶
- SHOULD run the race detector locally before PRs (
task coverage:raceorCOVERAGE_RACE=1 CGO_ENABLED=1 bash hack/coverage.sh). CItask coverageruns without-race(CGO disabled in release/CI paths). - SHOULD prefer Ginkgo/Gomega matchers in
_test.gofiles overtestify/assert(depguardon tests). - Pyramid tiers, coverage floors, and sink integration gates: Testing strategy.
Security and supply chain¶
- MUST run
govulncheck—task vulncheckin CI on every PR (remediation thresholds: SCA remediation policy). - MUST pass
gitleaksandtask scrubbefore commit (see Security below).
Container builds¶
- MUST build the operator manager with
CGO_ENABLED=0for the shipped image (Dockerfile); enable CGO locally only fortask coverage:race. The runtime stage is Debian bookworm-slim (nonroot UID 65532) withgitandopenssh-clientforspec.git.engine: cliandgit ls-remoteprobes;go-git(default) does not use those binaries.
Go style and lint¶
| Tool | Config / command | Purpose |
|---|---|---|
| golangci-lint v2 | .golangci.yaml · task lint |
Static analysis, formatters, dependency policy |
| go-arch-lint | .go-arch-lint.yml · task arch-lint |
Package import boundaries |
| gofmt / goimports | task format:check |
Formatting drift gate (gofmt + goimports) |
Run task lint locally before every PR. It includes golangci-lint and go-arch-lint check.
CI workflow CI (.github/workflows/ci.yaml) runs lint and format checks; preflight
runs codegen/changelog/module drift only.
Key linters enabled in .golangci.yaml include errcheck, govet, staticcheck, gosec,
depguard, gomodguard, and logcheck (custom plugin via hack/tooling/.custom-gcl.yml). Maintainer setup
and arch-lint baseline workflow: tooling-setup.md.
Logging¶
Use structured logging via log/slog or controller-runtime/log (logr). Do not introduce
github.com/sirupsen/logrus — blocked by depguard and gomodguard.
The logcheck linter enforces Kubernetes logging conventions:
stable message strings with variable data in key/value pairs; never log secrets, tokens, or full
payloads. Operator logging rules: guidelines § 1.
Dependency policy¶
depguard denies deprecated stdlib shims (io/ioutil), non-standard error/logging packages, and
legacy protobuf imports. gomodguard blocks logrus and pkg/errors in go.mod. When adding a
legitimate dependency, extend gomodguard.blocked / depguard rules in .golangci.yaml and keep
task lint green. Details: tooling-setup.md.
Tests and matchers¶
In _test.go files, prefer Ginkgo/Gomega matchers over testify/assert (depguard on tests).
Testing¶
Follow the six-tier pyramid (L0–L5) in Testing strategy and ADR-0706.
| Requirement | Detail |
|---|---|
| Coverage floor | 65% statement coverage on ./internal/... today (task coverage, COVERAGE_MIN) |
| Pre-v0.10 target | 80% — ratchet the floor only when coverage is sustained |
| Behavior tests | Table-driven unit tests; envtest for controllers/webhooks; golden contracts for extractors |
| New sink backends | Must reach L3 integration (task test-integration) before merge |
| Codegen drift | task verify must pass (manifests, deepcopy, RBAC, mocks committed) |
Integration-tagged tests (-tags=integration) and e2e packages are excluded from the default
coverage profile.
Security¶
| Control | Where |
|---|---|
| CodeQL | .github/workflows/codeql.yaml — Go analysis on main and PRs |
| Secret scan | gitleaks in CI; task scrub + gitleaks protect --staged before commit |
| Vulnerability scan | task vulncheck (govulncheck) in CI |
| SCA policy | SCA remediation policy — CVE/license thresholds |
| Threat model | SECURITY.md |
| Security ADR | ADR-0104 — TLS, RBAC, redaction, secrets |
Secrets and scrubbing¶
- Credentials only via
secretRef; never in CR spec/status, logs, or committed files. - Run
task scrubon staged diffs before commit to catch private strings and old project identities. - Profile redaction uses
scrubKeysbefore items enter the store (ADR-0303).
Git sink validation¶
Git and GitLab sink paths, refs, and config values are validated at admission and export time to block path traversal, shell injection, and unsafe refs:
ValidateGitRef— safe ref charset; rejects leading-and..segmentsvalidateObjectPath/ workdir containment — object paths cannot escape the workdirValidateGitSinkWarnings— admission warnings for risky git sink settings (webhook)
Implementation: internal/sink/git/validate.go, internal/validation/git.go. Extend these
validators when adding git-related fields; do not bypass with #nosec without an ADR note.
Transport and hub ingest¶
Sink and doc endpoints must use verified TLS with org CA support (caBundle / caSecretRef).
Hub HTTP ingest listens in plain HTTP inside the pod — terminate TLS at the ingress or service
mesh before traffic reaches the operator (ADR-0503).
Commits¶
CONTRIBUTING.md § Commit messages defines the format:
- Conventional Commits types and scopes
- Optional gitmoji shortcode prefix (
:sparkles:, not Unicode emoji) - Breaking changes only when a tagged release exists and adopters must migrate
Changelog entries are generated with git-cliff (hack/release/cliff.toml). Only
feat, fix, perf, refactor, and breaking commits appear in the user-facing changelog.
Architecture¶
Package layout and dependency flow are documented in ARCHITECTURE.md § Package boundaries.
Import rules are enforced by .go-arch-lint.yml (task arch-lint).
When introducing a new internal/ package or cross-component import:
- Update
.go-arch-lint.ymlto describe the intended graph. - Run
task arch-lintand legalize existing violations incrementally (see tooling-setup.md). - Record non-trivial decisions in
docs/adr/.
Non-trivial API, tenancy, sink, or multi-cluster changes require an ADR before merge — see ADR index.
Pull request and CI gates¶
main is protected: linear history, required checks preflight, test, and kind-smoke
(E2E Tier 0), no force-push. Add kind-smoke in GitHub branch protection after the workflow
has run green on main at least once. Use Rebase and merge on PRs
(CONTRIBUTING.md § Changelog and releases).
| Gate | Workflow / task | Blocks merge? |
|---|---|---|
| Preflight | .github/workflows/preflight.yaml |
Yes |
| E2E smoke | .github/workflows/e2e-smoke.yaml → job kind-smoke |
Yes |
go mod tidy drift |
preflight job | Yes |
go mod verify |
preflight job | Yes |
| Codegen drift | task verify |
Yes |
| Stale changelog | task changelog:verify |
Yes |
| CI | .github/workflows/ci.yaml |
Yes |
| Lint + arch fitness | task lint (ci.yaml lint job) |
Yes |
| Format (gofmt + goimports) | task format:check (ci.yaml lint job) |
Yes |
| Coverage floor | task coverage (ci.yaml test job; no -race) |
Yes |
| Integration (L3) | task test-integration |
Yes |
| Secret scan | gitleaks | Yes |
| Helm / image smoke | task helm-test, task docker:build |
Yes |
Optional jobs (e2e-extended Tier 1, e2e-nightly, perf-report, SonarCloud) do not block merge unless noted in ADR-0706.