diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7e5f87..a616897 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,94 +1,243 @@ # Contributing to FIPS -## Getting Started + -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 ``` -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. diff --git a/docs/branching.md b/docs/branching.md new file mode 100644 index 0000000..9131777 --- /dev/null +++ b/docs/branching.md @@ -0,0 +1,143 @@ +# FIPS Branching and Merging Strategy + + + +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.