Track

Products & Platforms

Kalor, a live event-ticketing platform, built twice: React Native on Firebase, then rewritten to Flutter on a .NET 10 and PostgreSQL backend.

From a local-storage side project to a live platform handling real payments for real organizers, with a commercial NFC-payments client engagement in between.

2024

FitMe

Jul 2024 – Aug 2024

Solo · archived

A nutrition tracker, and my first Flutter app.

  • Editing or deleting a food copies the old version into a history file first, so meals already logged keep resolving and past totals never change retroactively.
  • Meals are sharded one JSON file per month, keyed by day, behind an in-memory cache, which is what makes browsing previous days cheap.
  • Products missing per-serving macros are rejected on import rather than stored incomplete, and results are re-ranked so exact substring matches come first.
  • A charting library lasted five days before I replaced it with a hand-written bar chart, because macro targets need a minimum and a maximum band.
commits
26
LOC
2,236
FlutterDartOpenFoodFacts API

2025

Kalor (first generation)

Feb 2025 – Feb 2026

Team of 4, 59% of commits · archived

An event-ticketing platform shipping iOS, Android and web from a single Expo codebase, on Firebase.

Technical lead. Owned payments, the service layer and the security rules; screens and components were shared.

  • Charges are created against the organizer's connected Stripe account with an application fee attached, so the organizer is paid directly and the platform takes its cut in the same intent.
  • The webhook keys off application_fee.created rather than payment_intent.succeeded, then walks fee to charge to payment intent to metadata, because on a connected account the fee event is the reliable platform-side signal.
  • With Firestore the rules are the API rather than a check behind one: eleven named predicates, a collection-group rule so a user's tickets stay authorized per document across every event, and a payments rule where the only client write permitted is cancelling your own.
  • Redeem links are claimed inside a transaction: check the link, create the ticket, increment the counter and append to the guest list, or none of it.
  • 2,600 lines of service layer where errors are returned rather than thrown, and nothing above it knows what a Firestore document looks like.
commits
900
releases
11
branches
107

Retired, and the repository stays private: it carries a year of a live product's payment and identity plumbing.

One test file and no CI, against a contributing guide that required a pipeline and 80% coverage.

React NativeExpoTypeScriptFirebaseCloud FunctionsStripe

NFC Wearable Payments App

Jul 2025 – Feb 2026

Client work · shipped

A companion app for a contactless payment ring, wrapping a licensed provisioning SDK in a custom React Native native module on both platforms.

Freelance client work I found and contracted myself, under a signed NDA. Sole developer, and the only developer on it until handover. Described generically for that reason.

  • Provisioning is modelled as events rather than promises, because it is a multi-round conversation (tap, server round-trip, user input, tap again) and a single promise cannot express step three of seven.
  • The NFC hardware has one callback slot and separate manager classes were each claiming it, so whichever claimed it last silently won. Collapsing them into one owner of the session makes that race impossible rather than guarded against.
  • A four-state delivery machine decides when recovery is appropriate: a dropped session restarts itself while data is moving to the device, and deliberately does not while the flow is waiting on the user.
  • The platform boundary is a TypeScript discriminated union rather than a class hierarchy, so exactly one platform branch survives in the whole interaction path, and only because app store links use different URI schemes.
  • Once the device identifier is cached some operations need no tap, so a parallel entry point emits a flag that tells the UI to show a spinner instead of a tap prompt, and NFC starts lazily.
commits
146
hand-written native LOC
~2,160
authored
100% to handover

No client, product or SDK named, and no code, repository or screenshots. Architecture and platform technique only.

Delivered as a release candidate that passed the payment partner's review. Store submission sat with the client.

No tests and no CI, on a codebase whose difficulty is entirely state machines and failure paths.

React NativeExpoTypeScriptSwiftCoreNFCKotlinRxSwiftRxJavaFirebase

2026

Kalor

Jan 2026 – present

Co-founder and CTO · shipped

The live ticketing platform, rebuilt on Flutter over a .NET 10 and PostgreSQL backend.

Founded with two partners. The only developer present through every stage of the product. The backend is 98% mine, including the data layer, payments, permissions and invoicing. The Flutter client is 88% mine, with one developer contributing the design system and a set of screens over the first five months.

  • Rebuilt the platform off Firestore onto PostgreSQL and .NET once query limits made the analytics we had planned impossible. Large guest lists went from over a minute to instant.
  • Payments are destination charges: the money lands with us and only the ticket price transfers on to the organizer, so the service fee is simply the part we do not transfer. That is what makes partial refunds and matching credit notes possible.
  • QR check-in validates against a per-ticket scan window, so a ticket cannot be used before doors open or after the event has finished. The largest event run through it admitted over 2,000 attendees.
  • Overselling is prevented by one conditional update that increments only if the result stays within the cap. Per-option promoter caps were layered in front of it rather than replacing it, so older links kept working without a migration.
  • Staff, guests, promoters, discounts and announcements are one invite abstraction rather than five features: a type, an optional token, a usage cap and a typed payload.
  • Authorization is an endpoint filter rather than something each of the 119 handlers remembers to do, and notifications dispatch after commit so a rolled-back transaction can never tell a guest they have a ticket.
Paid events run
20+
Organizers
10+
Accounts created
8,000+
API endpoints
119

A shipped commercial product, so there is no repository and no code on this page. Architecture and decisions instead.

Started as guest-list management for my co-founders' own events; now a general ticketing platform used by other organizers.

The backend suite of 594 tests runs on every pull request, but it was generated rather than designed, so it detects change rather than encoding intent. The Flutter client has four.

FlutterRiverpodDart.NET 10C#EF CorePostgreSQLStripeDockerSvelteKit

Visual Builder

Jun 2026 – Jul 2026

Solo · prototype

The beginning of a Figma-like app maker: a desktop tool for composing an application visually.

  • Elements live in a normalized id-keyed store holding parent and child pointers rather than a nested tree, so moving or deleting one is a local operation instead of a walk.
  • The type system encodes that a view can never be a child, and a typed lookup narrows the static type from a runtime check, so the invalid case cannot be written rather than being checked for.
  • Canvas zoom is anchored to the cursor, and dragging divides pointer movement by the zoom factor, taking zoom as a getter so the handler is not rebuilt every time the zoom changes.
commits
9

Early, and nine commits in. The editor shell works; persistence and export are not built yet.

Svelte 5SvelteKitTypeScriptTauriRust