# Endpoint Monitor Service Contract ## Resource and lifecycle A monitor has an immutable UUID `id`, `name`, HTTP(S) `url`, positive `timeout_seconds`, timestamps, and a current check snapshot. New and updated monitors are `unknown` until checked. State is process-local memory, guarded by one async lock; it disappears on restart and is neither shared nor replicated across workers. Run exactly one worker. ## HTTP API - `POST /monitors` -> 201; create. - `GET /monitors` -> 200; list. - `GET /monitors/{id}` -> 200 or 404. - `PUT /monitors/{id}` -> 200 or 404; replace mutable fields and reset status. - `DELETE /monitors/{id}` -> 204 or 404. - `POST /monitors/{id}/check` -> 200 check result, 404 absent monitor, 400 blocked/invalid destination. Transport failures return a recorded `error` result rather than a gateway error. - `GET /monitors/{id}/status` -> 200 current snapshot or 404. - `GET /healthz` is liveness; `GET /readyz` reports readiness and storage mode. Status is `unknown`, `up` (HTTP 200-399), `down` (HTTP 400-599), or `error` (DNS, policy, timeout, or transport failure). Latency is wall-clock monotonic elapsed milliseconds. Updates are committed atomically and only if the monitor still exists and has not changed during the check. FastAPI/Pydantic validation errors use the framework 422 shape. Application errors use `{ "detail": "..." }`. There is no authentication. ## Security and logging Only HTTP(S) URLs with hostnames are accepted. Every destination is DNS-resolved immediately before each request; all answers must be globally routable. Loopback, private, link-local, multicast, reserved, unspecified, and metadata-style non-global addresses are blocked. Redirects are followed manually up to the configured bound and every target is revalidated. Credentials in URLs are forbidden. Structured JSON logs include monitor id, event, status and a redacted URL (`?REDACTED`); query values and URL credentials are never logged. ## Architecture `app/config.py` validates environment settings; `models.py` is the wire/domain schema; `store.py` owns locked state; `security.py` enforces destination policy; `checker.py` performs bounded checks; `main.py` wires routes and exception behavior. Tests mock DNS and outbound HTTP. See README for runtime constraints and commands.