Software engineer · Dubai

I build complete products, on my own.

Not features — products. A commercial Windows app with native screen capture, on-device machine learning and the billing that pays for it, plus a computer-vision system that has never once alerted on a human. No team, no framework doing the hard part.

Available for remote work. Dubai, self-sponsored on a UAE Golden Visa — no sponsorship needed.
77,000lines of Rust, TypeScript
and SQL, shipped
208IPC commands across the
Rust ↔ front-end boundary
10languages, fully
translated
1engineer — design, code,
backend, billing, support

Selected work

Two things I built, finished, and put in the world.

Both are running right now. Neither is a tutorial, a clone, or a weekend prototype that stops at the happy path.

Commercial desktop product

Clarus

A screenshot and screen-recording app for Windows, sold as a subscription. It takes over Win+Shift+S from the Snipping Tool, records the screen, extracts text out of any capture without touching the network, and carries two full annotation editors — one for stills, one for video with a real timeline.

I designed it, built it, wrote the backend, built the installer, wrote the marketing site, and handle the billing. There is no team.

  • Native Windows, not a wrapper. Screen capture through DXGI Desktop Duplication with a GDI fallback, recording through Windows.Graphics.Capture into a Media Foundation encoder, audio through WASAPI, plus low-level input hooks and WndProc subclassing where the framework had no answer.
  • Machine learning that runs on your machine. PaddleOCR executing on tract, a pure-Rust inference runtime, with the models compiled into the binary. No API call, no upload, no account needed to read text off a screenshot.
  • A video editor that compiles to ffmpeg. Independent clip timeline, zoom segments, and redaction baked per-frame from the real source so moving content stays censored — all of it emitted as an ffmpeg filtergraph.
  • The unglamorous half, done properly. An Ed25519-signed auto-updater that checks hourly but only installs when you are not recording; a custom installer written in Rust; and an uninstaller that hands Win+Shift+S back to Windows on the way out.
  • A real backend behind it. Postgres with row-level security, Deno edge functions, Stripe checkout and webhooks as the only writer of entitlement, per-seat team billing, and a share-link service on Cloudflare that I took from 5.3 s to 0.6 s.

Computer vision · self-hosted

Cat Sentry

My cat has a medical problem, so he can sleep on the bed but must not be on the floor. That produces a genuinely awkward specification: fire when the cat is on the floor, never fire for the person in the bed, work in total darkness, store no footage, and be loud enough to wake someone asleep. Every off-the-shelf camera app fails at least two of those.

  • The safety property is geometric, not probabilistic. An alert needs the object labelled cat and its position inside a hand-drawn floor polygon that the bed lies entirely outside. No confidence threshold and no model update can ever produce an alert about the sleeping human — the guarantee comes from geometry rather than from trusting a classifier.
  • Tuned to prefer false positives, deliberately. A false ping costs one Telegram message. A miss costs a mattress. Optimising for precision would be optimising the wrong metric.
  • Zero JavaScript on the control page. It ships Content-Security-Policy: default-src 'none', so every animation is CSS keyframes on clipped SVG and state rides the form POST the page already causes.
  • Nothing leaves the house. Three hardened containers, the broker with no host ports at all, recording permanently off, and one line of outbound text to Telegram. About USD 18 of hardware and no cloud services.

How I work

I don't guess. I measure, and I keep the receipts.

Anyone can list a stack. What I think is worth showing is the debugging — the bugs where the obvious explanation was wrong, and the thing that found the real one was an instrument rather than an opinion. Every one of these is from a shipping product.

Reading the library, not the logs

A string comparison, three levels down someone else's stack

Frigate rejected my config silently and booted with no camera. The cause was upstream: it decides whether zone coordinates are fractions or pixels using p > "1.0" — a string comparison. "1.000" sorts after "1.0", so my fractions were read as pixel values and int("0.000") threw.

Writing 1.0 instead of 1.000 fixed it. There was nothing in the logs; I found it by reading the parser.

The asymmetry was the diagnosis

The overlay drew itself wrong, but cropped correctly

Occasionally my capture overlay rendered shifted and clipped — yet selecting where a letter should be still captured it. That asymmetry is the whole answer: the crop was right, so only the picture was wrong.

Both sides derived from devicePixelRatio and were exact inverses, so the maths always looked correct — relative to each other, and to a number the page merely believed. Both now read one measured rect off one element. It logs both halves of the moment, because Rust cannot see the CSS viewport and JS cannot see the window rect, and the bug is precisely those two disagreeing.

Two engines, opposite failures

Combining two OCR models that are each wrong

Windows' OCR gets word spacing right and characters wrong. PaddleOCR gets characters right and drops spaces entirely — Amazonorders, NotYetDispatched.

So the accurate pass keeps its characters and borrows the fast pass's split points. Strictly better than either alone, and it repairs what no dictionary could. One guard is load-bearing: a boundary is only adopted between two alphanumerics, or get_annotate_image comes back mangled.

2% → 100%

A search that was quietly finding almost nothing

Searching the text inside 1,543 screenshots "felt unreliable". It was not the OCR. Normalising strips spaces from the stored text and the query, so a two-word search was joined into one string and demanded the words be adjacent — cloud upload could never match a capture reading "cloud storage upload".

I measured it with 293 queries built from word pairs taken out of the captures themselves, so both words were provably present. Joined: 7 / 293. Per word: 293 / 293.

733 MB → 422 MB

Twenty processes down to seven, with one flag

Chromium gives every window its own renderer, and each costs roughly 100 MB of process baseline before any of your code runs. Fifteen pre-created windows idled at 733 MB and 4.4% CPU.

They are all one origin, so one flag — --process-per-site — put them in a shared renderer: 20 processes to 7, 733 MB to 422 MB, CPU to 2.0%. Measured as private working set; summing working sets across processes double-counts shared pages and would have told me a comfortable lie.

The instrument was the bug

Four measurements that were all wrong before one was right

A page I was optimising kept returning readings that made no sense. In order: the "database latency" I measured was a CDN accepting the connection, not the database answering. Cutting out the compute layer bought a fraction of what I predicted. And one timing that looked like a 4× win was an error page, which is fast because it is empty.

What actually mattered was the number of round trips. The lesson I kept is cheaper than the hours: check the status code before believing a timing, and make the subsystem say which path it took.


Capabilities

What I've actually used in anger.

Everything here appears in something I shipped, not in a course I finished.

Systems & native

  • Rust
  • Win32 / Windows API
  • DXGI Desktop Duplication
  • Windows.Graphics.Capture
  • Media Foundation
  • WASAPI audio
  • Low-level input hooks
  • ffmpeg filtergraphs
  • C-interop & COM
  • Multithreading, RAII, Drop safety

Product & front end

  • TypeScript
  • React 19
  • Tauri 2
  • Vite
  • Tailwind CSS
  • zustand
  • Konva / canvas
  • HTML, CSS, SVG animation
  • Accessibility & reduced motion
  • Internationalisation (10 locales, RTL)

Backend, infra & ops

  • Python
  • PostgreSQL, row-level security
  • SQLite
  • Supabase, Deno edge functions
  • Stripe: checkout, webhooks, seats
  • Cloudflare Pages / Workers / KV
  • Docker & Compose
  • MQTT, event-driven design
  • Computer vision & on-device ML
  • Git, CI, release engineering

Alongside those: threat modelling and privacy engineering, CVE triage, performance profiling, technical writing, and the parts of running a product that are not code — pricing, VAT, refunds, support.

Contact

Looking for a remote role where I can ship.

I work best given a hard problem and the room to finish it. If you have one, I'd like to hear about it — and I'm happy to walk through any of the work above in as much depth as you want.