How one carrier's evidence is kept away from every other carrier's, what the database cannot hold no matter who asks it, and the parts we have not done yet.
Last updated
An ELD records a continuous log: location, speed, engine activity, every duty status change. Carriers are finding that record subpoenaed and used against them, and it has to be kept for six months whether they like it or not.
This is not that. It holds the moment a driver tapped arrived, the moment he tapped departed, and the page he photographed. There is no speed in it, no route, no hours of service, and no position between taps, because the product has no way to collect one. The driver screen is a web page opened from a text message. A web page cannot run in the background, and on iOS it cannot geofence, which is why nothing we publish uses that word.
We are not lawyers and this is not advice about your exposure. It is a description of what the database contains.
Every table that holds tenant data carries row level security policies in Postgres, split by operation rather than one blanket rule, and membership of an organisation is what a policy checks. The separation is in the database, not in the application, so a bug in a page cannot hand one carrier another carrier’s loads.
That claim is tested rather than asserted. The test suite runs 129 guards against a real Postgres 16 on every change, six of them acting as the privileged role, and a change that opens a hole fails the build. The guards exist because writing them found real holes: a column level revoke that Postgres silently ignored, which would have let any customer set their own subscription tier, and a writable invoice sequence that would have let one rewind their own numbering and mint a duplicate.
The browser gets a public key that can do nothing a policy does not allow. Server rendering uses the same key with the signed in person’s session. The key that bypasses row level security is importable only from route handlers, which is enforced two ways: a lint rule that names the boundary, and a server-only import that makes a client component referencing it fail the build rather than fail review.
Missing configuration fails at startup rather than at two in the morning. The environment is validated when the process loads and the build aborts naming the variable that is absent.
Postgres cannot see a URL. Every driver and broker request goes through a route handler that validates a hashed, scoped, expiring token on the server before anything is read, and the anonymous role holds no policies at all on business tables. A leaked link is scoped to one driver and can be revoked; it is never the thing a table trusts.
Opening a driver link ties it to that phone. A forwarded link lands on a re-claim screen that deliberately does not name the run before the person says who they are.
That is held in the query layer rather than in the interface. The rate is not selected on the driver’s side of the product, so there is no view to misconfigure and no field to accidentally render.
A recorded event is never updated. A correction is a new row that supersedes the old one, and both stay. Status, detention and a load’s progress are all derived from those rows rather than stored beside them, so the driver’s screen and the office board cannot disagree.
Three timestamps are kept for every tap: the device clock, the server clock, and the difference between them. Server time never overwrites the device time, because that would be fabricating evidence rather than recording it. A tap entered by a dispatcher on a driver’s behalf is stored as unverified, with no location and with the dispatcher named.
There is no SOC 2 report. We have not commissioned an external penetration test. There is no bug bounty programme yet. A security page that lists only what a product has is a page nobody can check, so those absences are here rather than left to be inferred.
If you have found something, write to security@primedispatchify.com and we will reply.