Endpoint Monitor
A typed FastAPI service that stores endpoint monitors in process-local memory and performs secure, on-demand HTTP checks.
Quick start
Requires Python 3.12.
python -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
make check
uvicorn app.main:app --reload --workers 1
Create and check a monitor:
curl -sS -X POST http://localhost:8000/v1/monitors \
-H 'content-type: application/json' \
-d '{"name":"example","url":"https://example.com/health?token=do-not-log"}'
curl -sS -X POST http://localhost:8000/v1/monitors/MONITOR_UUID/checks
curl -sS http://localhost:8000/v1/monitors/MONITOR_UUID/status
Interactive OpenAPI documentation is at /docs. The full route/status/error contract and SSRF model are in docs/service-design.md.
Configuration
All values are startup-validated and use environment prefix MONITOR_:
| Variable | Default | Constraint |
|---|---|---|
MONITOR_APP_NAME |
endpoint-monitor | string |
MONITOR_LOG_LEVEL |
INFO | logging level |
MONITOR_CHECK_TIMEOUT_SECONDS |
5 | >0, <=30 |
MONITOR_MAX_REDIRECTS |
5 | 0–10 |
MONITOR_MAX_RESPONSE_BYTES |
1000000 | 1–10000000 |
Checks resolve every hop and block any hostname with a non-public answer. Redirects are manual and bounded; total elapsed timeout and response size are bounded. Query and fragment data and URL credentials are redacted from structured logs. URL credentials are rejected. Application checks cannot create a perfect DNS-rebinding boundary; use deny-by-default egress networking or a hardened outbound proxy in sensitive deployments.
Tests and quality
make lint # Ruff formatting check and lint
make type # strict mypy
make test # pytest API/unit suite
make check # all three
Tests are directly inspectable under tests/: CRUD/operations and API mapping (test_api.py), lock concurrency and stale compare-and-set updates (test_store.py), timeouts/statuses/DNS and redirect SSRF/log redaction (test_checker.py), and environment validation (test_config.py). Outbound requests use httpx.MockTransport; tests do not access the network.
Container
docker build -t endpoint-monitor:local .
docker run --rm -p 8000:8000 endpoint-monitor:local
# or
docker compose up --build
The image runs as a non-root user, has a liveness healthcheck, and deliberately starts one Uvicorn worker.
Layout
app/ service implementation
checker.py bounded checks, DNS/redirect SSRF policy
config.py validated environment settings
logging.py structured JSON and URL redaction
main.py FastAPI routes and lifecycle
models.py API schemas and status semantics
store.py lock-protected in-memory state and CAS updates
tests/ mocked unit and API tests
docs/service-design.md contract and architecture
docs/verification.md execution record and reproducible commands
Dockerfile, compose.yaml, pyproject.toml, Makefile
Important limitations
All monitors disappear on restart. State is not shared between processes, hosts, or Uvicorn workers, so run exactly one worker. There is no authentication; place the service behind an authenticated gateway if exposed. For durable or horizontally scaled use, replace MonitorStore with a transactional shared datastore and coordinate check ownership.