Thanks for your interest in improving openvox-ca. This guide covers building, testing, and submitting changes. If you are deploying openvox-ca rather than working on it, start with the README and the user-facing documentation.
AGENTS.md is the authoritative reference for repository
conventions (testing framework, compatibility contracts, commit style). This
guide is the human-friendly entry point; where the two overlap, AGENTS.md wins.
- Go 1.25+ (see
go.modfor the exact version) - git (any supported version — the magefile suite's fixtures isolate themselves
from your own git config by redirecting
HOMEas well as settingGIT_CONFIG_GLOBAL, so they do not depend on the 2.32+ variables alone) - Mage, the build tool this repo uses instead of Make:
go install github.com/magefile/mage@latest(or run targets withgo run mage.go <Target>) - A C compiler (gcc or clang;
build-essentialon Debian/Ubuntu, the Xcode Command Line Tools on macOS), becausemage test:unitandmage test:backendsRedisGorun their suites under the race detector, which needs cgo — see Testing. Present by default on most systems; a slim container image may not have one. The product build is unaffected — it is stillCGO_ENABLED=0. - Docker or Podman with the Compose plugin, for the integration and stack tests
- Only if you are changing the Helm chart under
charts/: Helm and kubeconform (go install github.com/yannh/kubeconform/cmd/kubeconform@v0.8.0)
git clone https://github.com/voxpupuli/openvox-ca.git
cd openvox-ca
# Build both binaries to bin/
mage build:all
# Or with plain Go
go build -o bin/openvox-ca ./cmd/openvox-ca
go build -o bin/openvox-ca-ctl ./cmd/openvox-ca-ctlThe core CA uses the Go standard library only and builds with CGO_ENABLED=0
by default. A FIPS-mode build links BoringCrypto:
mage build:fips # → bin/openvox-ca + bin/openvox-ca-ctl (GOEXPERIMENT=boringcrypto, CGO_ENABLED=1)# Run all unit tests (with coverage, under -race; needs cgo and a C compiler)
mage test:unit
# Format, vet, tidy modules, lint, and check the workflows for drift (the CI gate)
mage dev:check
# Run integration tests using the compose stack
mage test:integComposeThe full set of suites — unit, compose integration, the OpenVox stack test, load tests, and the per-backend integration suites — is documented in development/testing.md.
Markdown documentation is linted with
markdownlint-cli2 against
.markdownlint-cli2.yaml. Run it (and auto-fix most
issues) before pushing doc changes:
markdownlint-cli2 --fixSee AGENTS.md for the details. The essentials:
- Tests use Ginkgo v2 + Gomega — no plain
testing.Ttests (beyond the one suite bootstrap per package) and no other assertion library. - Compatibility contracts must not be renamed. openvox-ca is a drop-in for the Puppet CA, so the
/puppet-ca/v1route prefix, thePUPPET_CA_/PUPPET_CA_CTL_environment prefixes, thepuppetca_metric namespace, and the defaultpuppet-ca//etc/puppet-ca//var/lib/puppet-capaths are deliberately preserved. - Non-test code logs through
log/slog— no other logging library, and no in-treeslog.Handler. slog's handlers escape control characters, which is what the CodeQLgo/log-injectionexclusion depends on; the lint rule that backs this is a denylist, so it will not stop you reaching for an unlisted one. - Route test artifacts to
.test-output/(gitignored). - British English in prose (docs, comments, commit messages, PR text); code identifiers follow the surrounding codebase.
-
Branch off
main; open pull requests againstmain. A change that builds on another unmerged branch may instead be stacked — branch off that branch and target it — in which case say so in the PR description and note that it cannot merge until its base does. CI and CodeQL run on pull requests whatever their base, so a stacked PR is exercised before it merges into its base — but only pull requests targetingmainare gated by the repository ruleset, so on a stacked one read the results rather than relying on a merge block. -
Every Go file carries a GPL-2.0-or-later header, and a new one leads with your own copyright line, above Vox Pupuli's:
// Copyright (C) 2026 Your Name // Copyright (C) 2026 Vox Pupuli and contributors // // This program is free software; you can redistribute it and/or modify // ... (the rest of the notice, verbatim from any existing file)
Do not copy a neighbouring file's header wholesale: the nearest example is somebody else's attribution, and copying it credits your work to them. A file written by more than one person carries each of them, with
Vox Pupuli and contributorslast; a build constraint goes above the header, not below it. Only Go files carry it — YAML, workflows and shell scripts do not. -
Keep commits focused: imperative subject ≤ 72 characters, with a body that explains why. Stage files by name and review
git diff --stagedbefore committing. -
Make sure
mage dev:check,mage test:unit,mage test:magefile, andmarkdownlint-cli2pass. -
lefthook installadds git hooks that cover part of the above: pre-commit runs gofmt and golangci-lint, and pre-push runsgo test -race ./..., the build-taggedgo test -tags mage ., and a refusal of anyv*tag whose version does not match the tree. An untidygo.mod, a drifted chart pin or a markdownlint violation is not among them — those stay withmage dev:check,markdownlint-cli2and CI. Each hook check stands aside with a SKIP when the tool it drives is absent fromPATH. On Linux the test run additionally needs a C compiler, since-racerequires cgo there, and fails rather than skipping without one; macOS builds-racewithout cgo. -
The pre-push run is a different environment from a bare
mage test:magefile: git exportsGIT_DIRand friends to the hooks it runs, which is a hazard for any test that shells out to git. See the testing conventions in AGENTS.md. -
Changed the Helm chart?
mage chart:validateandmage chart:testare required checks too. A new template branch needs a fixture undercharts/openvox-ca/ci/, and anything a reader has to trust needs a case inchart:test.chart:validatecaches the remote Kubernetes schemas under.test-output/; that cache never revalidates, so if it passes locally and fails in CI, clear it withmage dev:cleanand re-run. -
Internal design notes live under docs/development/; update them when you change the behaviour they describe.