Independent technical review · Prepared by MDS Diversified
A fleet compliance platform with strong foundations and an unfinished last mile. Here is what works, what is broken, what it scores, and the three realistic routes to a mobile app.
Seven dimensions, independently assessed, each challenged by a second reviewer
Scored against a competently built commercial platform of this budget, where 5 is the industry median, not a pass mark. The seven weighted dimensions average 4.9. Built well, finished badly — and almost everything wrong with it is cheap to fix.
The architecture, identity layer, API design and domain modelling underneath. Real server-side security, clean production output, enterprise login, and compliance knowledge a generic development shop simply does not have.
What a customer meets every morning. It works, but it opens with a red failure warning that is not true, then an undismissable tutorial, a dead link in the main navigation, and no phone app.
That gap, and its direction, is the most useful thing in this document. If the surface were good and the foundation rotten, he would be facing a rebuild and another six-figure spend. It is the other way round: decent bones, unfinished finish. The honest framing to take to his developer is not this was built wrong, it is this was never finished, and nobody was watching it in production.
One root cause explains defects across five of the seven dimensions: there was no quality gate. No code review, no pre-release click-through on a phone, and no error monitoring of any kind. That is why a red failure banner has been sitting on a paying customer's dashboard on a completely healthy system, and why nobody would ever have been told.
Verified, not assumed
It is worth being precise about this, because the defects further down are loud and the strengths are quiet. Several of these are better than what most commercial software ships.
A full Content-Security-Policy, HSTS with preload, cross-origin isolation, clickjacking protection, and a permissions policy that actually restricts the camera and microphone. This is above the commercial norm and it is not easy to retrofit.
Every data endpoint was tested without a login and correctly refused. Nothing leaks. Session cookies are inaccessible to JavaScript, so a browser-side attack cannot steal a login.
Passwords and multi-factor authentication are handled by Keycloak, a dedicated enterprise identity system, rather than home-built login code. This is the right call and most builds at this price get it wrong.
A versioned REST interface split sensibly by domain. Data comes back well structured. This is what makes any future app or integration possible at all.
If a driver saves something with no signal, the app stores it on the device and replays it when the connection returns. That is a deliberate, correct decision for a fleet product and it is already built.
Electronic work diary, prestarts, vehicle defects, faults, registration and inspection expiry tracking by state, licence renewals, incident recording. This is genuine Australian heavy-vehicle domain modelling, not a generic app with compliance labels.
No secrets left in the published files, no source maps exposing internals, no debug logging, and very little copy-paste duplication. Someone had standards.
Proper buttons rather than fake clickable boxes, semantic page structure, correct dialog markup and visible keyboard focus. Most teams skip this entirely.
Ranked by severity · every item verified directly
The application takes the text of a notification straight from the server and injects it into the page as live HTML rather than plain text. There is no sanitising library anywhere in the application — every published file was checked and there is not one.
Whether this is dangerous depends on something not visible from outside: whether the server cleans that text before storing it. If notification messages are ever built from things people type — fault reports, incident notes, defect descriptions — then someone could put code into a defect note and have it run inside the owner's browser session.
What to doAsk the developers one question: is notification message content sanitised on the server before it is stored and served? If the answer is anything other than an immediate yes, render it as plain text and sanitise on the way in. Roughly a day.
After signing in, the app sends the user to a destination supplied in the web address, checking only that it begins with a slash. A destination beginning with two slashes passes that check and sends the freshly logged-in user to an outside website — a convincing set-up for a phishing page, because the user has just successfully authenticated.
The framework they are already using ships the correct check, and it is sitting unused in the same file.
What to doUse the framework's own destination check instead of the slash test. A one-line change.
The app checks two health addresses to decide whether it is online. Both are blocked by a password requirement at the web-server layer, so they return an authorisation error. The app reads that as "cannot reach the server" and paints a permanent red banner reading Offline — could not be reached with a score of zero out of a hundred, while everything else works perfectly.
Someone protected those addresses to keep them private and did not realise the customer's own browser calls them. This is the single most damaging item on the list, because it tells every customer the product is broken when it is not.
What to doAllow the app's own health checks through at the web-server layer, or point the app at addresses that are not password-protected. Under an hour.
The offline feature — the one built specifically so a driver in a dead spot can still save a record — depends on a browser capability that Apple does not implement. On Android it works. On an iPhone the saved record sits in the phone's storage waiting for a signal that never arrives, and there is no manual "send now" trigger anywhere in the shipped app to release it.
For a platform whose entire purpose is proving that records exist, this is the most serious operational risk on the list. A driver would be told their entry was saved. It would be on the phone. It would not be in the system.
What to doTest the offline flow end to end on a real iPhone before anything else. Add a fallback that flushes the queue when the app regains connection rather than relying on background sync. One to two days, and it should be verified, not assumed.
The site already registers the background service an installable app needs, already declares itself app-capable, and already ships the home-screen icons. But the one small file that tells a phone "this is an installable app" is missing entirely, and the two addresses where it should live both return "not found".
Because of that single missing file, Android will never offer to install it. The expensive work is done; the switch was never flipped.
What to doAdd the app manifest file and link it. About a day including icons and testing. This is the highest-return change available anywhere in the system.
The profile picture is re-downloaded every ten seconds, and each copy is held in memory and never released. Over an eight-hour shift with the tab left open that is thousands of unnecessary requests and thousands of retained images. The app gets progressively slower and heavier the longer someone works in it.
Notably, a different part of the same file handles this correctly, with proper cancellation and cleanup. The quality is inconsistent rather than uniformly poor.
What to doRelease each image after use, raise the refresh interval, and stop polling when the tab is in the background. Copy the pattern already used elsewhere in the same file. Half a day.
The app cannot tell the difference between "you are not logged in" and "I could not reach the server just now". Any momentary drop — a tunnel, a lift, a server restart during a deploy — is treated as logged out and throws the user back to the login screen mid-task.
For a product whose primary users are drivers and workshop staff on mobile data, this is likely a recurring real-world complaint that nobody has traced back to its cause.
What to doDistinguish "unknown" from "logged out", and retry before giving up. Half a day.
Loading a single page fetches the company logo six separate times, the user profile three times, the profile picture three times and notifications four times. There is no layer preventing the same request being made repeatedly. On a phone on mobile data in a yard, this is slow and it costs the customer money.
What to doAdd a standard request-caching layer. Two to three days, and it improves every screen at once.
The platform runs a preview release of its underlying framework rather than a stable one. Preview releases carry no stability guarantee and may not receive security fixes. Separately, there is no error monitoring of any kind installed, so when something breaks for a customer, nobody finds out unless the customer rings up.
What to doMove to a stable framework release and install error monitoring. A few days, and the monitoring pays for itself the first time it catches something.
The onboarding tour reopens at step one on every single page load, including in a brand-new browser tab, after being dismissed repeatedly.
The cause is worth knowing, because it is not a general bug. One specific customer's email address is written directly into the application code, tagged with a setting that means keep re-offering this tour until they finish all 52 steps. That customer is the account owner. So the owner of the business is the one person guaranteed to be shown an undismissable tutorial forever, while other users are not. It reads as a bug aimed squarely at the person paying.
Worth saying separately: a product needing a 52-step explanation is telling you something. Some of that complexity would be better designed out than narrated.
What to doRemove the hardcoded entry and honour the dismissal. An hour.
The same entry described above — a specific customer's email address and full name — is written into the application file that every single user of the platform downloads and can read. This is a small privacy disclosure on top of being business logic in the wrong place.
What to doMove it into configuration. An hour, and it fixes the tour at the same time.
A stub "App Demo" page reachable from the main header that leads nowhere useful. Mistyped page addresses silently show the dashboard instead of an error. Animation code throughout the app that has been disabled and never runs. Pop-up messages that can collide and dismiss each other. Map styling loaded from a third-party public CDN, which is avoidable exposure for a system holding safety records. Date formats that differ between screens.
What to doBatch these into one clean-up pass. Under a week for the lot.
On the two security items. Both were confirmed by reading the application's own published code. Nothing was attacked, exploited or tested against the live system, and nothing in this document is a working attack method. Both need a developer to confirm what happens on the server before their real severity is known — that is why they are framed as questions to answer rather than confirmed breaches.
One further note: a browser tab locked up for over a minute during testing, but a fresh tab behaved normally and the cause could not be isolated. It is recorded here for completeness and deliberately excluded from the score.
What each actually delivers, and what it costs
First, the technical constraint that shapes all three. The platform's data interface is locked to its own website by design: it sends no cross-origin permissions and authenticates only with a browser cookie, with no token method a separate app could use. Reading the published code reveals the shape of every data endpoint, but it provides no way to log in from outside. That rules out one option entirely and shapes the other two.
He built it, he has the code and the servers, and the two changes that matter most — the missing app file and the false offline warning — are a day's work between them for someone already inside the system. Nobody else can do those two things at all without access.
The honest reservation is that the finishing pass is precisely what has not happened so far. A health check that was never tested from the customer's side, an app file that was never added, a tour that was never checked twice, a preview framework left in production, no error monitoring: these are all last-ten-percent failures by the same team. Handing them a new app is adding scope before the current scope is closed.
With read access and a working branch, this becomes straightforward. Add the app file and the site becomes installable on every driver's phone almost immediately. Close the defect list above. Then, if store presence is wanted, wrap the finished product so it lists properly on the App Store and Google Play with push notifications, camera access for defect photos and background location for the GPS features.
This route keeps everything already paid for, fixes what is wrong, and reaches a real app in weeks. It does not require taking the codebase over or replacing his developer — it needs cooperation, not a handover.
It is worth naming the one honest catch. Without code access, the only option is wrapping the live website in an app shell, which needs nothing from anyone. That does produce genuine App Store and Play Store listings. But every defect above ships inside it under our name, the clever offline feature is unreliable inside an iPhone app shell precisely when a driver is out of signal, and Apple rejects apps that are only a website in a shell unless real device features are added. Doing it without fixing the underlying product first is buying someone else's bugs.
This means rebuilding the electronic work diary, prestarts, defects, faults, asset compliance tracking, GPS integration and enterprise login from nothing — and that is the part of the existing system that is genuinely good and genuinely expensive. The existing data interface cannot be reused, because a separate app has no way to authenticate against it.
Realistically this is many months and a cost comparable to, or above, what has already been spent, to arrive at something the platform already does today.
The recommendation. Do route one and route two in that order, and reject route three unless the objective is ownership rather than an app.
This week, his developer adds the missing app file and fixes the false offline warning. That is roughly a day's work, it needs nobody else, and on its own it turns the existing website into an app that installs on every driver's phone. That is most of what "an app" means for a fleet product.
Then decide whether App Store presence is genuinely worth the next step. If it is, that is where we help — but fix the product first, then wrap it. Wrapping it as it stands would put our name on a red offline warning and a memory leak.
In this order