Thank you for helping improve Hive. This guide is for contributing code and documentation to this repository. If you want to donate compute to a running hive, see Contribute to a Hive instead.
New here? Start with the getting-started guide for first-time contributors — it walks the end-to-end journey (finding an issue, local setup, testing without a cluster, key concepts, and what the review/CI process looks like) and links back into this guide for the mechanics. For live community help, Join our Discord.
- Open issues and pull requests in this repository. Use the issue templates when they are available, and link related issues from the PR body.
- A good issue is a real contribution here, not a lesser one. Much of this repository is written by its maintainers and by the hive's own agents, so the highest-leverage thing an outside contributor can usually do is describe a problem precisely enough to be acted on. When a PR resolves your issue, the commit credits you as a co-author — you appear in the repository's contributor list and on your own GitHub contribution graph, exactly as if you had written the patch. See Crediting issue authors.
- Not sure why the hive has not picked up your issue? It automatically works issues only from OWNER, MEMBER, and COLLABORATOR authors; anyone else waits for a maintainer to add
triage/accepted. See Reporter trust and escalation for what those roles mean, how to become a trusted reporter, and how to escalate. - Pull requests are welcome too, and nothing above changes how they are reviewed.
- Discuss design and review questions in public issues and pull requests so decisions remain public and searchable.
- Follow the CNCF Code of Conduct, adopted in CODE_OF_CONDUCT.md, and Hive governance.
- Report vulnerabilities privately; see SECURITY.md.
- Extending Hive? Start with the Integration guide for the current work source, ClankeR/Flue-style external execution, and Spektacular Project Inception seams.
src/— the current Go module (github.com/hivecommons/hive) and the main development target for this repository.src/cmd/hive— main Hive binary.src/cmd/hivectl,src/cmd/apiproxy,src/cmd/hive-backup— supporting command-line tools.src/pkg/— Go packages for GitHub integration, scheduling, policies, dashboards, hubs, backups, and related runtime behavior.src/crux/— the Crux agent runtime (agent manager, inference, sandbox, egress proxy, tool approval) as a nested Go module; seesrc/docs/design/crux.md.src/policies/— policy prompts and rule files used by the deterministic/agent pipeline. Treat policy changes like code: review the behavior they enable, test where possible, and explain risk in the PR.src/deploy/andsrc/examples/— deployment manifests and example configuration.src/docs/— architecture and operator/developer reference material.
src/pkg/dashboard/theme/themes/— one YAML file per dashboard theme; palette-only additions are good first issues when they use original colors/text and pass the theme parser tests.src/test/— integration and regression tests.
bin/— deterministic pipeline, supervision, enforcement, deployment, and maintainer helper scripts. Seebin/README.mdfor the script-by-script index.config/hive-project.yaml.example— project metadata for the top-level deterministic shell pipeline; see config/README.md. This is separate from the Go runtime config insrc/hive.yaml.example.dashboard/,docs/,config/,systemd/,launchd/, and top-level scripts — supporting assets for hub, dashboard, installation, and operational workflows.Justfile— contributor relay recipes; see Just recipes.
Hive develops on parallel release lines (see ROADMAP.md for the authoritative line policy). Pick your base branch by the kind of change:
| Base branch | Use it for |
|---|---|
v5 (default) |
Bug fixes, security fixes, dependency updates, docs, operability improvements, and structural or protocol-level changes (the latter gated by public [v5 RFC] issues per ROADMAP.md). v5 is the stable line: it publishes candidate/latest and is promoted to stable after soak. |
v4 |
Feature-frozen maintenance line (in effect since 2026-09-21, v4 SHA c354a5008; policy #6346, ROADMAP.md): security fixes and critical fixes only. The required freeze-gate check (repository variable V4_FEATURE_FREEZE) rejects any v4 PR not carrying security, agent/security, priority/critical-urgent, or v4-freeze-exempt and escalates it needs-human — retarget anything else at v5. v4→v5 movement is cherry-pick-only. Runbook: src/docs/v4-freeze-runbook.md. |
v6 |
Dashboard-optional-operation work under the #7563 epic only — such PRs are out of scope on v4 and v5 (see ROADMAP.md). GitHub only derives the issue-list PR icon from Fixes #N for default-branch PRs; hive links v6 PRs to their issues itself via addCloseIssueReferences when it opens them or sees them as strong claims (#11888), so keep the explicit Fixes #N in the body. |
The main branch is not the active target for changes. When in doubt, or if a
maintainer asks for a different base, follow the maintainer's guidance.
Before starting work (substitute your chosen base branch for v5):
git fetch origin
git switch -c <topic-branch> origin/v5See docs/development.md for the full local setup guide. The short path is:
cd src
go build ./... ./crux/...
go test ./... ./crux/...src/ is a two-module Go workspace (src/go.work): the root module plus the Crux agent runtime at src/crux/ (github.com/hivecommons/hive/crux). ./... covers the root module only, so the Crux module is named explicitly as ./crux/.... The Go version is declared in src/go.mod (and mirrored in src/crux/go.mod). Install that version or newer compatible tooling before building.
The root Justfile exposes the public contributor relay workflow. Run just --list to see the current recipe signatures; private implementation details are intentionally not listed there.
| Recipe | What it does |
|---|---|
just contribute-check <backend> |
Runs the same read-only backend CLI preflight used by setup, then reports whether the machine is ready for contribute-setup. |
just contribute-setup <backend> |
Checks the Justfile version, verifies the backend CLI, signs in with GitHub, registers with the configured hub, and writes ${HOME}/.config/hive/contributor.env. |
just contribute-hive [backend] [mode] |
Starts the contributor relay. The default mode is containerized; pass local as the mode to run natively when the local tools are installed. |
just contribute-status |
Queries the configured hub for status and contributor profile information. |
just contribute-tui |
Opens the Hives overlay in hivectl tui --hives, so contributors can list, switch, add, rename and remove profiles. If this checkout has no hivectl, the recipe stages ./bin/hivectl from the checkout's release-tag image when available, otherwise from the branch channel, and records the source digest so later runs refresh or refuse instead of using a stale binary. Use HIVECTL_BOOTSTRAP_IMAGE=... to pick a different image, or HIVECTL=... to use an explicit binary. |
just contribute-hives [args] |
Manages the named hive profiles in ${HOME}/.config/hive/profiles.yml — list (the default), add, use, rename, remove. A thin alias for hivectl hives that uses the same checkout-local bootstrap and skew refusal as contribute-tui; see src/docs/hivectl.md. |
just contribute-browse |
Discovers available public hive projects. |
just contribute-stop |
Stops a background contributor relay if one is running. |
just contribute-k8s [namespace] [outfile] [image_tag] |
Emits Kubernetes manifests for a headless contributor workload. It writes to stdout or the requested file; it does not apply the manifest. |
just hive-api <endpoint> |
Calls a hub API endpoint, defaulting to /status, using the configured hive URL. |
just hive-api-docs |
Opens the hub API documentation in a browser. |
Container mode limits the contributor workload to 4 GiB of memory and 2 CPUs,
matching the contribute-k8s workload ceiling. Set HIVE_CONTAINER_MEMORY or
HIVE_CONTAINER_CPUS before just contribute-hive to tune those limits for
your machine (for example, HIVE_CONTAINER_MEMORY=6g just contribute-hive),
or set either value to none on a host that cannot enforce that controller.
See src/docs/contributor-relay.md for the end-to-end contributor relay workflow and Kubernetes workload details.
- Format Go changes with
gofmt. - Prefer small, focused PRs with tests or a clear explanation when tests are not practical.
- Keep configuration values configurable instead of hard-coding environment-specific paths, tokens, or endpoints.
- Do not commit secrets, generated credentials, or local runtime state.
- For documentation changes, verify every command, path, and branch name you mention.
The docs under src/docs/ cite source locations in path/to/file.go:NNN
form (about 360 such cites at the time of writing). They pin claims to real
code, which makes them valuable — and fragile: any refactor that moves lines
in a cited file silently breaks them. Three remap waves (PRs #7495, #7669,
#7680) have each had to re-verify dozens of cites after large refactors.
When you touch cited code or the docs that cite it:
- Refactoring a file cited by docs? Grep for cites into it before you
open the PR —
grep -rn "yourfile.go:" src/docs/— and remap any that your change moves. A cite remap belongs in the same PR as the refactor, not in a later cleanup wave. - Writing a new cite? Point at the symbol's declaration line, and prefer
citing a symbol name alongside the line (e.g.
`ConnectVault`, `pkg/knowledge/api.go:588`) so a future remap can re-locate it by name when the line number drifts. - Ranges (
file.go:588-606) are fine for a block; keep them tight so drift is detectable.
A change to behavior must come with a test that would fail without it. This is the project's standing expectation, not a per-PR negotiation.
- Bug fixes add a test that reproduces the bug — one that fails on the parent commit and passes on the fix. A fix whose test passes either way has not demonstrated it fixes anything.
- New functionality adds tests covering its normal path and the failure modes a caller can actually hit.
- Security-relevant changes assert the invariant, not the implementation. A test that merely calls a guard proves nothing; it must fail when the guard is removed.
- Tests are in scope for review. A test asserting the wrong thing is worse than no test, because it reports green while the behavior is broken.
Where a test is genuinely impractical — a change that only affects real cloud infrastructure, or a docs-only edit — say so in the PR body and explain what you did to verify it instead. "Tests not practical" without that explanation is a reason for a reviewer to push back.
Static analysis runs in CI (go vet, golangci-lint, gosec, and
govulncheck; see .github/workflows/go-security-analysis.yml).
Fix findings rather than suppressing them; when a suppression is genuinely
right, comment why at the suppression site.
The repository includes githooks/post-checkout. Install it only if you want the local checkout guard:
git config core.hooksPath githooksThe hook runs after branch checkouts in the primary worktree. It prevents that worktree from staying on a branch other than main by printing guidance and checking main back out. It is intended for long-running dashboard checkouts where feature work should happen in separate git worktree add ... directories. It does not run for file checkouts or linked worktrees, because linked worktrees have a .git file instead of a .git directory.
If the hook is not installed, normal Git behavior applies. If it blocks a checkout unexpectedly, use a separate worktree from an unprotected checkout or remove the hooksPath setting for repositories where the guard is not desired.
Every commit must include a Developer Certificate of Origin (DCO) sign-off. The DCO is your certification that you have the right to submit the contribution under this repository's license, and Hive requires it on every non-merge commit. Use:
git commit -sThe -s flag adds a Signed-off-by: trailer using your configured git identity,
for example:
Signed-off-by: Your Name <you@example.com>
The sign-off email must match the commit author email. A GitHub noreply address
for the same authoring account is also acceptable, in either GitHub form:
<login>@users.noreply.github.com or
<id>+<login>@users.noreply.github.com. Do not sign with an arbitrary second
personal address unless it is also the commit author email; the checker cannot
verify that two unrelated email addresses belong to the same person.
Check your local identity before committing:
git config user.name
git config user.emailIf your last commit is missing the trailer, or it used the wrong email, fix it before review:
git commit --amend -s
git push --force-with-leaseTo add sign-offs across a branch, rebase with sign-off and then force-push:
git rebase --signoff origin/v5
git push --force-with-leaseOnly rewrite your own pull-request branch. Once bad DCO history lands on a
protected branch such as v5, contributors cannot repair it in place: protected
branch history is not rewritten, and maintainers must not add a DCO sign-off on
someone else's behalf. That is why the post-merge checker has narrow per-commit
waivers for already-merged history; waivers record a maintainer disposition, but
they are not a substitute for signing new commits correctly.
Four workflows check sign-offs, at different points and for different failure classes. The first two run at pull-request time, while your branch is still writable — if either fails, fix it by rewriting your branch as shown above. The last two report on protected-branch history that can no longer be rewritten; their failures are resolved by maintainer disposition, not by you.
- Copilot DCO (
.github/workflows/copilot-dco.yml) — runs on every pull request. Every non-merge commit on the PR branch must carry aSigned-off-by:trailer matching the commit author's email (or an acceptable GitHub noreply form of it, as described above). This is the check you will see most often; remediation isgit commit --amend -sorgit rebase --signoff. - DCO squash attribution (
.github/workflows/dco-squash-attribution.yml, the "Sign-off survives the squash" check) — runs on every pull request. Requires at least oneSigned-off-by:on each branch commit to name you, the PR author — your commit email or your own GitHub noreply address. A sign-off naming anyone else fails, whether that identity is a bot (#6798) or a different human's noreply address (#6971). GitHub's squash merge writes the landing commit with the PR author as author while keeping the branch commit's message — and therefore itsSigned-off-by:— so a trailer naming someone other than you lands as a permanently mismatched sign-off on history that can no longer be fixed. A sign-off that is valid on each branch commit can still fail here. Fix: re-sign the branch commits with your own identity (git rebase --signoffafter setting youruser.email). The check is skipped when the PR author is itself a bot, because a bot-authored squash commit creates no mismatch. - DCO push-delta gate (
.github/workflows/dco-push-delta.yml) — runs on direct pushes tov4/v5and checks exactly the commits that push introduced (#6756). This is a detection gate, not a rejection gate: by the time it turns red, the commits are already on the protected branch, so the failure is recorded and dispositioned rather than fixed in place. - Post-merge DCO trailer check (
.github/workflows/dco-post-merge.yml) — hourly sweep of a rolling window of recentv4/v5history. It owns the waiver accounting (DCO_WAIVED_COMMITSfor single historical commits,DCO_ALLOWLIST_EMAILSfor maintainer-accepted identities) and files issues on failures. The waiver semantics — what each instrument accepts and its blast radius — are documented insrc/docs/v5-sync-policy.md.
All four reuse the same trailer validation (src/scripts/check-dco-trailers.sh
for the range-scanning gates), and the sign-off identity rules — noreply
matching and author-login lookup — live in one shared library,
src/scripts/lib-dco-identity.sh, sourced by both the pre-merge squash gate
and the post-merge monitor. One copy on purpose: the two checkers once carried
separate copies of that logic, the pre-merge copy fell behind, and a
different-human sign-off sailed through to protected history
(#6971). With a single
source of truth they cannot disagree about what a valid sign-off looks like.
When your PR resolves an issue somebody else filed, credit them on the commit with a Co-authored-by: trailer (#6588). This is what turns "thanks, closing" into a contribution that GitHub actually records: a co-authored commit puts the filer in the repository's contributor list and on their own contribution graph.
src/scripts/issue-coauthor.sh builds the trailer from the issue, so you do not have to look the identity up:
# print it
src/scripts/issue-coauthor.sh 6588
# → Co-authored-by: hanthor <5840441+hanthor@users.noreply.github.com>
# or add it to the commit you just made
git commit -s -m "🐛 fix: ..."
src/scripts/issue-coauthor.sh --amend 6588The script emits nothing (and exits 0) when there is nobody to credit — a bot filed the issue, or you did. It fails rather than emitting a half-right line if the issue or its author cannot be resolved, because GitHub silently ignores an address it cannot match: a malformed trailer looks like credit while crediting nobody.
Two things worth knowing if you write the trailer by hand instead:
- Use the
<id>+<login>@users.noreply.github.comform. It is the only address guaranteed to resolve to the account, and it does not republish a personal address the filer never offered for this repository's git history. - Co-authorship is attribution, not certification. It does not sign off for anyone: the
Signed-off-by:trailer is still yours alone, and a commit carrying only aCo-authored-by:still fails the DCO check. Adding a co-author never changes your own DCO obligations, and never satisfies theirs.
Credit the filer of the issue the PR fixes, not everyone who commented. If several people's issues are genuinely resolved by one PR, add one trailer each.
When a fix PR merges with Closes #N, Fixes #N, or Resolves #N, the issue closes automatically. If the reported problem persists, the reporter (or anyone with write access) can reopen it with /reopen or by commenting on the closed issue. Reopened issues are treated as still-open work.
- State deployment reach, not just merge status. For a reporter on a hosted or auto-updating hive, "fixed" means running where they are looking, not merely merged to a branch. Status comments on adopter-filed issues should say which channel or deployment the fix reached, or that it has not shipped yet.
- Use the opt-in reporter-confirmation gate only when needed. Add
hive: needs-confirmationto the issue body or as a label when the reporter genuinely must verify the symptom before closure; operators can setissues.reporter_confirmation: trueto restore the legacy default for every human-filed bug-family issue.hive: close-on-mergeandhive: reporter-confirmedare still honoured as legacy no-op/bypass markers. - Bot- and agent-filed issues still close against evidence. Verify against the stated evidence (a green run, a passing check, or the agent's reproduction) and close when it is green.
- Reporters can reopen their own issue with
/reopen. Comment/reopen(optionally followed by what you are still seeing) on the closed issue and theIssue Reopen Commandworkflow reopens it. This works for the issue's author and for people with write access, and exists because GitHub's own permission model will not let a reporter without write access reopen an issue the App bot closed — the dead end that produced #6762.
- Target
v5for all code and documentation contributions (the active development branch).v4is feature-frozen and accepts security and critical fixes only (see the base-branch table above). - Start PR titles with the repository's emoji convention:
✨ feature: ...,🐛 fix: ...,📖 docs: ...,📝 proposal: ...,⚠️for a breaking change,🌱for chores and everything else.📝is for proposals only — a documentation PR is📖 docs: ..., and the requiredPR Typecheck fails the swap. - Include
Fixes #<issue>lines for issues the PR closes. - Credit the issue's author with a
Co-authored-by:trailer on the commit when the PR resolves somebody else's issue — see Crediting issue authors. - Describe what changed, why, and how you tested it.
- Include the relevant command output or a short note such as
Not run (docs only)when tests are not applicable. - Add a changelog fragment under
changelog.d/for user-visible changes — features, fixes, security changes, migrations, deprecations, and breaking changes (see "Changelog fragments" below). Routine refactors, test-only changes, and dependency churn are explicitly out of scope — add theno-changeloglabel if the requiredchangelog-fragment-guardcheck asks anyway. Do not append toCHANGELOG.md's## Unreleasedsection directly: every PR editing that one shared heading is what made unrelated PRs merge-conflict with each other (#5675); fragments are compiled into CHANGELOG.md automatically at release time. - Expect maintainers to ask for focused follow-up changes rather than broad drive-by edits.
One file per PR, named changelog.d/<category>-<pr-or-slug>.md where the category (added, changed, deprecated, fixed, security) picks the CHANGELOG subsection — and, through it, the semver bump of the next release. The file's content is exactly your entry: a single - bullet in the same narrative style as existing CHANGELOG.md entries, no headings. The compiler owns the ### headings, so do not put headings in a fragment. The complete workflow:
echo '- The relay no longer drops long tasks ([#1234](https://github.com/hivecommons/hive/issues/1234)).' > changelog.d/fixed-1234-relay-drop.md
git add changelog.d/fixed-1234-relay-drop.md
git commit -schangelog.d/README.md has the full format, the no-changelog exemption, and the release-marker escape hatch. The transition window that let in-flight PRs rely on direct CHANGELOG.md edits closed on 2026-09-09. For a user-visible code change, write a fragment instead: the changelog-fragment-guard check no longer accepts a direct CHANGELOG.md edit as a substitute, and reports This PR edits CHANGELOG.md's Unreleased section directly; the transition window ended 2026-09-09 before asking for a fragment or no-changelog label. If a fragment starts with prose or a heading instead of a - entry bullet, the guard fails with a fragment must start with a '- ' entry bullet (or a '<!-- release: ... -->' marker) — it IS the entry; the compiler owns the ### headings. For refactors, test-only changes, docs-only changes, dependency churn, or other changes that are not user-visible, use the no-changelog label when the guard asks instead of adding a fragment.
Project governance lives in GOVERNANCE.md. The current owner/approver signal is also reflected in OWNERS. Security disclosure is handled through SECURITY.md, not public issues.