A small-scale take on UptimeRobot / Datadog synthetic monitoring. Register an API, and distributed workers check it on a schedule, store every result, open an incident after N consecutive failures, resolve it after M consecutive successes, and notify you by email, webhook or Discord.
| Monitor details | Incident timeline |
|---|---|
![]() |
![]() |
| Landing page | Public status page |
|---|---|
![]() |
![]() |
Screenshots follow your GitHub theme; the app itself follows your system's light or dark setting.
- Monitors: URL, method (GET/POST/PUT/PATCH/DELETE), headers, JSON/text body, expected status, timeout, interval (30 s–24 h), response-time threshold, failure/recovery thresholds, optional immediate retries on network errors
- Pluggable response assertions (
body_contains,body_not_contains,json_field) - Error classification: timeout, DNS, connect, invalid response, unexpected status, HTTP error, assertion failed, slow, blocked target
- Incident engine: pure state machine, DB-enforced deduplication, full timeline; pausing or re-pointing a monitor closes its open incident and restarts its state (no false recovery alert)
- Notifications with retry/backoff: email (SMTP), generic webhook, Discord
- Dashboard: a one-line health headline ("1 monitor is down" / all up), 24 h uptime and active incidents, a last-hour response-time lane per monitor with failures marked in red, and a sortable monitor list (UP / DOWN / PAUSED, recent-check strip, 24 h uptime, response, last check); refreshes every 10 s
- Monitor page: 1 h/24 h/7 d/30 d charts for response time, time-weighted availability and errors by cause, each with a "Show as table" view; avg/P50/P95/P99; 24 h/7 d/30 d uptime and downtime; every recent check. Monitors carry a short catalog number (
PLS-004) - Incidents list and incident page: root cause, consecutive failures/passes, and a timeline of every state change, check and notification
- Landing page at
/with the quick-start commands and an animated replay of an outage being caught; the app lives at/dashboard - Light and dark themes, following the system setting
- Account settings: change email/password (re-authenticated; a password change signs out other sessions) and delete account; settings organised into Account / Notifications / Status page / API keys
- Public status page, API keys (hashed, shown once, revocable)
- Security: Argon2, HttpOnly cookies, tenant isolation, SSRF protection with DNS-rebinding defence, secrets (monitor headers, webhook URLs) encrypted at rest with key rotation, rate limiting, security headers, request limits
- Observability: structured JSON logs, Prometheus metrics,
/health,/ready; Pulse monitors itself: scheduler-tick and queue-depth metrics, ready-made Prometheus alert rules, and an optional dead-man's-switch ping
See docs/ARCHITECTURE.md: service layout, scheduling, incident state machine, failure handling, and the design rationale (why Redis/Celery/Postgres, how incidents are deduplicated, how SSRF/tenancy/retries work).
Next.js ─► FastAPI ─► PostgreSQL Celery Beat ─► Redis queue ─► Celery workers ─► your APIs
└─► incident engine ─► notifications
The API never runs checks; Beat only enqueues; workers do the work.
Next.js 16 · React 19 · TypeScript · Tailwind 4 · Recharts │ Python 3.12 · FastAPI · Pydantic · SQLAlchemy 2 · Alembic │ PostgreSQL 16 · Redis 7 · Celery │ Docker Compose · GitHub Actions │ pytest · Vitest + Testing Library · Playwright
git clone https://github.com/Parth-Vasave/pulse.git && cd pulse
cp .env.example .env
docker compose up --build # or: make dev| URL | What |
|---|---|
| http://localhost:3000 | Landing page; Log in with the seeded demo@example.com / demo-password-123 (or register) to reach the dashboard at /dashboard |
| http://localhost:8000/docs | API docs |
| http://localhost:8025 | Mailpit: catches alert emails |
| http://localhost:9000 | Demo target API with a fail/restore switch |
Startup order is handled by Compose: Postgres + Redis (healthchecks) → migrate (Alembic + seed) → API, worker, scheduler → frontend.
- Open http://localhost:3000, click Log in and sign in as the demo user. Charts are pre-populated from seeded history.
- Add monitor → URL
http://demo-service:9000/switch, check every 30 s, open incident after 2, resolve after 1. - Add an email channel in Settings (use Send test and watch Mailpit).
- Open http://localhost:9000 → Make it fail. Within ~1 minute the monitor turns DOWN, an incident opens, and an email arrives.
- Restore. After the next passing check the incident resolves automatically; open it for the timeline.
Other demo endpoints: /healthy (200), /error (500), /slow (delayed), /flaky (random).
Requires Python 3.12+, Node 22+, PostgreSQL, Redis.
createdb monitor && createdb monitor_test
cd backend && python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
export DATABASE_URL=postgresql+psycopg://$USER@localhost/monitor PYTHONPATH=.:..
.venv/bin/alembic upgrade head
.venv/bin/uvicorn app.main:app --reload # API :8000
.venv/bin/celery -A worker.celery_app worker -P threads -Q checks,notifications # worker
.venv/bin/celery -A worker.celery_app beat # scheduler
cd ../frontend && npm ci && npm run dev # UI :3000 (proxies /api → :8000)To monitor something on your own machine, set SSRF_ALLOWED_HOSTS=localhost (dev only; see Security).
All via environment (see .env.example):
| Variable | Purpose |
|---|---|
DATABASE_URL, REDIS_URL |
connections |
SECRET_KEY |
JWT signing key, ≥32 chars. Generate your own |
ENCRYPTION_KEY |
Fernet key(s) encrypting monitor headers and webhook URLs in Postgres. Required outside development; see Security |
HEARTBEAT_URL, HEARTBEAT_INTERVAL_SECONDS |
optional dead-man's-switch ping, so an external service alerts you if Pulse stops scheduling |
ACCESS_TOKEN_MINUTES, COOKIE_SECURE |
session lifetime; set COOKIE_SECURE=true over HTTPS |
CORS_ORIGINS, INTERNAL_API_URL |
allowed origins; where Next.js proxies /api |
SSRF_ALLOWED_HOSTS |
dev only hostnames exempt from private-IP blocking |
SMTP_* |
email delivery |
RATE_LIMIT_PER_MINUTE, AUTH_RATE_LIMIT_PER_MINUTE, TRUST_PROXY_HEADERS |
rate limiting |
CHECK_RETENTION_DAYS |
result retention (default 35) |
SEED_DEMO_DATA |
seed on first start |
make migrate # docker
cd backend && alembic upgrade head # local
alembic revision --autogenerate -m "describe change"CI applies, rolls back and re-applies all migrations.
make test # backend + frontend unit/integration
make e2e # Playwright against the running stack
make lint # ruff, mypy, eslint, tsc- Backend (302 tests): unit (state machine, SSRF, assertions, retry, cache fail-open and single-flight), time-weighted metrics on exact timelines (DST, gaps, pauses), API (auth, validation, CRUD, tenant isolation, rate limiting, headers, size limits, status page, API keys), integration against real Postgres/Redis and a real local HTTP target (checker, incident lifecycle, 4-thread race → one incident, scheduler incl. broker outage, scheduler self-monitoring and the heartbeat ping, encryption at rest incl. key rotation and the data migration, notification retry/failure, Redis-outage recovery), and an API-level end-to-end lifecycle test.
- Frontend (46 tests): formatting, form parsing and notices, session expiry (401 handling, safe redirects, stale-data notice), account settings, and the status badge, heartbeat, banner, timeline, dialog, button, toggle, segmented control, copy button and toast components.
- Browser E2E (2 specs): an account flow (change password signs out a second browser, change email, delete account) and the lifecycle flow: register → create monitor → fail demo API → incident → restore → resolved, driven through the UI.
Backend tests need Postgres database monitor_test and Redis (TEST_DATABASE_URL, REDIS_URL override the defaults).
Full detail in docs/SECURITY.md. Highlights: Argon2id; HttpOnly SameSite cookie or hashed API keys; every query scoped to the owner (foreign IDs → 404); SSRF: blocked schemes, localhost, private/loopback/link-local/metadata/CGNAT ranges, IPv6-mapped forms, validated at creation and at connect time against the exact IP used (DNS-rebinding safe), redirects never followed; rate limiting, CORS allow-list, size limits, security headers, log redaction.
See docs/API.md or /docs on the running API.
backend/ FastAPI app (api, core, models, schemas, services, repositories), Alembic, tests
worker/ Celery app, check/notify tasks, HTTP checker, scheduler tasks
frontend/ Next.js app, components, hooks, unit + e2e tests
demo-service/ target API with a fail switch
infrastructure/ Dockerfiles, Prometheus config and alert rules
docs/ ARCHITECTURE, SECURITY, API, DEPLOYMENT, screenshots
Tagging vX.Y.Z publishes images to GHCR and a GitHub release; see docs/DEPLOYMENT.md and docker-compose.prod.yml.
Python dependencies are pinned with hashes: edit backend/requirements.in (and requirements-dev.in), run make lock, and commit the generated requirements.txt / requirements-dev.txt. Docker and CI install only those exact, hash-verified versions, so a build next month is the same build as today. Node dependencies are pinned by frontend/package-lock.json (npm ci). Dependabot opens weekly update PRs, and CI runs pip-audit, npm audit and a Trivy image scan (HIGH/CRITICAL) and enforces a backend coverage floor of 88%. Infrastructure images use fixed tags (postgres:16, redis:7, mailpit:v1.31.4, prometheus:v3.15.0).
MIT (c) 2026 Parth Vasave
Verified on an Apple-silicon (ARM64) machine: docker compose up from empty volumes reaches all-healthy in about 25 s (images cached) and seeds the demo data; the full alert loop runs unattended (failure → incident → alert email in Mailpit → recovery → resolved → recovery email); both Playwright specs pass against the Docker stack; and stopping Redis or Postgres for 30–40 s leaves every container running, serves clean 503s, and resumes checking by itself. The GitHub Actions workflow runs on every push and pull request (lint, types, tests, migrations, audits, image scans, browser E2E). No email verification, password reset or MFA yet. Single region.
Future: multi-region probes · Kubernetes · OpenTelemetry tracing · Grafana dashboards · maintenance windows · multi-step synthetic workflows · browser checks · teams/RBAC/SSO · alert routing and escalation policies · anomaly detection · AI incident summaries · JWT revocation, encrypting monitor request bodies at rest.



