Repository navigation
Persistence and recovery
This document defines the current on-disk authority, compatibility, atomicity, and recovery contract. The code and storage/domain tests remain the executable source of truth. See Operations for backup, restore, upgrade, and incident procedures. Event history details current workflow events, exact retries, and historical replay compatibility.
<datasetsRoot>/
.labello-server/
auth.json
users/<user-id>/keybindings.toml
imports/
exports/<job-id>/
prelabels/<dataset-id>/
<dataset-id>/
labello.dataset.toml
labello.schema.json
images-index.json
images/
annotations/<image-id>/
events.jsonl
state.json
users/<user-id>/
keybindings.toml
.labello/
scoring/focus-v1.json
imports/<import-id>/
manifest.json
source-objects.jsonl
migrations/schema-v2-to-v3/
journal.json
generations/
snapshots/<snapshot-id>/
manifest.json
...
Import job workspaces and API control records below
.labello-server/imports have additional internal files. Their layout is
storage-private and must not be consumed directly by API, UI, operator scripts,
or external integrations.
| Artifact | Classification | Recovery rule |
|---|---|---|
.labello-server/auth.json |
Authoritative secret authentication/session state | Restore only from the matching full-root backup; never log, publish, or merge it |
.labello-server/imports/ |
Authoritative private in-progress import, reservation, upload, and API control state | Let startup recovery reconcile it; do not delete apparently stale workspaces manually |
.labello-server/exports/ |
Private derived captures, job state, and verified archives | Startup interrupts unpublished jobs and preserves completed artifacts until expiry; never edit job records or publish partial files |
.labello-server/prelabels/ |
Private durable hint generations, pause/run state and signing keys; derived indexed result files | Preserve in full backups; use authenticated reset/retry commands and automatic interrupted-run recovery; omitted from snapshots |
labello.dataset.toml |
Authoritative dataset configuration, workflow definition, and role state | Valid supported schema required; restore rather than hand-edit damaged data |
images-index.json |
Authoritative image identity, hash, path, and metadata index | Valid supported schema required; image-directory contents alone do not reproduce stable identities |
images/ |
Authoritative image bytes addressed by the image index | Include in full backups; omitted from Labello snapshots |
annotations/<image-id>/events.jsonl |
Authoritative append-only audit and workflow history | Replay in sequence; never truncate, reorder, merge, or edit by hand |
annotations/<image-id>/state.json |
Derived, rebuildable cache | Rebuilt automatically when absent, stale by event sequence, on a supported older schema, or with an older review projection generation |
labello.schema.json |
Generated schema bundle | Regenerated during supported artifact migration, configuration saves, and before the first event publication by each repository instance; do not treat it as annotation authority |
.labello-server/users/<user-id>/keybindings.toml |
Authoritative personal keyboard, mouse-button, and pan-drag shortcuts shared across datasets | Include in full-root backups; omitted from dataset snapshots |
<dataset-id>/users/<user-id>/keybindings.toml |
Preserved legacy dataset-specific shortcuts | Retain for explicit migration and rollback; never overwrite from global settings |
.labello/imports/<import-id>/manifest.json |
Authoritative committed import provenance | Must match the dataset and directory import ID |
.labello/imports/<import-id>/source-objects.jsonl |
Authoritative committed source-object audit record | Preserve with its manifest and event history |
.labello/migrations/... |
Durable migration journal and staged generation | Recovery state until migration completion; do not remove during an interrupted migration |
.labello/snapshots/ |
Derived point-in-time annotation/audit packages | Downloadable but not directly restorable; retain according to operator policy |
| Statistics and process caches | Derived | Recompute or invalidate after authoritative writes |
.labello/scoring/focus-v1.json |
Authoritative versioned 20-minute focus selections | Preserve in full backups and snapshots; score totals rebuild from events plus this file. Never discard as a cache |
Browser IndexedDB/local-storage drafts and availability caches are recoverable client conveniences. They are outside the server root and never authoritative workflow state.
Global shortcuts use the existing versioned TOML KeybindingSet shape. Reads
validate the owner and normalize supported missing bindings. Writes validate the
current schema, serialize through a shared process-local lock, and use the
existing atomic replacement primitive. One server process per root is required.
An interrupted replacement leaves either the previous complete value or the new
complete value; temporary artifacts are not authoritative.
Before a global save, the client uses defaults and opens Settings when saved shortcuts are available from accessible datasets. The user explicitly selects a saved set or saves the current settings for all datasets. Conflicting copies never depend on traversal order. Existing dataset files remain untouched by this selection, apart from their normal supported schema migration. Copies in inaccessible datasets are preserved but not offered. The first global save ends legacy discovery; later dataset changes cannot override it. An older server continues using its dataset-local copies and does not see global edits.
Mouse-button shortcuts retain the existing keybinding record shape and schema
version. The key strings MouseRight, MouseExtra1, and MouseExtra2 identify
right-click and mouse buttons 4 and 5; modifier fields have the same meaning as
for keyboard bindings. Existing keyboard-only settings continue to load.
Older clients/servers that do not recognize these names cannot use settings
containing mouse bindings.
IDs and relative paths are validated before filesystem access. Dataset repository JSON/TOML replacement writes use a temporary file, file sync, atomic rename, and directory sync where required by the operation. Event mutation transactions:
load and authorize
-> acquire the per-image process-local lock
-> reload exact current state
-> validate and simulate the full event batch
-> atomically replace events.jsonl with the appended sequence
-> replay/update state.json
-> invalidate derived caches
The event append is the authoritative commit. If cache publication fails or a process stops after that boundary, later state loading replays the event log. No transaction spans multiple server processes, multiple dataset roots, or an external backup tool.
Migration transactions complete artifact migration, acquire the repository configuration read guard, load and authorize current configuration, then acquire the image lock. They retain both guards through event publication and cache updates. Dataset configuration publication takes the matching write guard, so a guide/task/role change either precedes command validation or waits for its commit. This configuration-before-image order also covers explicit companion reconciliation and administrator migration repair. The guard is process-local; external configuration-file edits do not participate in this serialization.
Image-index publication is serialized with replacement of the shared parsed cache. Dataset-root mutation locking coordinates normal dataset creation and import publication only inside one process. Running multiple server processes against one root is unsupported even when the backing filesystem is shared.
Import builds a complete dataset below the same root, verifies event replay and sealed output, and publishes with one atomic no-replace directory rename. There is no partial merge into an existing dataset.
The current persisted schema is version 3; version 2 is the supported legacy
schema. Current artifacts must carry schemaVersion. Unknown older or newer
versions are rejected rather than guessed.
Version 2 event entries are decoded through explicit wire types and upcast for
current replay. Dataset configuration, image indexes, generated schema,
keybindings, and state caches migrate through
.labello/migrations/schema-v2-to-v3. The journal records a prepared
generation and each publication phase so a later access can resume after
interruption. State caches are rebuilt from event history during migration.
Current dataset configuration writes assignment balance directly as
imbalance.maxDifference. Ratio configuration cannot be converted to an
equivalent absolute window without an operator choice. Before upgrading,
replace imbalance.maxRatio in version-2 or version-3 dataset files with an
explicit maxDifference; loading rejects ratio and tagged-policy shapes. This
configuration change does not alter historical event bytes.
An upgrade is one-way unless a release explicitly documents reverse
compatibility. Preserve a full pre-upgrade backup; rollback means restoring
that backup, not changing schemaVersion fields manually.
Loading an image compares state.json with the image ID, supported schema, and
last event sequence. The current review projection generation must also match,
so a same-sequence cache from before review-round tracking is rebuilt. Missing or stale state is replayed from events.jsonl and
written back when appropriate. A malformed authoritative event prevents replay
and requires backup restore or maintainer-led forensic repair.
The repository validates an existing migration journal, verifies staged-file hashes, resumes the next incomplete publication phase, rebuilds state caches, and records completion. A completed journal is retained as evidence. Unknown files must not be substituted into its generation.
Startup import recovery can:
- recognize a destination published before the job reached
succeeded; - verify and publish a sealed
committingoutput; - rewind interrupted preflight/build/verification to
awaiting_decisionwhen durable artifacts are valid, otherwisesealed; - expire abandoned non-protected work; and
- release reservations not owned by active jobs.
See Dataset Import for the full lifecycle. Configured terminal-job retention cleanup is not currently scheduled by the production server.
Snapshot creation reads the dataset configuration and image index, copies the
generated schema when present, includes committed import manifests and
source-object records, copies authoritative event logs, and rebuilds each
included state.json from those events. The completed snapshot directory is
published by rename.
Snapshots omit:
- image bytes;
-
.labello-server/auth.jsonand all session/authentication state; - user keybindings; and
- private import/export job and control state.
There is no native snapshot restore. Use the full-root procedure in Backup And Restore.
Snapshots include recorded scoring focus history when present. Contribution scores themselves remain derived. See Contribution scoring for the first-activation, atomic-publication, historical-credit, and recovery rules.
- Stop the server and preserve the complete root before investigating.
- Determine authority from the table above; do not infer it from file size or modification time.
- Rebuild only documented derived artifacts. A
state.jsoncache can be removed or rebuilt by repository behavior only after the matching event log is known valid. - Never repair authority by copying a nearby dataset's IDs, event entries, authentication file, import records, or migration journal.
- Never edit persisted schema numbers to bypass validation.
- Restore authoritative corruption from one consistent backup generation.
- Keep incident logs redacted according to Operations.
The current contract is exercised by:
-
crates/labello-domain/src/v2_contract_tests.rsandv3_import_tests.rsfor wire compatibility and upcasting; -
crates/labello-storage/src/repository/tests.rsfor event authority, stale/missing state rebuild, interrupted artifact migration, snapshot contents, and committed import records; -
crates/labello-storage/src/import/tests.rsfor publication and startup recovery; and - API tests for authorization and safe access to snapshots and import state.
Persistence-format changes must update this reference and event history and the smallest fixture or test that would fail if the stated compatibility or recovery rule regressed.
Encoded previews, including the 256-pixel Thumbnail v1 gallery proxies, are
disposable derived artifacts outside datasetsRoot in the
production server. They are not dataset images, image-index entries, import
outputs, events, export/snapshot contents, or authoritative backup contents.
The embedded ApiState default uses its private .labello-server/previews
subdirectory for tests/in-process composition; production overrides that default
with the configured separate cache root.
A cache key includes dataset repository identity, image ID, original BLAKE3, source decoder format, fixed versioned profile, resize/orientation/color/alpha policy, and a build-time digest of the dependency lockfile (covering encoder and decoder versions). Uploaded names or arbitrary requested dimensions cannot name cache files. Every read opens the indexed original beneath the dataset root, rejects symlinks/traversal, and verifies its hash. Removed or changed originals therefore cannot yield stale cached pixels, even before index reconciliation. Unreachable entries are evicted under the finite quota.
Each private entry contains a bounded header and checksummed WebP payload. Publication writes and syncs a private temporary file, renames it, then syncs the directory. Corrupt or missing entries regenerate. A filesystem lock excludes another live cache owner. Restart removes recognized interrupted temporary files, validates the bounded directory inventory, and enforces quotas. Eviction uses least-recently-read order in a process and oldest publication time after restart. Unrecognized files are preserved and fail cache initialization. Cancelling a caller before work starts publishes nothing; after start its bounded worker retains permits and completes or cleans up atomic publication.
Working-image loads always use Data Saver v1. The application no longer reads or
writes the old :data-saver localStorage preference under an endpoint/account
StorageIdentity prefix. Existing values are harmless and ignored; neither a
saved false nor an invalid value can select a larger preview. Workspace and
draft persistence formats are unchanged. Signing out or changing endpoint clears
image references and rejects/cancels obsolete transfers. Derived previews do not
authorize offline work or restore a server assignment.
The shared UI keeps at most eight decoded working previews within a 32 MiB
memory budget. Auth/workspace invalidation clears this cache. Pixel reuse never
replaces fresh state reads or assignment display validation.
The completion projection also tracks live assignments for prospective preload admission. Actual counts remain separate from reservations. Both derive from the same replayed image state and are observed together after event publication, before state-cache publication. Interrupted or failed event publication invalidates the projection; restart rebuilds it from events. Warm balance checks read in-memory counts and live reservations without rescanning image states per item. Expiry is filtered against server time on every read.
preloadQueueSize is a defaultable dataset configuration field in supported
version-2 and version-3 representations. Missing values mean two; current storage
reads and writes validate the supported range. Snapshots retain the configured
value. This change adds no assignment or workflow event fields.
The process-local review history cache also indexes displayed workflow visits by
user, task, assignment kind and variant. It reads committed ItemSeen events and
root visit identities. Reopening retains the original visit's order. The configured
history depth plus the current visit bounds the returned window. Leases remain
ordinary persisted assignments; history does not authorize stale or foreign work.
The index is rebuilt on demand after restart and updated after committed changes.
Legacy terminal review/revision indexing remains for older image assignments. Neither index is an on-disk authority. Explicit state rebuild refreshes both.
Export captures replay exact per-image event cuts under the existing image locks and copies original bytes after releasing those locks. Source event logs, caches, and configuration are not modified. Fresh configuration and index digests, root identity, and original-image hashes are checked before artifact publication. Captured event sequences remain fixed if later source events are appended.
Private mode-0700 job directories contain job.json, staged payloads, and
an unpublished archive. A verified archive is linked without replacement as
dataset.zip, synced, and then recorded as succeeded. The blocking worker
holds the shared configuration and image-index read guards from its final
source verification through the no-replace link and directory sync. It does
not wait for the job map between verification and publication. Cancellation
before durable success removes even an already linked archive. Only succeeded jobs
are downloadable; each download verifies size and BLAKE3. A crash before the
durable succeeded status makes the job interrupted on restart and removes
its payload. Completed archives survive restart until retention expiry.
Orphan reservations and expired entries are cleaned before retained capacity
is checked. See export for the artifact contract.
Current-user daily activity replays authoritative per-image event history.
The domain projection counts one submission or final review per image/task/user
within a supplied UTC day. It includes TaskStateChanged submission and
no-review annotation completion, final ReviewRecorded records, and final
records in ReviewRevisionCommitted. It uses event commit timestamps and keeps
historical work counted after later task or review changes.
The storage owner caches one day and one statistics generation per repository,
with all users sharing the same coalesced scan. The scan has at most 32 image
workers, validates replay, and never derives activity from state.json or
current task status alone. Existing commit/index/config cache invalidation also
invalidates daily activity; restart recomputes it from events. No new persisted
artifact or schema is introduced. A malformed log fails the request rather than
silently omitting work. Atomic event-log replacement prevents partially appended
reads. A commit concurrent with a scan makes its generation stale for the next
request; the result is a bounded scan, not a global instantaneous snapshot.
Server presence derives from replayed assignment state for indexed images.
Only Active annotation and review assignments with an expiry strictly later
than the server time count. Legacy assignments without expiresAt use the
existing updatedAt plus 30-minute lease duration. Multiple leases for a user
in one dataset reduce to their latest expiry. No presence artifact is persisted.
Repository clones share a presence cache and a single refresh lock. Cold reads load at most 32 images concurrently. Refreshes reuse compact per-image assignment facts shared with availability checks, loading only invalidated images. These facts include workflow status, import eligibility, active assignments, review revision contexts, and final-review participation. Objects/Overview availability also retains the replayed image state needed for source matching and review targets, including annotation versions and prepared prediction geometry. Complete event logs are not retained. User-specific permissions and eligibility are still checked, and claims still reload authoritative state under the image lock. Workflow polling shares the cached visibility-adjusted state. It copies that state only when the visibility policy changes while an older reader still holds it; image mutations retain the existing invalidation contract. At reservation capacity, claims consider only images with an active lease owned by that user in the selected workflow. They do not prepare or reload unrelated images under dataset admission. Existing leases remain reusable, and foreground claims retain their extra reservation slot. When an item is displayed, history reservation cleanup checks only images in the user's recorded workflow visits. It still reloads releasable assignments under the image lock. Workflow departure also scans for unseen prefetch reservations. Expiry is filtered on every presence read, independent of writes. Presence is a sampled view, not a transaction snapshot across datasets; reading never renews leases.
Event publication and explicit repair invalidate the affected image facts while holding the image lock, before publication can rename the event log. Thus cancellation, directory-sync failure, or a failed state-cache write cannot leave a stale entry. Concurrent cold readers share the image lock. Configuration changes recompute availability decisions without discarding unchanged image facts. Index saves discard removed image entries. Restart reconstructs facts lazily. A concurrent invalidation prevents a refresh from publishing its aggregate as current. These process-local caches require the existing single-server ownership.
Statistics retain image-local task/class/provenance/migration totals, contributor days, and unfinalized scoring awards. A relevant event publication invalidates only that image's contribution before the event-log rename; an assignment-only event does not affect statistics. Configuration identity is checked before reuse. Refreshes combine unchanged contributions with newly computed ones, with at most 32 cold image readers. They do not reread unchanged histories after ordinary labeling writes. Global scoring order, daily multipliers and focus selection are applied after combining contributions, preserving cross-image scoring semantics.
The aggregate generation still detects concurrent writes. A request may return its bounded sample, but a newer generation forces the next refresh. Index changes remove departed image contributions; explicit repair invalidates its image. Restart rebuilds from authoritative files. Focus-window selection retains its historical event-prefix replay at a new boundary; current-user daily activity retains the separate projection described above.
Memory grows with indexed images, configured tasks/classes, active lease contexts, contributor days and credited label awards. Raw events and annotation-version geometry are not retained by the statistics projections; the shared workflow polling projection retains replayed annotation versions. They are not a fixed-memory capacity guarantee: measure peak RSS with representative histories using the synthetic polling workload and reserve room for active requests, previews, imports, exports and inference. No persisted artifact, schema, polling interval or browser behavior changes.
The repository shares a process-local cache of per-image workflow statuses and active annotation class IDs. This is a disposable query projection, not an authoritative artifact. Initial access loads the event-validated image state; queries and writers share the per-image lock. Event publication invalidates the image summary before the atomic rename so cancellation, directory-sync failure, or a failed subsequent state-cache write cannot leave a stale summary. Explicit state repair also invalidates the summary. Concurrent cold reads coalesce through the image lock, and a changed image does not invalidate other images.
Image-index saves discard summaries for removed identities. Record/path changes and configured Pending defaults are composed from current index/configuration values. Restart rebuilds summaries lazily. No schema, snapshot, import/export identity, or audit-history change is introduced. Warm filtering still evaluates lightweight metadata in memory; this does not claim constant-time queries.
Model prelabels owns the model contract, result publication,
retention, reset, and recovery details. Private generation results are derived
data. Queue preparation captures the predictions and their evidence in Prepared
events so displayed work remains reproducible after a reset.
Acceptance holds the prelabel control guard before taking the image transaction
lock; reset and API configuration writes use the same control guard. The normal
lock/reload/validate/append/replay path rejects duplicate or suppressed acceptance.
Accepted prediction provenance lives in immutable annotation origin and remains
in event/state schema 3, snapshots, and offline bundles. Derived hint files,
private signing keys, pause markers, and jobs are excluded from snapshots.
Checked prelabel profiles add optional output tensor name, total class count, explicit model-to-dataset mappings and model digest to dataset configuration. Historical positional mappings remain readable without rewriting their meaning. The private hint index records the successful server provider; historical index entries without that field default to server CPU. Neither change rewrites events.
Schema-3 workflow events preserve source preparation, seen markers, item contexts,
partial object geometry, provisional Overview/reviewer edits, exact confirmations
and review exceptions. All are derived into state.json with projection version 3,
including completed Objects preserved through Overview edits and contribution
tracking that excludes empty edit proposals.
A cache using an earlier projection is replayed even if its event sequence matches.
Snapshots and offline bundles retain these fields. See
workflow events for replay semantics.
Unused prediction files remain private derived data. Seen prediction geometry and signed evidence are captured in the image's authoritative preparation events, so reset, expiry or removal of private result files cannot erase displayed work.
Objects/Overview availability reuses these image projections across users and annotation/review polls. Cold reads use at most 32 workers and do not acquire the dataset admission lock. Warm polls reread only invalidated images, reapply current configuration, and reevaluate lease expiry. User/kind results are cached for at most 30 seconds or until the next lease expires. Event, configuration, repair and membership invalidations advance the shared generation; a racing scan cannot publish a reusable result for an obsolete generation. Results remain advisory samples. Claims and mutations retain admission serialization and reload the selected image exactly.
Edit documentation in the Labello repository. The wiki is generated from the current documentation; archived plans stay in the repository.
- Labello documentation
- Labello
- Getting started
- Product glossary
- Annotation and review
- Stylus input
- Dataset administration
- Model prelabels
- Dataset import
- Guided box-to-skeleton migration
- Dataset export
- Assignment
- Contribution scoring
- Current limitations