Skip to content

Contributing to kafkito

Thanks for considering a contribution!

DCO

Every commit must include a Signed-off-by: trailer certifying the Developer Certificate of Origin. Add it automatically with git commit -s. The DCO check is enforced by GitHub Actions.

Workflow

  1. Fork, branch, code.
  2. make check — runs Go tests, golangci-lint, the OpenAPI lint and generated-types drift check (make api-check) and all frontend checks. Requires Go, Bun and curl on your PATH. make lint downloads the pinned golangci-lint release (GOLANGCI_LINT_VERSION in the Makefile, the same version CI uses) into ./bin on first use and lints both the default and the btp build. To bump it, change the Makefile variable and the golangci-lint-action version in .github/workflows/ci.yml together (make lint-version-check enforces this). CI runs the same Makefile targets (make test, make api-check, make frontend-check); only golangci-lint runs via its GitHub Action, pinned to the same version. make actionlint lints the GitHub workflows.

make frontend-check runs Biome (lint and format, including the accessibility rules and the file-naming convention), TypeScript, knip (unused files, exports and dependencies), the production build and Vitest. The Vitest run includes the repo-wide checks in frontend/src/__checks__/ (design tokens, contrast, query keys, route orphans, date formatting).

make e2e is opt-in: it starts Kafka and the Schema Registry via Compose and a devauth build of kafkito, then runs the Playwright walks in frontend/e2e/ (including axe accessibility scans). Besides Docker, Bun and Go it needs curl and jq on your PATH (the fixture seeding talks to the Schema Registry REST API). After a failed run, stop the binary and the Schema Registry with make e2e-down. See frontend/e2e/README.md.

CI jobs: go, frontend, api-breaking (oasdiff against the base branch, PRs only), docker-build, actionlint and dco in ci.yml; playwright in e2e.yml; the secret scans in secret-scan.yml. 3. Changing the HTTP API? Edit api/openapi.yaml (the contract, see ADR-0005) and run make api-generate to refresh the generated server interface and frontend types. A new endpoint then needs its strict handler method, its mount in internal/server/api_routes.go and an apiOps test entry; the steps are in ADR-0005, "Adding an endpoint". 4. Open a PR with a clear description and a Test plan. 5. Sign off your commits (-s).

Pure formatting commits are listed in .git-blame-ignore-revs. GitHub skips them in blame views; locally run git config blame.ignoreRevsFile .git-blame-ignore-revs once per clone.

Local development

Requires Go 1.26+, Bun 1.4+, and Docker.

make worktree-init    # writes .env.dev with a free port pair
make dev              # Compose + backend (air) + frontend (Vite), one command

Open the Vite URL printed in the [frontend] stream (default http://localhost:37422). Backend changes under cmd/, internal/, or api/ rebuild automatically; frontend changes hot-reload through Vite. Press Ctrl-C in the make dev terminal to stop both processes (the Compose stack stays up — tear it down with make dev-down). The backend listens on 127.0.0.1 only, because auth mode off requires a loopback address; Vite proxies /api to it.

Multiple git worktrees can run make dev in parallel; each calls make worktree-init once to claim a free port pair, and they share the same Compose-backed Kafka and Schema Registry.

From an IDE, running air directly works too — .air.toml loads .env.dev itself, as long as you've run make worktree-init once.

Pre-commit hooks

Optional. .pre-commit-config.yaml defines lean hooks that only look at staged files:

  • gitleaks — blocks credentials (see Secret scanning).
  • golangci-lint-fmt — gofmt/goimports via the pinned golangci-lint in ./bin (installed on first use, like make lint).
  • biome-check — Biome lint and format check for the frontend sources (frontend/src, frontend/e2e, config files); needs make frontend-install.

Enable them once per clone:

brew install pre-commit   # or: pipx install pre-commit
pre-commit install

The formatter hook rewrites files in place and fails the commit; review and stage the changes, then commit again. make check and CI remain the gate.

Style

  • Backend: idiomatic Go 1.26, golangci-lint clean.
  • Frontend: Tailwind tokens from @theme; the default palette is disabled there. See docs/DESIGN_GUIDELINES.md.
  • Frontend layout: primitives in frontend/src/components/ui/, domain components in frontend/src/features/<domain>/, PascalCase component files (enforced by Biome). See docs/DESIGN_GUIDELINES.md § 10.
  • UI strings and code comments are English only. No emojis in UI chrome, logs, or commit messages.

AI coding agents

AGENTS.md and frontend/AGENTS.md are the instructions for coding agents (Claude Code, Codex, Cursor, Copilot); CLAUDE.md and frontend/CLAUDE.md import them and add what is specific to Claude Code. The shared Claude Code configuration is committed: .claude/settings.json (permissions and hooks), .claude/hooks/, .claude/skills/, .claude/agents/ and .mcp.json. Personal settings belong in .claude/settings.local.json and CLAUDE.local.md, which are gitignored. Agents follow the rules above (DCO sign-off, make check, no edits to generated files) and open pull requests as drafts, because .github/workflows/auto-merge-from-owner.yml enables auto-merge for every non-draft PR by the owner. When you change a command, a path or a convention these files mention, update them in the same PR.

Releasing

Releases are cut manually by pushing a signed v* tag; everything else is automated by .github/workflows/release.yml and .goreleaser.yaml.

  1. In the release-prep commit, add an entry for the new version (without the leading v) and today's date (YYYY-MM-DD) to frontend/src/content/changelog.ts. This is the curated, user-facing "What's new" shown in the app. Check it with make release-check VERSION=vX.Y.Z.
  2. Tag and push: git tag -s vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z.
  3. The release-gate job fails the release before anything is built if step 1 is missing. Then goreleaser builds the binaries and archives (with SHA256SUMS and SBOMs), pushes the ghcr.io/finkeflo/kafkito images (vX.Y.Z, latest, and the -btp / -local variants) and publishes the GitHub release. Tags with a pre-release suffix (e.g. v1.2.0-rc.1) are marked as pre-releases.

The GitHub release notes are generated from the Conventional Commit subjects since the previous tag (grouped into Features, Bug Fixes, Security, Others and Dependencies; chore, ci, test, style and docs(changelog) are left out), so write commit subjects for humans.

make release-snapshot runs the whole pipeline locally without publishing (requires goreleaser v2, Docker with buildx and syft); output goes to ./dist.

Secret scanning

Secrets must never enter the repo. Three layers protect against accidents:

  1. Local pre-commit hook (Gitleaks). Enable the pre-commit hooks once after cloning; git commit then aborts when Gitleaks finds a credential in the staged diff.
  2. CI scan (TruffleHog). .github/workflows/secret-scan.yml scans every push to main and every PR. Verified findings fail the job.
  3. GitHub Push Protection. Enabled in repo settings; blocks pushes containing recognised provider tokens server-side.

If a secret leaks despite this, rotate the credential at the provider first, then clean the history (git filter-repo).

License

By submitting a contribution, you agree that your work is licensed under the Apache-2.0 License (this project's outbound license), as certified by the DCO sign-off.