# Endpoint Monitor service contract ## Resources and lifecycle A monitor has an immutable UUID, `name`, HTTP(S) `url`, timestamps, and a current check snapshot. New and URL-updated monitors are `unknown`. State is process-local memory, protected by one async lock, and is lost on restart. Multiple workers do not share state. No authentication is provided; deploy behind an authenticated gateway. ## HTTP API | Method and path | Success | Notes | |---|---:|---| | `POST /monitors` | 201 | Create; duplicate names are allowed | | `GET /monitors` | 200 | Stable creation-order list | | `GET /monitors/{id}` | 200 | Retrieve | | `PUT /monitors/{id}` | 200 | Full update | | `DELETE /monitors/{id}` | 204 | Delete | | `POST /monitors/{id}/check` | 200 | Run one bounded check and atomically publish it | | `GET /monitors/{id}/status` | 200 | Current snapshot; no network call | | `GET /health/live` | 200 | Process is serving HTTP | | `GET /health/ready` | 200 | Configuration and in-memory store are available | Missing monitor IDs return `404 {"detail":"monitor not found"}`. Validation failures use FastAPI's 422 response. A check transport failure is a successful invocation and returns a snapshot with `error`; an unsafe target returns `blocked`. These are not API 5xx responses because the service completed the requested check. ## Status semantics `unknown` means never checked or URL changed. `up` means final status 200-399 after safe redirects. `down` means final status 400-599. `error` means timeout, DNS failure, or HTTP transport failure. `blocked` means SSRF policy rejected a host/address or redirect. Snapshots contain completion time, latency in milliseconds, optional HTTP status, and a bounded non-sensitive error code. ## Check security and consistency Only HTTP(S), no URL credentials, and a non-empty hostname are accepted. Every hop is resolved before I/O; every resolved address must be globally routable. Redirects are followed manually and revalidated, with a configured maximum. Timeouts and redirect counts are bounded. The store uses a revision compare-and-set so a result from an old URL cannot overwrite a concurrent update. This DNS preflight mitigates ordinary SSRF; deployments requiring protection from DNS rebinding should also enforce egress policy at the network layer. Logs are JSON and include monitor ID, outcome, latency, and a URL with credentials, query, and fragment removed. Raw exception text and redirect locations are never logged. ## Layout `app/` contains settings, models, locked store, SSRF validation, checker, logging, and FastAPI composition. `tests/` contains API/unit security tests. Packaging is in `pyproject.toml`, `Dockerfile`, and `compose.yaml`; operational evidence is in `VERIFICATION.md`.