Kartik Kumar

Software engineer, product builder,
and open-source maintainer.

I’m fascinated by all things technology. Lately, that curiosity has turned into an obsession with local AI.

Selected work

Open source

Curiosity keeps me building.

Project breakdown

Product ownership

Building products at AXSYS.

From the first version to daily operation

At AXSYS, I’ve been the sole developer and technical lead, taking products from the first implementation through launch and day-to-day operation. My work spans the interfaces people use, the services behind them, and the systems that keep them connected. I built and operated these as customer-funded, self-sustaining products, so shipping a feature was only one part of the responsibility. I also owned the ongoing engineering work after that feature became part of someone’s everyday workflow.

Real-time systems

Zyrable is a good example of the backend side of my work. I built the path from incoming data through processing and fanout, including WebSocket streaming and communication between services. Bursty workloads meant that queueing, backpressure, and rate limits were part of that design. I also isolated CPU- and memory-heavy work so it could be managed separately from the rest of the pipeline.

Mobile products

On the product side, I built Resellify’s cross-platform React Native app and its access controls for community subscriptions. That scope included both the subscriber-facing application and the native behavior underneath it. When the work called for platform-specific notification features, I wrote Objective-C and Java modules alongside the React Native code. On iOS, that included time-sensitive and communication notifications with custom contact images.

Visit AXSYS
Project breakdown

Personal project / Local AI

An everyday agent, running locally.

I’m fascinated by all things technology. Lately, that curiosity has turned into an obsession with local AI.

Starting with my own machine

I bought and set up a Mac Studio with an M4 Max and 64 GB of memory to run local models. My current setup uses oMLX with Qwen 3.6 35B A3B in oQ4. That machine is the foundation for the local-agent work described here. I’ve also worked on custom GPU kernels for concurrent performance, so the project has taken me from choosing a model down into how the hardware is being used.

Taking it with me

I combined Jev and Browser Use with Hermes over iMessage so I could ask my agent to handle everyday tasks from my phone. I’ve gradually connected services I use in both my personal life and my work. Access has been added deliberately, with read access and restricted writes rather than treating every connected service as unrestricted. The useful part for me is having one conversational entry point into tasks that previously meant sitting down at a computer.

iMessageLocal agentConnected services

What changed for me

Over the past year, I estimate that more than 80% of the situations that used to require my laptop or desktop can now be handled by messaging my agent. That is an estimate of my own workflow, not a measured benchmark of the system. The change I care about is how often I can deal with something from my phone and get back to what I was doing. It is the reason this project has become part of my daily life instead of staying a local-model experiment.

My estimate of my own workflow, not a benchmark or a guarantee of agent reliability.

Tuning the GPU

With assistance from Fable and Claude, I built custom GPU kernels to push utilization on my Mac. My focus was performance under concurrent workloads, with the aim of making better use of the GPU when work overlaps. That took the project beyond model configuration and into lower-level performance work. I’m describing the engineering direction here without claiming a benchmark speedup I have not published.

Memory across tasks

I built a vector-database-backed memory store that the agent can query across tasks. It retrieves relevant pieces of long-term context instead of depending on one ever-growing conversation. That separates information I want to keep from the smaller amount of context useful for a particular task. I maintain that memory system alongside the prompts, because persistence and a focused working context are both parts of the setup.

Keeping the context focused

I periodically trim the system prompts to keep the working context focused. The goal is to preserve useful instructions without letting every past task become permanent prompt material. The vector-backed memory store gives the agent a separate way to retrieve relevant context across tasks. I treat prompt maintenance and memory retrieval as ongoing work, rather than assuming the initial setup will stay useful unchanged.

Project breakdown

Real-time systems

Zyrable

Zyrable monitors social profiles and search queries, routes alerts to Discord, and exposes monitored events through WebSocket and REST APIs.

Public Zyrable product artwork

What I built

I built Zyrable’s ingestion, processing, and fanout pipeline. The work covered both moving data between services and streaming updates over WebSockets. Those are connected responsibilities: accepting an event is only the beginning of getting it to the consumer. I worked across that entire path, including the flow controls needed when incoming work arrives in bursts.

Handling bursts

I used queueing, backpressure, and rate limits to manage bursts instead of treating every incoming event as work that had to run immediately. Each addresses a different part of the problem: pending work needs somewhere to wait, producers need limits, and slower consumers need to be accounted for. I also isolated CPU- and memory-heavy work from the rest of the pipeline. That gave me distinct parts of the workload to manage rather than one shared stream of competing tasks.

System sketch / ZyrableConceptual flow
01Monitored eventsProfiles & search queries
02Process & routeIngestion → processing → fanout
Discord alertsWebSocket / REST APIs
The constraint

Used queueing, backpressure, rate limits, and isolated CPU- and memory-heavy work to manage bursty workloads.

Conceptual overview · ingestion, processing, and fanout.

Recovering connections without a retry storm

In the alert-distribution work behind Zyrable, I handled the connection lifecycle as well as message delivery. Concurrent requests to connect the same account share the pending operation. Temporary failures back off with jitter; configuration failures pause retries instead of repeatedly attempting a connection that cannot succeed.

When an old connection finishes late

Replacing a connection creates another problem: the old client can still emit events or finish logging in. I guarded callbacks against the current client and retired stale instances, so a late event cannot tear down the replacement. Timed-out logins release their queue slot rather than blocking other accounts indefinitely.

The failure cases I account for

The recovery tests exercise repeated service failures, silently stalled clients, normal reconnects, hung logins, late completions, and account removal during a pending login. These cases distinguish a connection that briefly reports ready from one that has actually stabilized. They document the intended behavior; they are not a claim of uninterrupted delivery.

Visit Zyrable
Project breakdown

Mobile product

Resellify

Resellify offers subscription access to resale-product research and configurable mobile alerts, with individual subscriptions and organization-level access.

The app and access controls

I built Resellify’s cross-platform application in React Native, together with the access controls for community subscriptions. This was a multi-tenant product, so the application had to reflect the community and membership a subscriber belonged to. My work covered the mobile experience as well as the backend state that determined access. I treated those as connected parts of the product, especially when subscription changes needed to affect an existing session.

Public Resellify mobile product artwork

Getting alerts to the device

I implemented the notification-routing path from dispatch through delivery to APNS and FCM. The system uses in-memory dispatch and Redis caching, with direct delivery to the two platform services. That put routing and platform delivery within the same area of responsibility. It also connected the backend work to the native notification behavior I built in the mobile app.

System sketch / ResellifyRouting → native delivery
Routing layerIn-memory dispatchRedis caching
APNSiOS
FCMAndroid
React NativeThe last mile is native.Objective-C & Java modules
The implementation

Wrote Objective-C and Java native modules for React Native, including iOS time-sensitive and communication notifications with custom contact images.

Conceptual overview · mobile notification delivery.

Going beyond React Native

I wrote Objective-C and Java native modules alongside the React Native application. The shared app was only part of the implementation; platform-specific notification features needed native code as well. On iOS, I implemented time-sensitive and communication notifications with custom contact images. This let me work on the actual notification experience on the device, rather than stopping at the server’s delivery request.

Billing changes have to change access

I built the subscription lifecycle around more than a checkout screen. The web backend verifies billing webhook signatures, handles completed purchases, subscription changes, and cancellations, and updates membership state. When access is paused or canceled, it invalidates the relevant web and mobile sessions so an old session does not keep representing an active membership.

The tools behind the subscriber experience

I also built the business dashboard used to manage notification topics and dispatch alerts. Staff can inspect notification history, choose a topic, and send a test to their own device before sending more broadly. That made the product an operational tool for community staff as well as an app for subscribers.

Keeping configuration changes consistent

A topic rename or merge affects more than its label. My implementation updates the associated automation references and requests a migration of notification history. I also exposed topic ordering and role-based restrictions, so organization-level configuration could shape what subscribers saw.

Visit Resellify
Project breakdown

Historical product

Supply

From commission to co-founder

I joined Supply through a commissioned development project and later became a co-founder. My engineering contribution centered on the React Native application for sneaker news, release information, and real-time alerts. That makes Supply a different chapter from a straightforward client handoff: my relationship with the product changed while I was involved. I describe both stages because they are part of the history of the work.

Historical Supply product artwork

What I shipped

I built Supply’s React Native application around sneaker news, release information, and real-time alerts. The scope brought an information product and an alerting experience together in a mobile app. My contribution was the application itself, rather than a single screen or isolated integration. The product artwork here shows that historical work, including the release and news experience it was built around.

Where it ended

Supply was subsequently sold and decommissioned. I include it here as historical product work, with that outcome made explicit. The screenshots represent the application from that period and should be read as an archive, rather than a preview of a currently available service.

Project breakdown

Websites & dashboards

defye

My scope

Through defye, I delivered production websites and operational dashboards. My scope covered the frontend, backend, integrations, and deployment, so the work extended beyond how a page looked. A website and an operational dashboard serve different needs, but both required me to connect the interface to the systems behind it. The examples here are historical product artwork from that work, including StockFinder, CopTools, and Genesis.

Historical product artwork from the AXSYS archive.

Project breakdown

Open source

HyperExpress

I created and maintain HyperExpress, a Node.js HTTP/WebSocket framework.

HyperExpress combines an Express-like JavaScript API with uWebSockets.js for HTTP and WebSocket services. It includes routing, middleware, server-sent events, multipart uploads, and TLS host support. Compatibility with Express middleware is partial.

2K+ GitHub stars · 5K+ npm downloads in the week of September 10–16, 2026

GitHub snapshot: 18 September 2026. Downloads count package fetches, not unique users.

Public library / conceptual flowRequest lifecycle
ApplicationHandlers & streams
HyperExpressRouting & middleware
uWebSockets.jsNative binding
TransportHTTP / WebSockets
Explicit ownership. Completion guards. Backpressure-aware output.

Based on the project README and native lifecycle contract.

Working across the native boundary

Working across the native boundary means I have to account for the lifetime of objects and handles. Native request objects are only valid inside their callback, so HyperExpress copies request metadata at entry and guards response operations after completion. Incoming callback memory must also be copied before it is retained. The shutdown contract tracks listen-token ownership and forbids closing foreign or already-closed handles, keeping cleanup subject to the same ownership rules.

Engineering lens / HyperExpressExplore payload ownership

What survives
the callback?

Follow the lifetime of a WebSocket message.

Callback beginsCallback returnsAsync work
Callback lifetime
Callback-owned memory

Retain the message? Copy it before the callback ends.

WebSocket payload ownership is explicit: callback-lifetime ArrayBuffer messages are distinct from copied ArrayBufferSafe messages that can survive asynchronous work. This exposes a choice between retention safety and copying.

Completing requests once

Middleware can call next() and later settle its returned promise, or accidentally call next() twice. In HyperExpress, I account for that by advancing the chain only once. The regression scenario checks that the first completion succeeds and the duplicate is ignored. This matters at the framework boundary because an application mistake should not accidentally dispatch the next handler a second time.

Streaming to slow consumers

In the streaming implementation I maintain, a partially accepted write is not treated as a completed chunk. The code tracks the native write offset and retries the remaining bytes after drain. It also checks a declared content length against the bytes read and destroys the source stream when the response closes. Those details tie transport progress, source cleanup, and response completion into one lifecycle.

Keeping the project maintainable

I maintain separate runtime, TypeScript, and load-test commands for the project. The load gate covers HTTP, multipart handling, aborts, WebSockets, and memory stress, rather than just a successful request. Version 7 supports Node.js 22, 24, and 26 while preserving CommonJS and snake_case APIs. It also pins uWebSockets.js to v20.69.0, making the native dependency version an explicit part of the supported setup.

Project breakdown

Open source

LiveDirectory

A directory that stays in sync

I built LiveDirectory to make file-backed content easier to use in a web server. It watches a directory with chokidar and updates its content store as files change, including files in subdirectories. That makes file watching part of the library’s responsibility instead of plumbing each application needs to assemble. The directory becomes a managed source of content that can follow changes on disk.

Files as reusable objects

I separated the directory-level API from individual LiveFile objects. That gives application code a way to work with a managed file as well as the collection it belongs to. Alongside asynchronous file management, the package supports ETags and hot reloading. These pieces bring file changes and reusable content objects into the same API, which is the main job I wanted the library to handle.

Read the source & documentation
Project breakdown

Open source

Cached Lookup

Reuse expensive work

I built Cached Lookup around a lookup function and the arguments passed to it. A caller specifies how old a cached value may be, and the package retrieves a fresh one when there is no suitable result. The lookup can be synchronous or asynchronous, so the API is not tied to one kind of data source. The basic boundary is small: application code supplies the work, and the library manages whether its result can be reused.

Freshness is a choice

I exposed freshness as an explicit choice instead of hiding it behind one caching policy. The cached() path waits for a sufficiently fresh result. The rolling() path returns an existing cached value while refreshing in the background. Those are different tradeoffs between freshness and response latency, and the application can choose the one that fits the call.

Control the lifecycle

I included explicit expiry, clearing, update timestamps, and inspection of in-flight lookups. A cache needs a usable lifecycle as well as a fast path for returning a value. Automatic purging handles removal, while fresh and purge events expose changes to callers. Together, these controls make it possible to work with the cache’s state instead of treating it as an opaque wrapper around a function.

Read the source & documentation
Project breakdown

Open source

NetworkCluster

One provider, multiple consumers

I built NetworkCluster around a provider that accepts multiple consumers. Each consumer connects to one provider, using WebSockets and HyperExpress underneath. Connection and message events expose that relationship to application code. The scope is a communication layer with an explicit topology, rather than a promise that the library will coordinate every aspect of a distributed application.

Connections need a lifecycle

I exposed authentication hooks, TLS options, heartbeat settings, payload limits, and backpressure limits. The connection needs those controls in addition to a method for sending messages. Consumers also have a configurable reconnect policy. I separated a temporary disconnect from a permanent close after retries are exhausted, so application code can distinguish those states.

A boundary applications can use

I provided readiness promises so application code can wait for a connection before using it. Message handlers and send methods form the communication surface once that connection is available. Explicit destroy methods clean up the underlying components when the work is done. That gives callers a lifecycle they can follow from readiness through communication to teardown.

Read the source & documentation

Background & recognition

Jersey CityStaten IslandBrooklynQueensManhattanNew YorkNEW JERSEYN
2021–2023 / Education

Computer science

B.S. Computer Science , CUNY College of Staten Island, September 2021–December 2023.

2018 / Recognition

Congressional App Challenge

Co-created SchoolCrypt with Anthony Squillacioti and David Remyes, the 2018 Congressional App Challenge winner for New York’s 11th District.

Recognition at the Intrepid in New York City.

View the award record
2016–2020 / Early foundations

Tottenville High School

Tottenville High School, 2016–2020. Graduated in the top 10 of my Science Institute cohort, served as president of the Computer Science / Entrepreneurship club, and participated in the school’s technology and hardware overhaul.

Resume

The same experience, with the focus that fits your team.

Want a closer technical look? Extended resume2 pages · PDF

Get in touch

Have an engineering role or project in mind? Let’s talk.