- Failover strategy: try resolvers in order until one succeeds - Env var overrides: DOH_LISTEN, DOH_UPSTREAM_STRATEGY, DOH_UPSTREAM_RESOLVERS - Graceful shutdown on SIGINT/SIGTERM - Strategy parsed from config string to enum - 14 tests passing, clippy clean, fmt clean
3.5 KiB
3.5 KiB
DEVELOP.md — Development Rules & Tracking
Quick Start
# Prerequisites
rustup show # Rust 1.80+
cargo install cargo-watch
# Run locally
cargo run -- --config config/default.toml
# Run with auto-reload
cargo watch -x run
# Run tests
cargo test
# Lint
cargo clippy -- -D warnings
cargo fmt --check
Development Rules
Code Quality
- No warnings. All code must compile with zero warnings. CI treats warnings as errors (
-D warnings). - Format before commit. Run
cargo fmt— CI checks formatting. - Clippy clean. Run
cargo clippy -- -D warnings— CI checks lints. - Tests required. Every new module/function must have unit tests. Public API changes require integration tests.
- Doc comments. All public items (
pub fn,pub struct,pub enum) must have///doc comments.
Git Workflow
- Branch naming:
<type>/<short-description>feat/dns-cache,fix/ttl-overflow,docs/readme,chore/ci-update
- Commit messages: Conventional Commits format
feat: add DNS response cachefix: handle truncated UDP responsesdocs: update plan.md
- PR required. All changes go through a pull request. No direct pushes to
main. - Squash merge. PRs are squash-merged into
main. - CI must pass. Lint, test, and build must succeed before merge.
Architecture Rules
- No panics in production code. Use
Result<T, E>everywhere.unwrap()only in tests. - Structured errors. Define error types per module using
thiserror. Useanyhowonly inmain.rs. - Async everywhere. All I/O is async (Tokio). No blocking calls in async context.
- Config-driven. No hardcoded values. All tunables come from config file or env vars.
- Dependency injection. Accept traits, not concrete types. Makes testing easy.
Testing Rules
- Unit tests in the same file (
#[cfg(test)] mod tests { ... }). - Integration tests in
tests/directory. - Test naming:
test_<what>_<condition>_<expected>— e.g.,test_cache_expired_entry_returns_miss. - No external dependencies in unit tests. Mock upstream resolvers.
- Integration tests may spin up the full server on a random port.
Security Rules
- No secrets in code. Use env vars or config files (gitignored).
- Validate all input. DNS queries from the network are untrusted — validate lengths, offsets, labels.
- Timeouts on all upstream calls. Never hang indefinitely.
- Rate limiting. Protect against abuse from a single IP.
- Dependency audit. Run
cargo auditin CI.
Commit Checklist
Before pushing, verify:
cargo fmt --checkpassescargo clippy -- -D warningspassescargo testpasses- New code has tests
- Public items have doc comments
- No
unwrap()outside of tests - Config values are externalized (not hardcoded)
Milestone Tracking
See docs/plan.md for the full project plan and milestone checklist.
| Milestone | Status |
|---|---|
| M1 — Core Forwarding | ✅ Done |
| M2 — Multiple Upstreams & Config | ✅ Done |
| M3 — Caching | 🔲 Not started |
| M4 — Observability | 🔲 Not started |
| M5 — Blocking & Filtering | 🔲 Not started |
| M6 — Production Hardening | 🔲 Not started |
Useful Commands
# Build release binary
cargo build --release
# Run with specific log level
RUST_LOG=debug cargo run
# Generate and open docs
cargo doc --open
# Dependency audit
cargo install cargo-audit
cargo audit
# Watch tests
cargo watch -x test