# Endpoint Monitor A typed FastAPI service that stores endpoint monitors in process-local memory and performs secure, on-demand HTTP checks. ## Quick start Requires Python 3.12. ```sh python -m venv .venv . .venv/bin/activate pip install -e '.[dev]' make check uvicorn app.main:app --reload --workers 1 ``` Create and check a monitor: ```sh curl -sS -X POST http://localhost:8000/v1/monitors \ -H 'content-type: application/json' \ -d '{"name":"example","url":"https://example.com/health?token=do-not-log"}' curl -sS -X POST http://localhost:8000/v1/monitors/MONITOR_UUID/checks curl -sS http://localhost:8000/v1/monitors/MONITOR_UUID/status ``` Interactive OpenAPI documentation is at `/docs`. The full route/status/error contract and SSRF model are in [docs/service-design.md](docs/service-design.md). ## Configuration All values are startup-validated and use environment prefix `MONITOR_`: | Variable | Default | Constraint | |---|---:|---| | `MONITOR_APP_NAME` | endpoint-monitor | string | | `MONITOR_LOG_LEVEL` | INFO | logging level | | `MONITOR_CHECK_TIMEOUT_SECONDS` | 5 | >0, <=30 | | `MONITOR_MAX_REDIRECTS` | 5 | 0–10 | | `MONITOR_MAX_RESPONSE_BYTES` | 1000000 | 1–10000000 | Checks resolve every hop and block any hostname with a non-public answer. Redirects are manual and bounded; total elapsed timeout and response size are bounded. Query and fragment data and URL credentials are redacted from structured logs. URL credentials are rejected. Application checks cannot create a perfect DNS-rebinding boundary; use deny-by-default egress networking or a hardened outbound proxy in sensitive deployments. ## Tests and quality ```sh make lint # Ruff formatting check and lint make type # strict mypy make test # pytest API/unit suite make check # all three ``` Tests are directly inspectable under `tests/`: CRUD/operations and API mapping (`test_api.py`), lock concurrency and stale compare-and-set updates (`test_store.py`), timeouts/statuses/DNS and redirect SSRF/log redaction (`test_checker.py`), and environment validation (`test_config.py`). Outbound requests use `httpx.MockTransport`; tests do not access the network. ## Container ```sh docker build -t endpoint-monitor:local . docker run --rm -p 8000:8000 endpoint-monitor:local # or docker compose up --build ``` The image runs as a non-root user, has a liveness healthcheck, and deliberately starts one Uvicorn worker. ## Layout ```text app/ service implementation checker.py bounded checks, DNS/redirect SSRF policy config.py validated environment settings logging.py structured JSON and URL redaction main.py FastAPI routes and lifecycle models.py API schemas and status semantics store.py lock-protected in-memory state and CAS updates tests/ mocked unit and API tests docs/service-design.md contract and architecture docs/verification.md execution record and reproducible commands Dockerfile, compose.yaml, pyproject.toml, Makefile ``` ## Important limitations All monitors disappear on restart. State is not shared between processes, hosts, or Uvicorn workers, so run exactly one worker. There is no authentication; place the service behind an authenticated gateway if exposed. For durable or horizontally scaled use, replace `MonitorStore` with a transactional shared datastore and coordinate check ownership.