Iresh Sharma's software engineering portfolio, themed as a Cursor window. Member of Technical Staff at Salesforce in Bengaluru. Previously Twilio and SuperTokens. WatchIreshStruggle by night.
Iresh Sharma
Fullstack by day. WatchIreshStruggle by night. The old website still thinks I work at Twilio. This window is the patch.
Bengaluru. Member of Technical Staff at Salesforce, currently helping build native Voice infra. Before that: Twilio, SuperTokens, a stack of internships and campus clubs, and a freelance habit I have not successfully quit. NIE Mysore, Computer Science, class of someone who started a GitHub account in 2017 and never emotionally left.
I ship production systems at scale and then immediately start a scrappy MVP because idle hands open Cursor. Kafka at tens of millions of events a minute, Flutter SDKs, Gmail APIs, 3D printers, basketball, hip-hop, and a LeetCode repo whose README admits competitive programming never really attracted me. That is the brand.
How to read this window
- about.md — the human patch notes.
- career.md — jobs, in chronological self-roast.
- projects.ts — the index. projects/ — the actual writeups.
- projects/codeloom.md, lens-distill.md, seeksphere.md, reach.md, g-notify.md — roast, then the real systems.
- extras/ — hobbies, gaming (PSN, Steam, Valorant), YouTube, the blog.
- Outline — posts and projects, because I process trauma as markdown.
Also on the internet
about.md
Engineering Voice infra at Salesforce by day. Hacking on products that become blog posts by night. Open for freelance, closed for small talk about work-life balance.
I am a fullstack engineer who treats side projects like cardio: painful, public, and somehow always scheduled after midnight. I like TypeScript enough to argue with it, Python enough to automate my YouTube problem, and Flutter enough to have owned an SDK at a startup that authenticates half the internet's side projects.
The through-line is not a stack. It is 'I would rather build the tool than do the chore.' G-Notify exists because Gmail would not send HTML. The Shorts generator exists because making vertical videos by hand felt like a war crime. Lens Distill exists because I could not finish Venture Deals. SeekSphere exists because filter drawers are a query planner with worse UX. CodeLoom exists because grep is not comprehension. This portfolio exists because a normal landing page felt like lying. The long versions are in projects/.
Operating system
- Bengaluru, after Mysuru, after NIE.
- GDSC Lead, IEEE webmaster, campus ambassador — I collected titles like they were Pokémon.
- Photography when the light is good. Hip-hop when it is not.
- Basketball as cardio, allegedly.
- 3D printing as a second compiler, except the errors are plastic.
I write at WatchIreshStruggle, which is not a humble brag. It is a content strategy and a warning label.
career.md
The LinkedIn version is 'shipped production systems at scale.' The comments version is below.
Salesforce — Member of Technical Staff
Dec 2025 → now. Native Voice infra. I went from Twilio, a company whose entire personality is APIs for talking, to Salesforce, a company whose entire personality is CRM, to work on… talking. The bit writes itself. Specialist on paper. Still googling internal acronyms in private.
Twilio — L1, then L2, plus a summer as an intern who stayed in the plot
Intern on Flex in 2022: Java microservice for account config, a CRM plugin that stitched the same customer's calls across channels, and a reliability fix so the thing could boot without memcached. Then I came back as L1 in 2023 and did the grown-up sequel: Kafka Streams chewing tens of millions of events a minute under a sub-5-second SLA. Disaster recovery that copied terabytes across Aurora and DynamoDB with a 30-minute delay and the reporting APIs to prove nobody had lied. Notifications that pager-duty your custom rules and then apologize when the system recovers. Also UI, because fullstack means you don't get to pick.
L2 lasted four months in 2025 and then Salesforce called. I like to think I speedran Big Tech.
SuperTokens — Software Engineer
Nov 2022 → Aug 2023. Open-source auth. I touched the Flutter, Go, Node, and Python SDKs so developers in four languages could have the same identity crisis. Owned search on the user-management dashboard. Lived in Discord helping people who had read the docs and chosen violence. Started by taking the Flutter SDK, adding Dio, and updating drivers. Ended by knowing too much about sessions.
The origin story, compressed
- Triomics, 2022 — joined mid-release, fixed codegen, inactivity auth, and enough UI that the pixels stopped arguing.
- Exinous, 2022 — custom enterprise software. ERPs. The word 'custom' was doing a lot of work.
- Learners Digital, 2020–21 — shipped a cross-platform edtech app. Play Store. Real users. Real crashlytics.
- Deshik Labs, 2020–22 — Mysuru fullstack years. This is where I learned that 'full stack' is a personality.
- AcadBoost, 2020 — recorded a web course. I have been WatchIreshStruggle longer than the blog admits.
- NIE Mysore, CSE 2019–23 — GDSC Lead, DSC web lead, IEEE Computer Society tech lead, IEEE webmaster. I was, briefly, a student org.
projects.ts
// iresharma/projects.ts
// compiled from github, linkedin, and poor impulse control
// long versions: projects/codeloom.md, lens-distill.md, seeksphere.md, reach.md, g-notify.md
export type Project = {
name: string;
pitch: string;
stack: string[];
shipped: boolean;
originStory: string;
};
export const dayJob = {
company: "Salesforce",
doing: "native Voice infra",
previously: ["Twilio", "SuperTokens"],
};
export const shipped: Project[] = [
{
name: "CodeLoom",
pitch: "Devin-shaped agent: explore, patch, test, open a PR. You keep merge",
stack: ["tree-sitter", "LSP", "Claude tool-use", "a while loop"],
shipped: true,
originStory: "educative attempt, typo in the GitHub bio, four landing pages",
},
{
name: "Lens Distill",
pitch: "PDF plus a topic lens becomes claims, a vocabulary, and a concept graph",
stack: ["Next.js", "Neon pgvector", "Haiku", "Sonnet", "Opus"],
shipped: true,
originStory: "I could not finish Venture Deals. I built a pipeline. I still have not read Venture Deals.",
},
{
name: "SeekSphere",
pitch: "natural language in, SQL-shaped catalog results out, Clerk-shaped SDK",
stack: ["intent classifier", "LLM-to-SQL", "MCP connectors", "@seeksphere/sdk"],
shipped: true,
originStory: "filters are a query planner with worse UX. SuperTokens muscle memory.",
},
{
name: "Reach",
pitch: "work desk for creators who also have a calendar, inbox, and a Shopify tab they should close",
stack: ["Remix", "Next", "shadcn", "AI tools I will not name in a type"],
shipped: true,
originStory: "started as a SaaS, became a lifestyle, still in the repo as reach-io-remix and reachv2",
},
{
name: "G-Notify",
pitch: "HTML mailer that talks to Gmail APIs because nodemailer felt like cheating",
stack: ["Nuxt", "Express", "GAPIs", "the GDSC core-team recruiting spreadsheet"],
shipped: true,
originStory: "I was GDSC Lead. Twenty offer emails. Gmail said no to HTML. I said fine, I'll do it myself.",
},
{
name: "Shorts Content Generator",
pitch: "text in, YouTube Short out, ~22 seconds, CPU first, Pexels for the B-roll",
stack: ["Python", "MoviePy", "Coqui TTS", "five fallbacks because TTS is a gaslight"],
shipped: true,
originStory: "open sourced because paying for faceless-video SaaS felt like a skill issue",
},
{
name: "git-accounts-manager",
pitch: "Electron app for people with too many GitHub identities and one laptop",
stack: ["Electron", "ssh-agent diplomacy"],
shipped: true,
originStory: "work account, personal account, the third one I do not talk about",
},
{
name: "GpayRedesign",
pitch: "Flutter UI that Google Pay could have shipped if Google Pay asked me",
stack: ["Flutter", "audacity"],
shipped: true,
originStory: "redesign energy. 2020. We do not apologize for the era.",
},
{
name: "Sapphire",
pitch: "a Python templating engine whose files end in .sph, on purpose",
stack: ["Python", "the belief that Jinja needed a rival"],
shipped: true,
originStory: "small effort. big extension. nobody asked.",
},
{
name: "Variable bitrate streaming server",
pitch: "video in, HLS + MPEG-DASH out, a web page to pretend I was Netflix",
stack: ["ffmpeg", "hope", "a 2019 blog post"],
shipped: true,
originStory: "I was in college and thought protocols were a personality",
},
];
export const also = [
"create-react-app-v2 — I forked the generator because the official one made me refactor",
"106 public repos — this is not a flex, it is a cry for help",
];
// The index is a bit. The systems are in projects/*.md.
hobbies.md
When I am not coding I like to play basketball, try photography, listen to hip-hop, and learn 3D printing. That sentence is from the old site. It was optimistic about the 'not coding' part.
The rest of this window is jobs and repos. This file is basketball, printers, boards, cameras, and playlists. Games got their own file because I doxed the rank on purpose.
The list, compressed
- Basketball — pickup, not a podcast. Bengaluru courts are a distributed system. I defend, allegedly.
- 3D printing — another Benchy, slightly wrong. Slicer settings are compiler flags. Photography of the print is the only stage that ships.
- IoT — boards that speak UART and spite. Firmware at 3am, after Voice infra at 3pm.
- Photography and hip-hop — good light, worse playlists, walk home, open Cursor.
gaming.md
The dock shipped. PSN, Steam, and a Valorant tracker sitting next to the résumé on purpose.
Hobbies.md got the analog stuff. This file is ranked anxiety with URLs. Riot ID iresharma#noob, PSN iresharma, Steam ireshrma. Add me if the tag does not scare you.
I have 106 GitHub repos and a LeetCode profile I maintain after saying competitive programming never attracted me. The trackers below have the same energy: casual in the copy, unhinged in the friend request.
Live gaming stats are shown in the editor window when JavaScript is available.
Elsewhere
youtube.md
Channel name: WatchIreshStruggle. I then wrote a pipeline that makes Shorts in 22 seconds. The bit is load-bearing.
Same brand as the blog, moving pictures. I show up on camera the way I show up in markdown: a fullstack engineer who will automate the embarrassment if it takes more than one take. The Shorts generator exists because opening CapCut felt like a war crime. The channel exists because the blog needed a face, and I was available.
If you wanted a polished creator-economy funnel you came to the wrong extras folder. Subscribe if you like Voice infra by day and watching someone debug a TTS fallback ladder by night. Numbers below are live-ish. The jokes are cached forever.
Live youtube stats are shown in the editor window when JavaScript is available.
Elsewhere
blog.md
WatchIreshStruggle. Hashnode. Bengaluru. Building Voice by day, publishing the outage report by night.
I write when a side project becomes a confession. Latest hits: why a coding agent needs tree-sitter and LSP, the pipeline I built because I could not finish Venture Deals, and a local-first Shorts factory. Older hits include Reach's tech stack, learning gRPC the hard way, an Appwrite hackathon, and Nuxt SSG from when I still believed in static generation as a personality.
Individual posts also live in the Outline, like a second index I will not keep in sync. This file is the front door. That panel is the grep. Numbers below are scraped off the homepage once a day, because paying Hashnode for JSON felt like a bit.
Live blog stats are shown in the editor window when JavaScript is available.
Elsewhere
why-agents-read-code-three-ways.md
WatchIreshStruggle · Aug 2026 · 8 min. The first problem is not generation. It is comprehension. Relatable.
If you are building something like Devin, the model cannot just grep a five-file toy repo and call it architecture. I wrote about why a coding agent needs tree-sitter for cheap local lookups and LSP for the expensive global ones — goto definition, find references, hover, diagnostics — and why conflating them either wastes tokens or wastes time.
Tree-sitter is instant and does not care if the rest of the project is on fire. LSP is slow to start and actually understands imports. The punchline is in the system prompt: prefer the cheapest tool that answers the question. I have been trying to live like that. I have not succeeded. Hence this portfolio.
i-never-finished-venture-deals.md
WatchIreshStruggle · Aug 2026 · 17 min. I grep books. Books do not grep. So I built Lens Distill.
I started Venture Deals three times. I finished a pipeline instead. PDF in, atomic claims out, each citing the exact paragraphs they came from, plus a concept graph. Seven stages. Haiku extracts, Sonnet merges duplicates, Opus builds the graph, pgvector holds the embeddings. On that book: 3,001 paragraphs became 978 claims, 54 concepts, 318 edges. About an hour. About two dollars. Zero pages actually read in order.
The part worth stealing: I label every paragraph [p412] in the prompt so citations are a range check, not a vibe. The part worth fearing: a silent fallback shipped an empty graph and the UI looked fine. I wrote the post so future me cannot pretend that was a feature.
youtube-shorts-in-22-seconds.md
WatchIreshStruggle · Jun 2025 · 5 min. Local-first. CPU. Five TTS engines. One personality.
I open-sourced a Python pipeline that turns text into a YouTube Short in about 22 seconds. MoviePy, Coqui TTS, Pexels for images, a fallback ladder that goes neural → cloud → pyttsx3 → macOS say → espeak, which is the five stages of grief. Perfect audio-video sync, 9:16, and the kind of architecture post you write when you are both proud and a little scared of how far you will go to avoid opening CapCut.
This is the same man who named his YouTube channel WatchIreshStruggle and then automated the struggle. I contain multitudes, and also a videoOrchestrator.py.
CodeLoom
The latest venture. He named it like a company, licensed it MIT, stood up four coming-soon sites, and wrote in the GitHub description that it is an 'aducative' attempt at Devin — typo included, honesty included. Then he published the part that is not a landing page: an agent cannot grep a real repo and call it architecture.
CodeLoom is a family of surfaces around one job: take a ticket, understand the repository, patch it, run tests, open a pull request. The public pitch is a Devin-shaped loop. The actual engineering so far is the comprehension layer — how an agent reads code without burning the context window — plus a deliberately small tool-use runtime that proves the loop is a loop, not a framework.
What it is
The mother repo is an educational attempt at an end-to-end coding agent, with experiments and docs checked in on purpose. Submodules point at the experiment scripts and at a Turborepo of four Next.js landings — agent, IDE, TUI, CLI — so the family looks like a product line while the runtime is still being grown in public. The landing copy is load-bearing: explore, patch, test, pull request. You keep the merge button.
CodeLoom is a Devin-shaped agent that explores the repo, plans, patches, runs tests, and opens a PR. Educational, open, and still very coming soon.
codeloom.iresharma.com
An aducative attempt at making something like devin, since it is an educative attempt this repo includes all experiment and relate docs
github.com/iresharma/codeloom
The first problem is not generation
The blog post that belongs to this project is not a launch announcement. It is a systems argument. Before an agent can safely edit a codebase it has to answer local questions (where is this function) and relational ones (what calls it, what does the type checker think, is this even valid). Handing the model read_file and grep works on a toy. On anything real it rereads whole files for one symbol, and it cannot tell two parse_config functions apart across directories.
If you're building something like Devin, an agent that goes end-to-end from a task description to a working code change. The very first problem you hit isn't code generation. It's comprehension.
WatchIreshStruggle · Aug 2026
The design is two tools that sound similar and are not. Tree-sitter parses one file in milliseconds, finds definition-shaped nodes, and returns 1-based coordinates. LSP is a long-running JSON-RPC process — initialize handshake, a background reader thread because servers talk back, warm-start because gopls and pyright do their expensive work as a side effect of didOpen. Tree-sitter does not resolve imports. LSP does. The system prompt tells the model to prefer the cheapest tool that answers the question.
That's a small detail, but it matters LSP calls are positional (a line/column in a file), and models are bad at counting characters by eye from a text dump. Tree-sitter becomes the thing that hands the model correct coordinates to hand back to the LSP.
WatchIreshStruggle · three ways to read code
- find_symbol is tree-sitter: parse this file, match a name, return a position. No project index. Works with broken imports elsewhere.
- goto_definition, find_references, hover, get_diagnostics are LSP. pyright, gopls, typescript-language-server — each configured honestly, not as one generic server.
- Warm-start opens up to 500 source files in a background thread so indexing overlaps the agent's first exploration turns.
- Tools speak 1-based lines to the model; LSP is 0-based. Every call translates both ways. Wrong by one is a silent wrong edit.
- Open questions written down on purpose: diagnostics live in memory, no didChange yet, no invalidation after the agent starts writing.
The runtime is a while loop
The experiments repo is the other half of the honesty. The first script is a single-file CLI agent: Claude tool-use, a dict of Python functions, a turn cap so a buggy loop cannot run forever. read_file, write_file, run_shell. No hidden state. That is the shape everything else has to earn.
Nothing here is magic. There's no framework, no hidden state. Just a while loop, a dict of Python functions, and the Claude API's tool-use feature.
codeloom.experiments · ex_1.py
The pages repo is pnpm workspaces and Turborepo: four Next.js apps, shared UI, product metadata and hosts in packages/config. Railway, one service per surface. The README is explicit that these are not a company. They are an open-source resume with a domain name.
Lens Distill
He started Venture Deals three times. He finished a pipeline. The book is still unread in order. The graph has 318 edges. This is the most Iresh project in the tree: a format problem treated as an infrastructure problem, then written up so the silent failures cannot be rebranded as features.
Lens Distill takes a PDF and a free-text topic lens and turns them into atomic claims with paragraph citations, a canonical concept vocabulary, and a sparse graph of prerequisite, related, and confusable edges. The Next.js app is a window. The work is a linear seven-stage job queue on Neon Postgres with pgvector. Parse is the boss fight. Silent fallbacks are how demos lie.
Seven stages, one line
The whole system is a linear job queue. No DAG, no fan out, no orchestration framework, no cron. Each stage writes its rows and enqueues exactly one next job, or returns null and the book is done.
WatchIreshStruggle · Venture Deals pipeline
- 0 parse — PDF bytes to paragraphs with global indices. pdfjs-dist. Binary discarded; paragraphs stay.
- 1 chunk — ~1200 token windows, 150 overlap, hard break on chapter. js-tiktoken. Average-size gate catches exploded chapter detection.
- 2 embed — text-embedding-3-small, 1536-d, batch 100, HNSW cosine on Neon.
- 3 extract — Haiku, 40 chunks per job, concurrency 4, forced emit_claims tool. Persona is fenced user text, not the system prompt.
- 4 dedupe — cosine 0.86 clusters, 0.92 auto-merge, Sonnet only on the band in between. ~70% of wall clock.
- 5 canonicalize — string first, embeddings last. Negation prefixes never merge. participating vs nonparticipating is a regex, not a better model.
- 6 concepts — a tag becomes a node at ≥8 claims. primary_chapter is the modal chapter, not MIN().
- 7 concept_graph — Opus, typed edges, ≥80% coverage or throw. Prerequisite cycles broken with a recursive CTE.
Citations are a range check
Every paragraph is labeled [p412] in the prompt, using a global monotonic para_index as primary key (book_id, para_index). The model cites by marker. The code checks that every support_paras index falls inside the source chunk. Hallucinated [p9001] is dropped and counted. No LLM judge. Per-chapter indices were tried first; they collide on joins and the join succeeds.
Inline markers turn citation into a range check. Labeling paragraphs [p412] costs a few tokens and buys deterministic verification. Never let a model invent identifiers you plan to join on.
WatchIreshStruggle · cheat sheet
Fail loud, or the UI will look fine
A wrapped heading produced 157 chapters instead of 21. Nothing threw. Chunks fragmented, claims went context-free, ten dollars later the graph was a cloud of junk. The fix is gates: chapter count in [5, 40], demote 'chapters' with fewer than 15 paragraphs, drop headers that appear on >30% of pages, fail closed on page numbers unless offset agreement clears 0.8. The graph bug that shipped was worse: max_tokens truncated JSON, the parser returned [], the fallback drew a tag-only graph of disconnected dots, the book was marked ready.
Silent fallbacks are how demos lie. A fallback that produces a structurally different result (empty edges, fake pages, a tag only graph) is worse than a crash, because a crash gets fixed and a fallback gets shipped.
WatchIreshStruggle · concept_graph
On Venture Deals: 3,001 paragraphs, 21 chapters, 109 chunks, ~978 live claims, ~54 concepts, ~318 edges. About an hour. About two dollars. Site-wide quota of three books per rolling week, advisory lock, PDF never stored. Forced tools, citation range checks, and a spend cap — in that order — because a deny list is the weakest layer and the one everyone reaches for first.
Portfolio demo: upload a PDF and an extract.md persona, run a real book-distillation pipeline, and inspect claims, concepts, a concept graph, and claim-embedding clusters. Drain starts automatically after upload (after() + chained continue). No cron worker, no manual stage-advance UI.
github.com/iresharma/lens-distill
SeekSphere
He worked at SuperTokens. Of course the search SDK is pitched like Clerk and SuperTokens. 'Show me red running shoes under three thousand that are actually in stock' is just sessions for people who sell shoes. The satire writes itself. The pipeline does not: intent, entities, SQL, millisecond reads off a pre-processed catalog.
SeekSphere is a natural-language search layer for e-commerce. The product bet is that filter drawers are a UI for a query planner humans should not have to operate. A shopper talks. A classifier pulls intent, brands, categories, attributes. An LLM step, exposed through MCP, compiles that into SQL against a catalog that has already been shaped for retrieval. The developer surface is an SDK you drop in the way you drop in auth.
The problem, as the product states it
E-commerce search still mostly means a text box plus a stack of facets. That works if the shopper already knows the schema. It fails the moment the query is a sentence: constraints, negations, stock, price, brand, 'like this but not that.' SeekSphere's public architecture is a four-step path from that sentence to a result set, with the interesting work in the middle two steps — classification and compilation — not in the search box.
Transform complex filter-heavy experiences into simple, conversational queries. The future of intelligent search for e-commerce platforms.
seeksphere.ai
- Natural language in. A classifier reads intent, entities, and context — brands, categories, attributes, preferences.
- LLM processing over MCP turns that structure into optimized SQL, not a keyword soup scored by hope.
- Reads are aimed at a pre-processed catalog so the interactive path stays in milliseconds even when the product set is large.
- MCP connectors sit in front of catalogue APIs and inventory databases instead of pretending one schema is universal.
Our classifier analyzes intent, entities, and context. Advanced NLP converts to optimized SQL queries.
seeksphere.ai · How it works
Developer-first, on purpose
The go-to-market is two-tier, and the interesting one is the SDK. Large catalogs get an embeddable client in the same shape as an auth provider: install, configure, search(query). Smaller shops get a plugin. That split is a SuperTokens lesson applied to retrieval — the hard product is not the demo query, it is the integration surface that does not force a rewrite of the storefront.
Easy integration with existing e-commerce platforms. Similar to Clerk and SuperTokens model.
seeksphere.ai · Developer-first SDK
The public snippet is the whole contract: install @seeksphere/sdk, call seeksphere.search(query), get results. Behind that call is the classifier, the LLM-to-SQL step, and the connector layer. The portfolio joke is that this is auth-shaped infrastructure. The technical claim is that search quality lives in how you compile language into a query plan, and how you keep that plan off the hot path of an unindexed product table.
Reach
Started as a SaaS. Became a lifestyle. The GitHub org chart is the confession: reach-io-remix, reachv2, reach-auth-proxy, reach-page-server, reach-page-analytics, Reach-file-server, reach-kanban-service, reach-k8s, a Bun starter script so he could boot the whole circus. He said he wanted to learn error handling. He learned gRPC, Glacier, and how many databases a calendar app can acquire.
Reach is a work desk for content-heavy teams: issue tracking, files and versions, review, calendar, mail, Shopify, analytics, and a Linktree-shaped brand page. Version one was a Remix monolith with loaders full of backend. Version two is a Remix SSR talking to a Go auth proxy that fans out over gRPC to Go and Python services, Postgres, Redis, Cloudflare R2 with a Kafka relay into Glacier, and a Next.js ISR farm for the public pages.
Why the monolith had to go
The first app put API surface in Remix loaders, actions, and handler routes. No TypeScript. Prisma on MongoDB. It taught error handling. It did not teach a clean boundary, and it did not perform. The rewrite keeps Remix as the SSR shell and moves authority to an auth proxy that is the only thing on the internet.
I will still have a remix SSR that talks to RESTful API which acts as a auth proxy, this auth proxy then delegates traffic to all the other services via gRPC. … The go auth proxy on look is giving me extremely good performance. On my local setup with postgres and redis cache the auth proxy responds with sub millisecond responses.
WatchIreshStruggle · Remix to microservices
ROP as a stack, not a slogan
The later architecture post is explicit about the three goals — resiliency, observability, performance — and about the tax. Microservices give feature isolation, independent scale, and mixed runtimes. They also scatter logs and add network failure modes. The proxy exists so browsers never speak gRPC, so caching has one throat to choke, and so only one service is exposed. Protobufs and HTTP/2 are the inter-service bet; REST is the browser bet.
Browser support for gRPC isn't great. Utilizing a proxy becomes particularly valuable when dealing with microservices that communicate using gRPC and Protobufs. … gRPC provides strong typing through Protobuf's schema definition.
WatchIreshStruggle · Learning ROP
- Frontend: Remix, shadcn/ui, Tailwind — reachv2 describes real-time collaboration, tasks, roles, chat, an analytics dashboard.
- Auth proxy: Go, Redis sessions, protoc-generated clients for kanban, page, and storage. Postman collection is the public contract.
- Files: Flask talking to Cloudflare R2, with a Kafka relay so objects also land in Glacier. Two vendors on purpose.
- Brand pages: Next 13 App Router, generateStaticParams from Mongo at build, generateMetadata from the page template, ISR. 'A URL shortener which instead of redirecting you to a link, shows you a page with a multitude of links.'
- Analytics: Flask ingest, Mongo aggregations. Views and clicks are events. Uniques are IP plus user-agent, which also feeds geo. The blog says a queue would be correct at scale; they shipped the simple path first.
- Orchestration: reach-k8s, plus a Bun script that starts every local service because twelve terminals is not a personality.
A simple file server for handling files in Reach, also a relay for kafka to support data duplication into glacier.
github.com/iresharma/Reach-file-server
I create a Next 13 app with ISR. Now next allows me to use server components and things like generateStaticParams and generateMetaData. So during build time I look up my database for existing pages and generate params based on route.
WatchIreshStruggle · Linktree on Next 13
G-Notify
GDSC Lead. Twenty offer emails. Gmail's client would not send HTML. Nodemailer felt like cheating, or at least like not learning GAPIs. He wrote a mailer, then a product, then a landing page, then a blog post about fighting Nuxt SSG on Netlify the night before a DBMS exam. The origin story is recruiting. The stack is spite plus googleapis.
G-Notify is a mass HTML emailer. Nuxt on the front, Express on the back, Gmail API in the middle, no nodemailer. Templates are created, uploaded, and sent one-to-one or in bulk. Handlebars renders them. MIME is assembled in process. Mongo stores the records. Google Cloud Storage holds assets. Tracking exists; stats were the honest unchecked box.
Why it exists
The LinkedIn writeup is the spec. As GDSC Lead he had a core team to notify and did not want twenty manual emails. Gmail would not send HTML. A Python script with Jinja and the Gmail API proved the path. The product is that path with a UI: if student orgs and small teams cannot send crafted HTML from the account they already have, give them templates and OAuth instead of a new ESP.
G-notify is a mass mailer written in nuxt snd express without any third party libraries like (nodemailer). We directly contact GAPIs to send the mail.
github.com/iresharma/G-Notify
What the repo actually ships
- googleapis talks to Gmail. mimetext builds the message. handlebars fills templates. That is the send path.
- mongoose plus a dbURI. Templates, sends, tracking events — not a spreadsheet.
- @google-cloud/storage and a service-account JSON for the unofficial CDN: people upload images, the mailer hosts them.
- exceljs / read-excel-file for recipient lists. The recruiting use case was a roster, not a marketing automation suite.
- Puppeteer is in the tree for render or preview work that a browser, not an email client, can do.
- ApexCharts on the client. README: tracking yes, stats not done. The chart library arrived before the aggregation did.
- OAuth env: GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REDIRECT_URL. You send as the user, through Google, not through a rented SMTP.
The Netlify post is the operations chapter. asyncData in Nuxt SSR does not survive generate. He rewrote those hooks to mounted so the static site could fetch. Putting the Express app on Netlify Functions hit a 69,905,067-byte CreateFunction cap — puppeteer will do that. The workaround was CORS to the existing Heroku SSR API, with the generated frontend on Netlify. Ugly. Documented. Shipped the night before an exam.
Request must be smaller than 69905067 bytes for the CreateFunction operation. … Then I needed up realising I already have an SSR version hosted on heroic, so I added soon CORS policies on that hosting and voila I had a version of my Application on Netlify contiously fetching data from a heroic server.
WatchIreshStruggle · Nuxt on Netlify