Zudo POS
A multi-tenant point-of-sale system — shifts, sales, suppliers, purchase orders, loyalty and crypto settlement — with four role-specific portals on one Fastify process.
- 19
- Prisma models
- 27
- Route modules
- 4
- Role portals
- 1
- Fastify process
- Role
- Author
- Year
- 2026
- Type
- Point-of-sale & retail back office
01Overview
Zudo POS is the till and the back office for a small business: cashiers ring up sales inside a shift, managers handle stock and suppliers, owners see the numbers, and the platform operator administers the tenants above all of them.
Nineteen Prisma models and twenty-seven route modules on Fastify, with libSQL as the datastore and four separate front-of-house portals — cashier, manager, admin and superadmin — plus an onboarding flow.
It is deliberately the opposite of Telente Store. That one optimises for an online catalogue; this one optimises for someone standing at a counter with a queue behind them.
02The problem
A point-of-sale system is judged on the two seconds between scanning an item and the drawer opening. Anything that adds a round trip to that path is a design failure, whatever it does for the architecture.
But the same data has to serve the back office, where the questions are entirely different — what did we sell, what do we owe suppliers, who was on shift when the till came up short, what is this month's tax position.
And in the market this targets, "payment" is not one thing. It is cash, transfer, card and increasingly crypto, and the reconciliation story for each is different.
03Architecture
- 01
Business as the tenant boundary
Business and BusinessSetting sit at the root; every other model hangs off a business. Multi-tenancy is a data-model decision made once, not a filter remembered in every query.
- 02
Shifts as the unit of accountability
A Shift opens with a float and closes with a count. Sales, expenses and discounts attach to it, so a discrepancy has a person and a window attached rather than being a number that does not add up at month end.
- 03
Twenty-seven route modules
Separate routers for auth, sales, products, customers, staff, roles, shifts, suppliers, purchase orders, returns, expenses, taxes, discounts, loyalty, billing, billing plans, crypto, analytics, reports, notifications, mail, uploads, settings, audit log, admin, public and page serving.
- 04
Crypto settlement as its own module
BusinessCryptoWallet and CryptoPayment are modelled separately from the sale rather than as another payment method, because settlement is asynchronous and confirmation is probabilistic. A sale can be complete while its crypto payment is still pending.
- 05
Four portals, one process
public/cashier, public/manager, public/admin, public/superadmin and public/onboarding are separate static front-ends served by the same Fastify instance, each seeing only the routes its role can use.
- 06
libSQL over a hosted Postgres
Prisma with the libSQL adapter, so the same schema runs against a local SQLite file or a replicated Turso database. A shop with bad connectivity gets a local database; a chain gets replication.
- 07
Purchase orders close the loop
Supplier, PurchaseOrder and PurchaseOrderItem mean stock arrives against an order rather than appearing. Receiving is what updates inventory, which is what makes the numbers reconcilable.
04Key decisions
Static per-role portals, not one app with guards
A cashier's screen has perhaps six actions on it. Shipping them the manager's bundle and hiding it is slower to load, easier to get wrong, and a worse experience for the person who uses it eight hours a day.
Crypto payments modelled as pending by default
Treating an unconfirmed transaction as settled is how a POS system loses money. The sale records the intent; the payment record carries the confirmation state independently.
Audit log from the first commit
AuditLog and Session exist because cash-handling software gets disputed. Retrofitting an audit trail after the first disagreement is too late by definition.
05Stack
- TypeScript
- Fastify
- Prisma
- libSQL / Turso
- Zod
- JWT
- bcrypt
- Nodemailer
- QR codes