# Endpoint Monitor service contract ## Scope and lifecycle This is a single-process FastAPI service with an asynchronous, lock-protected in-memory repository. Data is lost at process exit and is neither shared nor replicated between workers; production deployments must therefore use exactly one worker. There is no authentication. ## Resource and routes A monitor has `id` (UUID), `name`, HTTP(S) `url`, creation/update timestamps, and a current check status. Status starts as `unknown`. `up` means the last response was HTTP 200–399, `down` means HTTP 400–599, and `error` means no usable HTTP response (timeout, network/protocol failure, redirect-policy failure, or blocked target). | Method | Route | Meaning | |---|---|---| | POST | `/v1/monitors` | Create; 201 | | GET | `/v1/monitors` | List; 200 | | GET | `/v1/monitors/{id}` | Retrieve; 200 | | PATCH | `/v1/monitors/{id}` | Update name and/or URL; 200 | | DELETE | `/v1/monitors/{id}` | Delete; 204 | | POST | `/v1/monitors/{id}/checks` | Run one bounded check; 200, or 400 for an unsafe target | | GET | `/v1/monitors/{id}/status` | Retrieve current status; 200 | | GET | `/healthz` | Liveness; 200 | | GET | `/readyz` | Readiness of process-local dependencies; 200 | Missing resources return `404` with `{"detail":{"code":"monitor_not_found","message":"Monitor not found"}}`. Validation uses FastAPI's 422 response. Unsafe initial targets and unsafe redirect destinations return 400 with code `unsafe_target`; the monitor records `error`. Endpoint timeouts and transport failures are check outcomes, not service failures, and return a typed `error` result. ## Check and concurrency semantics Checks use configured total timeout, response-byte and redirect limits. Every initial target and redirect is HTTP(S), has no user information, and has all DNS answers checked; loopback, private, link-local, multicast, reserved, unspecified, and otherwise non-global addresses are blocked. Redirects are followed manually and revalidated. DNS validation materially reduces SSRF exposure, but this application-level approach cannot fully remove DNS time-of-check/time-of-use rebinding risk in the underlying client; a production egress proxy/firewall is required for a hard network boundary. Repository operations are serialized with `asyncio.Lock`. A check captures the monitor revision before I/O. Its status update is a compare-and-set and is discarded if the monitor was updated or deleted while I/O was in flight, preventing stale results from overwriting newer state. Latency is monotonic elapsed time for the complete redirect chain. ## Logging and configuration Logs are one-line JSON. URLs are emitted without user information and with query/fragment replaced by redaction markers. Bodies and request headers are never logged. Environment settings use the `MONITOR_` prefix and are validated at startup; see README. Secrets in URL query strings are not persisted in logs, although the monitor resource itself necessarily stores the configured URL. ## Project structure `app/` contains settings, schemas, lock-protected storage, checker/security policy, logging, and API assembly. `tests/` contains API and focused unit tests. `docs/` contains this contract and an honest verification record. Runtime/package files live at repository root.