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
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
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.
Merge representation
| Operation | Merge representation |
|---|---|
MediaCreation | Immutable creation record keyed by media_id, with a deterministic dot tie-break for the same-ID conflict that normal use cannot produce. |
MediaRename | LWW register of Option<MediaName>. |
MediaPropsUpdate | Per-key LWW String register. |
MediaTrashSet | LWW bool register. true hides the live item in the virtual Trash collection; false restores it. |
MediaDeletion | Permanent per-media tombstone. It prevents all later resolution and retains creation metadata only for local/remote blob cleanup. |
ApplePhotosResourceOriginAdded | Append-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. |
ApplePhotosCollectionLinkAdded | Append-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. |
AlbumCreation | Marks the entity created and writes name and desired parent to their LWW registers using the creation dot. |
AlbumMediaAdd / AlbumMediaRemove | Add-wins observed-remove set. An add stores its dot; a remove carries the exact observed set of add dots it cancels. |
AlbumDeletion | Permanent tombstone. It hides the album and its contents without erasing their CRDT metadata. |
AlbumRename | LWW register of Option<AlbumName>. |
AlbumReparent | LWW desired-parent register, followed by deterministic acyclic parent projection. |
AlbumThumbnailSet | LWW register of Option<MediaUuid>. |
GroupCreation | Immutable creation record keyed by group_id; its parent is permanently fixed. |
GroupMediaAdd / GroupMediaRemove | Add-wins observed-remove set, as for albums. |
GroupDeletion | Permanent 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.
| Tier | Maximum operations in one file | Maximum files before compaction |
|---|---|---|
| 1 | 20 | 10 |
| 2 | 200 | 10 |
| 3 | 2,000 | 10 |
| N | 20 × 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.