Inspiration
We've spent years on the operations side of software, and on-call is where the pressure is most acute: it's 3 AM, something is down, and the entire response depends on one tool reliably waking up the right person. So we kept coming back to an uncomfortable question — what happens when the alerting tool goes down during the exact incident it's supposed to page us about?
It's not hypothetical. A large outage takes down a region, and if your alerting platform lives in that same region, it goes silent right when you need it most. A fire alarm that stops working during the fire. The second thing that always bothered us is subtler: under load, on-call tools get their own state wrong. A responder taps Acknowledge at the same instant the escalation timer fires, and now two people are awake for one incident — or the ack is lost entirely. Small bugs, but they erode trust in the one tool that has to be flawless.
When we saw that Aurora DSQL gives you a multi-region, active-active SQL database with strong consistency and zero replication lag, both problems suddenly had the same answer. That was the spark for Klaxon.
What it does
Klaxon is a lean on-call and incident-management platform. The flow is simple:
- An alert arrives via webhook (compatible with sources like Datadog, Grafana, or Alertmanager).
- Klaxon opens an incident and looks up who is on shift.
- It notifies the on-call responder.
- If no one acknowledges within the configured timeout, it escalates to the next tier, then the team lead.
- The moment someone acknowledges, escalation stops — and the full incident timeline is recorded.
Schedules, rotations, escalation policies, and an auditable timeline — the core mechanics of an on-call platform, built on a data foundation that doesn't fall over when your infrastructure does.
How we built it
- Frontend & API: A Next.js app (App Router) deployed on Vercel — UI, route handlers, and server actions in one deployment. We used v0 to scaffold the interface quickly so we could spend our time on the data layer, which is where the project actually lives.
- Database: Amazon Aurora DSQL, connected over IAM authentication with short-lived tokens (no stored passwords). UUID primary keys throughout, as recommended for distributed workloads, to spread writes across the key range.
- Escalation engine: A Vercel Cron job ticks every minute, finds incidents whose escalation deadline has passed, and advances them to the next tier — all logic in the application layer, since DSQL doesn't run triggers or stored procedures.
- Ingestion: An idempotent webhook endpoint that deduplicates alerts by a
dedup_key, so a noisy source can't open the same incident twice. - Concurrency safety: Every mutation is wrapped in retry logic to handle Aurora DSQL's optimistic concurrency conflicts cleanly (more on that below).
Challenges we ran into
1. "PostgreSQL-compatible" is not PostgreSQL. Aurora DSQL speaks the Postgres wire protocol, but a lot of familiar features simply aren't there: no foreign keys, no triggers, no PL/pgSQL, no SAVEPOINT, and DDL runs asynchronously (CREATE INDEX ASYNC). We had to push referential integrity and all business logic up into the application, run the engine in AUTOCOMMIT mode, and stop thinking of the database as a place to hide logic. It's a genuinely different design model, not a drop-in.
2. Optimistic concurrency instead of locks. This was the heart of the project. DSQL doesn't block on locks — it lets transactions run and detects conflicts at commit, returning SQLSTATE 40001 when two transactions touch the same row. That's exactly our ack-vs-escalation race: the responder's acknowledgement and the cron tick collide on the same incident row, one commits first, and the other has to retry. The fix was to make every operation idempotent and retry the loser with exponential backoff and jitter:
$$ t_n = \text{rand}\left(0,\; \min\left(t_{\max},\; t_{\text{base}} \cdot 2^{\,n}\right)\right) $$
On retry, the escalation re-reads the row, sees the incident is already acknowledged, and becomes a no-op. The result is the property we actually wanted: you cannot escalate an incident that was just acknowledged — even when the ack and the escalation happen in different regions.
3. Serverless meets a database connection. Vercel functions are short-lived; database connections want to be long-lived. We had to generate IAM auth tokens per invocation and keep sessions short and reusable rather than leaning on a traditional connection pool.
4. Designing contention away. Because OCC turns hot rows into retry storms, we leaned on UUID keys and kept the incident row the only point of intentional contention — the one place where serializing two writers is the correct behavior, not an accident.
Accomplishments that we're proud of
A live demo where we trigger the acknowledge/escalation race on purpose — and the system resolves it to exactly one outcome with a clean, consistent timeline, across regions. And the headline property itself: an alerting tool that keeps running when the region it's monitoring goes down.
What we learned
- Distributed SQL forces you to design for the distributed model up front. You can't retrofit a monolithic-database app onto DSQL; referential integrity and concurrency stop being the database's job and become yours.
- OCC inverts how you reason about correctness. Instead of preventing conflicts with locks, you make operations safe to repeat. Idempotency went from a nice-to-have to the central discipline of the whole codebase.
- Choosing the right database is a real engineering decision. On-call is a low-write, low-contention workload where correctness under failure matters more than raw throughput — close to an ideal fit for Aurora DSQL. The same OCC model that makes Klaxon correct would have been a poor fit for a high-contention, hot-key workload. Knowing when not to use a tool is as important as knowing how.
What's next for Klaxon
Real notification channels (SMS, voice, Slack), rotation rules beyond materialized shifts, multi-tenant workspaces, and SLA / MTTR analytics built on top of the incident timeline.
Built With
- amazon-aurora-dsql
- aws-iam
- javascript
- next.js
- node-postgres
- node.js
- node.typescript
- react
- resend
- shadcn/ui
- sql
- tailwind-css
- v0
- vercel
- vercel-cron
Log in or sign up for Devpost to join the conversation.