|
| 1 | +# Doctor |
| 2 | + |
| 3 | +`hawkapi doctor` is a one-shot health-check CLI that lints a running HawkAPI application for common misconfigurations across security, observability, performance, correctness, and dependency hygiene. |
| 4 | + |
| 5 | +## Usage |
| 6 | + |
| 7 | +```bash |
| 8 | +hawkapi doctor <APP_SPEC> [--format={human,json}] [--severity={info,warn,error}] [--fix] |
| 9 | +``` |
| 10 | + |
| 11 | +| Argument | Default | Description | |
| 12 | +|---|---|---| |
| 13 | +| `APP_SPEC` | required | `module:attr` reference, e.g. `main:app` | |
| 14 | +| `--format` | `human` | Output format: `human` (coloured TTY) or `json` | |
| 15 | +| `--severity` | `info` | Minimum severity to report: `info`, `warn`, or `error` | |
| 16 | +| `--fix` | off | Apply safe auto-fixes (v1: prints a notice; no fixes implemented yet) | |
| 17 | + |
| 18 | +**Exit codes:** |
| 19 | + |
| 20 | +| Code | Meaning | |
| 21 | +|---|---| |
| 22 | +| `0` | No findings at or above the chosen severity | |
| 23 | +| `1` | At least one warning | |
| 24 | +| `2` | At least one error | |
| 25 | + |
| 26 | +### Example |
| 27 | + |
| 28 | +```bash |
| 29 | +$ hawkapi doctor main:app |
| 30 | +hawkapi doctor — main:app |
| 31 | + |
| 32 | +✗ DOC010 DOC010 |
| 33 | + CORSMiddleware allows all origins ('*'). This exposes all endpoints to any origin. |
| 34 | + Fix: Whitelist specific origins in CORSMiddleware(allow_origins=[...]). |
| 35 | + https://hawkapi.ashimov.com/doctor/DOC010 |
| 36 | + |
| 37 | +⚠ DOC011 DOC011 |
| 38 | + State-changing routes (POST/PUT/PATCH/DELETE) exist but CSRFMiddleware is not installed. |
| 39 | + Fix: Add app.add_middleware(CSRFMiddleware, secret=...) to protect browser-facing endpoints. |
| 40 | + https://hawkapi.ashimov.com/doctor/DOC011 |
| 41 | + |
| 42 | +Summary: 1 errors, 1 warnings, 0 info · exit 2 |
| 43 | +``` |
| 44 | + |
| 45 | +### JSON output |
| 46 | + |
| 47 | +```bash |
| 48 | +hawkapi doctor main:app --format=json |
| 49 | +``` |
| 50 | + |
| 51 | +```json |
| 52 | +{ |
| 53 | + "app": "main:app", |
| 54 | + "summary": {"errors": 1, "warnings": 1, "info": 0, "total": 2}, |
| 55 | + "findings": [ |
| 56 | + { |
| 57 | + "rule_id": "DOC010", |
| 58 | + "severity": "error", |
| 59 | + "message": "CORSMiddleware allows all origins ('*').", |
| 60 | + "fix": "Whitelist specific origins in CORSMiddleware(allow_origins=[...]).", |
| 61 | + "location": null, |
| 62 | + "docs_url": "https://hawkapi.ashimov.com/doctor/DOC010" |
| 63 | + } |
| 64 | + ] |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +## Rule reference |
| 69 | + |
| 70 | +### Security |
| 71 | + |
| 72 | +| ID | Severity | Title | Fix | |
| 73 | +|---|---|---|---| |
| 74 | +| `DOC010` | error | CORS allows `*` in production | Whitelist specific origins in `CORSMiddleware(allow_origins=[...])` | |
| 75 | +| `DOC011` | warn | CSRFMiddleware not installed but state-changing routes exist | Add `CSRFMiddleware` | |
| 76 | +| `DOC012` | warn | TrustedProxyMiddleware missing behind a known proxy | Add `TrustedProxyMiddleware` | |
| 77 | +| `DOC013` | error | Hardcoded placeholder secrets on `app.state` | Load secrets from env vars or a secrets manager | |
| 78 | +| `DOC014` | info/warn | HTTPSRedirectMiddleware absent | Add `HTTPSRedirectMiddleware` (warn if `ENV=production`) | |
| 79 | + |
| 80 | +### Observability |
| 81 | + |
| 82 | +| ID | Severity | Title | Fix | |
| 83 | +|---|---|---|---| |
| 84 | +| `DOC020` | warn | No request-ID / observability middleware | Add `RequestIDMiddleware` or `StructuredLoggingMiddleware` | |
| 85 | +| `DOC021` | info | No `/metrics` endpoint (prometheus_client installed) | Add `PrometheusMiddleware` | |
| 86 | +| `DOC022` | info | opentelemetry installed but no OTel wiring | Install hawkapi-otel and register the plugin | |
| 87 | +| `DOC023` | info | sentry_sdk installed but no Sentry wiring | Install hawkapi-sentry and register the plugin | |
| 88 | + |
| 89 | +### Performance |
| 90 | + |
| 91 | +| ID | Severity | Title | Fix | |
| 92 | +|---|---|---|---| |
| 93 | +| `DOC030` | info/error | `debug=True` in production | Set `debug=False` (error if `ENV=production`) | |
| 94 | +| `DOC031` | warn | GZipMiddleware absent and routes return large payloads | Add `GZipMiddleware(minimum_size=1000)` | |
| 95 | +| `DOC032` | warn | Handler returns bare `dict`/`list` without `response_model` | Add a msgspec.Struct return annotation | |
| 96 | +| `DOC033` | info | Heavy I/O routes without a bulkhead | Wrap with `@bulkhead(...)` | |
| 97 | + |
| 98 | +### Correctness |
| 99 | + |
| 100 | +| ID | Severity | Title | Fix | |
| 101 | +|---|---|---|---| |
| 102 | +| `DOC040` | info | Route handler missing return annotation | Add a return type annotation | |
| 103 | +| `DOC041` | info | Route without docstring or `summary=` | Add a docstring or `summary=` parameter | |
| 104 | +| `DOC042` | warn | Suspicious middleware order: CORS after auth | Add `CORSMiddleware` before authentication middleware | |
| 105 | + |
| 106 | +### Dependencies |
| 107 | + |
| 108 | +| ID | Severity | Title | Fix | |
| 109 | +|---|---|---|---| |
| 110 | +| `DOC050` | info | HawkAPI version older than latest on PyPI | `pip install --upgrade hawkapi` | |
| 111 | +| `DOC051` | warn | msgspec < 0.19 | `pip install --upgrade 'msgspec>=0.19'` | |
| 112 | + |
| 113 | +## `--fix` mode |
| 114 | + |
| 115 | +`--fix` is reserved for safe, deterministic auto-fixes. In v1, no rules implement `auto_fix`, so the flag prints a notice and exits normally. Future rules may opt in on a case-by-case basis. |
| 116 | + |
| 117 | +## CI integration |
| 118 | + |
| 119 | +Add a step to your GitHub Actions workflow to gate deployments on doctor output: |
| 120 | + |
| 121 | +```yaml |
| 122 | +- name: hawkapi doctor |
| 123 | + run: | |
| 124 | + uv run hawkapi doctor main:app --severity=warn --format=json |
| 125 | +``` |
| 126 | +
|
| 127 | +This step exits non-zero if any warning or error is found, blocking the workflow. Use `--severity=error` to only gate on errors: |
| 128 | + |
| 129 | +```yaml |
| 130 | +- name: hawkapi doctor (errors only) |
| 131 | + run: uv run hawkapi doctor main:app --severity=error |
| 132 | +``` |
0 commit comments