● CASE STUDY · IDENTIUM TECH SOLUTIONS
Identium Retail
RFID exit gates, tag-reading POS and stock counts for retail stores
OVERVIEW
Retail shrinkage is a hardware problem pretending to be a software problem. The tags, the door readers and the handheld all exist; what was missing was one system where selling an item at the till is the same event that stops the door alarming on it, and where a floor count updates the stock page while it is still running. I built the backend, the web app and the Android handheld for this. Each company gets a physically separate SQLite file, and a host console onboards them.
THE CHALLENGE
The server sits on a box on the internet and the readers sit inside shops it can never reach, so nothing can be polled and every device has to push. That means the URL itself has to carry identity, because a reader cannot log in and a bare token does not say which tenant's database file to open. The other hard part was that fixed UHF readers free-run: they emit the same tag hundreds of times a second while anyone stands near the door, so there is no natural event boundary. I had to invent one out of the quiet gap between customers, and dedupe at three separate layers, because the browser wedge, the gate engine and the count ingest each see a different shape of the same flood.
WHAT I BUILT
- 01
An exit-gate decision engine in backend/lib/gateEngine.js: the single place where a tag read becomes a pass or an alarm. It judges the read against the store the gate belongs to, so another store's tag is not this gate's business, and an EPC that is not in the catalogue at all (a shopper's own tagged belongings) never alarms. It groups a free-running reader's continuous reads into sessions using a quiet-gap timeout, so one person walking through the door is one row and not forty.
- 02
Multi-tenancy where each company gets its own SQLite file. backend/db/tenants.js pools the handles and carries the active tenant through the request with AsyncLocalStorage, so every existing route kept its plain db.prepare() calls instead of threading a handle through hundreds of call sites. A separate isolation test suite checks that a tenant token is rejected by host routes, a host token by tenant routes, that suspending a company kicks live sessions, and that per-tenant quotas hold.
- 03
A reader ingest path built around the fact that a hosted server can never dial into a customer's shop LAN. Fixed readers POST their reads to a per-reader webhook URL carrying the tenant slug plus a 64-hex token (backend/routes/readerPushPublic.js), with a TCP listener for firmware that speaks newline-delimited JSON instead. The tag list is pulled out of whatever shape the vendor firmware sends, since every vendor spells it differently.
- 04
A stock count that is one system with the stock page. backend/lib/countIngest.js is the shared state machine, so a shelf-mounted fixed reader on inventory duty classifies an EPC as found, missing or extra exactly as the handheld does. Each scan also stamps last_counted_at and last_count_state onto the asset row and broadcasts on the change stream, which is what makes the web stock page show a live progress banner and a colour-coded count pill per item while someone is still walking the floor.
- 05
A POS that bulk-reads a basket of UHF tags, prices it, takes payment and prints a PDF receipt, marking every line item sold in one transaction. A per-menu device support model (backend/lib/deviceKinds.js mirrored in frontend/src/lib/deviceSupport.js) declares which of the five capture device kinds each screen accepts, shows whether they are alive right now, and routes their reads. Reader kind is derived from the model number on the label rather than typed as a second field that can disagree.
OUTCOME
Deployed and in live use at v2.7.0, with the last three releases driven entirely by problems found during real reader installs, such as a push token bleeding between two open reader forms and every copy button failing silently because the box is served over plain HTTP. Backend tests boot the real server, provision a throwaway tenant, exercise the handheld to web count interlink and every Excel and PDF export endpoint, then delete it.