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
Some checks failed
ci / test (push) Has been cancelled
This commit is contained in:
41
SPEC.md
41
SPEC.md
@@ -1,33 +1,24 @@
|
||||
# Endpoint Monitor Service Contract
|
||||
|
||||
## Architecture
|
||||
## Resource and lifecycle
|
||||
A monitor has an immutable UUID `id`, `name`, HTTP(S) `url`, positive `timeout_seconds`, timestamps, and a current check snapshot. New and updated monitors are `unknown` until checked. State is process-local memory, guarded by one async lock; it disappears on restart and is neither shared nor replicated across workers. Run exactly one worker.
|
||||
|
||||
A single FastAPI process exposes typed REST routes, delegates process-local state to an `asyncio.Lock` protected store, and delegates outbound checks to a checker that validates and pins DNS results independently on every redirect hop. The production fetcher uses `aiohttp` with a per-hop pinned resolver, normal TLS hostname verification, proxy/environment settings disabled, manual redirects, and a total bounded timeout.
|
||||
## HTTP API
|
||||
- `POST /monitors` -> 201; create.
|
||||
- `GET /monitors` -> 200; list.
|
||||
- `GET /monitors/{id}` -> 200 or 404.
|
||||
- `PUT /monitors/{id}` -> 200 or 404; replace mutable fields and reset status.
|
||||
- `DELETE /monitors/{id}` -> 204 or 404.
|
||||
- `POST /monitors/{id}/check` -> 200 check result, 404 absent monitor, 400 blocked/invalid destination. Transport failures return a recorded `error` result rather than a gateway error.
|
||||
- `GET /monitors/{id}/status` -> 200 current snapshot or 404.
|
||||
- `GET /healthz` is liveness; `GET /readyz` reports readiness and storage mode.
|
||||
|
||||
This service intentionally has no authentication. It must therefore only be exposed where that is acceptable. State is volatile, is not shared between workers, and disappears on restart; run exactly one worker.
|
||||
Status is `unknown`, `up` (HTTP 200-399), `down` (HTTP 400-599), or `error` (DNS, policy, timeout, or transport failure). Latency is wall-clock monotonic elapsed milliseconds. Updates are committed atomically and only if the monitor still exists and has not changed during the check.
|
||||
|
||||
## Resource and routes
|
||||
|
||||
A monitor has UUID `id`, `name`, HTTP(S) `url`, creation/update timestamps, integer `revision`, and nullable `current_status`. Status is `unknown`, `up`, or `down`; a check records HTTP status (if any), elapsed milliseconds, check time, and a stable error category/message (if any).
|
||||
|
||||
* `POST /monitors` -> 201
|
||||
* `GET /monitors` -> 200
|
||||
* `GET /monitors/{id}` -> 200 or 404
|
||||
* `PUT /monitors/{id}` -> 200 or 404; status is reset when URL changes
|
||||
* `DELETE /monitors/{id}` -> 204 or 404
|
||||
* `POST /monitors/{id}/check` -> 200 or 404. Network and policy failures are completed checks represented as `down`, not transport errors from this API.
|
||||
* `GET /monitors/{id}/status` -> 200 or 404
|
||||
* `GET /health` -> liveness
|
||||
* `GET /ready` -> readiness and explicit process-local storage mode
|
||||
|
||||
Validation errors use FastAPI's 422 response. Application errors have `{"detail":{"code":...,"message":...}}`. A check updates status atomically only if the monitor still exists at the same revision; its response says whether it was applied, preventing stale in-flight checks from overwriting an edit.
|
||||
FastAPI/Pydantic validation errors use the framework 422 shape. Application errors use `{ "detail": "..." }`. There is no authentication.
|
||||
|
||||
## Security and logging
|
||||
Only HTTP(S) URLs with hostnames are accepted. Every destination is DNS-resolved immediately before each request; all answers must be globally routable. Loopback, private, link-local, multicast, reserved, unspecified, and metadata-style non-global addresses are blocked. Redirects are followed manually up to the configured bound and every target is revalidated. Credentials in URLs are forbidden. Structured JSON logs include monitor id, event, status and a redacted URL (`?REDACTED`); query values and URL credentials are never logged.
|
||||
|
||||
Only HTTP and HTTPS are accepted. Userinfo is forbidden. Before **each** request and redirect, every DNS answer is classified with `ipaddress`; any non-global, private, loopback, link-local, multicast, reserved, or unspecified answer rejects the hop. Literal IPs follow the same rule. The validated addresses are pinned into that hop's connector, preventing a second resolver lookup/DNS rebinding. Redirects are manual and bounded. Total, connect, and read timeouts are bounded. Environment proxies are disabled.
|
||||
|
||||
Logs are one-line JSON. URLs are sanitized to scheme/host/port/path; query, fragment, and userinfo never enter structured fields. Error strings are normalized and never include exception text that could echo a secret-bearing URL.
|
||||
|
||||
## Project layout
|
||||
|
||||
`app/` contains configuration, models, locked storage, SSRF policy, outbound checker, logging, and API assembly. `tests/` exercises API/store/checker policy with fake outbound I/O. `pyproject.toml`, Docker assets, and README provide developer and runtime workflows.
|
||||
## Architecture
|
||||
`app/config.py` validates environment settings; `models.py` is the wire/domain schema; `store.py` owns locked state; `security.py` enforces destination policy; `checker.py` performs bounded checks; `main.py` wires routes and exception behavior. Tests mock DNS and outbound HTTP. See README for runtime constraints and commands.
|
||||
|
||||
Reference in New Issue
Block a user