Files
crucible-agent-build-fastap…/docs/SERVICE_DESIGN.md

32 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Service contract and architecture
## Resource
A monitor has an immutable UUID `id`, `name`, HTTP(S) `url`, timestamps, and current status. `status` is `unknown` until checked, `up` for a final HTTP 200399 response, `down` for a final HTTP 400599 response, and `error` when no accepted HTTP response is obtained. Check metadata comprises `checked_at`, non-negative `latency_ms`, optional `http_status`, and a bounded, non-sensitive `error` category.
## API
All JSON is UTF-8. Routes are under `/api/v1` except operational probes.
* `POST /api/v1/monitors` -> 201 and monitor; duplicate names are allowed.
* `GET /api/v1/monitors` -> 200 list sorted by creation time and id.
* `GET /api/v1/monitors/{id}` -> 200 or 404.
* `PUT /api/v1/monitors/{id}` -> 200 or 404. Updating name/URL resets check state to `unknown`.
* `DELETE /api/v1/monitors/{id}` -> 204 or 404.
* `POST /api/v1/monitors/{id}/check` -> 200 and atomically persisted check result, or 404 if absent/deleted during the check.
* `GET /api/v1/monitors/{id}/status` -> 200 current status projection or 404.
* `GET /healthz` -> liveness 200; `GET /readyz` -> readiness 200 after app initialization.
FastAPI's validation errors use 422. Application errors use `{"detail":"..."}` and do not expose network exception text. There is intentionally no authentication.
## Checker and SSRF policy
Only HTTP(S), non-credentialed URLs are accepted. Before every request and after every redirect, the host is resolved and every returned address must be globally routable. Loopback, private, link-local, multicast, reserved, unspecified, and non-global addresses are rejected. Redirects are followed manually up to the configured bound. Timeouts and response-body streaming are bounded. This validation substantially reduces SSRF risk, but DNS validation and connection are separate operations in the standard HTTP stack; production deployments should additionally enforce an egress proxy/firewall to eliminate DNS-rebinding TOCTOU risk.
Checks use a monotonic timer. Store mutation occurs under one asyncio lock. A delete that races with a check wins and the result is not resurrected.
## Lifecycle and deployment
`MonitorStore` is process-local and starts empty on every process start. It is concurrency-safe within one event loop, not shared across workers, and has no persistence. Run exactly one worker. Readiness describes initialization, not durability or external dependency health.
Configuration is environment-driven and validated at startup. Logs are one-line JSON. Logged URLs have credentials, query, and fragment removed; only controlled error categories are logged.
## Project layout
`app/models.py` defines the contract; `store.py` owns state; `security.py` validates targets; `checker.py` performs checks; `main.py` wires HTTP routes and lifecycle. Tests inject DNS and HTTP transports so outbound calls are deterministic.