Outpostworks with no signal

A working demo · not a product

The plant room is in the basement.

Half of field work happens where there is no signal — basements, lift shafts, cold stores, tunnels, the far end of a valley. An app that needs the network to accept a form is an app that gets replaced by a paper pad within a week.

Switch the connection off below and keep working. Nothing will stop. Then switch it back on and watch the queue drain.

Today’s round

Why it is built this way

The network is never in the way of the work

Every write goes into IndexedDB and returns immediately; the outbox syncs later, or in a week. There is no spinner between an engineer and a form, because the moment there is one, the form gets filled in on paper and typed up wrong that evening.

local first · sync second

Every write carries an id made on the device

A patchy tunnel means the same batch is sent twice, or — worse — the reply is lost after the server applied it, so the device has no way of knowing it succeeded. The id is generated before the request leaves, so the replay returns the original result instead of a second inspection. Press Lose the next reply, change an answer, and watch the automatic retry come back duplicate.

idempotency · not hope

Conflicts are per field, and they are evidence

Each write carries the version it was based on, so the server can tell a real conflict from a stale echo — and only for the field that actually moved. Two people answering different questions is not a conflict. The value that loses is written to the record, never dropped.

three-way, field by field

It really installs

A service worker precaches the shell, so it opens from the home screen with the aeroplane mode switch on and no network stack underneath it. That is the difference between a website that copes with a bad connection and an app that does not need one.

service worker · manifest · standalone

Try it properlytwo minutes
  1. On a phone. Open this page, add it to the home screen, then turn on aeroplane mode and open it from the icon. It starts. Fill in a job, take a photo, add a note.
  2. Turn the aeroplane mode off. The outbox drains on its own, each item reporting what the server did with it.
  3. Make a conflict. Press Go offline, answer “Access to the plant room”, then press Someone edits it from the office and go back online. The sync reports the clash, keeps your answer, and records what the office had said.
  4. Try to make a duplicate. Press Lose the next reply, then change an answer. The server applies it and the reply is thrown away on this device, so it still believes the write is pending — and retries a couple of seconds later. The retry comes back duplicate, and nothing is written twice.
What it is made ofand what it will not do

Built with

  • A service worker and a web app manifest — installable, and it starts with no network
  • IndexedDB for jobs, the outbox and photo blobs — not localStorage, which is synchronous, string-only and capped
  • Photos downscaled and re-encoded in a canvas on the device before they are ever queued
  • Cloudflare Workers on outpost.malikakhtar.dev/api/*, D1 behind it
  • Hand-written HTML, CSS and JavaScript. No framework, no build step

Limits, on purpose

  • Rounds are kept for 7 days

The jobs, the addresses and the contractor are invented. The offline switch simulates a dead network so the demo works on a desktop — but the machinery underneath is the real thing, and aeroplane mode on a phone exercises exactly the same code path.