chalkline

A sync log for crews that lose signal.

Chalkline is the server half of an offline-first field app: tablets push batches of edits whenever they can reach the network, pull everything after a cursor when they come back, and two crews editing the same punch item in a basement both keep their work. Rails 8 on Postgres, deliberately small enough to read in one sitting.

Idempotent pushEvery op carries a client id. Retry a batch a hundred times; it lands once.
Dense cursorsSequence numbers are allocated under a per-project advisory lock, so a pull from since never skips a change committed late.
Per-field LWWTwo crews editing different fields both win. Same field, later client clock wins, and the loser is still in the log.
Versioned API/api/v1 is frozen by a contract test. /api/v2 adds clocks and a cursor object without touching v1.

Projects

The API, in three calls

# authenticate as a device (token identifies project + device)
curl -H "Authorization: Bearer $TOKEN" https://chalkline.levelbrook.com/api/v1/items

# push a batch; op_id and item_uuid are client-generated
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://chalkline.levelbrook.com/api/v1/sync/push -d '{"ops":[{"op_id":"<uuid>","item_uuid":"<uuid>","kind":"punch",
  "op":"upsert","fields":{"title":"Cracked tile, unit 204","status":"open"},"client_ts":1789600000000}]}'

# pull everything after your cursor, in order, dense
curl -H "Authorization: Bearer $TOKEN" "https://chalkline.levelbrook.com/api/v1/sync/pull?since=0&limit=500"

Push returns applied (what actually won per field), duplicate (op_ids already seen), rejected, and head_seq. Pull returns changes, next_since, more, head_seq. Device tokens for the demo project are on its page.