Contributions are welcome. This document explains how to set up the project, submit changes, and what we expect during code review.
- Open an issue first for non-trivial changes so we can discuss the approach before you invest time writing code.
- Keep PRs focused — one concern per pull request.
- Stay aligned with the project's philosophy — Stealth is a local-first, privacy-preserving analysis engine. Changes that introduce external service dependencies or move away from this model are generally not a good fit.
See GOVERNANCE.md for how decisions are made.
- Rust (stable, >= 1.93.1)
- Bitcoin Core (>= 29.0) — needed for integration tests
cargo build --workspacecargo test --workspaceIntegration tests start a local bitcoind in regtest mode automatically via corepc-node.
CI enforces both. Run these before pushing:
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warningsFormatting rules are defined in rustfmt.toml (100-char line width, 4-space indentation).
- Fork the repository and create a branch from
main. - Write your code, add tests if applicable.
- Make sure
cargo fmt,cargo clippy, andcargo testpass. - Open a pull request against
main.
Use conventional commits format:
feat(engine): add peeling chain detector
fix(bitcoincore): handle missing address in listunspent
refactor(model): extract WalletHistory builder
test(engine): add CIOH edge case
docs: update README with CLI usage
Once your PR is open and under review:
- Do not rebase or force-push. If you need to make changes, add new commits. Force-pushing destroys the review context — reviewers lose track of what changed between rounds and have to start over.
- Do not squash commits during review. Keep fixup commits separate so reviewers can see exactly what changed in response to each comment.
This follows the same approach used by Envoy, LLVM, and Miri.
stealth/
model/ # Shared types, traits, configuration (stealth-model)
engine/ # Detection engine, heuristics, TxGraph (stealth-engine)
bitcoincore/ # Bitcoin Core RPC gateway (stealth-bitcoincore)
- Use
bitcoincrate types (Txid,Address,Amount) instead of raw strings and floats. - Prefer borrowing (
&) over.clone()when you only need to read. - Prefer generics over
dyntrait objects when the concrete type is known at compile time. - Keep thresholds and constants configurable via
DetectorThresholds, not hardcoded. - All detectors should be covered by integration tests.
By contributing, you agree that your contributions will be licensed under the MIT License.