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

2.5 KiB
Raw Blame History

Service contract and architecture

Resource

A monitor has UUID id, name, absolute HTTP(S) url, timestamps, and current_status. Status is initially unknown; an HTTP 200399 result is up, any other HTTP response is down, and timeout/network/security failures set error. Status records include check time and latency; successful HTTP exchanges also include the status code. Secrets or response bodies are never retained.

HTTP API

Method/path Result
POST /monitors Create, 201
GET /monitors List, 200
GET /monitors/{id} Read, 200
PUT /monitors/{id} Replace name/url, 200; resets status if URL changes
DELETE /monitors/{id} Delete, 204
POST /monitors/{id}/check Check now and atomically update status, 200
GET /monitors/{id}/status Current status, 200
GET /healthz Liveness, 200
GET /readyz Readiness, 200 after state/checker initialization

Unknown resources return 404. Invalid requests return 422. Capacity returns 409. Unsafe targets return 400, outbound network failures 502, and timeout 504. Errors use {"error":{"code":"...","message":"..."}}. No authentication is provided; deploy behind an authenticated trusted gateway if exposed.

Check security

Only HTTP(S), host-bearing URLs without userinfo are accepted. A dedicated resolver rejects every non-global address from every DNS answer. Redirects are followed manually, resolved relative to the previous URL, and revalidated before each hop. The production aiohttp connector uses that same resolver, disables DNS caching, and closes connections, so connection resolution cannot bypass filtering. Total timeout and redirect count are bounded. The service does not return response bodies.

Concurrency and lifecycle

MonitorStore owns one dictionary guarded by an asyncio.Lock. Mutations and status publication are atomic. Status publication uses the checked URL as a compare condition: a concurrent URL change cannot publish a stale result and produces a 409. Memory is per process, starts empty, and disappears on restart. A one-worker deployment is required unless the store is replaced.

Logging and structure

Application events are single-line JSON. Logged URLs are transformed to scheme/host/path only; credentials, query, and fragment are removed. Error text from remote systems is not logged. main owns routing/lifespan, store owns state, checker owns outbound security, models owns wire types, config owns environment validation, and logging_config owns JSON/redaction behavior.