Pranav Kalambi · Bangalore, India

Things I've built, and what was hard about them.

I lead platform engineering and SRE for India at a global financial institution. Outside that, I design, build, deploy and operate production systems end to end — this page is what came out of that, and the engineering decisions behind each one.

How to read this. Every system below is live and running on infrastructure I own and operate. Some are open; some need a sign-in because they hold data or cost money to run — those are marked, and there are screenshots so you don't have to sign up to see them. Where something is a simulation rather than a production integration, it says so.

01  Platform engineering

CloudPro

Live

A federated multi-cloud provisioning control plane — Azure + AWS

CloudPro provisioning console showing a live nine-stage pipeline

The platform team's paved road, end to end. An application owner answers a 52-question intake across 13 sections; a policy engine gates it; then tenancy vending, Terraform, JFrog and Harness run the provisioning — streamed live, stage by stage, with an architecture diagram derived from the same code that generates the infrastructure.

The engineering

Ports and adapters, done so that going live is configuration rather than a code change. Every external system — cloud, Terraform Enterprise, Harness, JFrog — sits behind an interface, and a single registry picks the implementation per port from one environment variable. The orchestrator names no mock anywhere. An earlier version imported mocks directly at eight call sites, which meant "swap the adapter" was really "edit the workflow" — that was the bug worth fixing.

Resolution is lazy per port, so a bad value fails the provisioning run visibly in the console stream instead of killing the whole app at import.

The policy engine is real code, not a mock — twelve rules, and a FAIL genuinely blocks provisioning. Exactly one non-mocked decision component, chosen deliberately: it's the part where the interesting logic lives.

No LLM calls anywhere in it. Policy is rule functions, config generation is deterministic, the adapters are state machines. None of that should be probabilistic, and knowing where not to reach for a model is part of the design.

Next.js 16TypeScriptDrizzle + SQLite Server-Sent EventsTerraformHarnessJFrogAuth.js

02  Multi-tenant SaaS

TaxPro

Live

A compliance suite for Indian chartered accountants — Income Tax, GST, TDS

TaxPro landing page showing Income Tax, GST and TDS modules

Income-tax returns with old-versus-new regime side by side, GST filings with GSTR-2B reconciliation that flags input credit at risk, TDS registers and Form 16 generation, a compliance calendar, a per-client document vault and a workflow board. Every CA firm on it is a separate tenant.

The engineering

Tenant isolation is enforced by the build, not by discipline. CI gates on tests, typecheck, lint and a dedicated isolation audit — a cross-tenant read has to fail the pipeline, because "we'll remember to filter by tenant" is not a security model.

The audit itself is the part I'd want to talk about. My first harness reported nine isolation breaches. All nine were false positives — the harness was bypassing the application's own filtering, so it was testing the database rather than the product. Adding two controls, a row-count check and a random-UUID probe, disproved every one of them. The rebuilt harness found three real defects, which were fixed: 51 requests correctly refused, none returning the wrong status.

That's the lesson I take from it — a test that can't distinguish "the app is safe" from "my test is wrong" will happily report either.

Next.js 15TypeScriptPostgreSQL DrizzleAuth.js421 testsCI-gated isolation audit

03  Data platform & research

DRISHTI / TrendRiders

Live

A market-research platform — screener, backtester, options analytics, paper trading

The TrendRiders platform landing page with a live market ticker

A stock screener with a custom expression language, a backtester that shares the screener's evaluation engine, options-flow analytics over the NSE derivatives bhavcopy, sector rotation, and paper-trading books for Indian and US equities. Scheduled scans deliver email alerts intraday and after the close.

The engineering

Screening is evaluated on wide frames rather than per symbol. The panel is dates × ~750 symbols, so a 50-day moving average is one rolling mean across the entire universe instead of 750 separate passes — a full scan lands in roughly 80 milliseconds. The same frame then powers the backtest, so a strategy is tested by exactly the code that screens it.

User expressions are parsed, not evaluated. A whitelisted AST walker with arity and literal checks — no eval, and deliberately no LLM anywhere in the execution path. Language models are useful for helping someone write a query; they have no business being inside it.

Most of the work is getting the data honest. Exchange bhavcopy is unadjusted, so corporate actions are reconstructed from the exchange's own previous-close field. Two sources are spliced to reach back further, validated at r = 0.972 across 484,640 symbol-days before being trusted.

Every result is measured against a matched control, with block bootstrap over dates. That discipline is why several of my own ideas are documented as not working — which is the point of building the harness.

PythonFastAPIpandas / NumPy React + ViteSQLiteAPSchedulerResend

04  Native application development

PDFLab

macOS · not web

An Acrobat-class PDF editor for macOS

Page operations, native text editing, ink and drawn signatures, OCR to searchable text, cryptographic signatures with a visible signature box, and password add/remove backed by the system keychain. Swift, no Electron, 81 automated checks.

The engineering

Apple's PDFKit cannot edit page text, and has no cryptographic-signature API at all. Both were built from scratch: text editing by rewriting the PDF content stream with real paragraph reflow, and signing via CMS to produce PKCS#7. The common workaround is to bundle PDFium; it turned out not to be necessary.

Three findings that cost real time and are not in the documentation: write(to:) preserves encryption even after unlock(), so a document has to be rebuilt page by page to genuinely decrypt; CMSEncoder emits indefinite-length BER rather than DER; and verifying with openssl needs -binary or line-ending canonicalisation makes a perfectly good signature look broken.

One more worth mentioning because it was pure developer experience: signing imported the certificate into the login keychain, and macOS gates key access per application — so every freshly compiled binary re-prompted for a password. Several hundred prompts before I switched to a throwaway keychain. Now 61 of 61 signing tests run unattended.

SwiftPDFKitCMSEncoder / PKCS#7 Vision OCRKeychain Services81 checks

TinguDraw

Live · no sign-in

A whiteboard with its own rendering engine — built for stylus input

TinguDraw landing page

Three modes — a freeform board, a project/Kanban mode and a kids' drawing studio — plus eleven brainstorming templates. Runs entirely in the browser as a static bundle: no server process, no database, nothing to sign into.

The engineering

I evaluated the obvious libraries and rejected both, for specific reasons. Excalidraw has open issues for exactly the two things that matter on an iPad — no stylus-only mode, and smoothing strong enough to feel laggy. tldraw became source-available in 2025, so it ships a watermark unless you pay for a key. Writing the engine was the cheaper option.

Stylus support on the web is thinner than it looks. Safari doesn't implement getCoalescedEvents(), so on the browser most iPad users have you lose the intermediate points between frames. The freehand library silently fabricates pressure values unless explicitly told not to. And the Pencil's squeeze and double-tap gestures aren't exposed to web pages at all.

The detail that made it feel right: ink base width has to be roughly 3–11× the shape stroke width, or pressure variation lands below one pixel and the stylus feels completely dead — technically correct, and unusable.

Tested on a real iPad with an Apple Pencil in both Safari and Chrome, which is the only test that counts here.

TypeScriptCanvasPointer Events perfect-freehandStatic bundleAtomic symlink deploys

05  Where AI actually does the work

Samajh

Live

A voice-first AI tutor for Indian school children

Samajh voice tutor sign-in page

A child speaks a doubt in Maths, Science or English. Whisper transcribes it, Claude reasons through it, and ElevenLabs speaks the answer back — using everyday Indian analogies, rotis for fractions and cricket for force, broken into small steps. Covers NCERT CBSE classes 5 to 12.

The engineering

A real-time voice loop has a latency budget, and three model calls in series will blow it. The interesting work is in the orchestration — streaming, overlapping the stages, and failing gracefully when one provider is slow — rather than in the prompts.

Access sits behind Google sign-in with an approval step, for the ordinary reason that unbounded API credits and children's voice data both need a gate.

Next.jsClaude APIWhisper ElevenLabsPostgreSQLAuth.js

Raahi

Live

An AI travel planner for when you don't know where to go

Raahi travel planner landing page

Pick a vibe — beaches, mountains, biking, spiritual — and Raahi matches destinations to your budget and who you're travelling with, including safety-first options for women travelling solo, then builds a day-by-day itinerary with hotels across budgets and live weather.

The engineering

The model proposes; the application constrains. Budget arithmetic, date handling and hotel tiering are ordinary code — a language model asked to do sums inside a paragraph will get them subtly wrong, and nobody notices until someone books.

Next.jsPostgreSQLOpenRouterAuth.js

06  The platform underneath all of it

One box, eight applications

Live

Self-hosted, self-operated — the part that isn't a side project

trendriders.tech

Everything above runs on a single VPS I administer: nginx terminating TLS for every host, an OAuth proxy in front of the gated surfaces, PostgreSQL and SQLite, Let's Encrypt, systemd units per application, and CI from GitHub for each repository.

The engineering

The deployment design came out of a real failure. The GitHub runner's SSH connection into the box timed out roughly half the time — a TCP connect timeout, upstream, not a key or firewall problem. The failure mode was the dangerous kind: the workflow went red, the push looked fine, and the box quietly stayed on the previous commit.

So I inverted it. Each application has a systemd timer that pulls every two minutes, guarded by a flock so the timer and the CI job can't race, and by a build stamp written only after a build that actually succeeded — because on a monolith, a failed build leaves the repository on the new commit while the service still runs the old bundle, and a naive "am I behind?" check sails straight past that.

A green build is not a healthy deploy. Verification checks the deployed commit and the application's own response, and accepts any real HTTP answer rather than demanding a 200 — insisting on 200 just produces false alarms on every redirect and every authenticated route.

Ubuntu 24.04nginxsystemd oauth2-proxyPostgreSQLLet's EncryptGitHub Actions