Thanks to visit codestin.com
Credit goes to dank0i.github.io

Aryan Malhotra

I build things I actually use. Everything below runs in my apartment or on my own hardware, and most of it started because something annoyed me.

Reston, Virginia[email protected] github.com/dank0i linkedin.com/in/aryanmal

Right now I'm patching a USB mixer's audio routing. Open to software engineering roles.

Move your cursor across the page. The field behind this is encrypted firmware, and wherever you pass, the cipher resolves into the code underneath. It is the SC3 project below, running as a shader.

I graduated from the University of Maryland in May 2026 with a BS in Computer Science, a 3.91 GPA, Dean's List throughout, Cum Laude, and a nomination for the University Medal.

Most recently I interned at Civic Group, where I shipped production features on a case management platform that caseworkers and admins use every day: analytics dashboards, automated inactivity detection, notification integrations, and sentiment analysis over case notes that flagged cases before they escalated. React and TypeScript on the frontend, with Node.js and AWS behind it.

pc-bridge

Systems programming

1.3 MB
for the whole agent, using under 1% of a CPU

A small Rust program that lets my home automation system see and control my gaming PC.

Dec 2025 to nowRustWindows and Linux254 commits

Figure 1The seven power states. Each transitional one carries a timeout, so a boot that fails resolves instead of stranding the machine.

I play most of my games from bed, streaming the PC across the room rather than sitting at it, so Home Assistant, which is the open source home automation platform I run the apartment on, needed some way to wake the machine, start things on it and put it back to sleep without me getting up. This is the agent that does it. Windows tells it directly when a game process starts, so nothing sits there polling and it costs almost nothing while idle, and it finds my Steam library by reading Steam's own binary catalogue file, which took a while to work out.

Every feature is off until you switch it on. Commands arriving over the network get rejected if they contain shell metacharacters, so a malicious message cannot smuggle in a second command, credentials live in the Windows credential store and never touch a config file, and updates are signed, so a tampered or older build refuses to install itself. I wanted that settled early, since this thing listens on the network and launches programs on my behalf.

Two releases shipped a download progress bar that lied, and I pulled both. Steam quietly shrinks its own total when you resume a download, so a download that was 68% finished reported 13%, and I had only ever tested against fresh installs where that never happens. The number now comes off a channel the Steam client itself uses to report to other devices, which gives the true totals, and nothing extra runs on the gaming PC to get it.

Figure 2The settings window. Every feature is off until you turn it on, here 19 of 33.

The Home Assistant half is a separate integration that models the PC as seven power states, because a machine that is booting is neither on nor off, and for those thirty seconds everything watching it does something stupid. It checks the machine is alive two independent ways, so a PC that answers the network but has no agent running is still distinguishable from one that is genuinely off, and it waits for three missed replies before believing anything, since one dropped reply under load is not a shutdown and a state that flickers will fire every automation watching it. I found that out the annoying way.

Drive it, and watch the two signals disagree
off
ping agent over MQTT transitional

The trace scrolls in real time. Through booting the ping line is already up while the agent line is still down, and that gap is the whole reason the integration exists.

SC3

Cryptanalysis

100%
of it recovered, all 315,654 words, none left unsolved

Broke the encryption on a desk mixer's firmware so I could fix a bug the manufacturer never did.

Aug 2026Firmware reverse engineeringCryptanalysisPython

I run my motherboard's line out into a FIFINE SC3 desk mixer, mainly because it means the line-in fader works as a hardware volume knob, which is easily the most useful thing on my desk. The catch is that line in also gets summed into the microphone the mixer sends back to the computer, so everyone on the call just hears themselves back. FIFINE's own V22 update says this is fixed, and since it closes that loop and leaves the analog path alone, that is exactly what I wanted.

The problem is that the routing lives inside the firmware, and the firmware is encrypted, and there is no public tooling at all for the audio chip this thing uses. So I left the encryption alone for a while and went looking at everyone else's firmware, collecting every image I could find built on the same chip. 81 of the 87 I ended up with ship completely unencrypted, because encryption is a per-vendor option and most vendors just leave it off, and once you have the same code in both encrypted and plain form you can work the cipher backwards from the pair. Two of those 87 cost me a weekend before I worked out they were a different revision.

Figure 1The same 64 bytes of firmware, before and after. On the left there is nothing to search for. On the right the audio code is naming its own functions.

All 315,654 words of the code come out that way, with nothing left unsolved, which I honestly did not expect to get to.

Then the goal changed on me. The mixer has four physical faders and the firmware only tells the computer about one of them, and I had been meaning to build a knob box for a per-application volume mixer for months, so it was slightly ridiculous to be sitting in front of four faders that were already wired up and just never sent anywhere. The patch exposes all four, it is flashed and running on my desk right now, and the repository ships the analysis tooling only, no firmware.

The same 64 bytes, before and after the keystream

Shipped firmware

After the keystream

7.999 how random the bytes look. 8.0 is pure noise, real code sits far below it

Real bytes lifted out of the shipped image. On the left there is nothing to search for; on the right the audio code names its own functions, which is what made the routing findable.

Lock screen cards

iOS development

157 h
of gaming measured to calibrate the four colour bands

The live iPhone cards that airlines and delivery apps get, except mine are for my gaming PC and my washing machine.

2026Swift and SwiftUIApple push notificationsHome Assistant

Live Activities are those cards that sit on your lock screen and update themselves while a flight or a delivery is in progress. Apple hands them to any app with a push certificate, so I could never see a reason my washing machine should not have one. Four of them run on my lock screen now: washer, dryer, vacuum, and the gaming PC.

Figure 1Two hours into an Apex session, on my lock screen. Each bar carries a warning band and a critical band.

My home server pushes them straight to the phone through Apple's notification service, so a card just appears on the lock screen without the app ever being opened, and a custom piece of software I wrote for the server keeps track of which cards are live and makes sure an old one is torn down before a new one goes up, so they cannot stack.

For about three weeks the washer card would appear and then vanish a second later. I blamed the token, then the lifecycle, then the app itself, and it turned out that the second message I was sending to wake the card was tearing down the one that had only just been created. Sending the first message alone is completely stable, and the second is now only a fallback for when the app has been force quit.

The gaming PC card is a gauge, where the GPU, the processor, the room temperature and the wall power each draw their bar and their warning thresholds from the same numbers that pick the colour. I set those thresholds off 30 days of recorded history, because the first version spent 62% of my gaming time showing orange at temperatures that are actually cold for this card, and the room row never once left its first band in a month, so two of the three rows were carrying no information at all while the third one just shouted.

The same card, rebuilt in the browser
No game running
desktop
0:24:47
Elapsed

A different session from the photo above, driven by the same payload shape. Each bar carries a warning band and a critical band, drawn from the same numbers that pick the colour.

Cyclone 2

Reverse engineering

138%
of the stock report rate, from one changed byte

Found out why a game controller's fast wireless mode was the slow one, and fixed it.

2026GhidraRaspberry PiBluetooth firmware

I got curious about why the 2.4 GHz dongle mode on my GameSir Cyclone 2 felt worse than Bluetooth, which is backwards, since the dongle is meant to be the fast one. So I pulled the firmware off it and read through the disassembly until I found the code that decides how often the pad tells the computer where the sticks are. One byte was holding the dongle mode down to 166 updates a second while the same radio managed 480 in the other mode. Changing that byte took it to 230.

The first byte I patched did nothing at all. I had found something that looked exactly like the limiter, flashed it, measured, and got back exactly the number I started with, which was deflating. The real one was a divide-by-four two functions away that only the dongle path ever reaches.

Figure 1The finding. Both send paths check the same counter, but the dongle path adds one that has to reach four before anything is sent.

A Raspberry Pi sitting in between then pretends to be an Xbox wireless receiver, so Windows sees a normal Xbox controller, and separately pretends to be a PlayStation pad, which gets back rumble and battery reporting and lighting control that the stock Bluetooth mode never exposes at all.

After that I wanted to know how much headroom was left, so I measured the physical limit of the radio link, and just fitting the packets into the air caps it somewhere near 400 a second while the shipped firmware was already running at about 85% of that. So I stopped tuning. I would have liked more, but there just is not more there.

The byte that was holding it back

Football forecasting

Machine learning

92%
as accurate as the betting market, using no betting data

Predicts European football results, and grades itself against the bookmakers rather than against nothing.

Apr 2025 to Aug 2026PythonPyTorch and LightGBM25,979 matches

I am a huge soccer fan. I grew up watching it without ever quite managing to pick a club to support, and on nights when the team my dad and brother supported was playing we would bet on the score to decide who picked dinner. That is more or less where this came from.

It forecasts 25,979 European matches across 11 leagues, and every prediction gets scored against what the bookmakers were offering, since an accuracy number for football on its own tells you nothing at all. The home team wins about 46% of all matches, so a model that just always picks the home side already looks respectable, and anything I build has to clear that before it has done anything. This one gets 70% on matches that end decisively against a 61% baseline, and 52% on the full three way result against 46%, and without using a single piece of betting data it captures 92% of the bookmakers' forecasting skill.

There are four different models in it: a rating system that works like chess ratings, squad ratings from the FIFA games joined in as of each kickoff using the actual starting eleven, a statistical model built for football scorelines specifically, and a neural network that learns its own representation of each team. I spent a long time trying to close the last of the gap to the bookmakers before working out that the gap is information I do not have, and there is a section in the writeup showing how I established that.

Figure 1When the model says 60%, it happens 60% of the time.

Draws stay hard the whole way through and I wish I could fix it. Soccer is just beautifully unpredictable, and that is most of why I like it.

Figure 2Every combination of inputs, measured against the bookmakers' line.
Every number against the baseline it has to clear

An accuracy figure for football means nothing on its own. The home side wins about 46% of matches, so a model that always picks home already looks respectable.

Ledger

Backend and databases

100%
of postings must balance before they reach the database

An accounting ledger that stays correct when many transactions are posted at the same time.

Jul 2026Java and Spring BootPostgreSQLReact

I built this one because backend interviews keep asking about transactional correctness, and I would rather have written the thing than talk about it in the abstract. It is a double entry ledger, meaning every transaction moves money out of one account and into another so the books always balance, with a Java and Spring Boot API, a PostgreSQL database behind it, and a React frontend for accounts, balances and statements.

Submitting the same transaction twice does not post it twice, reconciliation runs on a schedule, and imported CSV files drop rows they have already seen. The part I actually care about is what happens when several postings land at once, where each one locks the accounts it touches and always takes those locks in the same order, and that ordering is the whole trick, since it is what stops two postings sitting there waiting on each other forever and what stops them reading a balance that the other one is halfway through changing.

Figure 1Accounts, posting, and a statement with a running balance.
Post to it. Try to break it.

Money is never created, only moved, so the balances always sum to zero. A repeated idempotency key returns the original transaction instead of writing a second one, and an entry whose debits do not equal its credits is refused before it reaches the database.