Introduction
The LVE Sync Protocol keeps the inventory data of LVE ("Lagerverwaltung einfach", simple warehouse management) consistent across devices. Articles, storage locations, recipients, stock bookings, delivery notes and loans of equipment are written on phones and tablets, often without a network connection, and exchanged later through a server or through iCloud.
This site is the complete specification of version 1. It is written so that a client or a server can be built from it without reading the reference implementations: the iOS app LVE and the Laravel reference server in the same repository.
Design goals
Section titled “Design goals”- Local first. The device holds the primary copy of the data and keeps working without any server. Every change is made locally first and sent later. A server never has to be asked before something can be booked.
- Offline for any length of time. A device may be offline for days. When it reconnects, its changes are merged with everyone else's without manual conflict resolution.
- Eventual consistency. Every node applies the same deterministic merge rules, so all nodes that have seen the same changes hold the same data, whatever order the changes arrived in.
- Server optional. An inventory can live on a single device, be shared between devices through iCloud, or be relayed by a server. The data model is the same in all three cases.
- Small server. A server stores records, hands out changes in the order it accepted them and keeps binary attachments. It does not compute stock, does not resolve conflicts beyond comparing two strings and does not need to understand most payload fields.
- Open. The protocol, its JSON Schemas and test vectors are published under the MIT licence. Anyone may write a server or a client.
| Role | Meaning |
|---|---|
| Inventory | One data set: the records of one warehouse or company. A server holds exactly one inventory, identified by a UUID. |
| Client | One installation of an app on one device. It keeps a full copy of the inventory, makes changes locally and syncs them. In this specification "client", "device" and "node" mean the same thing. |
| Server | A hub that relays changes between the clients of one inventory, stores attachments and can reserve document numbers. It may mirror the data into other systems, for example an ERP. |
| User | A person who signs in to a server. Several users and devices share one inventory. |
| Organisation | The company that runs its own server for its staff. It can hold an organisation licence that unlocks the paid features of the app for everyone who syncs with that server. |
| Publisher | The publisher of LVE, who signs organisation licences with a private Ed25519 key. |
flowchart LR
subgraph Devices
A[Client A<br/>full copy]
B[Client B<br/>full copy]
C[Client C<br/>full copy]
end
S[(Server<br/>records, change log,<br/>attachments)]
E[(ERP or other system)]
A <-- push and pull --> S
B <-- push and pull --> S
C <-- push and pull --> S
S -. optional mirror .-> E
How it works in one paragraph
Section titled “How it works in one paragraph”All data is a set of records. Each record has a type, a UUID, a version stamp from a hybrid logical clock and a JSON payload. A client changes a record by writing a whole new version with a new stamp and puts it in its outbox. It pushes the outbox to the server, which keeps a version only if it is newer than the one it has (merge rules). Clients pull the server's ordered change log and apply the same rules. Stock levels, serial number states and what is still out on loan are never stored: every client derives them from the stock movements, which are written once and never changed.
Versioning and compatibility
Section titled “Versioning and compatibility”The protocol version is an integer. Version 1 lives below the path prefix /lve/v1, and a server lists the versions it supports in GET /lve/v1/info.
Within version 1 the following changes are allowed and do not raise the version:
- new optional payload fields,
- new entity types,
- new optional endpoints, announced as values in
features, - new error codes and new values in open lists (
features,auth), which clients ignore when they do not know them.
Implementations keep working across such changes because of three rules that every client and server MUST follow:
- Unknown payload fields are stored and relayed unchanged, and a client that rewrites a record keeps the fields it does not know.
- Unknown entity types are stored and relayed unchanged.
- Unknown values in open lists are ignored.
Anything that would break an existing implementation (removing or renaming a field, changing a type or the meaning of a value, changing a merge rule) gets a new version with a new path prefix, /lve/v2. A server may serve several versions at once.
The changelog lists every addition to version 1.
Conventions
Section titled “Conventions”- The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as described in RFC 2119 when they appear in capitals.
- JSON field names are shown in
code. Payload examples are complete and valid unless marked otherwise. - "Reference app" means the iOS app LVE, "reference server" the Laravel server in
backend/. Where they behave in a way that a compatible implementation has to know about, the page says so. Reference app behaviour collects these notes.
Sources
Section titled “Sources”The machine-readable parts are the authority for formats; the prose on this site is the authority for behaviour.
| File | Content |
|---|---|
openapi.yaml |
Every endpoint, request and response, rendered as the API reference |
schemas/*.json |
JSON Schema (draft 2020-12) of the change envelope and of every entity payload |
test-vectors/*.json |
HLC, merge, push, licence, numbering, penalty and loan state vectors |
README.md |
Short overview of the protocol |
LICENSE |
MIT licence of the specification |