Skip to content

Contributing

This guide covers the development workflow, tooling, and conventions for contributing to opnsense2otel.

This page is the canonical contribution guide. The repository root CONTRIBUTING.md is a short pointer to this page — edit only this file; do not duplicate content into the root file.

Prerequisites

  • Go - Check go.mod for the required version (currently Go 1.27)
  • just 1.58+ - For build automation
  • golangci-lint - Optional; runs in CI but can be used locally
  • Docker - Optional; for container builds

Getting started

Clone the repository:

git clone https://github.com/rknightion/opnsense2otel.git
cd opnsense2otel
just setup

Build commands

CommandDescription
just setupInstall repo-local release tooling and warm the module cache
just buildBuild the binary (static, version-embedded)
just testRun the full test suite under the race detector
just fmtRewrite Go and justfile formatting
just lintReport golangci-lint findings without mutating source
just checkRun the bare-toolchain pre-commit gate
just ciAdd the Docker and cross-compilation CI legs when those prerequisites are available
just sync-vendorgo mod tidy && go mod vendor
just cleanRemove reproducible build output

Building on macOS

just build uses the same static-link flags as CI. If your local toolchain cannot support them, use this instead:

CGO_ENABLED=0 go build -o opnsense2otel .

Running a single test

go test ./internal/collector/ -run TestCollector
go test ./opnsense/ -run TestFetchGateways

Project conventions

Vendor directory

The vendor directory is committed to the repository. Always run just sync-vendor after modifying go.mod:

just sync-vendor

Static binary

The build produces a fully static binary with:

  • CGO_ENABLED=0
  • -ldflags "-s -w" for stripped, optimized output
  • -trimpath and -mod=vendor for reproducibility

Version

The version is embedded at build time via -ldflags -X main.version=.... The version.txt file at the repository root is managed by release-please and tracks the current released version; GoReleaser embeds the git tag as the version in release builds, while local just build builds embed local-test.

Linters

The project uses golangci-lint with:

  • gosec, misspell, and revive enabled
  • unused disabled

Linting runs in CI. Locally, just fmt rewrites formatting and just lint only reports findings (the golangci-lint step may fail if the tool is not installed, which is expected).

Commit messages

This project uses conventional commits for automated changelog generation via release-please:

feat(collector): add new subsystem collector
fix(kea): handle disabled DHCP service response
docs: update README with new collector descriptions
refactor: modernize Go syntax patterns

Changelog

Release history lives entirely in CHANGELOG.md, generated by release-please from conventional commit messages. There is no separate "changes from upstream" section to maintain — do not add one to README.md; it was deliberately removed and conventional commit messages are what drive the changelog instead.

Pull request checklist

Before submitting a PR, run just check. It is the bare-toolchain pre-commit gate; run just ci too when Docker and cross-compilation are available. If generated artifacts changed, run just gen and commit the resulting diff.

  • just check passes
  • Generated artifacts are current: just gen was run when needed and its diff is committed — never hand-edit content between <!-- docgen:begin/end --> markers
  • Vendor is synced when dependencies changed: just sync-vendor
  • Conventional commit messages used
  • New collectors follow the adding a collector guide

CI additionally verifies the container image, its embedded version, Compose contracts, and a disposable kind cluster (docker-build); the local just ci superset runs the plain image and deployment legs.

Project structure

.
+-- main.go                      # Entry point
+-- internal/
|   +-- collector/               # Prometheus collectors
|   |   +-- collector.go         # Top-level collector, interface
|   |   +-- arp_table.go         # Per-subsystem collector files
|   |   +-- gateways.go
|   |   +-- ...
|   +-- options/                 # CLI flags and configuration
|   |   +-- ops.go               # OPNsense connection config
|   |   +-- exporter.go          # Server config
|   |   +-- collectors.go        # Collector enable/disable switches
|   +-- logship/                 # Syslog + Zenarmor receivers, log enrichment, OTLP log sink
|   |   +-- syslog/              # Per-program syslog parsers
|   |   +-- zenarmor/            # Zenarmor HTTP receiver
|   |   +-- enrich/              # Device/service enrichment
|   |   +-- flowlog/             # Sink for internal/flow's correlated flow logs
|   |   +-- ...
|   +-- flow/                    # NetFlow receiver, rollup, correlator
|   |   +-- netflow/             # NetFlow wire decode + listener
|   |   +-- correlate.go         # NetFlow + Zenarmor conn merge
|   |   +-- ...
|   +-- webui/                   # Operator console served at /
|   |   +-- server.go            # Route registration, self-registering tabs
|   |   +-- status.go
|   |   +-- ...
|   +-- metricsnap/              # Passive metric capture, teed at both scrape sites
|       +-- recorder.go
+-- opnsense/                    # API client
|   +-- client.go                # HTTP client, TLS, retries
|   +-- gateways.go              # Per-subsystem Fetch methods
|   +-- ...
+-- deploy/                      # Deployment manifests
|   +-- k8s/                     # Kubernetes manifests
+-- grafana/                     # Grafana dashboard builder, panels, alert rules
+-- docs/                        # Documentation (this site)
+-- vendor/                      # Vendored dependencies