Notes · · 3 min read
A till is a distributed system
ÇabukPOS is a point of sale for restaurants and grocery stores. The demo is easy: turn off the Wi-Fi and keep selling. The engineering is in what happens when the line comes back, the printer jams and two tills act at the same time.
A till that stops when the internet stops is not a till. That sentence sounds like a networking requirement, but it is really a statement about where the sale lives. If the sale is a request to a server, the server is part of the shop. If the sale is a local record with a sync process behind it, the shop can keep trading while the connection is gone.
The network is optional; the queue is not
Offline-first is often described as a mode. In practice it is a queue with a policy. A sale is written locally, the interface moves on, and a background process sends it when it can. The queue has to survive a reload, a crash and a closed laptop lid — otherwise “offline support” is a demo that works until the first busy Saturday.
Ordered sync, and conflicts you can review
Once more than one device can write, “sync” becomes a question about order. ÇabukPOS syncs queued sales in order and surfaces conflicts for review instead of silently overwriting the last version. A conflict is not an edge case to hide; it is information about what happened in the shop while the network was down.
The alternative — last write wins — is easy to implement and impossible to explain to a cashier who has just discovered that a sale disappeared.
Hardware is part of the boundary
A point of sale is not only a web page. Receipt printers and kitchen screens sit on the local network, and they cannot depend on a cloud round trip. The product ships a local print agent and a LAN hub for kitchen and floor tablets, so taking an order and printing it remain one action even when the internet does not cooperate.
This is where offline-first stops being a frontend pattern. A payment can succeed while a receipt fails, a printer can be out of paper, and a kitchen screen can be on a different subnet from the till. Each of those is a failure mode with its own recovery path.
Numbers that never skip
Fiscal document numbers are the least forgiving part of the system. They cannot be burned, duplicated or reordered to make a retry look clean. ÇabukPOS issues them under a row lock that waits rather than skips, so concurrent tills cannot take the same number or leave a hole.
A queue makes this harder, not easier: a retry after a timeout must be able to discover whether the first attempt already committed. The number is a piece of state that has to survive every uncertain outcome between the till and the database.
Tenant isolation belongs in the database
Multi-tenant point of sale has a quieter failure mode: one query forgets a business filter and returns another shop's sales. In ÇabukPOS every business table runs under forced row-level security in PostgreSQL, so a query without the right tenant context returns nothing instead of someone else's data.
The application still filters. The difference is what happens when it does not.
What this costs
Offline-first adds work everywhere: durable queues, conflict states in the interface, hardware agents, and sync monitoring that can tell you a device has been offline for an hour rather than a week. It also makes testing slower, because the interesting failures happen between two systems rather than inside one function.
The payoff is a product that behaves like equipment rather than a website. The till keeps selling, the kitchen keeps printing, and the numbers stay correct when the network comes back.
Built while working on
ÇabukPOS — read the case study