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 / validate (push) Has been cancelled
Some checks failed
ci / validate (push) Has been cancelled
This commit is contained in:
@@ -1,30 +1,33 @@
|
||||
# Endpoint Monitor Service Contract
|
||||
# Service contract and architecture
|
||||
|
||||
## Resource and lifecycle
|
||||
## Resource
|
||||
|
||||
A monitor has an immutable UUID `id`, a `name`, an HTTP(S) `url`, and timestamps. Its current status starts as `unknown` and becomes `up` only when an on-demand check receives a 2xx or 3xx (after following redirects); all other HTTP responses and transport failures are `down`. Status records include check time, latency, HTTP status when available, and a redacted error.
|
||||
A monitor has UUID `id`, `name`, absolute HTTP(S) `url`, timestamps, and `current_status`. Status is initially `unknown`; an HTTP 200–399 result is `up`, any other HTTP response is `down`, and timeout/network/security failures set `error`. Status records include check time and latency; successful HTTP exchanges also include the status code. Secrets or response bodies are never retained.
|
||||
|
||||
State is process-local and in memory. It is lost at restart and is neither shared nor replicated across workers; production deployment must therefore use one worker. Store operations and status replacement are protected by one `asyncio.Lock`. A check updates the record only if it still exists.
|
||||
## HTTP API
|
||||
|
||||
## API
|
||||
| Method/path | Result |
|
||||
|---|---|
|
||||
| `POST /monitors` | Create, 201 |
|
||||
| `GET /monitors` | List, 200 |
|
||||
| `GET /monitors/{id}` | Read, 200 |
|
||||
| `PUT /monitors/{id}` | Replace name/url, 200; resets status if URL changes |
|
||||
| `DELETE /monitors/{id}` | Delete, 204 |
|
||||
| `POST /monitors/{id}/check` | Check now and atomically update status, 200 |
|
||||
| `GET /monitors/{id}/status` | Current status, 200 |
|
||||
| `GET /healthz` | Liveness, 200 |
|
||||
| `GET /readyz` | Readiness, 200 after state/checker initialization |
|
||||
|
||||
- `POST /monitors` -> 201, create a monitor.
|
||||
- `GET /monitors` -> 200, list monitors.
|
||||
- `GET /monitors/{id}` -> 200 or 404.
|
||||
- `PATCH /monitors/{id}` -> 200 or 404; omitted fields are unchanged.
|
||||
- `DELETE /monitors/{id}` -> 204 or 404.
|
||||
- `POST /monitors/{id}/check` -> 200 check result, 400 for a security-policy rejection, or 404.
|
||||
- `GET /monitors/{id}/status` -> 200 current status or 404.
|
||||
- `GET /healthz` is liveness; `GET /readyz` confirms the process store and checker are initialized.
|
||||
Unknown resources return 404. Invalid requests return 422. Capacity returns 409. Unsafe targets return 400, outbound network failures 502, and timeout 504. Errors use `{"error":{"code":"...","message":"..."}}`. No authentication is provided; deploy behind an authenticated trusted gateway if exposed.
|
||||
|
||||
Errors have the shape `{"error":{"code":"...","message":"..."}}`; validation errors additionally carry `details`. There is no authentication.
|
||||
## Check security
|
||||
|
||||
## Outbound security and operations
|
||||
Only HTTP(S), host-bearing URLs without userinfo are accepted. A dedicated resolver rejects every non-global address from every DNS answer. Redirects are followed manually, resolved relative to the previous URL, and revalidated before each hop. The production aiohttp connector uses that same resolver, disables DNS caching, and closes connections, so connection resolution cannot bypass filtering. Total timeout and redirect count are bounded. The service does not return response bodies.
|
||||
|
||||
Checks accept only HTTP and HTTPS, reject URL credentials, resolve every initial and redirect target, and reject a target if any resolved address is loopback, private, link-local, multicast, reserved, unspecified, or otherwise non-global. Redirects are followed manually up to the configured bound so each hop is checked. Requests and connection pools have bounded timeouts. This DNS validation prevents ordinary private-address and redirect SSRF; like resolver-then-connect designs generally, it cannot completely eliminate DNS rebinding TOCTOU without a transport that pins the validated address.
|
||||
## Concurrency and lifecycle
|
||||
|
||||
Logs are JSON objects and include monitor ID, event, status, latency, and a URL with query and fragment removed. Credentials and query values are never logged. Environment settings are prefixed `MONITOR_` and validated at startup.
|
||||
`MonitorStore` owns one dictionary guarded by an `asyncio.Lock`. Mutations and status publication are atomic. Status publication uses the checked URL as a compare condition: a concurrent URL change cannot publish a stale result and produces a 409. Memory is per process, starts empty, and disappears on restart. A one-worker deployment is required unless the store is replaced.
|
||||
|
||||
## Project layout
|
||||
## Logging and structure
|
||||
|
||||
`src/monitor_service` contains settings, models, store, checker, logging, and API composition. `tests` contains unit and API tests. Packaging, container files, validation commands, and operating guidance live at repository root.
|
||||
Application events are single-line JSON. Logged URLs are transformed to scheme/host/path only; credentials, query, and fragment are removed. Error text from remote systems is not logged. `main` owns routing/lifespan, `store` owns state, `checker` owns outbound security, `models` owns wire types, `config` owns environment validation, and `logging_config` owns JSON/redaction behavior.
|
||||
|
||||
Reference in New Issue
Block a user