Files
crucible-agent-build-fastap…/docs/service-design.md

47 lines
3.0 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 design and contract
## Architecture
A single FastAPI process owns a `MonitorStore` guarded by an `asyncio.Lock`. Routes call the store and an `EndpointChecker`; the checker validates every initial/redirect URL, resolves every hostname, applies a total bounded timeout, and returns a typed result. No authentication is provided. State is intentionally neither durable nor shared.
## Resource
A monitor has UUID `id`, unique `name`, HTTP(S) `url`, timestamps, monotonically increasing `revision`, and latest status fields. Create accepts name/url. Update is PUT-like for supplied fields. URL changes clear prior status.
Status values:
- `unknown`: never checked or target changed
- `up`: final HTTP response is 200399
- `down`: final HTTP response is 400599
- `error`: DNS, policy, timeout, redirect, or transport failure
A check records UTC attempt time, elapsed milliseconds, final redacted URL, optional HTTP code, and a bounded non-secret detail. Results update the monitor under the store lock. A deletion racing with a check wins: the result is not resurrected and the check route returns 404.
## Routes
- `POST /v1/monitors` → 201
- `GET /v1/monitors` → 200
- `GET /v1/monitors/{id}` → 200
- `PATCH /v1/monitors/{id}` → 200
- `DELETE /v1/monitors/{id}` → 204
- `POST /v1/monitors/{id}/check` → 200 result; 400 policy rejection
- `GET /v1/monitors/{id}/status` → 200 status snapshot
- `GET /healthz` → liveness
- `GET /readyz` → process/store readiness
Errors use FastAPI's stable `{"detail": ...}` envelope: validation 422, not found 404, duplicate/capacity conflict 409, blocked check 400. Transport failures are check outcomes rather than API failures.
## SSRF policy
Only `http` and `https`, explicit hostnames, and no user-info are accepted. Every DNS answer must be globally routable according to Python `ipaddress`; loopback, private, link-local, multicast, reserved, unspecified, and documentation ranges are rejected. The same validation is repeated for each redirect, redirects are followed manually, and redirect count and total request time are bounded. URL query and fragment data are omitted from logs and persisted final URLs.
DNS validation followed by a separate client connection has an unavoidable DNS-rebinding time-of-check/time-of-use window in this compact implementation. Production environments should additionally enforce an egress proxy/firewall that only permits public destinations or use a transport that pins validated addresses while preserving TLS SNI. The application-level checks remain defense in depth, not the sole network boundary.
## Logging
Logs are one-line JSON. Check events contain monitor ID, outcome, latency, and a redacted URL only. User-info, query strings, fragments, and response bodies are never logged. Error details are normalized and bounded.
## Lifecycle
The store is created with the app and reports ready while available. A restart loses state. Exactly one Uvicorn worker is required; horizontal scaling requires replacing the store with shared durable storage and distributed check coordination.