| title | Architecture |
|---|---|
| description | System architecture and data flow |
The Leaderboard system follows a build-time data aggregation pattern with static site generation.
graph TB
GitHubAPI[GitHub API]
SlackAPI[Slack API]
OtherSources[Other Sources]
GitHubPlugin[GitHub Plugin]
SlackPlugin[Slack Plugin]
CustomPlugin[Custom Plugin]
PluginRunner[Plugin Runner]
LibSQL[(LibSQL Database)]
ContributorsMD[contributors/*.md]
ActivitiesJSONL[activities/*.jsonl]
NextBuild[Next.js Build]
StaticSite[Static Site]
GitHubAPI --> GitHubPlugin
SlackAPI --> SlackPlugin
OtherSources --> CustomPlugin
GitHubPlugin --> PluginRunner
SlackPlugin --> PluginRunner
CustomPlugin --> PluginRunner
ContributorsMD --> PluginRunner
ActivitiesJSONL --> PluginRunner
PluginRunner --> LibSQL
PluginRunner --> ContributorsMD
PluginRunner --> ActivitiesJSONL
LibSQL --> NextBuild
NextBuild --> StaticSite
Purpose: Orchestrate data collection and management
Responsibilities:
- Load configuration from
config.yaml - Fetch and validate plugins from URLs
- Import existing data from data-repo
- Execute plugin setup, scrape, and aggregate methods
- Evaluate config and plugin badge rules
- Export updated data back to data-repo
CLI Commands:
plugin-runner # Run all phases
plugin-runner import # Import data only
plugin-runner setup # Plugin setup only
plugin-runner scrape # Plugin scrape only
plugin-runner aggregate # Aggregation only
plugin-runner evaluate # Badge evaluation only
plugin-runner export # Export data only
plugin-runner --data-dir=/path/to/data # Custom data directoryPurpose: Unified database utilities and plugin type definitions
Technology: LibSQL (SQLite-compatible)
Schema:
contributor- User profiles and metadataactivity_definition- Activity types defined by pluginsactivity- Individual activity recordsglobal_aggregate- Organization-level metricscontributor_aggregate- Per-contributor metricsbadge_definition- Badge types and variantscontributor_badge- Badges earned by contributors
Storage:
- Default location:
${DATA_DIR}/.leaderboard.db - Persists
activity_definitiontable across runs - Temporary storage for
contributorandactivitytables
Provides:
- Database abstraction layer
- Query builders and helpers
- TypeScript type definitions
- Plugin interface specification
- Context object structure
Example Plugin:
import type { Plugin } from "@ohcnetwork/leaderboard-api";
export default {
name: "my-plugin",
version: "1.0.0",
badgeDefinitions: [
// Optional: Define custom badges
],
badgeRules: [
// Optional: Define custom badge evaluation rules
],
async setup(ctx) {
// Define activity types
},
async scrape(ctx) {
// Fetch and store activities
},
async aggregate(ctx) {
// Optional: Compute plugin-specific aggregates
},
} satisfies Plugin;Purpose: Enable AI assistants to query leaderboard data via Model Context Protocol
Features:
- 20+ query tools for comprehensive data access
- Dual transport support (STDIO and HTTP)
- Read-only operations for security
- Batch query operations
- Input validation with Zod schemas
Tools Categories:
- Contributor Tools: Query contributors, get stats, batch operations
- Activity Tools: Query activities, search, timeline analysis
- Leaderboard Tools: Rankings, top contributors, active users
- Badge Tools: Badge definitions, awards, top earners
- Aggregate Tools: Global and contributor-level metrics
Usage:
# STDIO transport (for Claude Desktop)
leaderboard-mcp --data-dir ./data
# HTTP transport
leaderboard-mcp --transport http --port 3001Integration:
- Claude Desktop configuration
- MCP-compatible AI clients
- Natural language queries
See MCP Server Documentation for detailed usage.
Purpose: Generate static website
Features:
- Server-Side Generation (SSG) at build time
- Reads data directly from LibSQL database
- Markdown documentation via Fumadocs
- Customizable themes via CSS
Pages:
/- Home page with overview/leaderboard- Rankings and leaderboards/people- All contributors/[username]- Individual contributor profiles/badges- Badge definitions and achievements/data- Data Explorer (browser-based SQL REPL)/docs- Documentation
-
Import Phase
data-repo/contributors/*.md → LibSQL data-repo/activities/*.jsonl → LibSQL -
Setup Phase
For each plugin: Execute plugin.setup(ctx) → Populate activity_definition table -
Scrape Phase
For each plugin: Execute plugin.scrape(ctx) → Fetch data from API → Insert activities into database -
Aggregation Phase
Calculate standard global aggregates Calculate standard contributor aggregates -
Plugin Aggregation Phase
For each plugin (with aggregate method): Execute plugin.aggregate(ctx) → Compute plugin-specific aggregates -
Badge Evaluation Phase
Evaluate badge rules from config (threshold, streak, growth, composite) For each plugin (with badgeRules): Evaluate plugin badge rules -
Export Phase
LibSQL → data-repo/contributors/*.md LibSQL → data-repo/activities/*.jsonl -
Build Phase
Next.js pre-build setup: → Copy theme overrides → Copy database to public/data.db → Copy custom assets from data-repo → Download & optimize contributor avatars (WebP, 256×256) Next.js reads LibSQL → Generate static pages → Output static site
Contributor profiles store avatar_url pointing to external sources (e.g., GitHub avatars). During the build phase, these are downloaded, converted to WebP, and resized to 256×256 using sharp. The optimized images are saved to public/avatars/{username}.webp and served locally, eliminating external dependencies at page load.
Avatars are cached between builds — only new or missing avatars are downloaded, making subsequent builds fast.
In addition to the pre-rendered static pages, the database file is shipped as a static asset (public/data.db). The Data Explorer page loads this database in the browser via sql.js-httpvfs (SQLite compiled to WebAssembly). A Web Worker makes HTTP range requests to fetch only the database pages needed for each query, enabling ad-hoc SQL queries without any backend.
graph LR
CDN["Static CDN\n(data.db)"] -->|"HTTP Range Requests"| Worker["Web Worker\n(sql.js WASM)"]
Worker -->|"Query Results"| UI["Data Explorer UI"]
UI -->|"SQL Query"| Worker
Format: Markdown with YAML frontmatter
Rationale:
- Human-editable profiles
- Supports rich bio content
- Version control friendly
Location: data-repo/contributors/<username>.md
Format: JSON Lines, one file per contributor
Rationale:
- Efficient for large datasets
- Easy per-user updates
- Fast import/export
Location: data-repo/activities/<username>.jsonl
Format: SQLite table
Rationale:
- Managed by plugins
- No manual editing needed
- Avoids sync issues
Location: data-repo/.leaderboard.db
graph LR
DataRepo[Data Repository]
CI[CI/CD Pipeline]
BuildServer[Build Server]
CDN[Static CDN]
Users[Users]
DataRepo -->|Clone| CI
CI -->|Run Plugin Runner| BuildServer
BuildServer -->|Next.js Build| BuildServer
BuildServer -->|Deploy| CDN
CDN -->|Serve| Users
Steps:
- CI/CD clones data repository
- Runs plugin-runner to update data
- Builds Next.js static site
- Deploys to CDN (Netlify, Vercel, etc.)
- Users access static site from CDN
The system follows 12-Factor App principles:
- Codebase: Single repo, multiple deployments
- Dependencies: Explicitly declared in
package.json - Config: Environment variables and
config.yaml - Backing Services: LibSQL as attachable resource
- Build/Run/Release: Clear separation of phases
- Processes: Stateless static site
- Port Binding: Not applicable (static export)
- Concurrency: Plugin execution parallelizable
- Disposability: Fast startup, clean shutdown
- Dev/Prod Parity: Same build process everywhere
- Logs: Structured logging in plugin-runner
- Admin Processes: Plugin-runner as separate process
- Plugins fetched from configurable URLs
- Basic validation of plugin structure
- Consider using signed plugins in production
- All data stored in your infrastructure
- No external data transmission (except plugin API calls)
- Contributor data fully under your control
- No server-side code execution
- No authentication required
- Can be deployed behind authentication if needed