From b11d042732dc4e4ebc738235eab89b131a3d88e3 Mon Sep 17 00:00:00 2001 From: Mohammadreza Khani Date: Mon, 6 Jul 2026 16:00:48 +0330 Subject: [PATCH] docs: add project plan and development rules - docs/plan.md: project overview, architecture, milestones, directory structure - DEVELOP.md: dev rules, code quality standards, git workflow, testing rules, milestone tracking --- DEVELOP.md | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/plan.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 DEVELOP.md create mode 100644 docs/plan.md diff --git a/DEVELOP.md b/DEVELOP.md new file mode 100644 index 0000000..da48b76 --- /dev/null +++ b/DEVELOP.md @@ -0,0 +1,113 @@ +# DEVELOP.md — Development Rules & Tracking + +## Quick Start + +```bash +# 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 + +1. **No warnings.** All code must compile with zero warnings. CI treats warnings as errors (`-D warnings`). +2. **Format before commit.** Run `cargo fmt` — CI checks formatting. +3. **Clippy clean.** Run `cargo clippy -- -D warnings` — CI checks lints. +4. **Tests required.** Every new module/function must have unit tests. Public API changes require integration tests. +5. **Doc comments.** All public items (`pub fn`, `pub struct`, `pub enum`) must have `///` doc comments. + +### Git Workflow + +1. **Branch naming**: `/` + - `feat/dns-cache`, `fix/ttl-overflow`, `docs/readme`, `chore/ci-update` +2. **Commit messages**: Conventional Commits format + - `feat: add DNS response cache` + - `fix: handle truncated UDP responses` + - `docs: update plan.md` +3. **PR required.** All changes go through a pull request. No direct pushes to `main`. +4. **Squash merge.** PRs are squash-merged into `main`. +5. **CI must pass.** Lint, test, and build must succeed before merge. + +### Architecture Rules + +1. **No panics in production code.** Use `Result` everywhere. `unwrap()` only in tests. +2. **Structured errors.** Define error types per module using `thiserror`. Use `anyhow` only in `main.rs`. +3. **Async everywhere.** All I/O is async (Tokio). No blocking calls in async context. +4. **Config-driven.** No hardcoded values. All tunables come from config file or env vars. +5. **Dependency injection.** Accept traits, not concrete types. Makes testing easy. + +### Testing Rules + +1. **Unit tests** in the same file (`#[cfg(test)] mod tests { ... }`). +2. **Integration tests** in `tests/` directory. +3. **Test naming**: `test___` — e.g., `test_cache_expired_entry_returns_miss`. +4. **No external dependencies in unit tests.** Mock upstream resolvers. +5. **Integration tests** may spin up the full server on a random port. + +### Security Rules + +1. **No secrets in code.** Use env vars or config files (gitignored). +2. **Validate all input.** DNS queries from the network are untrusted — validate lengths, offsets, labels. +3. **Timeouts on all upstream calls.** Never hang indefinitely. +4. **Rate limiting.** Protect against abuse from a single IP. +5. **Dependency audit.** Run `cargo audit` in CI. + +### Commit Checklist + +Before pushing, verify: + +- [ ] `cargo fmt --check` passes +- [ ] `cargo clippy -- -D warnings` passes +- [ ] `cargo test` passes +- [ ] 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](docs/plan.md) for the full project plan and milestone checklist. + +| Milestone | Status | +|-----------|--------| +| M1 — Core Forwarding | 🔲 Not started | +| M2 — Multiple Upstreams & Config | 🔲 Not started | +| M3 — Caching | 🔲 Not started | +| M4 — Observability | 🔲 Not started | +| M5 — Blocking & Filtering | 🔲 Not started | +| M6 — Production Hardening | 🔲 Not started | + +## Useful Commands + +```bash +# 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 +``` diff --git a/docs/plan.md b/docs/plan.md new file mode 100644 index 0000000..e7c1f2a --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,118 @@ +# DoH Forwarder — Project Plan + +## Overview + +A lightweight DNS-over-HTTPS (DoH) forwarder that accepts DNS queries over HTTPS (RFC 8484) and forwards them to upstream DoH resolvers, with caching, logging, and optional blocking. + +## Goals + +- Accept DoH queries (GET and POST per RFC 8484) +- Forward to configurable upstream DoH resolvers (Cloudflare, Google, Quad9, custom) +- In-memory DNS response cache with configurable TTL +- Query logging (stdout/file) +- Optional domain blocklist (ad-blocking / parental control) +- Health-check endpoint +- Low resource footprint, single binary + +## Architecture + +``` +┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ DNS Client │─────▶│ DoH Forwarder │─────▶│ Upstream DoH │ +│ (browser, │ HTTPS│ (this project) │ HTTPS│ (1.1.1.1, │ +│ OS, app) │◀─────│ │◀─────│ 8.8.8.8, …) │ +└─────────────┘ └──────────────────┘ └─────────────────┘ +``` + +## Tech Stack + +- **Language**: Rust +- **HTTP server**: Axum +- **DNS wire-format**: hickory-proto (formerly trust-dns-proto) +- **Async runtime**: Tokio +- **Config**: TOML (config file) + env vars +- **Logging**: tracing + tracing-subscriber + +## Milestones + +### M1 — Core Forwarding + +- [ ] Project scaffold (Cargo workspace, CI) +- [ ] HTTP server with `/dns-query` endpoint (GET + POST) +- [ ] Parse incoming DNS wire-format queries +- [ ] Forward to a single upstream DoH resolver +- [ ] Return DNS wire-format response +- [ ] Basic integration test + +### M2 — Multiple Upstreams & Config + +- [ ] TOML config file support +- [ ] Multiple upstream resolver support +- [ ] Upstream selection strategy (round-robin / failover) +- [ ] Environment variable overrides +- [ ] Graceful shutdown + +### M3 — Caching + +- [ ] In-memory DNS response cache +- [ ] Cache key = (query name, query type) +- [ ] Respect DNS response TTL (with configurable max) +- [ ] Cache eviction (TTL-based + LRU size cap) +- [ ] Cache stats endpoint (`/cache/stats`) + +### M4 — Observability + +- [ ] Structured logging (tracing) +- [ ] Request logging middleware (query name, type, latency, upstream used) +- [ ] Health endpoint (`/health`) +- [ ] Metrics endpoint (optional, Prometheus format) + +### M5 — Blocking & Filtering + +- [ ] Domain blocklist (file-based, one domain per line) +- [ ] Wildcard / suffix matching +- [ ] Return NXDOMAIN for blocked queries +- [ ] Hot-reload blocklist on SIGHUP + +### M6 — Production Hardening + +- [ ] Rate limiting (per-IP) +- [ ] Request timeout for upstream queries +- [ ] Dockerfile (multi-stage, distroless) +- [ ] Helm chart +- [ ] CI pipeline (lint, test, build, image push) + +## Directory Structure (target) + +``` +doh-forwarder/ +├── Cargo.toml +├── src/ +│ ├── main.rs # entrypoint, server setup +│ ├── config.rs # config parsing +│ ├── dns/ +│ │ ├── mod.rs +│ │ ├── query.rs # parse incoming DNS queries +│ │ └── response.rs # build/parse DNS responses +│ ├── upstream/ +│ │ ├── mod.rs +│ │ └── doh_client.rs # DoH forwarding client +│ ├── cache.rs # DNS cache +│ ├── blocklist.rs # domain blocking +│ ├── routes.rs # HTTP route handlers +│ └── middleware.rs # logging, rate-limit +├── config/ +│ └── default.toml +├── docs/ +│ └── plan.md +├── DEVELOP.md +└── tests/ + └── integration.rs +``` + +## Non-Goals (for now) + +- DNS-over-TLS (DoT) listener +- DNSSEC validation +- Persistent cache (Redis / disk) +- Web UI / dashboard