Overview • Why Kronos • Architecture • What's Implemented • What's Next • Tech Stack • Installation • Contributing • License
Kronos is a modular-monolith web application that functions as a developer productivity guardian for GitHub. It monitors repositories that users explicitly opt into tracking — called Krons — collects activity metrics via webhooks & scheduled tasks, and leverages an LLM to generate insights and visualizations. Kronos helps developers spot patterns, identify bottlenecks, and understand how they actually code over time.
In the age of AI, it is easy for developers to hop between side projects endlessly — starting fast, losing momentum, and never understanding why. Kronos is built around two ideas:
Intentionality — you choose which projects you track. Adding a Kron is a deliberate act: this is a project I am serious about.
Self-awareness — over time, Kronos builds a picture of how you code. When do you ship? Where do you slow down? What modules do you keep touching? The data already exists in your commits. Kronos surfaces it.
Kronos is organized into three layers:
-
Login — GitHub OAuth2 authenticates the user. Users repo email is scoped and stored. Refresh tokens are stored in Redis with a TTL and mirrored in MongoDB.
-
Add a Kron — When a user adds a repository as a Kron, two things happen atomically: a webhook is registered on GitHub pointing to Kronos, and a reference is stored in MongoDB. No Kron without a webhook. No webhook without a Kron.
-
Push event fires — GitHub sends a signed webhook payload to Kronos. The Change Detection system verifies the HMAC-SHA256 signature, then enriches the raw commit data with file-level diffs via the GitHub API — data that GitHub's webhook payload omits by design.
-
Change Collection — A cron job (every 4 hours) harvests the enriched commit data and forwards it to the Analytical Engine.
-
Analytical Engine — Processes the commit data, sends it to Gemini 2 for AI-generated insights, and persists the analysis result to MongoDB with a timestamp.
-
Notifications — Users receive periodic summaries and post-analysis alerts when insights are ready.
| Decision | Rationale |
|---|---|
| Redis for repo list caching + ETag invalidation | Avoid hammering GitHub API on every request |
| MongoDB virtuals + populate for Kron references | Store references, not snapshots — avoids data duplication |
| Transactional Change-Detection System (Kron + webhook registration) | Prevent orphaned webhooks or Krons with no watcher |
| HMAC-SHA256 webhook signature verification | Reject spoofed payloads before any processing |
| Constant-time signature comparison | Prevent timing attacks on webhook verification |
| Raw body preservation for webhook route | express.json() destroys bytes needed for HMAC verification |
| Transient storing for commit data | Raw diffs are not worth persisting — only the analysis result survives |
| Cron-based Change Collection | Decouples event ingestion from processing |
- GitHub OAuth2 sign-in and sign-up flow
- Refresh tokens stored in Redis (with TTL), MongoDB, and as a client-side cookie
- Dedicated
/validate-tokenroute — checks if access token (JWT) is still valid; if not, checks the refresh token from the cookie against Redis and MongoDB before deciding to revoke or renew - Redis TTL as the source of truth for session expiry — when the TTL expires, the cookie becomes meaningless and access is revoked entirely
- Access token stored in Redux state on the frontend — protected routes check Redux state, only public routes are accessible without it
- Axios interceptor auto-regenerates access tokens silently assisted by a failed request queue — concurrent requests that fail while a refresh is in progress are queued and retried, not lost
- Protected and public routes with loading states
- Full CRUD for repository list and Kron list
- Repo list fetched from GitHub, cached in Redis, and invalidated using GitHub's ETag header — no unnecessary API calls
- Kron references stored in MongoDB using virtuals and
populate— stores references to repos, not snapshots, avoiding data duplication
- Transactional Kron registration: when a user adds a Kron, a GitHub webhook is registered and a MongoDB reference is created atomically — if either fails, both are rolled back. No orphaned webhooks. No Krons without a watcher.
- Same transactional guarantee on deletion — webhook is removed from GitHub and MongoDB reference is cleaned up programmatically
- Raw body preservation: a custom
verifycallback onexpress.json()saves the raw request buffer toreq.rawBodyspecifically for the webhook route — before Express parsing destroys the bytes needed for HMAC verification - Webhook signature verification middleware: reads
X-Hub-Signature-256from the GitHub request header, computes an HMAC-SHA256 hash of the raw body usingWEBHOOK_SECRET, does a length check, then usescrypto.timingSafeEqual()for constant-time comparison — preventing both spoofed payloads and timing attacks - Commit data enrichment via secondary GitHub API call (
getRicherCommitData) — GitHub's webhook payload omits file-level diff data by design; Kronos fetches it explicitly via Octokit, addingfilename,additions,deletions, andchangesper file - Enriched commit data held in memory (transient) — Change Collection picks it up from here for processing
storeCommitBatchstores enriched commits per user in Redis underkron:{userId}:commitsas a JSON list- Cron job runs every 6 hours (
0 */6 * * *), scans allkron:*:commitskeys, skips empty keys, batches commits per user - Jobs pushed to BullMQ (
analysis-queue) with 3 retry attempts and exponential backoff starting at 2 seconds - BullMQ Worker picks up each job, processes the commit batch, clears the Redis key after completion
- Failed jobs retained in BullMQ for inspection — not silently dropped
- Analytical Engine integration stubbed and ready —
analyzeWithGeminiandsaveInsightscalls are next
- Diff analysis: files changed, lines added/removed, commit patterns, module focus
- Gemini 2 integration for AI-generated text insights
- Analysis results persisted to MongoDB with timestamps
- Post-analysis alerts when insights are ready
- Periodic summaries (daily rollup + midday sprint summary)
| Layer | Technology | Why |
|---|---|---|
| Frontend | React + TypeScript + Vite | Fast dev experience, type safety |
| Backend | Node.js + Express | Event-driven, fits webhook architecture |
| Database | MongoDB | Flexible schema for evolving commit data structures |
| Cache / Queue | Redis | Repo list caching, refresh token storage, transient commit data |
| Job Queue | BullMQ | Reliable job processing with retries, exponential backoff, and worker management |
| Auth | GitHub OAuth2 | Users already live on GitHub — zero friction login |
| Enrichment | Octokit (GitHub API) | File-level diff data not included in webhook payloads |
| AI | Gemini 2 | LLM for generating productivity insights from preprocessed commit data |
git clone https://github.com/theChibuikem/kronos.git
cd kronos# Backend
cd backend
npm install
# Frontend
cd ../frontend
npm install- Create a free cluster at MongoDB Atlas
- Create a database named
kronos - Copy your connection string
Backend .env
# ========================
# APP ENVIRONMENT
# ========================
MODE=local
# ========================
# DATABASE
# ========================
MONGO_URI=your_mongoDB_uri
# ========================
# AUTH / TOKENS
# ========================
ACCESS_TOKEN_SECRET=some_random_string
REFRESH_TOKEN_SECRET=some_random_string
# ========================
# GITHUB INTEGRATION
# ========================
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
WEBHOOK_SECRET=some_random_string
# ========================
# REDIS (CACHING / QUEUE)
# ========================
REDIS_HOST=your_redis_host
REDIS_PORT=your_redis_port
REDIS_PASSWORD=your_redis_password
# ========================
# URL CONFIGURATION
# ========================
# Backend
LOCAL_BACKEND_URL=http://localhost:5000
REMOTE_BACKEND_URL=https://kronos-dev.onrender.com
# Frontend
LOCAL_FRONTEND_URL=http://localhost:5173
REMOTE_FRONTEND_URL=https://kronos-fe.onrender.comFrontend .env
VITE_MODE=local
VITE_LOCAL_BACKEND_URL=http://localhost:5000
VITE_REMOTE_BACKEND_URL=https://kronos-dev.onrender.com
VITE_LOCAL_FRONTEND_URL=http://localhost:5173
VITE_REMOTE_FRONTEND_URL=https://kronos.comGenerate some random string with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Backend
cd backend
npm start
# Frontend
cd frontend
npm run devVisit http://localhost:5173
Make sure your GitHub OAuth app callback URL is configured correctly. See GitHub's OAuth docs.
Contributing are now open see — contributors.md
This project is licensed under the CC BY-NC 4.0 License.
Coming soon...
Built by David Chukwuemeka

