Model file versions as ordered block hashes

Represent each file version as an immutable blocklist to support delta sync, restore, deletes, renames, and garbage collection.

Blocklists and file revisions

A blocklist records which pieces make up a file and in what order. An immutable revision preserves that recipe, while a mutable path pointer selects the version users currently see. Publishing the recipe must wait until its referenced bytes are ready to read.

We'll connect the blocklist to a namespace journal, follow the publication transaction, and test missing bytes, corruption and retries in the local sync service.

One file, several kinds of identity

A path names a location within a namespace. A revision identifies an accepted publication. A digest identifies bytes. Two revisions can contain identical bytes, and two positions in one file can reference the same blob.

Our sixteen-byte file still contains AAAA , BBBB , CCCC , AAAA . Its four ordered references need three stored blobs. Turning the blocklist into a set loses the final AAAA ; sorting the references can produce a different file.

Record Key Relevant content --- --- --- Journal entry Namespace and revision Path, ordered blocks, size, digest, deletion state Current path Namespace and path Selected revision Operation receipt Device and operation Exact proposal and accepted result Blob Content digest within allowed storage Verified immutable bytes

A fuller file model may also retain timestamps, permissions and application metadata. Keep server acceptance order separate from client modification time. Dropbox's 2016 Magic Pocket account describes the same broad separation of mutable file history from immutable stored blocks; our SQLite schema is a teaching implementation, not its production database.

The cursor spans the namespace

A journal cursor answers “which accepted changes have I learned about?” across the file tree. A file's base revision answers “which version of this path did I edit?” Those numbers can differ.

For example, suppose notes.txt is revision 14 and another file changes at revision 15. A device can read through cursor 15 while still proposing a new notes.txt version against base 14. Rejecting it merely because 14 is not the namespace head would create a conflict with an unrelated edit.

The proposed query has this shape:

The server derives the permitted namespace from authenticated access. A page returns a continuation position for the rows it actually supplies; a client must not skip straight to a later head while earlier pages remain unread. Receiving metadata also does not mean its files have been applied locally.

Our fixture holds only Ada's namespace, so its tables omit a namespace column. It caps history and returns all later rows without pagination. Publication allocates revisions inside the transaction. Its restricted append/rollback path maintains contiguous history; SQLite AUTOINCREMENT alone does not promise gap-free IDs.

Prepare bytes before selecting the revision

The wiki/metadata-db-for-object-storage object-metadata lesson separated stored bytes from published state. Apply the same ordering here: