---
title: LocalElo
description: Competitive ranking for BJJ academies
section: craft
tags: [project, full-stack]
genre: reference
stability: stable
lastUpdated: 2026-04-19
url: https://fardiniqbal.com/docs/craft/projects/localelo
---


Glicko-2 rating platform for competitive communities. Built for BJJ
academies, esports teams, chess clubs, racket sport ladders, and any
group that runs its own ranking. Each organization gets an isolated
space, its own leaderboards, its own members, and its own rating
history.

## What it is [#what-it-is]

A multi-tenant ranking system. Organizations create leaderboards.
Members log matches. Ratings update via Glicko-2 on match completion.
Admins approve members, invalidate matches, and mint invite links.
Rating history is charted per leaderboard, per member.

## By the numbers [#by-the-numbers]

| Metric                            | Value                       |
| --------------------------------- | --------------------------- |
| Unit tests (invite system)        | 35                          |
| Integration tests (tRPC routers)  | 36                          |
| Total tests passing               | 71                          |
| Starting rating / RD / volatility | 1500 / 350 / 0.06           |
| Glicko-2 state per player         | rating + RD + volatility    |
| XP league tiers                   | 6 (bronze to champion)      |
| Membership statuses               | pending / approved / banned |
| Membership roles                  | member / admin / owner      |

## Architecture [#architecture]

```
src/
  app/
    (auth)/              Clerk sign-in / sign-up
    (dashboard)/         Authenticated app shell
      dashboard/         User home, org list, org creation
      org/[slug]/        Org home + leaderboard detail
      rankings/          Cross-org rankings
    api/
      trpc/[trpc]/       tRPC HTTP handler
      webhooks/clerk/    Clerk -> users table sync
    join/[slug]/         Public org join
    invite/[code]/       Invite-code redemption
  lib/                   tRPC client, Glicko-2 math
  middleware.ts          Clerk route protection
server/api/
  trpc.ts                Context, procedures, auth guards
  routers/               organizations, leaderboards, matches,
                         invites, ratings, activity, streaks,
                         achievements, goals, memberships
db/
  schema.ts              users, orgs, memberships, invites,
                         leaderboards, ratings, matches,
                         matchParticipants, ratingHistory,
                         streaks, XP, leagues, achievements,
                         challenges
```

Core entities: `users` synced from Clerk by `clerkId`; `organizations`
with `slug` and `visibility`; `memberships` pairing `(organizationId,
userId)` with per-org `username`; `leaderboards` scoped to an org;
`ratings` holding the Glicko-2 triple plus W/L/D counters scoped to
`(leaderboardId, membershipId)`; `matches` with nullable
`winnerMembershipId` for draws and soft-delete fields;
`matchParticipants` with before/after rating snapshots; `ratingHistory`
as immutable points for charting.

## Key features [#key-features]

* **Multi-org isolation.** Separate leaderboards, members, ratings, and
  history per org. A member can hold distinct ratings across orgs and
  across leaderboards within an org.
* **Glicko-2 ratings.** Tracks rating, deviation, and volatility. New
  players gain certainty as they play. Inactivity inflates RD.
* **Member approval workflow.** Public orgs auto-join. Private orgs
  queue pending members for admin approval.
* **Invite links.** Signed codes with optional `maxUses` and
  `expiresAt`. Join by slug or by invite code.
* **Match logging.** Members log their own results. Admins log matches
  between any two players. Ratings update atomically inside the match
  transaction; before/after snapshots are stored on every
  `MatchParticipant`.
* **Match invalidation with rollback.** Soft-delete a match and every
  downstream rating mutation is reversed via stored `ratingBefore`
  snapshots. `deletedAt` / `deletedBy` audit trail retained. No
  orphaned history rows.
* **Analytics.** Rating history charts (Recharts), win-rate trends,
  recent-opponent surfaces, head-to-head stats between any two players.
* **Gamification.** Daily streaks with freeze tokens and weekend
  amulets, weekly XP leagues across six tiers, achievement badges,
  rating milestones, member-to-member challenges.

## What makes it stand out [#what-makes-it-stand-out]

* **Rating math isolated.** Glicko-2 lives in a single module of pure
  functions with no I/O. Rating updates happen inside the match-
  creation transaction so partial writes cannot corrupt the ladder.
* **True rollback on invalidation.** Not a tombstone — the history tail
  is replayed and every participant's rating is reversed to its stored
  snapshot.
* **Per-org usernames.** A user can be `fardin` in one gym and `fi` in
  another. Uniqueness enforced on `(organizationId, username)`, not
  globally.
* **Webhook-driven user sync.** Clerk `user.created` / `updated` /
  `deleted` events write directly to `users`; the app never reads Clerk
  at request time.
* **71 tests, all passing.** Coverage targets the rating engine, invite
  lifecycle, and every tRPC router mutation. Fixtures build an
  in-memory org + leaderboard + members via a factory module.
* **Vercel cron.** Handles weekly league resets and daily streak
  reminders.

## Stack [#stack]

| Layer      | Technology                                  |
| ---------- | ------------------------------------------- |
| Framework  | Next.js 15 (App Router)                     |
| Language   | TypeScript                                  |
| Database   | PostgreSQL                                  |
| ORM        | Drizzle                                     |
| API        | tRPC (end-to-end type-safe)                 |
| Auth       | Clerk (webhook-synced `users` table)        |
| UI         | Tailwind CSS 4, Radix primitives, shadcn/ui |
| Charts     | Recharts                                    |
| Validation | Zod                                         |
| Tests      | Vitest                                      |
| Deploy     | Vercel                                      |

## Links [#links]

* **Source:** [https://github.com/FardinIqbal/localelo](https://github.com/FardinIqbal/localelo)
