Skip to content

Contributing

This page covers the development workflow from filing an issue through to a released version.

Issues use plain descriptive titles. The issue type is captured by the TYPE field (set via the template picker), not by title prefixes or labels.

Good: ICMP availability indicator on hosts page Bad: feat: ICMP availability indicator on hosts page Bad: ✨ ICMP availability indicator on hosts page

When you open an issue, pick one of three templates: Bug, Feature, or Task. The TYPE field is set automatically based on your choice.

Issue typeMaps to PR prefixUse for
Bugfix:Something is broken or incorrect
Featurefeat:New capability or enhancement
Taskchore:, refactor:, test:, perf:, ci:, docs:Maintenance, cleanup, docs, or improvement work
Epic(grouping)A collection of related issues

Labels are orthogonal to the type field. Use them for component scope and workflow signals, not to classify the kind of work.

CategoryLabelsPurpose
Componentweb, backendWhere the work happens
Cross-cuttingarchitecture, security, ciFlags special review or handling
CI triggerbuild, scan, testTriggers specific CI workflows

Add component labels when the scope is clear.

Branch from main. Name branches descriptively:

feat/icmp-availability
fix/dark-theme-readonly
chore/update-dependencies

The prefix is optional but helps with at-a-glance identification.

Local commit messages during development are flexible. They will be squashed on merge, so commit freely during development.

PR titles must follow Conventional Commits format:

<type>: <description>

Allowed types: feat, fix, docs, ci, chore, refactor, test, perf

Examples:

  • feat: add ICMP availability indicator to hosts page
  • fix: active-readonly CSS override in dark theme
  • docs: add contributing workflow guide
  • ci: enable multi-architecture builds

PR titles are validated by CI (pr-title.yml). The description must start with a lowercase letter.

Scopes are optional but can be used for clarity: fix(web): readonly field color in dark theme.

All PRs merge via squash merge. The PR title becomes the commit message on main. This gives us a clean, linear history where every commit on main is a self-contained, conventional-commit-formatted change.

Because of squash merge, the PR title is the only commit message that matters for release automation. Write it carefully.

Releases are fully automated by semantic-release. When a squash-merge commit lands on main:

PR type prefixRelease effect
feat:Minor version bump (0.1.0)
fix:Patch version bump (0.0.1)
docs:, ci:, chore:, refactor:, test:, perf:No release

Breaking changes trigger a major bump. Add BREAKING CHANGE: in the PR body (not the title) to signal this.

See Release Strategy for details on image tagging, promotion, and required secrets.

Issue (plain title + TYPE field: Feature)
→ Branch (feat/description)
→ PR (feat: description)
→ Squash merge (feat: description (#123))
→ semantic-release (minor bump → v0.2.0)

The type is expressed once at each stage, in the format native to that stage: a structured field on issues, a conventional commit prefix on PRs and commits, a semver bump on releases. No duplication, no ambiguity.