I turn ideas into reality.

I’m Emmanuel M Shine, a technical product manager and product engineer in Bangalore. Someone describes a problem in a sentence, and I take it the whole way: the product thinking, the interface, the database beneath it, the payments and integrations, and the site that sells it. Six products so far, under Axilerate Technologies.

How an idea becomes a product

  1. Start with one person

    Who it is for and the loop they repeat every day, in a sentence. Scope is cut until that loop works the moment two people sign up.

  2. Write the decisions down

    Framework, backend, pricing and who pays, each with its reason, before any screen exists. The file stays in the repository and keeps growing.

  3. Make it clickable first

    Every screen is built on seed data behind one data module. The idea can be held and argued with before a backend is paid for.

  4. Put the rules in the database

    Access, limits and money become policies, triggers and functions in Postgres, where a client cannot talk its way past them.

  5. Wire in the real world

    Payments, store billing, WhatsApp, push and file delivery, each connected through a server-side function with its secret kept off the device.

  6. Ship, then keep proving it

    A live site, store builds, the policy pages a payment provider checks, and scripts that test the deployed system instead of trusting it.

Hover a product to trace what it runs on. Select it to open the project.

ProductsRuntimePlatformServicesSmashDropBadminton networkEditPatraDaily editorial PDFsTrainXCoach and trainee appFolioGST invoicingJobedAI job packsAxilerateStudio siteExpo · React NativeiOS and Android, one codebaseNext.js App RouterServer components, route handlersVite · ReactOperator dashboardNode, zero dependenciesStatic marketing serverPostgres + row-level securityPolicies, triggers, RPCSupabase Auth · StorageSessions, private bucketsRealtime · Edge FunctionsBroadcast, Deno webhooksPrismaTyped schema and queriesRazorpay · CashfreeOrders, split payouts, autopayRevenueCatApp Store and Play subscriptionsWhatsApp Cloud APIOTP and document deliveryWeb Push · Expo PushNotificationsClaude APIWeb search and generationClerkGoogle sign-in

SmashDrop

A badminton network for players, coaches, academies and venues. Find a game nearby, score it live, climb a rank nobody can type in, and buy kit from a venue’s store with the payment split straight to the shop.

Surfaces
iOS and Android app, marketing site, venue and academy dashboard
Timeline
August 2026 to now
Status
Site live, apps in pre-launch builds
Scale
74 SQL migrations, about 80,000 lines of TypeScript
smashdrop.app
SmashDrop on a phone
Site tour

How it is wired

ClientsSupabase edgeDataThird partiesiOS + Android appExpo SDK 57, React Native 0.86Venue + academy dashboardVite, React 19, same anon keysmashdrop.appNode server, no dependenciesAuthPhone OTP with a send hookPostgREST + RLSEvery read and writeRealtime BroadcastOne channel per matchEdge Functionspayments, webhooks, send-pushPostgresPolicies, triggers, definer RPCStoragePhotos, checked before listingWhatsApp Cloud APIOTP template messageRazorpay Route · CashfreeSplit settlement to the shopRevenueCatPro, via App Store and PlayExpo PushOrder and booking alerts
  1. The scorer taps a point. The whole rally log is written through, not a delta.
  2. Postgres keeps the durable record, so someone joining at 15-12 still sees the full match.
  3. The same state is published on a Broadcast channel named for that one match.
  4. Every watcher receives it. A dropped message is repaired by the next write.

Integrations

Razorpay Route
Restrings and store orders are created as Razorpay orders with a transfers split, so the shop’s share lands in its own linked account on capture. Signatures are checked in the edge function and again by the webhook. Running on test keys while Route approval is pending.
Cashfree Easy Split
Added beside Razorpay when Route asked for turnover history a new company does not have. Same model, different provider: the shop is a Cashfree vendor, and each order carries its own notify_url. One payments function opens whichever gateway the shop is linked to.
RevenueCat
The Pro subscription goes through App Store and Play billing, as store rules require for digital access. A webhook mirrors the entitlement into Postgres, which is what actually grants access.
WhatsApp Cloud API
Sign-in codes are delivered as an approved template message through a Supabase Auth send hook, in place of SMS. The token lives only in edge function secrets.
Expo Push
An edge function sends order, booking, restring and reward updates to both sides of a transaction.
UPI apps
The iOS build declares the UPI app schemes so checkout can hand off to Google Pay, PhonePe, Paytm and the rest.
Device location
Nearby players, courts and stores are ordered by distance. A player’s own point is stored deliberately coarse.
EAS
Cloud builds for Android, local builds for iOS, and over-the-air updates keyed to a runtime fingerprint.

Decisions that shaped it

Rank is derived, never stored

The five rungs from Rookie to Smash Master are a pure function of games, hours, wins, win rate and verified skills. rankOf(profile) computes it in the app and rank_for(uid) does the same in Postgres. A stored column would be a second copy to keep in step, and the copy a client would try to edit. Because win rate can fall, a slipping player is demoted the moment the next result is confirmed, with no job to run.

Live scores ride Broadcast, not Postgres Changes

Postgres Changes re-evaluates row-level security per subscriber per change, which fails well before five hundred people watch a final. Broadcast is plain pub/sub. The database stays the durable record and the channel is only delivery, so nothing exists solely in flight.

The server is the authority

Every guard in the client repository is duplicated as a policy or trigger. The client copy exists so the app can explain a refusal in words. protect_profile_columns puts server-owned columns back on any client update, and trusted functions step past it with a transaction-local setting.

A policy bug that looked like a refused insert

PostgREST reads every new row back with RETURNING, which is checked against the SELECT policy. A policy that looked the row up again could not see its own uncommitted row. The fix was a rule (a SELECT policy never looks up the row it is judging) and two checks: verify:db replays all migrations on an in-process Postgres in about two seconds, and verify:queries reads and writes against the live project.

A store with a real counter

Venues list new kit with a counter price and an optional lower app price. Stock is a number kept by trigger, variants carry their own stock, and an order is a header with lines frozen at the price on the day. Unpaid orders expire after an hour and give their stock back. Pickup is over the counter against a code, so nothing is shipped.

Two gateways behind one function

A marketplace that collects everything and pays vendors later is acting as a payment aggregator. So the split happens at the gateway: on a ₹900 order, ₹855 settles to the shop and ₹45 stays as a 5% commission, set in a fee_rates table with dated rows. Razorpay Route was built first, and Cashfree Easy Split was added on the same model when Route needed turnover a new company lacks. A row is marked paid only on the provider’s own word, checked by signature.

Skills are vouched by opponents

A claimed shot is verified by six yeses from six different players who were across the net in a confirmed match. One vote per player per claim means a friendly opponent played six times yields exactly one vote.

EditPatra

The editorial pages of India’s English and Hindi newspapers, split into clean per-paper PDFs every morning for UPSC and government-exam aspirants.

Surfaces
Installable web app, admin console, WhatsApp delivery
Timeline
July 2026 to now
Status
Live, taking subscriptions
Scale
10 data models, 65 commits, English and Hindi papers daily
editpatra.com
EditPatra on a phone
Site tour

How it is wired

PeopleNext.js route handlersEngine + dataThird partiesReaderInstalled PWA or browserAdmin consoleOne combined upload a day/api/adminupload, analyze, publish/api/papers/[id]Access check, then stream/api/pay + webhooksOrders, autopay, verify/api/crondeliver, fetch-editorialsPDF enginepdfjs text, pdf-lib splitPostgres via PrismaUsers, editions, paymentsPrivate storage bucketeditions/<date>/<paper>.pdfRazorpayMonthly and yearly plansWhatsApp Cloud APIOTP and the morning PDFWeb PushEdition publishedNewspaper feedsEditorial headlines, RSS
  1. One PDF holding every paper’s editorial pages is uploaded.
  2. Each page’s text is read and matched against masthead keywords. A page with no masthead continues the previous paper.
  3. The file is cut into one PDF per newspaper and stored privately.
  4. Pages the engine could not place go to a review step before the edition is recorded.
  5. Readers who opted in are told the day’s papers are up.

Integrations

Razorpay
One-time orders for monthly and yearly plans, subscriptions for autopay, and a webhook. The payment signature is verified on the server before any access is granted.
WhatsApp Cloud API
Two jobs on one account: OTP for sign-up and password reset, and the morning edition sent as a PDF document on an approved template.
Web Push
VAPID keys and a service worker notify installed readers when the day’s papers are published. iPhone Safari users are guided to install first.
Supabase Storage
A single private bucket holds every edition. Nothing is public; the app streams files through its own route.
Newspaper RSS feeds
A cron route pulls editorial headlines with fast-xml-parser to build the free daily teaser.
Postgres via Prisma
Ten models cover users, OTPs, subscriptions, payments, editions, papers, audio bulletins and push subscriptions.

Decisions that shaped it

Splitting by masthead

The engine extracts text per page with pdfjs, matches it against a keyword list per newspaper, and treats an unmatched page as a continuation of the last matched one. Scanned pages and unmapped font encodings come back unassigned, so the admin flow has a review step instead of a silent wrong split. Adding a paper is one entry in lib/newspapers.ts, and the engine and the interface both pick it up.

Files are served, never linked

PDFs live in a private bucket and reach a reader only through an authenticated route that checks access per request. Each page is watermarked with the subscriber’s email, which makes forwarding into a public group traceable. Admin previews stay clean.

Untrusted PDFs are parsed defensively

Uploads are documents from outside. pdfjs runs with isEvalSupported: false, so embedded font programs never reach eval(), closing off CVE-2024-4367.

One identity, verified once

Sign-up and password reset both run over WhatsApp OTP on the same approved template. Duplicate sign-ups are blocked before an OTP is spent, and when two accounts could claim a reset the phone holder wins deterministically.

Storage that prunes itself

Editions older than thirty days are removed along with their records, and master uploads are not hoarded after splitting. That one change reclaimed 2.3 GB.

TrainX

Workouts, meals and progress kept in sync between a coach and their clients. For anyone without a coach, X-Guru writes the week on the device.

Surfaces
iOS and Android app, account site
Timeline
July 2026 to now
Status
Site live, apps in test builds
Scale
16 SQL migrations, about 16,500 lines of TypeScript
trainxapp.com
TrainX on a phone
Site tour

How it is wired

ClientsOn device + edgeDataThird partiesTraineeSame binary, routed by roleTrainerClients, plans, meal reviewtrainxapp.comNext.js 16, account and planX-Guru plannerRules and a seed, no modelPostgREST + RLSTrainer sees linked clients onlyRealtimeChat between coach and clientPostgresTriggers award pointsStorageMeal photosApp Store · PlayThrough RevenueCatDevice pedometerCoreMotion step counts
  1. The trainee photographs lunch. The image goes to storage.
  2. The meal is logged against the day’s plan.
  3. The coach ticks it off in their review queue.
  4. A trigger writes the points ledger. The client never writes points.
  5. The change is pushed out.
  6. The trainee sees the points land while the app is open.

Integrations

Supabase Auth
Email and password sign-in, with branded confirmation and reset emails, shared by the app and the website.
Supabase Realtime
Coach and client chat, and points that appear while the app is open.
Supabase Storage
Meal photos, readable only by the trainee and their linked coach.
RevenueCat
The design for Pro: store purchase, webhook, subscriptions table, is_pro(). The database side is built and enforced; the store products are still to be created.
Razorpay
The website has a checkout route ready for web subscriptions. It answers with a plain message until a merchant account exists, instead of a broken payment window.
Expo Notifications
Local reminders for workouts and meals, with responses recorded.
Device pedometer
Step counts from CoreMotion through expo-sensors, with a manual fallback where no sensor exists.

Decisions that shaped it

X-Guru is a row, not a system

The built-in coach is an ordinary trainer profile with a fixed id. Links, plans, meal review and points all flow through the tables and triggers that already existed, so there is no parallel path for trainees without a coach.

Deterministic by design

The planner picks from ten movements per muscle group and three meals per slot per diet, driven by a seed instead of randomness at call time. The same trainee and the same week produce the same plan, so reopening the app never rewrites what someone was told to do. It works offline and no request leaves the device.

A paywall that lives in the database

Anyone can talk to PostgREST with the anon key, so a limit enforced only by a disabled button is decoration. The free-tier limits are triggers, is_pro() reads a table only a webhook can write, and the app reads entitlement purely to decide what to say.

Insert-only was the wrong rule

Award triggers first fired on insert alone. Assigning is an upsert per trainee per day and X-Guru fills every day, so a coach taking a day over performed an update and earned nothing. The rule became: earn on a genuinely new plan, including one taken from another coach, and never for re-saving your own.

Folio

Multi-tenant invoicing for Indian small businesses. GST worked out correctly, an invoice that looks like the business made it, and a UPI code the client can pay from.

Surfaces
Installable web app, public invoice links
Timeline
2026
Status
Working build, runs on Supabase or on seed data
Scale
36 state codes, 5 tax slabs, money held as integer paise
folio · local build
Folio on a phone
Site tour

How it is wired

PeopleNext.js 16SupabaseOutsideBusinessWorkspace at /appTheir clientNo account, opens /i/[id]App RouterServer components, proxy.tsGST enginelib/gst.ts, integer paiseRosette generatorName hash to guilloché pathsAuthEmail code or passwordPostgres + RLSOne tenant per businessAny UPI appQR on the invoice
  1. Line items are edited beside a live preview of the printed sheet.
  2. Place of supply decides the split: CGST and SGST within a state, IGST across states.
  3. The business name hashes to its own security pattern, identical on server and client.
  4. The invoice takes the next gap-free number for the financial year and becomes immutable.

Integrations

Supabase Auth
Password or emailed code, with custom email templates. The code works on any device, which a magic link alone does not.
Supabase Postgres
One schema with row-level security scoping every table to a business. The app also runs on seed data when no project is configured.
UPI
The business’s own UPI id is rendered as a QR on the invoice with qrcode. Any UPI app can pay it and Folio is never in the money flow.
Web app manifest
Installable on a phone with its own icons and a service worker.
Railway
Deployed as a Node service. A helper reads the forwarded host so auth redirects use the public address, not the container’s.

Decisions that shaped it

Every business gets its own engraving

An FNV-1a hash of the business name seeds a small xorshift generator, which sets the lobes and line count of a guilloché rosette drawn as SVG splines. It is deterministic, so the server render and the client render produce the same paths and nothing flickers on hydration.

Money is integers

Amounts are stored and computed in paise. Tax is worked out per line against the five GST slabs, then split by comparing the seller’s state code with the place of supply.

Issued means immutable

A sent invoice cannot be edited. Corrections are credit notes, and numbering is sequential per financial year with no gaps, which is what an auditor expects to find.

Stay out of the money flow

Collecting payments on a business’s behalf needs a payment-aggregator licence. Folio shows the business’s own UPI id as a QR and records a manual mark-as-paid, so funds never touch the platform.

A design system with a material

The interface is specified as security paper, intaglio ink and a thread colour, in a DESIGN.md of named tokens. Six ink colours let a business tint its invoices without leaving the system.

Jobed

Upload a resume once. Jobed finds live openings that fit, then writes a tailored resume, cover letter and interview prep document for each one.

Surfaces
Web app and landing site
Timeline
May to June 2026
Status
Working build, not yet launched
Scale
13 API routes, 6 tables, 3 documents per matched job
jobed · local build
Jobed on a phone
Site tour

How it is wired

PersonNext.js 15 routesProcessing + dataOutsideJob seekerDashboard, signed in with Clerk/api/jobs/findTwo-pass search, search lock/api/jobs/tailorPlan check, generate, store/api/jobs/refreshReplace dead linksResume parserpdf-parse and mammothPDF builderpdfmake, server-sideSupabase PostgresSearches, jobs, documentsPrivate bucketSigned URLs onlyClaude APISonnet with web searchCompany job boardsGreenhouse, Lever, Workday
  1. A resume and target roles go in. The plan’s cooldown is enforced first.
  2. PDF or Word is reduced to text.
  3. Claude searches the web for eight candidates on applicant-tracking URLs, posted in the last fortnight.
  4. Every link is requested in parallel with a six-second timeout.
  5. If fewer than five survive, a second pass asks for replacements under a hard 90-second deadline.
  6. The top five by match score are saved. No dead link reaches the dashboard.

Integrations

Anthropic API
Claude Sonnet with the built-in web search tool finds postings, then writes and rewrites the documents. Output is parsed and normalised before anything is stored.
Clerk
Google sign-in, with user lifecycle events delivered by webhook and verified with svix.
Supabase
Postgres for searches, jobs and documents; a private bucket for resumes and generated PDFs, reached through signed URLs.
Company job boards
Links are checked directly against Greenhouse, Lever, Workday and LinkedIn before a job is shown.
Resend and Twilio
Clients for delivering the daily pack by email and WhatsApp.

Decisions that shaped it

The hard part is the links, not the writing

Search-indexed postings decay within a day. Every result is fetched before it is saved, and a second search pass fills the gaps, listing the dead URLs so they are not offered again.

Unknown beats a false positive

Greenhouse, Lever and Workday sit behind Cloudflare and refuse server requests even for live postings. isUrlAlive therefore marks a job dead only on an explicit 404 or 410, or on LinkedIn’s redirect away from a closed listing. Everything else is treated as alive.

Model output is normalised at the boundary

The model sometimes answers apply_url, url or link in place of applyUrl. One function accepts every known alias, drops entries without a valid https address, and logs what it dropped.

Long requests have deadlines

The second pass runs inside a Promise.race against 90 seconds and returns what the first pass found if it loses. A stale database connection after a long call is handled by recreating the search row and retrying the insert once.

Cost is a product constraint

Each plan fixes how many jobs are found and how often, and generated documents are cached per job. The cadence a user sees is the same control that bounds model spend.

Axilerate

The site for Axilerate Technologies, the studio these products ship under. Services, products, team and a contact path, in light and dark.

Surfaces
Marketing site, ten routes
Timeline
2026
Status
Live
Scale
Statically rendered, generated Open Graph image and sitemap
axilerate.com
Axilerate on a phone
Hero in motion

Integrations

Open Graph image route
The share card is rendered by the app itself, so it never goes stale.
Sitemap and robots
Generated from route files and submitted for indexing.
next-themes
Light and dark, following the system until the visitor chooses.

Decisions that shaped it

A canvas that earns its frame budget

The hero draws a particle network on a single canvas behind the headline, with spring-staggered entrances from Framer Motion on top. Everything below it is static HTML.

Components before pages

Buttons, badges and cards are small variant-driven primitives built on Radix Slot and class-variance-authority, so ten routes share one vocabulary.

Search and sharing are generated

The sitemap, robots file and Open Graph image come from route files, so a new page cannot ship without them.

What repeats across all six

The database is the authority
Limits, entitlements and counters are policies, triggers and functions. Client-side checks exist to explain a refusal, never to enforce it.
Screens never touch the data source
Each app reads and writes through one repository module of plain async functions. The whole interface was clickable on seed data before a backend existed, and the swap touched no screen.
Derive it, do not store it
Rank, balances and overdue status are computed on read from facts that cannot be typed in. There is no second copy to drift and no recalculation job to forget.
Payments shaped by the rules around them
Store subscriptions go through in-app purchase, marketplace money is split at the gateway so the platform never holds it, and the policy pages a provider checks are written from the actual flow.
Prove the deployment, not just the code
Migrations are replayed on a real Postgres in seconds, and a second check reads and writes against the live project to catch one that was never applied.
Write down why
Every repository carries a long decisions file: what was chosen, what it replaced and what it cost. This page is drawn from those files.