How to organise files

Use catalog records to give stored payloads names and directories. Run changes inside the application’s write transaction against the selected store cursor. The examples call that cursor store and assume objects aliases ol.xitstash.

Create a directory and file together

Publish the payload first. Pass its digest as digest:

(objects/create! store
  [{:name "invoices"}
   {:under 1 :name "november.pdf" :digest digest}])

The result is a vector containing the directory and file records. Save their :id and :revision values after the transaction commits.

:under 1 selects the first request in this batch. To put a file in an existing directory, use :parent with that directory’s ID. Omit both keys to create a node below root 0.

If a sibling already has the requested name, creation fails. Let the exception abort the transaction, including any payload publication earlier in that callback.

Find a file

Use a read cursor to the same store:

(let [directory (objects/lookup store 0 "invoices")]
  (objects/lookup store (:id directory) "november.pdf"))

children returns a directory’s records in UTF-8 byte name order. path returns a vector of names from the root. Neither result needs an open cursor after the call completes.

Rename without overwriting a concurrent change

Save the file record when the user starts editing. Pass that record’s revision to the later write transaction:

(objects/move! store (:id file) {:name "paid.pdf"} (:revision file))

A stale revision throws :xitstash/conflict, even if the requested change would now be a no-op. Read the current file and resolve the conflict before retrying. Do not silently substitute its new revision.

To move between directories, add :parent to the destination. To replace an occupied destination, add :replace? true. Replacement requires the same node kind and an empty destination directory. It preserves the source ID and removes the destination record.

Replace a file’s content

Prepare the new bytes outside the transaction. Publish them, then update the existing file within the same callback:

(objects/put! store prepared)
(objects/update! store (:id file) (:digest prepared) (:revision file))

The file keeps its ID, name and parent. Its previous payload stays available until explicitly deleted.

Remove the name

(objects/remove! store (:id file) (:revision file))

This removes the catalog record, not the payload. For content removal, follow How to delete content and reclaim space.

See create! for name and ID rules. See Branches need an explicit merge policy before combining catalogs from separate branches.