ratio'd is a dashboard built by students, for students. it's lowkey private, failproof, and designed to replace the stress of traditional academic portals.
ratio'd is a PWA dashboard for SRM students. the official academia portal is painfully slow, mobile unfriendly. ratio'd wraps it, scrapes the HTML, parses it into clean JSON, and serves it in an interface that doesn't make you want to close the tab immediately.
built by students. used by students. no data stored on our end.
| feature | what it does |
|---|---|
| sub-second sync | background refresh keeps data fresh without interrupting you |
| offline first | schedule, marks, and attendance cached locally — works without wifi |
| device-local encryption | credentials encrypted with a non-exportable AES-256 key stored in your browser's secure key store |
| dual themes | minimalist or brutalist |
| attendance predictor | calculates exactly how many classes you can bunk and still survive |
| marks target | reverse engineers what you need in finals to hit your target grade |
| live alerts | class reminders, attendance dips, new marks — all as push notifications |
| private notes | per subject notes, stored locally, never synced anywhere |
| session recovery | auto handles expired sessions and concurrent login conflicts |
students
│
▼
cloudflare pages ← static frontend (PWA)
│
▼
cloudflare worker ← proxy: HMAC signing, load balancing
│
├──▶ server 1 ─┐
├──▶ server 2 ─┼─ fastapi backends
└──▶ server 3 ─┘
│
▼
srm academia ← the official portal
the worker sits between the frontend and all backends. it adds a cryptographic signature to every request so the backends can verify it actually came from ratio'd and not some random person.
| layer | tech |
|---|---|
| frontend | Next.js 14, TypeScript, Tailwind CSS, Framer Motion |
| backend | Python, FastAPI, httpx, BeautifulSoup4 |
| proxy | Cloudflare Workers |
| hosting | Cloudflare Pages + Render |
| pwa | Workbox via @ducanh2912/next-pwa |
| auth | Web Crypto API, AES-GCM, IndexedDB |
ratio-d/
├── src/
│ ├── app/ # next.js app router pages
│ ├── components/ # ui components (themes: minimalist, brutalist)
│ ├── context/ # app state, theme, layout context
│ ├── hooks/ # custom react hooks
│ ├── utils/ # logic: attendance, marks, encryption, api
│ └── types/ # typescript types
├── backend/
│ ├── core/ # session handling, academia client, config
│ ├── services/ # parsers: attendance, marks, timetable, profile
│ ├── models/ # pydantic schemas
│ └── main.py # fastapi app, routes, middleware
├── worker/
│ ├── index.ts # cloudflare worker: proxy + HMAC signing
│ └── wrangler.toml # worker config
└── public/ # static assets, fonts, pwa icons
to develop locally, you can use the development bypass. the app automatically detects if you are running on localhost:
- backend: set
ENV=developmentinbackend/.env. this disables HMAC verification. - frontend: set
NEXT_PUBLIC_BACKEND_URLS=http://localhost:8000in.env.local.
the app will now hit your local backend directly without needing a cloudflare worker.
git clone https://github.com/projectakshith/ratio-d
cd ratio-dcd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000backend env (backend/.env):
ENV=developmentnpm install
npm run devfrontend env (.env.local):
NEXT_PUBLIC_BACKEND_URLS=http://localhost:8000for production, a cloudflare worker is required to handle HMAC signing and load balancing.
deploy the worker with your HMAC_SECRET and BACKEND_URLS.
| layer | variable | value |
|---|---|---|
| frontend | NEXT_PUBLIC_WORKER_URL |
your worker's address |
| backend | ENV |
production |
| backend | HMAC_SECRET |
must match worker secret |
how the encryption works
when you log in, ratio'd generates an AES-256-GCM key using window.crypto.subtle.generateKey with extractable: false. this means:
- the key is stored as a
CryptoKeyobject in IndexedDB - it cannot be exported or read by any JavaScript, including our own
- the browser's engine enforces this at the native level
your credentials are encrypted with this key. the ciphertext goes in localStorage. without the key object (which never leaves your device's secure store), the ciphertext is useless.
if you clear your browser data, the key is gone — and so is everything ratio'd stored. that's the kill switch.
how the HMAC signing works
every request from the worker to a backend carries an X-Ratio-Sig header:
X-Ratio-Sig: t=1234567890,v1=<hmac-sha256>
the signature is HMAC-SHA256(timestamp + "." + sha256(body), secret).
the backend verifies it and checks the timestamp is within ±5 minutes. requests without a valid signature are rejected with 403. the secret lives only in worker environment variables — it never touches the browser bundle.
this is a student project so keep it chill. if you find a bug or want to add something:
- fork it
- branch off
dev—git checkout -b your-feature - open a PR against
dev, notmain
Warning
don't commit .env files, don't hardcode secrets, don't push to main directly.
ratio'd is not affiliated with SRM in any way. we don't own the portal, we don't store your data, we just make it less painful to look at. use it at your own risk, gng.
⭐ star if you loved this hehe uwu