All work
TPOffline-First · OperationsFull-Stack PlatformsCase study

Technaut Parking — Offline-First Parking MS

Issues and closes a parking ticket in seconds with the internet down, and a bill that cannot be shrunk by changing the device clock.

Offline-first parking management for lots where the booth operator must issue and close a ticket in seconds, with or without internet: a Flutter booth terminal on local SQLite, a NestJS cloud API on MariaDB for consolidation and rates, and a React owner dashboard.

Source private
Technaut Parking — Offline-First Parking MS interface
Industry
Parking & Facilities
Role
Architect & Full-Stack Engineer
Type
SaaS Platform
Year
2026
Status
Delivered
Fully offline
Booth
Integer paise
Money
2 impls, 1 fixture set
Fee Engine

Overview

Technaut Parking is built around one constraint: the booth cannot wait for the network. The Flutter terminal — Android, Windows and iOS — runs fully offline against a local SQLite database and syncs to the cloud whenever a connection appears.

Entry is a single tap: pick the vehicle type, optionally add helmet or luggage, and a slip prints with a short token and a QR. The vehicle number is optional and can be filled at entry, at exit, or never — because insisting on it is what makes operators write tickets on paper instead. Exit is a QR scan or four digits.

Two decisions protect the money. Durations are computed from a monotonic counter rather than the device wall clock, so changing the device time cannot reduce a bill; clock jumps are logged and surfaced to the owner. And all amounts are stored as integer paise, with the fee engine implemented in both Dart and TypeScript and both tested against the same golden fixtures — so the price quoted at the booth and the price consolidated in the cloud cannot disagree.

The owner dashboard covers live occupancy, collections by payment method and vehicle type, tickets, shifts, alerts and paired booth devices.

The problem

A parking booth has seconds per vehicle and frequently no internet. Cloud-only systems stall the queue when the link drops, and any system that trusts the device clock or uses floating-point money can be made to under-bill — accidentally or deliberately.

Discovery

Watched where a booth actually loses time and found it was data entry, not payment: insisting on a vehicle number at entry is what pushes operators back to paper. Also found that device time is not trustworthy on shared terminals.

The solution

Made the booth terminal the source of truth while it is offline: a Flutter app on local SQLite that issues and closes tickets with no network, then reconciles with a NestJS cloud API. Durations come from a monotonic counter, money is integer paise, and the fee engine exists in both languages tested against one shared fixture set.

Technical stack

Frontend

  • Flutter
  • React
  • Vite
  • Tailwind CSS
  • TypeScript

Backend

  • NestJS

Data

  • SQLite

Infrastructure

  • Docker

Also used

  • Prisma
  • MariaDB 11.8
  • shadcn/ui

System architecture

  1. 1

    Flutter booth terminal (Android, Windows, iOS) with a local SQLite database as the offline source of truth

  2. 2

    NestJS + Prisma cloud API on MariaDB 11.8 for consolidation, reporting and rate management

  3. 3

    React + Vite + Tailwind + shadcn/ui owner dashboard

  4. 4

    packages/pricing-fixtures — golden fee cases the Dart and TypeScript engines are both tested against

  5. 5

    Device pairing so each booth terminal is a known, revocable client

  6. 6

    Docker Compose for local MariaDB; the same schema runs in production

Engineering challenges

Challenge

Duration billing read from the device wall clock, so moving the clock back turned a long stay into a short one and left no trace.

How I solved it

Computed durations from a monotonic counter that a clock change cannot rewind, and logged clock jumps as alerts the owner can see — turning tampering from invisible into reportable.

Challenge

The fee engine existed on the booth and again in the cloud, so the price printed on the slip could disagree with the price consolidated later.

How I solved it

Kept both implementations but made them answer to one authority: a shared golden-fixture package that both the Dart and TypeScript engines are tested against, so a divergence fails the build rather than a reconciliation.

Challenge

Requiring a vehicle number at entry cost more seconds than the queue had, and operators simply stopped issuing tickets.

How I solved it

Made the number optional throughout — fill it at entry, at exit, or never — and reduced entry to a single tap for vehicle type, keeping the token and QR as the identity.

Core features

  • Booth terminal runs fully offline on local SQLite; syncs when a connection returns
  • One-tap entry with printed slip carrying a short token and QR
  • Optional vehicle number — capturable at entry, at exit, or never
  • Exit by QR scan or four-digit token
  • Durations from a monotonic counter, so a clock change cannot shrink a bill
  • Clock jumps logged and surfaced to the owner as alerts
  • Amounts stored as integer paise; fee engine in Dart and TypeScript against shared golden fixtures
  • Vehicle types with helmet/luggage add-ons and owner-managed rate cards
  • Shifts with per-operator collection reconciliation
  • Device pairing for booth terminals
  • Owner dashboard — live occupancy, collections by method and vehicle type, history

Results & impact

The booth issues and closes tickets in seconds regardless of connectivity, and the owner still gets consolidated collections, shift reconciliation and occupancy in the cloud. Money handling is exact by construction, and clock tampering surfaces as an alert instead of a shortfall.

Screenshots

Technaut Parking — Offline-First Parking MS screenshot 1
Technaut Parking — Offline-First Parking MS screenshot 2
Technaut Parking — Offline-First Parking MS screenshot 3
Technaut Parking — Offline-First Parking MS screenshot 4
Technaut Parking — Offline-First Parking MS screenshot 5

What I learned

  • For a booth, offline is not a degraded mode — it is the normal mode, and the design has to start there.
  • Two implementations of a pricing rule are acceptable if one shared fixture set is the authority over both.
  • A monotonic counter is a cheaper anti-tamper measure than any amount of policy.

Related work

All projects
Next step

Have a system like this to build?

I take projects from the first conversation through to production, and stay on for the support that follows.