what you're reading
FlexMesh
The hard part is reading the label.
FlexMesh is a last-mile delivery driver app: pull your route, scan every parcel, mark it delivered with photo proof, and navigate the optimized path. The route platform is external — the engineering that actually matters lives on the phone: a geometry-first computer-vision pipeline that finds a shipping label in the camera and captures it by itself.
A driver client whose one hard part is the camera.
Underneath, FlexMesh is a thin client over a third-party route platform — the login payload literally sends is_flexmesh:1 against a fixed BUSINESS_ID of 1704, and every route and order comes from api.flexmesh.ca. The marketing line about "AI-powered collaborative dispatch" is thin over the code; there is no mesh network in here.
What is real, and genuinely hard, is capture. A courier holds a phone over a parcel in bad light, at an angle, sometimes with three barcodes on one label. The app has to find the label, decide when the shot is good, and take it — with no server, on a mid-range Android. That on-device CV is the whole product; the rest of the app is honest plumbing around it.
Three backends, and the phone talks to all of them.
The Flutter app talks to FlexDriver-be — a small FastAPI service — only for login, register and version. Everything else goes straight from the phone: route and order data to the external SuperRoute platform at api.flexmesh.ca (a Bearer JWT plus an X-BUSINESS-ID header), and every proof photo to Appwrite storage.
That shape is deliberate. The auth service exists mainly to bridge two identities (below) and to keep a stable X-Error-Code contract for old app builds, while the heavy traffic — routes, orders, photos — never routes through it. It sits beside the data path, not in front of it.
FlexDriver-be (FastAPI) handles only login, register and version. All route and order data goes straight from the phone to api.flexmesh.ca (Bearer JWT + X-BUSINESS-ID); proof photos go straight to Appwrite. The auth service sits beside the data path, not in front of it.
One frame in, one action out.
The scanner is a loop. Each camera frame goes to AutoScanOrchestrator with the decoded barcode, the image bytes and the current zoom. First gate: AutoCaptureService must report the device stable — steady for 250 ms under 0.6 m/s² on the accelerometer — or the frame is dropped. Only then does the orchestrator ask LabelDetectorService to locate the label.
A round is published as one probe, and ScanSessionCubit projects it to a phase. If the phase is zooming, ScanZoomPlanner picks a target and the camera drives itself toward the label; if it is ready, the shot latches. Lose the lock for four rounds while zoomed and it resets to 1× rather than hunting forever.
The camera drives itself toward the label. A frame is only processed once the accelerometer says the device is steady, and the phase advances only on trusted evidence. Lose the lock for four rounds while zoomed and it resets to 1× instead of hunting forever.
Geometry first; the model only as a hint.
Locating the label is ten geometry steps: decode and downscale to 480 px, extract line segments, assemble them into lines, form candidate quadrilaterals, then a chain of gates — the decoded barcodes must fall entirely inside a quad (a true half-plane containment, never a bounding box), opposite edges must be parallel, a paper margin must exist — and finally score the smallest quad that wraps the label structure.
The rule across the whole pipeline is no fallbacks: any step that produces nothing ends the run with null, so a gate never acts on a guess. A learned detector, LabelProposalService running YOLO11n, is demoted to a constraint — it offers a Rect hint adopted only if it still contains every decoded code. Two weights ship (w8a32, then fp32 as fallback); plain int8 is banned because it collapsed the confidence head.
Ten geometry steps, no fallbacks — any step that yields nothing ends the run with null, so a gate never acts on a guess. The learned detector (YOLO11n) is demoted to a constraint: its Rect hint is adopted only if it still holds every decoded code. Plain int8 was banned for collapsing the confidence head; w8a32 then fp32 ship instead.
Three laws, each paid for by a real bug.
Phase is a pure projection of evidence, not a set of mutable flags. scanPhaseFor checks the evidence in a fixed order, and that order is the sequence law — a later phase is structurally unreachable without the earlier one, so the scanner cannot capture before both the barcode and the frame are locked.
applyProbe lands a round’s bits, regions and bounding box as one emission, because per-field updates walked the UI through half-true states ("the icon went green but there was no box"). CodeMemory is keyed by the decoded barcode value, not per-frame geometry, because ML Kit reports a different subset of a multi-code label each frame — a DHL label’s three stacked barcodes made geometry-keyed memory thrash. And a watchdog force-resets the capture latch after 8 s, so a broken async leg can never deadlock the scanner.
scanPhaseFor checks the evidence in a fixed order, and that order IS the sequence law — a later phase is structurally unreachable without the earlier evidence, so the scanner cannot capture before both the barcode and the frame are locked.
Login is a bridge, and register compensates.
AuthCubit posts to FlexDriver-be, which logs the driver into SuperRoute (that is where is_flexmesh:1 goes) and, in the same call, mints an Appwrite session token so the app has an identity for photo storage. The response carries both tokens; the app stores them in flutter_secure_storage and the router redirects to /home off the auth stream.
Registration is where the two identities could drift, so it compensates: create the Appwrite user, then register with SuperRoute, and if SuperRoute rejects, delete the just-created Appwrite user. A stable X-Error-Code header rides alongside a human-readable message so older builds keep working.
Every driver carries two identities — a SuperRoute account and an Appwrite user for file storage — minted in one call. Register compensates: if SuperRoute rejects, the just-created Appwrite user is rolled back, so the two never drift. A stable X-Error-Code header rides alongside a human message for old builds.
The route, the parcel, and a thin flex story.
There is no local relational database; the app models the JSON it trades with SuperRoute. A RouteModel is one driver’s route — a Google-encoded polyline for the map, a status, a delivered count read off count.successful. It owns many OrderModels, each a stop: address, phone, lat/long, a delivery outcome and its proof photos.
The only place the "shared / flex" idea actually lives in code is a handful of permission flags on the route — allowHandsOffOrderByDriver, allowAddManualOrderByDriver, allowResetOrderStatusByDriver — which gate whether a driver may hand a stop off, add one, or reset its status. Everything else about a mesh is marketing; this is the real surface.
There is no local relational database — the app models the JSON it trades with SuperRoute. A RouteModel owns many OrderModels. The only place the "shared / flex" idea actually lives in code is a handful of permission flags on the route; everything else about a mesh is marketing.