Offline-first, and what that actually means
Every maritime software vendor says offline-first. Almost all of them mean a read-only cache. This page describes the mechanism, in enough detail that a technical superintendent can decide whether to believe it.
What most systems mean
A cache is not offline
The common arrangement is that the app downloads a copy of what it thinks you will need, shows it to you when the link drops, and refuses every write until the link returns. That is useful for about an hour and useless for a voyage.
The tell is what happens when you try to close a job with no signal. If the button greys out, or the change sits in a queue that fails silently three hours later, you have a cache.
What this is
The device holds the data and the rules
The whole of the vessel’s working data lives on the device, in the browser’s durable storage, mirrored in memory so reads are instant. The domain rules — the permission evaluator, the permit state machine, the drill interval engine, the rest-hour checks, the maintenance constraints — are a pure package that runs on the device unchanged.
So a write is evaluated immediately, against the same rules the server would apply. If it is refused, it is refused there and then with a reason. If it is accepted, it is accepted — it is not provisional, and it will not be quietly reversed when the link comes back.
- Writes queue in a durable outbox. The queue survives the browser being closed, the tablet being turned off and the device running out of battery. It is not an in-memory array that a refresh discards.
- Identifiers are generated on the device. UUIDv7, so a row created at sea has its final identity from the moment it is created and nothing has to be renumbered on arrival.
- Every operation is idempotent. A batch re-sent because a satellite link dropped mid-transmission is recognised as a duplicate and applied once. This is tested by replaying two devices’ entire queues verbatim.
Conflicts
Two devices, one row, no signal
The bridge PC and a tablet in the engine room both edit the same component, days apart, neither of them connected. Both eventually arrive. Something has to decide.
Each device keeps a hybrid logical clock — a timestamp that also counts, so two devices whose system clocks disagree still produce an ordering both of them will agree on afterwards. The merge is per column, not per row: if the bridge changed the location and the tablet changed the name, both changes survive. If they both changed the name, the newer clock wins and the older one is recorded as skipped rather than lost silently.
The property that matters is that the order of arrival does not affect the result. Whichever device reconnects first, both rows end up identical. That is asserted by a test that runs the same three edits in both orders and compares the rows.
We found a real defect in this during that test: the clock comparison had been keeping the earliest write to a column instead of the latest, so two devices reconnecting in opposite orders could diverge. It is fixed, and the test that found it is the reason we know.
The audit trail
Per vessel, hash-chained, and readable in the product
Every write the server accepts is recorded with who did it, on which device, at what time, and the SHA-256 of the previous entry. The chain is per vessel, so one vessel’s history can be handed to an inspector without handing over the fleet’s.
It is readable inside the product, by anyone with the permission, rather than being a table only the vendor can query.
The shell
A phone in a machinery space
The application is a progressive web app. It installs to a home screen, runs full-screen, and its shell — the code, the fonts, the icons — is cached so it starts with no network at all. The fonts are embedded in the stylesheet rather than fetched, because a font request that hangs is a screen that does not render.
The same code is packaged for the App Store and Google Play, with the device-specific parts — storage, secure storage, network state, printing, camera — behind a small set of ports so the rest of the application does not know or care which it is running in.