Scenario

Mount a Vault as a Drive

Open vault files from any program on the machine.

A vault is not a folder on disk. Its files are split into content-addressed, encrypted blocks that may live partly on this machine and partly on peers, fetched as you read them. Mounting presents that as an ordinary directory tree, so every program on the machine opens vault files the way it opens any others — and writes to them.

Three ways to do it, all read-write. On Windows and Linux prefer the first.

1. meshhold mount — a real filesystem

meshhold mount ~/meshhold              # every vault, each as a subdirectory
meshhold mount --vault Music ~/Music   # one vault, straight at the mountpoint

On Windows the mountpoint can be a drive letter:

meshhold mount X:
meshhold mount --vault Music M:

The mount runs in the foreground and unmounts when you interrupt it. It runs in your session and talks to the daemon over its REST API, so it also works against a different node — point --api at it.

Linux needs FUSE (fuse3 on Debian, Ubuntu and Fedora) and a mountpoint that already exists. No root: the mount belongs to the user who ran it.

Windows needs WinFsp, a filesystem driver MeshHold does not bundle — most installs never mount anything, and a kernel driver is not something to hand out unasked. Fetch it when you want it:

meshhold winfsp status     :: what this machine has
meshhold winfsp install    :: fetch the official signed package and run it

Windows asks for administrator rights once; mounting afterwards needs none.

2. From the web UI or the tray

Each vault's ⋮ menu has Mount as drive. Pick a drive letter (Windows) or a folder (Linux), optionally tick mount again when the daemon restarts, and the vault appears. A mount made this way belongs to the daemon: it survives closing the browser and comes back after a restart if you asked it to.

On Windows the tray menu has a Drives section with the same thing closer to hand.

The drive appears on the machine running the daemon. When that is not the machine running the browser — or when the daemon is a system service, whose mounts belong to the service account — the sheet says so and hands you the meshhold mount command to run where you actually are, rather than a button that appears to work.

Locking a vault unmounts it. A drive letter that survived the lock would leave the files readable through it.

3. WebDAV — every OS, nothing to install

node:
  webdav:
    enabled: true
    listen_addr: 127.0.0.1:3920   # default; loopback-only

Off by default, and bound to loopback so an accidental enable cannot expose vault contents to the network. Authentication is HTTP Basic and always required: the username is ignored and the password is any valid API token. Use a CLI token (meshhold auth login) for a mount that should survive daemon restarts.

Each trusted storage vault is a top-level directory:

# Linux
sudo mount -t davfs http://127.0.0.1:3920/ /mnt/meshhold
# macOS: Finder ⌘K → http://127.0.0.1:3920/
# Windows
net use Z: http://127.0.0.1:3920/ /user:mount <api-token>

What a mounted vault does

Reads are lazy. Listing a directory is instant — it comes from the catalog. Opening a file fetches its blocks on demand, locally first and then from peers, and decrypts them as you read. A large file on cold peers feels slow the first time. Range is honoured, so players seek.

Reading does not make this node a replica. To keep a file offline, pin it (the web UI's cache action).

Writes can land anywhere. Appending, writing into the middle of a file, truncating it, and reading back through the handle doing the writing all work. sqlite runs on a mount: create, insert, update a row in the middle, commit, reopen, read it all back.

What makes that affordable is a property of the chunking rather than a trick. A block's plaintext is a fixed slice of the file and encryption is convergent, so a block nobody touched re-seals to exactly the ciphertext it already has, under exactly the hash it already has. Appending to a 1 GB file re-encrypts the partial block at the end and nothing else.

What stays proportional to the file is content_hash, a sha256 over the whole concatenated ciphertext, so every new version re-hashes every block. That is a local read of bytes already on disk with no decryption in it, and it is the price of the identity peers merge on — but it does mean a program that rewrites one byte of a 10 GB file at a time pays for the file each time. A mount is a fine place to keep a database honest; it is not a fine place to run a busy one.

fsync seals, it does not commit. After it returns, every block is encrypted and in the block store; what is not yet written is the catalog entry naming them, which lands when the file is closed. A crash in between leaves the file at its previous version and some unreferenced blocks the collector removes.

Edits are versions, not overwrites. The previous content stays in the version history, and the blocks it is made of stay with it.

Folders exist even when empty. A vault's folders are otherwise implied by the paths of the files in them, which left an empty one nowhere to be. A folder made on a mount has a catalog entry of its own, so it replicates to peers and comes back after a restart. Removing one requires it to be empty — emptying a folder is a different operation, and you should have to say which you mean.

Folders still cannot be renamed: a folder is the shape of the paths beneath it, so moving one is a rewrite of every file below.

Renaming a file changes nothing about the bytes. The blocks stay where they are, the content hash is the same, and the file keeps its id — which matters because the album art, the video metadata and the play counts are all filed under it. A move onto a path that already holds a live file fails rather than quietly destroying it.

Directory listings are cached for a few seconds. Changes made on the node holding the vault appear immediately; changes arriving from a peer can lag by that much. A mount reached over the API has no way to be told about changes at all, so there the cache window is the whole story.

Two cases that need no mounting

  • A file-mode vault (one with storage_path) already materialises its plaintext at that path — it is a folder.
  • An untrusted vault (no key on this node) cannot be mounted: there is nothing to decrypt.

Android

The vault appears in the system file picker and in Files, through a DocumentsProvider — browse, open, copy in and out, create and remove folders. Bytes are streamed through the daemon in the app's own process; nothing decrypted is written to disk.