Inspiration

The same failure keeps happening. A clinic opens a block of vaccination appointments and the page hands the same 9am slot to three people. A venue puts 500 floor tickets on sale and the queue software lets through 520. A sneaker drop crashes, and nobody can tell whether the winners were chosen fairly or were just whoever ran the fastest bot.

Underneath, it is almost always the same problem: counting a shared, scarce resource under heavy concurrency is genuinely hard. Teams reach for Redis locks, queues, or nightly reconciliation jobs, and the guarantee leaks at the seams. We wanted to find out whether a modern, strongly consistent database could make "never oversell, and prove it" the easy path instead of the hard one.

What it does

Singleton is allocation infrastructure. A provider releases a fixed batch of scarce slots to a crowd, and Singleton guarantees three things that are usually bought with heavy infrastructure or a blockchain:

  • Correct. The batch can never oversell, no matter how many people click at the same instant.
  • Fair. Either strict first-come order, or equal odds for everyone inside an entry window. One slot per person, no line-jumping.
  • Verifiable. Every claimant gets a receipt whose rank is re-checkable against a public ledger, and a lottery's entire winner list can be recomputed in the visitor's own browser.

It ships with two allocation modes:

  • First-come: claim a slot, get a receipt that reads "#42 of 500", and see your place on the public, append-only ledger.
  • Windowed lottery: enter any time during a window, then a commit-reveal draw selects winners. Entering in the first second and the last second carry identical odds. The verify page re-runs the whole draw client-side and shows a MATCH banner when the winners reproduce.

It is also a multi-tenant marketplace. Operators (clinics, promoters, brands) self-register, create releases they own, and can manage only their own, while a public landing page lets visitors browse events across nine categories with a search, category, type, availability, date, and location filter rail.

How we built it

The whole design rests on one bet: Amazon Aurora DSQL is strongly consistent, serverless, and active-active multi-region at the same time, so the no-oversell guarantee reduces to one ordinary ACID transaction instead of distributed-lock plumbing.

The claim (first-come). Capacity is split across many counter rows (shards), because a single hot counter collapses under DSQL's optimistic concurrency control. Each claim runs one transaction: a conditional decrement UPDATE release_shards SET remaining = remaining - 1 WHERE ... AND remaining > 0, then an allocation insert. On an OCC conflict (SQLSTATE 40001), we retry the whole transaction with exponential backoff and jitter. Ranks are never stored, they are derived from (claimed_at, id) order, so the ledger cannot drift from reality.

The draw (lottery). A commit-reveal scheme. Before entries open we publish SHA256(seed); after the window we reveal the seed and score every entry:

$$\text{score}(e) = \mathrm{SHA256}(\text{seed} \mathbin{\Vert} \texttt{":"} \mathbin{\Vert} e)$$

The lowest capacity scores win, ties broken by entry id. Because the scoring is a pure function of public inputs, anyone can recompute the winners with Web Crypto in their browser, which is exactly what the verify page does.

The stack. Next.js 15 (App Router, Node runtime) on Vercel, pinned to the iad1 region so functions sit next to the us-east-1 cluster. The official @aws/aurora-dsql-node-postgres-connector mints a fresh IAM auth token per connection, so there is no database password anywhere. The allocation receipt card and the landing hero were scaffolded with v0 and imported into a hand-built data layer, which is the "Zero Stack" workflow this hackathon is about: v0 for the surface, Aurora DSQL for the foundation, Vercel for the deploy.

Multi-tenancy. Providers carry a secret api key; resolveActor maps every request to a platform admin (master token) or an operator (their key), and authorizeReleaseMutation enforces that an operator can only delete or draw on releases they own. All of it is additive: seven migrations, none of which alters an existing column's meaning.

Challenges we ran into

  • DSQL is not full Postgres. No foreign keys, sequences, or triggers; async index builds; one DDL per transaction. We wrote a custom migration runner that honors those rules and polls pg_index.indisvalid before continuing.
  • The hot-row problem. Our first counter was a single row. It melted under OCC. Sharding the counter (32 rows by default) was the single most load-bearing decision in the system.
  • Every path around the algorithm. Three adversarial review passes (one after each major feature) found twelve real bugs, including an idempotency-key hijack, a public-ledger identity leak, and, in the multi-tenancy work, an admin token that rode the URL query string into access logs. All twelve are fixed and documented.
  • WAN reality. Dropped connections surface without a SQLSTATE, so our error detector matches messages as well as codes, and only read-only statements ever blanket-retry.

Accomplishments that we're proud of

  • A stress run fired 10,000 concurrent claims at a 200-slot release on the live cluster: exactly 200 allocated, zero oversells, ranks 1..200 contiguous.
  • 5,000 lottery entries drawn and re-derived byte for byte from the revealed seed.
  • It is genuinely shippable: live on Vercel, IAM-authenticated, multi-tenant, with unit, live-integration, and Playwright end-to-end suites all green.

What we learned

Strong consistency changes what is worth building. The entire correctness story collapses to one transaction only because Aurora DSQL refuses to lie about order. We also re-learned that "the algorithm is correct" and "every path around the algorithm is correct" are very different claims, and that structured adversarial review is the cheapest way to close the gap.

What's next for Singleton

Wiring real payments and per-seat billing, production bot defense at the clean hooks we left on the claim and enter paths, provisioning the second peered region (the connection layer already supports failover), and claimant notifications.

Built With

Share this project:

Updates