Files
crucible-agent-build-fastap…/SPEC.md

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.