# Endpoint Monitor service contract ## Resource and lifecycle A monitor has an immutable UUID, name, HTTP(S) URL, per-check timeout, timestamps, and current status (`unknown`, `up`, or `down`). Status also records the most recent check time, HTTP status, latency, and a sanitized error. New and materially updated monitors are `unknown`. State is process-local and protected by one `asyncio.Lock`; it is lost at restart and is not shared between workers. ## HTTP API | Method | Path | Meaning | |---|---|---| | POST | `/monitors` | Create; `201` | | GET | `/monitors` | List | | GET | `/monitors/{id}` | Retrieve | | PATCH | `/monitors/{id}` | Partial update | | DELETE | `/monitors/{id}` | Delete; `204` | | POST | `/monitors/{id}/check` | Run a check and atomically store its result | | GET | `/monitors/{id}/status` | Current status only | | GET | `/health/live` | Process liveness | | GET | `/health/ready` | Readiness for traffic | A final 200–399 response is `up`; other responses and transport failures are `down`. A transport failure is a completed check and returns `200` with a down result. A policy-rejected target returns `400` after recording a sanitized down result. A monitor changed or deleted while its check is in flight produces `409`/`404`, preventing a stale result from overwriting newer state. Errors use `{"error":{"code":"...","message":"..."}}`; request validation adds `details`. No authentication is provided. ## Outbound security and observability Only HTTP(S), hostnames without credentials, and configured ports are accepted. Every initial and redirect target is resolved immediately before its request; all returned addresses must be globally routable. Loopback, private, link-local, multicast, reserved, and unspecified addresses are blocked. Redirects are followed manually up to a bounded limit. Timeouts and response-body reads are bounded (checks stream no body). Operators should still enforce egress policy at the network layer because application DNS checks cannot eliminate every DNS rebinding/TOCTOU risk. Logs are one-line JSON. URLs are normalized to remove user info, query strings, and fragments; exception text and response bodies are never logged. ## Layout `app/` contains configuration, models, locked storage, SSRF policy, checking, logging, and FastAPI composition. `tests/` exercises API and checker policy. Packaging is in `pyproject.toml`, `Dockerfile`, and `compose.yaml`; exact root `.gitignore` and `.dockerignore` files prevent local/build artifacts from entering source control or build context.