Skip to main content

CRDT operations

Every library mutation is one immutable, encrypted CRDT operation. A mutation is identified by a dot: { lamport_counter, device_id }. Dots are unique per device and globally comparable; they are the only duplicate-detection identity. Each operation is an independent protocol unit and merge order has no semantic meaning.

Operation envelope​

Before encryption, every log frame and every item in a compaction file is CBOR encoding of:

CrdtOperation {
dot: Dot,
author: LibraryUsername,
timestamp: RFC 3339 timestamp,
content: OperationContent,
}

author and timestamp are audit metadata. Conflict resolution uses only the dot and the operation content. Receivers may apply a duplicate operation: each affected CRDT substate is idempotent using its own stored dots.

OperationContent is one of the operations listed below. MediaCreation, ApplePhotosResourceOriginAdded, and ApplePhotosCollectionLinkAdded carry a struct of its own; every other variant carries its fields inline.

Operation list​

The materialized library is derived from CRDT state, not from replaying this list in a particular order. Every entity can have a placeholder state, because an operation may arrive before the creation it refers to. A placeholder is never visible; only the creation operation makes the entity exist.

Media​

MediaCreation
Adds a new media item to the library.
media_idMediaUuid
filename_originalMediaFilenameNewtype over String, the name the file had when it was imported.
dateDateTime<Utc>Capture date (EXIF DateTimeOriginal / PHAsset.creationDate).
storage_dateStorageDateStorageDate { year: u16, month: u8 } determines the storage path. Usually matches date but can differ.
size_bytesu64
content_hashMediaHashNewtype over [u8; 32], the BLAKE3 hash of the unencrypted file bytes.
modified_atOption<DateTime<Utc>>Last modification date (PHAsset.modificationDate or file mtime). None if unknown.
gpsOption<GpsCoords>GPS position at capture time. GpsCoords { latitude: f64, longitude: f64 } in WGS84 decimal degrees.
apple_aae_media_idOption<MediaUuid>Points at another MediaCreation's media_id, the Apple .AAE edit sidecar for this media item.
apple_live_photo_media_idOption<MediaUuid>Points at another MediaCreation's media_id, the video component of this Live Photo.
MediaRename
Sets or clears the user-facing display name.
media_idMediaUuid
nameOption<MediaName>None clears the name and is an ordinary written value, never an omitted operation.
MediaPropsUpdate
Attaches or updates a single key/value property.
media_idMediaUuid
keyString
valueStringOpaque to the format, which imposes no encoding. No client emits this operation yet; clients merge and display it.
MediaTrashSet
Moves a media item into or out of the virtual Trash collection.
media_idMediaUuid
trashedbooltrue moves the item to Trash; false restores it. The winning dot decides concurrent Trash and Restore operations.
MediaDeletion
Permanently tombstones explicitly selected media.
media_idsVec<MediaUuid>The public command emits exactly one selected media ID; hard deletion never expands to companions.
ApplePhotosResourceOriginAdded
Records immutable provenance for one imported or hash-reused resource of an Apple Photos asset revision.
media_idMediaUuidThe newly created or content-hash-reused Lasco media item.
cloud_asset_idApplePhotosCloudAssetIdOpaque serialized PHCloudIdentifier stringValue for the owning PHAsset; never a PHAsset local identifier.
modification_dateOption<DateTime<Utc>>The owning PHAsset.modificationDate. It is deliberately repeated on every resource origin.
resource_typeApplePhotosResourceTypeClosed PhotoKit-role enum: photo, full-size photo/video, adjustment data, or paired-video variants.
filenameString
ApplePhotosCollectionLinkAdded
Records immutable provenance for one imported Apple Photos folder or regular album.
album_idAlbumUuidThe Lasco album used for this mapped collection.
cloud_collection_idApplePhotosCloudCollectionIdOpaque serialized PhotoKit cloud identifier; never a collection local identifier or name.
kindApplePhotosCollectionKindClosed enum: folder or album.

A media referenced by another media's apple_aae_media_id or apple_live_photo_media_id is a companion. Companions are stored as ordinary media, but they are never added to an album or a group, so they never appear in listings on their own.

Media lifecycle​

For the complete lifecycle, cleanup, and remote-reclamation contract, see Media deletion.

Trash is a virtual system collection, not an AlbumEntry and not a reserved AlbumUuid. A soft-deleted media item remains live CRDT state and retains its encrypted data, thumbnail, and existing album/group membership dots. It is hidden from Home, albums, groups, and Orphans, and is shown only in Trash. Restore makes the retained memberships visible again.

Soft deletion keeps existing album and group membership dots. While the media is trashed, browse views hide it; Restore makes its previous collections visible again. Trash is never an orphan state. A local album or group add addressed to a trashed item is a successful no-op; a concurrent add remains hidden until Restore.

MediaTrashSet is a last-write-wins boolean register ordered by its dot. MediaDeletion is a permanent tombstone: for every listed ID its dot is retained as the maximum deletion dot and is never cleared. A tombstone wins over creation, rename, properties, membership, Trash, and Restore, including when the deletion arrives before creation. Creation metadata is retained behind a tombstone solely to locate encrypted blobs for cleanup; tombstoned media is never resolved into a browseable entry or a sync-upload candidate.

Soft delete and Restore apply to a media item and its connected AAE/Live Photo companions. Hard delete requires the selected item to be in Trash and records a tombstone for that item only; it does not automatically delete companions.

Albums​

AlbumCreation
Creates a new album with an optional parent.
album_idAlbumUuid
nameAlbumNameNewtype over String.
parent_idOption<AlbumUuid>None makes the album a root.
AlbumMediaAdd
Adds a media item to an album.
album_idAlbumUuid
media_idMediaUuid
AlbumMediaRemove
Removes a media item from an album.
album_idAlbumUuid
media_idMediaUuid
observedHashSet<Dot>The membership add dots this remove observed and cancels. An add it did not observe survives.
AlbumDeletion
Permanently tombstones an album.
album_idAlbumUuid
AlbumRename
Sets or clears the album name.
album_idAlbumUuid
nameOption<AlbumName>
AlbumReparent
Moves an album to a new parent.
album_idAlbumUuid
parent_idOption<AlbumUuid>
AlbumThumbnailSet
Sets or removes the thumbnail for an album.
album_idAlbumUuid
media_idOption<MediaUuid>

Groups​

Groups are leaf collections. They hold media items directly, cannot have child albums, and each belongs to exactly one parent album fixed at creation. Groups cannot be reparented, which is a product constraint rather than an implementation accident.

GroupCreation
Creates a group inside an album. Its parent is permanent.
group_idGroupUuid
parent_idAlbumUuid
GroupMediaAdd
Adds a media item to a group.
group_idGroupUuid
media_idMediaUuid
GroupMediaRemove
Removes a media item from a group.
group_idGroupUuid
media_idMediaUuid
observedHashSet<Dot>The membership add dots this remove observed and cancels.
GroupDeletion
Permanently tombstones a group. No cascade operations are emitted.
group_idGroupUuid

Merge representation​

OperationMerge representation
MediaCreationImmutable creation record keyed by media_id, with a deterministic dot tie-break for the same-ID conflict that normal use cannot produce.
MediaRenameLWW register of Option<MediaName>.
MediaPropsUpdatePer-key LWW String register.
MediaTrashSetLWW bool register. true hides the live item in the virtual Trash collection; false restores it.
MediaDeletionPermanent per-media tombstone. It prevents all later resolution and retains creation metadata only for local/remote blob cleanup.
ApplePhotosResourceOriginAddedAppend-only provenance entries, deduplicated by operation dot. The exact expected (resource_type, filename) set establishes completeness for a revision. A new Photos revision adds new entries; it never mutates older provenance.
ApplePhotosCollectionLinkAddedAppend-only provenance entries, deduplicated by operation dot. All entries are retained; lookup resolves a conflicting identity to the album associated with the smallest dot. Noncanonical albums are not deleted or merged automatically.
AlbumCreationMarks the entity created and writes name and desired parent to their LWW registers using the creation dot.
AlbumMediaAdd / AlbumMediaRemoveAdd-wins observed-remove set. An add stores its dot; a remove carries the exact observed set of add dots it cancels.
AlbumDeletionPermanent tombstone. It hides the album and its contents without erasing their CRDT metadata.
AlbumRenameLWW register of Option<AlbumName>.
AlbumReparentLWW desired-parent register, followed by deterministic acyclic parent projection.
AlbumThumbnailSetLWW register of Option<MediaUuid>.
GroupCreationImmutable creation record keyed by group_id; its parent is permanently fixed.
GroupMediaAdd / GroupMediaRemoveAdd-wins observed-remove set, as for albums.
GroupDeletionPermanent tombstone.

Local durable state​

local_state/crdt-state.enc is an atomically replaced, encrypted CBOR CrdtState:

CrdtState {
device_id: DeviceId,
lamport_clock: LamportClock,
// CRDT registers, sets, and tombstones
}

local_state/operations.log is an append-only sequence of encrypted frames. Each frame is one CrdtOperation; it is not a second state representation. Reads authenticate every frame and deduplicate by dot. A local edit appends the operation frame, saves the snapshot, then rebuilds the derived browse state. Interruption can create a duplicate frame, which is harmless.

The materialized browse state and computed views are derived caches. They are never persisted and are rebuilt after local merges and fetches.

Remote compaction files​

Remote files remain immutable and keep their existing names:

operations/{compaction_uuid}.op{tier}_{operation_count}

The encrypted CBOR plaintext is:

CompactionFile {
tier: u8,
operations: Vec<CrdtOperation>,
}

operation_count is the number of individual operations. Tiered compaction concatenates source operations, deduplicates by dot, writes one immutable replacement at the next tier, updates the last-known-state cache, and only then deletes source files while holding operations/LOCK.op.

Tier constraints​

The existing compaction limits are unchanged; only their unit has changed. Both limits count individual CrdtOperation values, never batches or any higher-level container.

TierMaximum operations in one fileMaximum files before compaction
12010
220010
32,00010
N20 × 10^(N - 1)10

Push selects the lowest tier that can hold the uploaded operation count. When a tier reaches ten files, the lock-protected cascade compacts every file in that tier into one next-tier file. The replacement filename's operation_count remains the number of individual CRDT operations after dot deduplication.

Fetch and push​

Fetch caches immutable remote files, verifies that all dots previously cached for that remote are still covered by the remote's current files, then applies their operations to CrdtState and the local operation log. Each immutable file is marked merged only after its operations have been persisted. Fetch records confirmed non-tombstoned MediaCreation objects in the positive-only media inventory. After it durably applies operations and rebuilds state, it removes local .data and .thumb cache files for every known MediaDeletion tombstone. The same cleanup runs when a library opens, so an interrupted cleanup is retried.

Push compares dots in the complete local operation log with dots covered by the target remote's cached files. It uploads uncovered operations in one compaction file and writes the same ciphertext into the target's last-known-state cache. CrdtState is a materialized CRDT-state snapshot, not an operation outbox. The operation log is the sole source used to decide what Push delivers.

Media reads first check live resolved CRDT state. A hard-deleted ID is rejected before local-cache or remote access. If a remote read was already in flight when Fetch applies a tombstone, the read checks live state again before writing downloaded ciphertext to the local cache and discards it if the item was deleted.

Debug UI​

Clients show an individual operation row with its dot, author, timestamp, kind, and content fields. They do not show grouping or parent-operation relationships.