Skip to main content
Version: 0.1.0

Data model

This page describes the core entities Openbeehive stores, how they relate, and how scopes decide what syncs to whom. It is written from the offline-first point of view: the same shape lives in the device's SQLite-WASM database and in the server's pluggable database, and the sync protocol keeps them in step.

If you want the mechanics of change tracking (HLC timestamps, last-writer-wins, OR-Sets, append-only events), read History and events first — this page focuses on the entities themselves.

The hierarchy

At the top sits the Apiary (a yard or location). Each apiary holds Hives; each hive has a current Queen and accumulates a stream of records over time.

Apiary
├── Hive ──────── Queen (current; queens form a succession over time)
│ ├── Inspection (a visit: what you saw)
│ ├── Task (something to do, with a due date)
│ ├── Event (append-only fact: requeened, split, died, moved…)
│ ├── Harvest (honey/wax taken off)
│ └── Treatment (varroa or disease treatment applied)

└── Placement (hive ↔ apiary, time-bounded — where a hive lived, and when)

ApiaryShare (apiary ↔ user — grants another beekeeper access via a scope)

A hive belongs to one apiary at a time, but Placement records the full history of where a hive has lived, so a hive can move between yards without losing its records.

Entities and key fields

Every entity shares a common envelope used by sync: a stable id (an offline-generated UUID), a scope_id (see Scopes), HLC bookkeeping columns, and a soft-delete flag. The fields below are the domain-meaningful ones.

Apiary

The container and the unit of sharing.

FieldNotes
idUUID
namee.g. "Home yard"
locationfree-text or lat/long
notesfree-text
scope_idequals the apiary's own id (see below)

Hive

A colony's housing within an apiary.

FieldNotes
idUUID; also encoded in the hive QR label
apiary_idcurrent apiary (the active placement)
name / short_codehuman label and the short code printed on the QR
typeone of Zander, Dadant, Deutsch Normal, Langstroth, Warre, Top-bar, Other — see Hive types
statuse.g. active, dead, sold
notesfree-text
scope_idthe apiary id

Queen

The reigning queen of a hive. Queens form a succession: when a colony is requeened the previous queen is closed off and a new record opens, so you keep the full lineage.

FieldNotes
idUUID
hive_idthe hive she heads
yearintroduction/birth year
marking_colourfollows the international colour scheme (1/6 white, 2/7 yellow, 3/8 red, 4/9 green, 5/0 blue)
originbred, bought, swarm, supersedure…
clippedwing-clipped (boolean)
scope_idthe apiary id of her hive

Inspection

A dated visit: the snapshot of what you observed.

FieldNotes
id, hive_id, datewho and when
brood, stores, temperamenttypical observations
queen_seen, eggs_seen, queen_cellsquick checks
varroa_countmite drop / wash count if taken
temp_hive, temp_outsidetemperature (°C) inside the hive and outside
humidity_hive, humidity_outsiderelative humidity (%) inside the hive and outside
notesfree-text
scope_idthe apiary id

The climate fields are plain optional scalars, so they sync per-field like any other column and can be filled by hand or by an automated sensor — see Automated trackers.

Task

Something to do for a hive or apiary, with a due date and a done state.

FieldNotes
idUUID
hive_id / apiary_idthe subject (a task may target either level)
title, due_date, donethe basics
scope_idthe apiary id

Event

An append-only fact about a hive — requeened, split, swarmed, died, moved, fed. Events are never edited or merged; they only accumulate, which is why they never conflict during sync. They are the backbone of the hive's timeline.

FieldNotes
id, hive_id, occurred_atwhen it happened
kindthe event type
payloadtype-specific detail (JSON)
scope_idthe apiary id

See History and events for the full event catalogue and how the timeline is assembled.

Harvest

Honey (or wax) taken off a hive.

FieldNotes
id, hive_id, datethe take-off
producthoney, wax, propolis…
quantity, unite.g. 12.5 kg
notese.g. forage, moisture
scope_idthe apiary id

Treatment

A varroa or disease treatment applied to a hive.

FieldNotes
id, hive_id, datesubject and application date
product, active_ingrediente.g. Oxuvar / oxalic acid
dose, methode.g. 50 ml, trickling
batch_numberbatch / charge (often legally required)
withdrawal_untildate honey is safe to harvest again
reasone.g. varroa
notefree-text
apiary_id, queen_idfrozen context at application time
scope_idthe apiary id
note

Treatment and dosing rules vary by country and product approval. Openbeehive records what you did; it does not prescribe. Always follow your local authorisations — see Varroa.

Placement

The time-bounded link between a hive and an apiary: where a hive lived and for how long. A new placement opens when a hive moves; the previous one closes.

FieldNotes
id, hive_id, apiary_idthe link
from / untilinterval; until is null while current
scope_idthe apiary id

ApiaryShare

Grants another beekeeper access to an apiary (and everything under it).

FieldNotes
id, apiary_idwhat is shared
user_idwho it is shared with
rolee.g. viewer, editor

Scopes and sync gating

Sharing happens at the apiary level, and a single value drives it: every record carries a scope_id.

  • For apiary-owned data — hives, queens, inspections, tasks, events, harvests, treatments, placements, and the apiary itself — scope_id is the apiary's id.
  • For data that belongs to a single user and is never shared (e.g. personal preferences), scope_id takes the form user:<id>.

When two devices sync, they exchange only the scopes the user is entitled to. The server resolves a user's scope set as:

scopes(user) = { "user:<their id>" }
∪ { apiary.id for each apiary they own }
∪ { share.apiary_id for each ApiaryShare granting them access }

Adding an ApiaryShare therefore makes a whole apiary — every hive and every record beneath it — appear on the recipient's devices on the next sync; revoking it stops further changes from flowing. Because the gate is the scope_id column, sharing is all-or-nothing per apiary and needs no per-record permissions.

tip

A hive id by itself grants nothing. Scanning a QR label opens the app at a hive only if that hive's scope has actually synced to your device.

Why it merges cleanly

The shapes above are chosen so that sync never needs a human to resolve a conflict:

  • Scalar fields (a queen's marking colour, a hive's name) use per-field last-writer-wins, decided by HLC timestamps.
  • List/set fields use add-wins OR-Sets, so concurrent additions all survive.
  • Events are append-only and immutable, so they simply accumulate.

For the full algorithm, continue to the sync protocol.