decomposer: generate deliverable files for Define the service contract and project architecture for the FastAPI endpoint monitoring service.; Implement the typed monitor CRUD API and concurrency-safe in-memory state according to the service design.; Implement secure on-demand endpoint checks with status updates, latency measurement, robust error handling, and redacted structured logs.; Add operational API endpoints and environment-driven runtime configuration to the monitoring service.; Create automated tests for the monitoring service.; Package the service with Docker and developer documentation.; Validate the complete project.
Some checks failed
ci / test (push) Has been cancelled

This commit is contained in:
2026-08-09 15:57:29 +00:00
parent 8c65363d26
commit b3817f2cab

View File

@@ -1,52 +1,56 @@
# Service contract and architecture # Endpoint Monitor service contract
## Resource and lifecycle ## Resources and lifecycle
A monitor has a server-generated UUID, name, absolute HTTP(S) URL, expected HTTP status, A monitor has an immutable UUID, `name`, HTTP(S) `url`, timestamps, and a current
timestamps, and a current status. State is process-local memory guarded by one `asyncio.Lock`; check snapshot. New and URL-updated monitors are `unknown`. State is process-local
it starts empty on every process start, is not shared across workers, and is lost on restart. memory, protected by one async lock, and is lost on restart. Multiple workers do not
Run exactly one worker unless an external store is added. Returned models are frozen so state share state. No authentication is provided; deploy behind an authenticated gateway.
cannot be mutated outside the store lock.
## HTTP contract ## HTTP API
| Method | Path | Success | Purpose | | Method and path | Success | Notes |
|---|---|---:|---| |---|---:|---|
| POST | `/monitors` | 201 | Create | | `POST /monitors` | 201 | Create; duplicate names are allowed |
| GET | `/monitors` | 200 | List | | `GET /monitors` | 200 | Stable creation-order list |
| GET | `/monitors/{uuid}` | 200 | Retrieve | | `GET /monitors/{id}` | 200 | Retrieve |
| PATCH | `/monitors/{uuid}` | 200 | Partial update | | `PUT /monitors/{id}` | 200 | Full update |
| DELETE | `/monitors/{uuid}` | 204 | Delete | | `DELETE /monitors/{id}` | 204 | Delete |
| POST | `/monitors/{uuid}/check` | 200 | Run and atomically persist a check | | `POST /monitors/{id}/check` | 200 | Run one bounded check and atomically publish it |
| GET | `/monitors/{uuid}/status` | 200 | Retrieve current status | | `GET /monitors/{id}/status` | 200 | Current snapshot; no network call |
| GET | `/health/live` | 200 | Process liveness | | `GET /health/live` | 200 | Process is serving HTTP |
| GET | `/health/ready` | 200 | In-memory service readiness | | `GET /health/ready` | 200 | Configuration and in-memory store are available |
Missing resources return `{"error":{"code":"not_found","message":"monitor not found"}}` Missing monitor IDs return `404 {"detail":"monitor not found"}`. Validation failures
with 404. Invalid UUIDs or bodies return a generic, input-redacting `invalid_request` with 422. use FastAPI's 422 response. A check transport failure is a successful invocation and
There is intentionally no authentication. returns a snapshot with `error`; an unsafe target returns `blocked`. These are not API
5xx responses because the service completed the requested check.
Status meanings: `never_checked` has no observation; `up` exactly matches `expected_status`; ## Status semantics
`down` is a completed nonmatching HTTP response; `error` is timeout/protocol/network failure;
`blocked` means outbound policy rejected the destination. Check failures are check results (200),
not API transport failures. A monitor deleted while its check runs yields 404 rather than being
recreated.
## Outbound security and logging `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.
Only HTTP(S), credential-free URLs are accepted by the checker. Every initial and redirected ## Check security and consistency
hop is resolved immediately before request; all returned addresses must be globally routable.
Redirects are handled manually and bounded, requests have bounded timeouts, bodies are streamed,
and exceptions map to stable non-sensitive messages. This blocks direct, DNS, and redirect
attempts to loopback/private/link-local/reserved addresses. Like most application-level DNS
checks, there remains a small resolver-to-connect rebinding race; production high-assurance
deployments should additionally enforce an egress proxy/firewall.
Structured JSON check logs include IDs, status, latency, and scheme/host/port/path only. Query, Only HTTP(S), no URL credentials, and a non-empty hostname are accepted. Every hop is
fragment, credentials, response bodies, and raw exception text are excluded. 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 ## Layout
`app/api.py` owns HTTP semantics, `models.py` schemas, `store.py` locked state, `checker.py` check `app/` contains settings, models, locked store, SSRF validation, checker, logging, and
orchestration, `security.py` outbound policy, and `config.py` environment settings. Tests isolate FastAPI composition. `tests/` contains API/unit security tests. Packaging is in
outbound traffic with `httpx.MockTransport`. `pyproject.toml`, `Dockerfile`, and `compose.yaml`; operational evidence is in
`VERIFICATION.md`.