docs: rewrite CONTRIBUTING.md, add docs/branching.md

The previous CONTRIBUTING.md read like a stock Rust contributing
template that mentioned FIPS. Replace it with an entry-point doc
that gives new contributors the FIPS-specific mental model they
need to make a useful first PR: the FMP/FSP layering and why
mesh-level changes need multi-node testing, the three-branch
release model and how to choose a target branch, structured bug
reporting expectations, and PR submission requirements (scope
discipline, the local-CI ladder, separate requirements for feature
PRs vs bug-fix PRs, squash-merge mechanics).

Add a contributor-facing AI coding assistant policy: use is
welcome, but the contributor must do a thorough manual review and
editorial pass before submission. The agent is a tool; the
contributor is accountable for the submission. Review effort
scales with submission effort -- unreviewed agent output will
receive an agent reply in turn, without human review.

Add docs/branching.md as the long-form companion covering the
release workflow, version conventions, and merge-direction
rationale. The new CONTRIBUTING.md is the day-to-day entry point;
docs/branching.md is the reference.

All cross-references resolve against current HEAD. Previous
stale links to fips-intro.md, fips-wire-formats.md, and
fips-configuration.md are gone; the doc points at the current
docs/design/ layout, docs/getting-started.md,
docs/tutorials/join-the-test-mesh.md, and testing/README.md.

No code changes.
This commit is contained in:
Johnathan Corgan
2026-05-13 21:32:45 +00:00
parent 6533276eda
commit 538ce077df
2 changed files with 360 additions and 68 deletions

View File

@@ -1,94 +1,243 @@
# Contributing to FIPS
## Getting Started
<!-- markdownlint-disable MD013 -->
Clone the repo:
FIPS is a mesh routing protocol for Nostr identities over arbitrary
transports. The architecture is layered, top to bottom:
```
- **IPv6 TUN compatibility layer** — presents the mesh as a local
network interface (`fips0`) so unmodified applications can use it.
Applications send IPv6 packets to `fd::/8` addresses derived from
Nostr pubkeys; the daemon converts between IPv6 packets and FSP
datagrams.
- **FSP** (FIPS Session Protocol) — end-to-end encrypted sessions
between identities, with periodic rekey.
- **FMP** (FIPS Mesh Protocol) — peer management, spanning tree,
bloom filters, routing and forwarding, and link encryption.
- **Transport** — the actual wire: UDP, TCP, Tor, Bluetooth LE,
Ethernet, and so on. Each transport plugs into FMP via a trait.
Most non-trivial changes affect behavior visible across the mesh —
how nodes find each other, how packets route, how sessions rekey, how
peers recover from failure. A single-node `cargo test` run is
necessary but not sufficient for that class of change; the integration
harness in [testing/](testing/) is where regressions actually surface.
This document covers the workflow assuming that context. Protocol
depth lives in [docs/design/](docs/design/).
## Quick start
```bash
git clone https://github.com/jmcorgan/fips.git
cd fips
```
Before changing code, read the protocol docs in this order:
- [docs/design/README.md](docs/design/README.md)
- [docs/design/fips-intro.md](docs/design/fips-intro.md)
- the specific design doc for the behavior you are touching
## Prerequisites
- Rust 1.94.1 and Linux with TUN support
- Use the pinned toolchain from [rust-toolchain.toml](rust-toolchain.toml) for deterministic builds
- For the default BLE-enabled build on Debian/Ubuntu:
`sudo apt install bluez libdbus-1-dev pkg-config`
- Docker is required for the integration harnesses under [testing/](testing/)
If you do not want BLE locally, build and test without default features:
```bash
cargo build --no-default-features --features tui
cargo test --no-default-features --features tui
```
## Local Verification
Choose the narrowest check that matches your change:
- Docs-only changes:
```bash
git diff --check
```
- Normal code changes:
```bash
cargo build
cargo test
cargo clippy --all -- -D warnings
```
- Local CI-style unit test run:
The pinned toolchain in [rust-toolchain.toml](rust-toolchain.toml) is
used for deterministic builds. On Debian/Ubuntu, BLE-capable builds
need `bluez`, `libdbus-1-dev`, and `pkg-config` installed; the default
build picks up BLE if those are present and skips it cleanly if not.
For multi-node integration runs, Docker is required. The harness
under [testing/](testing/) starts containerized topologies and
exercises real mesh behavior; see [testing/README.md](testing/README.md)
for the suite catalog.
For a guided first-run that joins the public test mesh, see
[docs/tutorials/join-the-test-mesh.md](docs/tutorials/join-the-test-mesh.md).
Pointing your local daemon at a `test-*` node is the cheapest way to
dogfood a change end-to-end before opening a PR.
## Choosing a branch to target
FIPS uses three long-lived branches, each a superset of the previous:
- **`maint`** — bug fixes for the latest released version.
- **`master`** — compatible work for the next feature release.
- **`next`** — wire-format-breaking and API-breaking work, staged for
the next forklift release.
Pick the branch that matches the scope of your change:
| Your change | Target |
| --- | --- |
| Bug fix in a feature that shipped in the latest release | `maint` |
| Bug fix in code added on `master` since the last release | `master` |
| Bug fix in `next`-only code (wire-format-breaking work) | `next` |
| New feature, no wire-format or API break | `master` |
| Wire-format-breaking or API-breaking change | `next` |
| Documentation, CI, contributor-facing changes | `maint` if they apply to released material, else `master` |
When in doubt, ask in the issue. The maintainer can retarget if
needed. The full release workflow, version conventions, and
merge-direction rationale are in [docs/branching.md](docs/branching.md).
## Reporting bugs
Search [open issues](https://github.com/jmcorgan/fips/issues) before
filing a new one — duplicates are common in a young project.
When you open a bug report, please include:
- **FIPS version** (`fipsctl --version`)
- **Rust toolchain version** (`rustc --version`)
- **OS / distro** (Linux distro + kernel, or macOS / Windows version)
- **What you expected to happen** — your mental model of the
behavior, ideally referencing the relevant docs or config field.
- **What actually happened** — the observed behavior, including the
surprise.
- **Reproduction steps** — minimal and deterministic if you can.
Multi-node bugs should include the topology and per-node config
excerpts.
- **Evidence** — relevant log excerpts (`journalctl -u fips` or stdout
with `RUST_LOG=info` or `debug`), `fipsctl show` output if relevant
(`peers`, `links`, `status`), and any visible mesh state.
One issue per bug. Don't bundle unrelated symptoms even if you
suspect they share a root cause — the maintainer will link them if
they turn out to be related.
## Submitting pull requests
### Scope discipline
Every PR should make one logical change. The reviewer should be able
to read the whole diff and trace every line back to the PR's stated
purpose.
- No drive-by reformatting of unrelated files.
- No unrelated refactors folded into a bug fix or a feature PR.
- No "while I was in there" cleanups in files outside the change's
natural footprint. Send them as separate PRs; they'll usually land
faster on their own.
- Pre-existing lint warnings in files you didn't touch are not yours
to fix in this PR.
### Required before opening any PR
Run these locally and confirm they all pass:
```bash
./testing/ci-local.sh --test-only
cargo fmt --check
cargo build
cargo clippy --all-targets -- -D warnings
cargo test
```
- Narrow integration run for transport, routing, Docker, or packaging-sensitive changes:
`fmt` and `clippy -D warnings` are CI gates — PRs with formatting
drift or new clippy warnings will fail CI and be sent back.
Then run the integration suite that exercises your change:
```bash
./testing/ci-local.sh --only static-mesh
./testing/ci-local.sh --only <suite>
```
See [testing/README.md](testing/README.md) for the available integration and chaos harnesses.
See [testing/README.md](testing/README.md) for the available suites
and what each covers. Routing, discovery, rekey, NAT, gateway, and
transport changes all have specific suites; pick the narrowest one
that touches your code path.
## Filing Issues
**Recommended before opening**: the full local CI run.
- Search existing issues before opening a new one.
- Include FIPS version, Rust version, and OS.
- For bugs: steps to reproduce, expected vs actual behavior.
```bash
./testing/ci-local.sh
```
## Pull Requests
This is the same matrix that runs on GitHub Actions. Catching a
regression locally is much cheaper than catching it in CI.
- All PRs must pass `cargo build`, `cargo test`, and `cargo clippy --all -- -D warnings`.
- Keep commits focused — one logical change per commit.
- Add tests for new functionality.
- Reference relevant design docs if the change touches protocol behavior.
- Pull requests are merged via squash-merge.
- Update docs in the same change when you modify:
- protocol or routing behavior
- wire formats
- configuration shape or defaults
- operational workflows or testing instructions
### Additional requirements for feature PRs
In practice this usually means updating one or more of:
- **New CI coverage.** Features added without a test that exercises
them won't be reviewed. Either extend an existing integration
suite or add a new one under `testing/`. Coverage of just the
happy path is fine for an initial PR; edge cases can land as
follow-ups.
- **Documentation updated alongside the code.** Protocol changes
update the relevant [docs/design/](docs/design/) page. Config
changes update the operator-facing docs in [docs/](docs/) and the
reference config. Behavior visible to operators updates
[README.md](README.md) and any tutorial it touches.
- [docs/design/fips-mesh-operation.md](docs/design/fips-mesh-operation.md)
- [docs/design/fips-wire-formats.md](docs/design/fips-wire-formats.md)
- [docs/design/fips-configuration.md](docs/design/fips-configuration.md)
- [README.md](README.md)
- [testing/README.md](testing/README.md)
### Additional requirements for bug-fix PRs
## Questions
- **A regression test** where practical. If a regression test isn't
tractable (some bugs only surface under timing or scale that's hard
to encode), say so in the PR description with a one-paragraph
explanation.
- **Commit message references the bug**: the symptom, the root cause
in one sentence, and the fix shape.
Open a GitHub issue for design or implementation questions.
### Merge mechanics
PRs are merged via **squash-merge**. One logical change per PR
becomes one commit on the destination branch, which keeps `git
bisect` useful across the integration suite. Your in-PR commit
history doesn't matter for the final landed history — the maintainer
rewrites the commit message at merge time.
## AI coding assistant policy
Use of AI coding assistants (Claude Code, Copilot, Cursor, Aider, and
similar) in preparing a contribution is welcome. These tools are
force multipliers and we have no objection in principle to their use
in writing code, tests, documentation, or PR descriptions.
What we require is that the contributor does a thorough manual review
and editorial pass over the output before submission. Concretely:
- Verify that the code does what it claims, not just that it
compiles.
- Verify that any tests the agent wrote actually test something
useful, not just that they pass.
- Verify that any documentation matches the behavior.
- Spot-check the diff for nothing-surprising: no unrelated files
modified, no fabricated APIs, no references to symbols that don't
exist, no version bumps you didn't intend, no churn outside the
change's natural footprint.
- Be ready to discuss the design choices in the PR as if you wrote
every line, because for the purposes of accountability you did.
The coding agent is a tool. The contributor is the author of record
and is accountable for whatever they submit. PRs are reviewed on
what they contain, not on who or what wrote them.
**Review effort scales with submission effort.** A submission that
shows signs of being unreviewed agent output — irrelevant edits
scattered across the tree, hallucinated function names, mismatched
test/behavior pairs, fabricated API references, ChatGPT-style summary
prose in comments — will receive an AI-coding-agent reply in turn,
without human review. If you want a human reviewer's attention, do
the editorial pass yourself first.
Repeated submissions of unreviewed AI output will result in the
contributor being asked to step back and may result in account
restrictions.
## Where the conversation happens
- **GitHub issues** — bugs, feature requests, design discussions
that don't fit on a specific PR.
- **GitHub PRs** — design discussion specific to a change in
flight. Comment threads on the diff are the right place to push
back on a decision.
- **[fips.network](https://fips.network)** — community page, podcast,
and the project's Nostr account. Broader project conversation and
announcements happen here.
For implementation questions specific to your PR, ask in the PR
itself. For design or roadmap questions that don't have a clear PR
home yet, file a GitHub issue with the `design` label.
## Further reading
- [docs/design/README.md](docs/design/README.md) — protocol design tree.
- [docs/branching.md](docs/branching.md) — full release workflow and
merge-direction rationale.
- [docs/getting-started.md](docs/getting-started.md) — operator
walkthrough for a new node.
- [docs/tutorials/join-the-test-mesh.md](docs/tutorials/join-the-test-mesh.md)
— how to dogfood your change against the public test mesh.
- [testing/README.md](testing/README.md) — integration suite catalog.

143
docs/branching.md Normal file
View File

@@ -0,0 +1,143 @@
# FIPS Branching and Merging Strategy
<!-- markdownlint-disable MD013 -->
This document explains how the three long-lived branches relate, when
to target each one, and how merges propagate fixes and features. For
the day-to-day "how do I send a PR" workflow, see
[CONTRIBUTING.md](../CONTRIBUTING.md).
## Branch Structure
Three long-lived branches track parallel development streams:
```text
next ──●──●──●──●──●──────────────●──●── (wire-format-breaking work)
\ /
master ────●──●──●──●──●──●──────●──●──●── (compatible features, latest release line)
\ /
maint ────────●──●──●──●──●────────────── (bug fixes for the latest release)
```
### maint
- Reset to each minor release tag at release time
- Accepts only bug fixes for functionality that shipped in the
latest release
- No new features, no API changes, no wire-format changes
- Patch releases tag from here (e.g., `v0.3.1`, `v0.3.2`)
- Periodically merged forward into `master` so fixes propagate
### master
- Compatible development for the next feature release
- Multiple feature releases may ship from master (`v0.4.0`, `v0.5.0`)
before `next` promotes
- No wire-format breaking changes; no API breaks
- Receives merges from `maint` so released-line fixes flow forward
- Periodically merged forward into `next`
### next
- Accumulates work that breaks wire format, API, or compatibility
- Receives merges from `master` so it stays current with bug fixes
and compatible feature work
- Cargo version on `next` is the expected release version with a
`-dev` suffix, updated if `master` ships additional minor
releases first
- Becomes the new `master` at the next breaking release; at the same
point the old `master` becomes the new `maint`
## Versioning
While the project is in the `0.x` era, semver treats minor bumps as
potentially breaking. Both `master` and `next` bump the minor version;
the distinction between compatible and breaking is captured in the
changelog and in which branch the work landed on.
The `-dev` suffix in `Cargo.toml` indicates an unreleased development
state on the branch.
## Merge Direction
Fixes and features flow in **one direction only**: `maint → master → next`.
Never merge backward (`next` into `master`, or `master` into `maint`).
```text
maint ──→ master ──→ next
```
This guarantees:
- Bug fixes shipped in a release reach all subsequent branches
- Compatible features reach `next`
- Wire-format-breaking work stays isolated on `next` until release
If you submit a PR on `next` that should also be on master or maint
(rare, since the criteria for needing it on multiple branches are
usually mutually exclusive), the PR stays on its target; the
maintainer either backports as a separate commit on the upstream
branch or asks you to.
## Choosing a Branch for Your PR
Pick the branch that matches the scope of your change:
| Your change | Target branch | Why |
| --- | --- | --- |
| Bug fix in a feature that shipped in the latest release | `maint` | Fix forward-merges to `master` and `next` |
| Bug fix in code added on `master` since the last release (not in any released version) | `master` | The released v0.x.y line is unaffected, so `maint` does not need the change |
| Bug fix in code added on `next` (wire-format-breaking work) | `next` | The bug only exists where the breaking work exists |
| New feature that does not break wire format or API | `master` | Becomes part of the next compatible release |
| Wire-format breaking change, API break, or fundamental protocol shape change | `next` | Stays isolated until the next forklift release |
| Documentation, CI, or contributor-facing changes | `maint` if they apply to released material, else `master` | Forward-merges propagate naturally |
If you are not sure, ask in the related issue. The safest defaults
are `master` for new features and `maint` for bug fixes; the
maintainer will retarget the PR if needed.
## Release Workflow
### Bug fix release (from `maint`)
1. Fix on `maint`
2. Bump patch version, tag (e.g., `v0.3.1`)
3. Merge `maint` into `master`
4. Merge `master` into `next`
### Compatible feature release (from `master`)
1. Finalize features on `master`
2. Merge `maint` into `master` to pick up any pending fixes
3. Set version, tag (e.g., `v0.4.0`)
4. Reset `maint` to the new tag
5. Bump `master` to the next `-dev` version
6. Merge `master` into `next`
### Breaking release (from `next`)
1. Finalize features on `next`
2. Merge `master` into `next` to pick up pending fixes and features
3. Assign version as the next minor after `master`'s last release, tag
4. `master` becomes the new `maint`
5. `next` becomes the new `master`
6. Create a new `next` branch from `master`
## Practical Guidelines
- **Commit to the appropriate branch for the scope of the change.**
Do not commit bug fixes to `master` when they apply to the latest
release — put them on `maint` and let the forward-merge propagate.
- **Feature branches base off the long-lived branch they target.**
Create with `git checkout -b my-feature maint` (or `master` or
`next`), not `git checkout -b my-feature origin/maint`. The
`origin/`-prefixed form auto-sets the new branch's upstream to
the source ref, which can cause `git push` to land on the wrong
ref under some configurations.
- **When in doubt about whether a change is compatible**, target
`next`. The maintainer can advise on retargeting.
- **Resolve merge conflicts on the receiving branch**, preserving
both the inherited fix and the new development.
- **PRs are merged via squash-merge.** One logical change per PR
becomes one commit on the destination branch, making bisect
clean across the integration suite.