Set and forget uptime monitoring
Beautiful status pages and real-time monitoring for your services. Built with Next.js, Convex, and Clerk.
- Node.js 18.17.0 or higher
- Bun runtime
- Convex account (free at convex.dev)
- Clerk account (free at clerk.com)
bun install# Login to Convex
bunx convex login
# Initialize Convex project (creates deployment)
bunx convex devThis will:
- Create a new Convex deployment
- Generate
convex/_generated/types - Start the Convex dev server
- Print your deployment URL
Copy the deployment URL - you'll need it for .env.local
- Go to dashboard.clerk.com
- Create a new application
- Get your API keys from the "API Keys" section
- Get your JWT issuer domain from "JWT Templates" → Default → Issuer
Copy the example env file and fill in your values:
cp .env.example .env.localRequired variables for local development:
CONVEX_DEPLOYMENTandNEXT_PUBLIC_CONVEX_URL(from step 2)NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY, andCLERK_JWT_ISSUER_DOMAIN(from step 3)
See .env.example for all available options including Stripe, Sentry, and PostHog.
# Runs Next.js + Convex + Stripe webhook forwarder concurrently
bun devNote: This runs all processes in one terminal with colored output:
- Cyan: Next.js dev server
- Magenta: Convex dev server
- Yellow: Stripe webhook forwarder (if configured)
If you prefer to run them separately:
# Terminal 1: Convex dev server
bun dev:convex
# Terminal 2: Next.js dev server
bun dev:nextNavigate to http://localhost:3000
- ✅ HTTP Monitoring: Check any URL every 1-5 minutes
- ✅ Status Pages: Public status pages at
/s/[project-slug] - ✅ Is It Down Tool: Public checker at
/is-it-downwith verdict + probe evidence - ✅ Real-time Dashboard: Live updates via Convex subscriptions
- ✅ Incident Tracking: Automatic incident creation after 3 consecutive failures
- ✅ Uptime Stats: 30-day uptime percentage and response time trends
- ✅ Mobile Responsive: Touch-optimized interface for all devices
- Landing Page:
/- Marketing page with feature overview - Dashboard:
/dashboard- View and manage all monitors - Status Pages:
/s/[slug]- Public status pages (ISR cached, 60s revalidation) - Is It Down:
/is-it-downand/is-it-down/[hostname]- Public diagnostic pages - Monitor Management: Create, edit, delete monitors with real-time updates
- Cron-based: Runs every minute via Convex crons
- Smart Failure Detection: 3 consecutive failures before incident
- Status Tracking: Up / Degraded / Down states
- Response Time: Measures and stores response times
- Data Retention: Automatic cleanup of checks older than 30 days
- Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS 4
- Backend: Convex (serverless database + functions + cron)
- Auth: Clerk (authentication & user management)
- Monitoring: HTTP checks via Convex actions
- Payments: Stripe (subscriptions)
- Error Tracking: Sentry
- Analytics: PostHog
- Email: Resend (optional, for incident notifications)
- Fonts: Geist Sans & Geist Mono
- Icons: Lucide React
- Charts: Recharts
heartbeat/
├── app/ # Next.js App Router pages
│ ├── dashboard/ # Authenticated dashboard
│ ├── s/[slug]/ # Public status pages (ISR)
│ ├── layout.tsx # Root layout with fonts & providers
│ └── page.tsx # Landing page
├── components/ # React components
│ ├── StatusIndicator.tsx
│ ├── MonitorCard.tsx
│ ├── StatusHeader.tsx
│ ├── UptimeChart.tsx
│ ├── IncidentTimeline.tsx
│ ├── AddMonitorForm.tsx
│ ├── MonitorSettingsModal.tsx
│ └── DashboardMonitorCard.tsx
├── convex/ # Convex backend
│ ├── schema.ts # Database schema
│ ├── monitors.ts # Monitor CRUD operations
│ ├── checks.ts # Check history queries
│ ├── incidents.ts # Incident queries
│ ├── monitoring.ts # Core monitoring engine
│ ├── crons.ts # Scheduled jobs
│ └── auth.config.ts # Clerk integration
└── public/ # Static assets
- Monitor configuration (name, URL, interval, etc.)
- User ownership via
userId - Project grouping via
projectSlug - Status tracking (
consecutiveFailures,lastCheckAt,lastResponseTime)
- Historical check results
- Status (up/down/degraded)
- Response time measurements
- Error messages for failures
- Incident records (investigating/identified/resolved)
- Start and end timestamps
- Associated with specific monitors
# Development
bun dev # Start Next.js + Convex (+ Stripe forwarder)
bun dev:next # Start Next.js dev server only
bun dev:convex # Start Convex dev server only
# Type checking
bun type-check # Run TypeScript compiler
# Linting
bun lint # Run ESLint
bun run lint:openapi # Validate OpenAPI v1 contract
# Building
bun build # Build for production
bun start # Start Next.js + Convex concurrently
bun start:next # Start Next.js production server only
# Convex
bunx convex deploy # Deploy to production
bunx convex dashboard # Open Convex dashboardGET /api/is-it-down?target=github.com- Returns JSON verdict + probe evidence from Heartbeat's public checker pipeline.
For detailed development testing instructions, see TESTING.md.
- Sign in at
/dashboard(Clerk will handle registration) - Click "Add Monitor" button
- Fill in:
- Name: "My API"
- URL:
https://httpstat.us/200(always returns 200 OK) - Project Slug:
my-project(auto-generated from name) - Check Interval: 60 seconds
- Click "Create Monitor"
Navigate to /s/my-project to see your public status page.
The monitoring cron runs every minute. You can:
- Test success: Use
https://httpstat.us/200 - Test failure: Use
https://httpstat.us/500 - Test flaky: Use
https://httpstat.us/random/200,500
After 3 consecutive failures, an incident will be created automatically.
- Click on a monitor card to expand and see recent checks
- Click the settings icon to edit monitor configuration
- Click the play icon to manually trigger a check (TODO)
- Cron Job: Runs every 60 seconds via
convex/crons.ts - Check Due Monitors: Queries monitors where
lastCheckAt + interval < now - Execute Checks: Parallel HTTP requests with timeout
- Record Results: Store check result in
checkstable - Update Status: Increment
consecutiveFailuresor reset to 0 - Incident Management:
- 3+ failures → Create incident (if none exists)
- 0 failures → Resolve open incident
- Up: 0 consecutive failures (green)
- Degraded: 1-2 consecutive failures (yellow)
- Down: 3+ consecutive failures (red)
- Checks: Kept for 30 days, then auto-deleted
- Incidents: Kept forever (historical record)
- Monitors: Kept until manually deleted
Run bunx convex dev to generate types in convex/_generated/
- Check
.env.localhas correct Clerk keys - Verify
CLERK_JWT_ISSUER_DOMAINmatches your Clerk dashboard - Restart Next.js dev server after env changes
- Check Convex dashboard shows cron executions
- Verify monitors are
enabled: true - Check monitor
lastCheckAtis being updated - Look for errors in Convex logs
Ensure monitors exist with that projectSlug in database
See TODO.md Phase 7 for production deployment steps (Vercel + Convex production deployment).
Private project - not licensed for redistribution.