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,
timestamps, and a current status. State is process-local memory guarded by one `asyncio.Lock`;
it starts empty on every process start, is not shared across workers, and is lost on restart.
Run exactly one worker unless an external store is added. Returned models are frozen so state
cannot be mutated outside the store lock.
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 contract
## HTTP API
| Method | Path | Success | Purpose |
|---|---|---:|---|
| POST | `/monitors` | 201 | Create |
| GET | `/monitors` | 200 | List |
| GET | `/monitors/{uuid}` | 200 | Retrieve |
| PATCH | `/monitors/{uuid}` | 200 | Partial update |
| DELETE | `/monitors/{uuid}` | 204 | Delete |
| POST | `/monitors/{uuid}/check` | 200 | Run and atomically persist a check |
| GET | `/monitors/{uuid}/status` | 200 | Retrieve current status |
| GET | `/health/live` | 200 | Process liveness |
| GET | `/health/ready` | 200 | In-memory service readiness |
| 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 resources return `{"error":{"code":"not_found","message":"monitor not found"}}`
with 404. Invalid UUIDs or bodies return a generic, input-redacting `invalid_request` with 422.
There is intentionally no authentication.
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 meanings: `never_checked` has no observation; `up` exactly matches `expected_status`;
`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.
## Status semantics
## 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
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.
## Check security and consistency
Structured JSON check logs include IDs, status, latency, and scheme/host/port/path only. Query,
fragment, credentials, response bodies, and raw exception text are excluded.
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/api.py` owns HTTP semantics, `models.py` schemas, `store.py` locked state, `checker.py` check
orchestration, `security.py` outbound policy, and `config.py` environment settings. Tests isolate
outbound traffic with `httpx.MockTransport`.
`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`.