34 lines
2.8 KiB
Markdown
34 lines
2.8 KiB
Markdown
# Endpoint Monitor Service Contract
|
|
|
|
## Architecture
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## 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.
|
|
|
|
## Security and logging
|
|
|
|
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.
|