Skip to content

Retail — grocery store automation

A till that keeps selling when the line goes down

Point of sale, stock and a central panel for small and mid-sized grocery stores. The till sells with no network at all; when the connection returns, the queued sales reach the server in a single request and none are lost.

Stack: Go · TypeScript · Electron · React · Next.js · SQLite · PostgreSQL · Docker

Measured results

61 ms
50 sales with the network cable pulled
M1 run report, 22 August 2026
792 ms
Sync after reconnection — a single push request
M1 run report, 22 August 2026
51/51
Events accepted by the server — 0 rejected, 0 retries
M1 run report, 22 August 2026
217 ms
Catalogue pull, one call
M1 run report, 22 August 2026
126
Contract tests — server output read against the client schemas
Market handover note, 27 August 2026

The problem

The desktop systems that small and mid-sized grocery stores in Turkey run on tend to repeat the same three things.

  1. Stock and pricing live in one program, selling in another. A transfer service bridges the gap once a minute. When that service stops, the till happily keeps selling a repriced product at yesterday’s price.
  2. The main menu carries around twenty-five modules. A small store might use eight of them. The rest is paid for in training time and in surface area for mistakes.
  3. The database sits on the store’s own computer. So installation means a technician visit and support means a remote desktop session.

All three rest on the same assumption: the till works as long as the system behind it is reachable. But when the store loses its connection, selling does not stop — it must not stop. There is a customer standing at the counter, and the queue does not wait for the network.

The approach

The till is a single application and it carries its own database. A sale is written locally — the sale, its lines, its payment and the record that enters the outbound queue, all in one transaction. The till never waits on the network. That is not a performance optimisation; it is the rule the architecture is built around.

When the connection comes back, the sync engine sends the queue to the server as one batch. Records leave the queue only when the server has accepted them. If an event is not accepted, it stays queued and is retried. Lost sales come from records assumed to have landed.

Ordering matters too. The event that opens the till session is queued before any sale; otherwise the server receives sales referring to a session it has never heard of and rejects the whole batch.

Because the server and the till are written in different languages, the data contract is generated from a single source. A contract suite then reads the server's real output against the client's schemas — so the claim that both ends speak the same wire is tested rather than assumed.

Distribution was part of the problem as well. The installer is downloaded from the web and the application updates itself, which removes the technician visit from the installation path entirely.

The evidence

The milestone was written as one sentence before any of it was built: fifty sales are made on a machine with the network cable pulled, and when the cable goes back in, every one of them reaches the server.

The run was executed on 22 August 2026 against the till's production code — the very functions the on-screen buttons call. Nothing was mocked.

  • The catalogue was pulled from the live server in 217 ms, in one call.
  • With the cable out, 50 sales took 61 ms, made against the real products and prices just downloaded, barcode lookup included.
  • 51 records accumulated in the outbound queue: 50 sales and 1 till-session opening.
  • On reconnection the sync completed in 792 ms, in a single push request.
  • The server accepted 51 of 51: no rejections, no transport errors, no retries. The queue ended the run at zero — no sale was lost.

Four days later the human end of the chain was walked through as well: a sale rung up by barcode closed as receipt A0001, ₺217.30, cash, and the same record was then read back from the server to confirm it. Catalogue search worked while the till was offline, and the queue drained on its own once the connection returned.

The distribution path was proved separately: the installer was published to object storage and downloaded with no authentication — HTTP 200, 77.2 MB, with the release manifest verified by sha512.

Where it stands

The product has been proved end to end in staging. It is not yet running in a store: production was deliberately left unbuilt and will be stood up with the first signed customer. The stock ledger is still being written.

We say so here because what those numbers are worth depends on knowing what they are not.