An offline-first till that cannot be stopped by the internet
The shop has to keep selling when the line dies, and nothing may be lost when the power goes with it.
| Qty | Item | Amount |
|---|---|---|
| 1 | Arabic coffee 250g | 3.750 |
| 2 | Basmati rice 5kg | 12.400 |
| 1 | Dates, medjool 1kg | 2.250 |
| 4 | Bottled water 1.5L | 8.900 |
| Net · VAT 5% | 27.300 · 1.365 | |
| Total | 28.665 | |
Interface rendered in code from the platform’s own structure — illustrative, not a screenshot. Basket contents are invented.
The problem
A desktop point-of-sale client for the same platform. Retail in the Gulf does not stop because a connection does, and a till that refuses to sell is worse than no till at all. The hard part is not working offline — it is coming back online without losing or duplicating a single sale.
In active development and pre-pilot — not in production, and not part of the twenty-plus live businesses above. I am saying so here rather than letting the two blur together.
What I built
I architected and own it end to end: the failure model, the sync protocol, the security design, the technology choices, the written standards and the review gate. The code itself is AI-assisted, written under those standards and reviewed by me. I say that plainly because it changes what you should ask me about, and I would rather you asked. Every review decision is recorded. Two are worth reading: the offline sign-in design I killed after reading how the server hashes passwords, and a dependency race I caught that would have shipped an unencrypted database with nothing visibly wrong.
Data flows two ways, and the two never meet. Products and prices come down from the server. Sales go up from the till. The till is never the authority on anything it receives, so two versions of the same record can never disagree. There is no conflict-resolution code in the system because the design makes conflicts impossible, which is cheaper than handling them. The till gives every document its own unique id before sending it. That makes the retry rule simply "send it again" — the server recognises a repeat and ignores it, so there is no did that work? logic to get wrong. The local database is encrypted, and its key is held by Windows rather than stored inside the database it protects. A copy is escrowed so a dead machine does not mean lost sales.
The ordering that decides everything
The receipt prints last — after the journal write and after the transaction commits. It would be friendlier to print first and commit after. But a customer walking out holding a receipt for a sale that is not durably on disk is an unrecoverable failure, and a customer waiting an extra 80 milliseconds is not. Every ordering decision in the write path falls the same way: always fail in the recoverable direction.
What it produced
- A written failure model: for every step in a sale, what happens if the power goes at exactly that moment, and which way the system should fail.
- Architecture rules the build enforces rather than a reviewer — a layer that reaches for something it should not is a failed build, not a comment.
- Four architecture decision records, a threat model, an incident-response procedure and ten numbered security rules, each carrying its why.
What I would do next
A pilot. Then TypeScript on the web platform: 1,026 JavaScript files with no types means a renamed field is only found when a user hits it. And a pipeline that gates releases, rather than the scripts I run against my own servers today.
Ten minutes on a screen-share tells you more than this page can. Get in touch.