Aurora Ops
Offline-first, AI-assisted field-ops platform for FMCG distribution

Overview
An FMCG distributor's most valuable assets are not in its warehouse. They are the fridges, dispensers and branded signage sitting in thousands of small shops and bars across the country, lent out in the hope they earn their keep. Deciding who gets one is a real decision with real money attached, and it usually travels up a chain of managers before anyone says yes. Traditionally that chain runs on email chains, phone calls, paper forms and a spreadsheet somebody maintains. Aurora Ops puts the whole cycle in one place: the rep's visit to the outlet, the request for equipment, the approvals it needs, the contract signed on the screen with a fingertip, and the delivery. It is built to work on a phone in a market with no signal, and it has an assistant that reads the history and flags requests that look risky. It is deployed and in daily use across the business, from field reps to executives.
My role
Solo fullstack developer on the build: the data model, the two-layer permission system, the approval engine, the offline layer that keeps the app usable without a connection, the AI assistant, and all thirty-seven screens along with the component library they are built from. The engagement is a contract, and it continues. The system is deployed, in use, and maintained continuously.
The problem
Field operations for a nationwide distributor usually run on paper and spreadsheets, and the cracks show in three places. The reps are the first. They visit outlets in places where mobile data is unreliable or absent, so anything requiring a live connection gets written on paper and typed up later, if at all, which is where accuracy goes to die. The second is approvals. A cooler is a high-value asset, and the request to place one travels informally up an organisation eight levels deep, from the rep in the market to an executive. Informal chains are slow, and worse, they are hard to reconstruct afterwards: nobody can say precisely who approved what, or why this outlet received equipment and that one did not. The third is the record itself. Compliance inspections and warehouse stock were tracked ad hoc, so the audit trail existed in pieces, none of them authoritative.
The solution
One application covering the whole cycle, from the rep's request through however many approvals it needs, to a signed contract and fulfilment. The structure came from two places: how the company already operates, and decisions about what would let it operate better. The eight levels from field rep to executive are real, and so is the geography they cover. Making the approval chains configuration rather than fixed rules was a choice, so the business can change how it approves things without needing a developer. It works offline as a matter of design, not as a fallback. Outlet and catalogue data is downloaded ahead of time, everything the rep does is queued on the device with a checksum attached, and it syncs when a connection returns, with the checksum ensuring the same action cannot be applied twice. Approval chains are configuration rather than code. The business can say that requests of this type above this value need these three approvals, and change it later without a developer, including thresholds below which something approves itself. Permissions are modelled as two separate questions that were previously tangled together: what is this person allowed to do, and which parts of the country are they allowed to see. Keeping them apart is what stops the rules collapsing as roles multiply. An assistant reads live data from the system to score requests for risk, flag inspection results and summarise for executives. Every one of those features has a deterministic fallback, so the app never stops working because a model is unavailable.
Key features
- Work a full day with no signal. Outlet and product data is already on the device, everything you do is queued locally, and it uploads itself when you are back in coverage without ever applying the same action twice.
- Request equipment for an outlet and watch it move through however many approvals it needs, with the chain decided by what is being asked for and how much it is worth.
- Change the approval rules without a developer: who signs off on what, at which values, and the threshold below which a request approves itself.
- See only your part of the country. What you can do and where you can see it are handled as two separate questions, so a territory manager and a regional head can share a job title without sharing visibility.
- Sign a contract on the screen, by hand with a finger or typed, valid for thirty days, with a clear record of whether it was signed or voided.
- Run compliance inspections against a scored checklist, with a path to re-inspect, and manage warehouse stock through stock-takes, dispatches, restocks and write-offs that keep a before-and-after history.
- Ask the built-in assistant about live data, get risk scores on incoming requests and flags on inspection results, with every answer backed by a fixed fallback so the system still works when the model does not.
Architecture & engineering
Next.js 16 App Router (with the new proxy.ts middleware) on React 19 + TypeScript. Data is PostgreSQL on Supabase accessed via Prisma 7 with the pg driver adapter, using a pooled connection at runtime and a direct URL for migrations. Authz is two orthogonal layers: a static permission matrix (usePermissions client-side; requirePermission → 401/403 server-side) plus buildGeoFilter row scoping. Approval chains are DB-modelled and executed as a sequential step-instance state machine. AI follows a "RAG-lite" pattern: routes pull live domain aggregates, inject them as prompt context, and extract structured JSON with per-feature temperature tuning. UI is a Base UI + Tailwind v4 shadcn-style component library, a single Zustand store, Recharts, and TanStack Table/Virtual. Scale: 23+ Prisma models, an 8-level role hierarchy, and ~37 dashboard pages.
The hardest problem
The core challenge was making the app trustworthy offline. The solution queues mutations client-side in IndexedDB with a SHA-256 checksum and a userId+deviceId+checksum idempotency key, flushes them to a sync endpoint via the Background Sync API and on-reconnect, and purges local copies after sync.
Results
The system is deployed and in use by the distributor, across every level of the business from the field reps to the executives, with real operational data running through it. It is maintained continuously rather than delivered and left. The part that matters most in practice is that it works without a connection. Reps complete outlet visits and submit equipment requests in markets with no mobile data, and the work uploads itself once they are back in coverage. That is the difference between a system people use where they are and one they fill in afterwards from memory, which is where accuracy goes.
Key learnings
Splitting authorisation into two independent questions, what someone may do and which records they may see, was the decision that kept the rules manageable. Tangled together, every new role multiplies the special cases; kept apart, they stay readable at eight levels of hierarchy. Treating approval chains as configuration rather than code meant the business could change how it approves things without waiting on a release. That is usually where internal tools go stale: not because the software is wrong, but because the process moved and the code did not. And every AI feature needed a deterministic fallback behind it. Pairing each model call with something predictable meant the product was never one API outage away from being unusable, which is the difference between AI as a feature and AI as a dependency.
Curious how this works?
This one's a private build; request a walkthrough and I'll show you around.