Local Folders
Clone a cloud folder into a real directory on your disk, edit it with your own editor, git checkout and scripts, and sync the two back together.
Clone a folder
monofile clone /Projects ./projects
cd projectsThe second argument is optional: monofile clone /Projects creates ./Projects, and cloning / (your whole workspace) creates ./monofile. The target has to be new or empty, and it cannot already be a clone, so two folders cannot merge by accident.
Cloning writes a .monofile/ state directory and then runs one full pull. From that point on the folder is a mirror. sync and status find it by walking up from your current directory, the way git finds a repository.
Sync both ways
monofile sync # one round, then exit
monofile sync --dry-run # show what would happen, touch nothing
monofile status # what is pending, without syncingOne round does everything in both directions: new and changed files travel whichever way they need to, deletes propagate, and renames arrive as renames. status is the read-only version and always exits 0, like git status.
To keep it running, leave a watcher on:
monofile sync --watch
monofile sync --watch --interval 15The watcher reacts to local file changes immediately and polls the cloud every five seconds by default. Rounds never overlap. Press Ctrl + C and it finishes the round in flight, releases the folder lock, and exits 0. You cannot combine --watch with --dry-run.
If you only want a one-time copy, use the stateless pair. monofile pull <remote> [local] downloads a tree, monofile push <local> [remote] uploads one, neither keeps any state, and push never deletes anything.
What lands in the folder
Text files arrive as text files, at the same relative paths they have in the cloud. Images and video arrive as real bytes up to 25 MB. Anything larger becomes a zero-byte placeholder, so the folder still shows you the shape of the tree without pulling a gigabyte you did not ask for. Deleting a placeholder locally never deletes the real file in the cloud.
The CLI skips some things and prints the reason for each:
| Skipped | Why |
|---|---|
| Binaries that are not image or video | There is no upload path for them |
| Symlinks | Never followed, never synced |
| Text files over 4 MB | Too large for the filesystem API |
| Names Windows cannot create | Reserved names and characters such as < > : " | ? * |
| Paths that collide only by letter case | On a case-insensitive disk, only the first can exist |
The .monofile/ directory
Every mirror carries its own state. You can read all of it; do not hand-edit it.
| File | What it is for |
|---|---|
config.json | Which remote folder this mirrors, which server it came from, the media size cap, and when it was cloned |
manifest.json | The base: the agreed state at the end of the last sync, which is what makes a three-way merge possible |
media.json | Content hashes for media files, plus the list of paths currently held as placeholders |
ignore | Paths the engine never looks at |
lock | Present only while a sync or watch is running |
The CLI writes everything here atomically. The layout is shared with the desktop app, so a folder cloned by the CLI opens in the desktop and a folder the desktop created syncs from the CLI.
A mirror remembers which server it came from. Point the CLI at a different origin inside a clone and it stops with a usage error.
The ignore file
.monofile/ignore is gitignore-flavoured and starts with .git, node_modules, .monofile and .DS_Store. A bare name matches that name in any directory, a pattern containing / matches a path relative to the mirror root, and a trailing / matches a whole subtree. .monofile itself is always ignored.
# .monofile/ignore
.git
node_modules
.DS_Store
build/
*.log
notes/scratch.mdHow conflicts are resolved
A conflict is when the same file changed on both sides since the last sync. It is not an error and does not stop the round; the only question is which version wins, and --on-conflict answers it.
| Mode | What happens |
|---|---|
local (default) | Your local file wins and is pushed. The version it replaced is still in the file's cloud history |
remote | The cloud version wins and overwrites your local file |
copy | Both survive. The CLI copies your version aside as notes.conflict-20260917-143005.md and pushes it as a new file, while notes.md takes the cloud version |
Deletes and renames
Deletes propagate in both directions, and the engine prunes emptied directories on both sides. The base manifest is what lets the engine tell "you deleted this" from "this arrived from the cloud after your last sync", which a simple two-way comparison cannot.
The engine detects renames by content hash, on either side, and sends them as renames. The file keeps its identity, so its version history, its share link and every [[Wiki-Link]] pointing at it survive a plain mv on your laptop.
One push, one changeset
A single sync round groups all its cloud writes into one changeset. When a round pushed nine files, that is one entry in monofile changesets list and one id to undo:
monofile changesets list --limit 5
monofile changesets revert <id>See Changesets for the full model.
One syncer per folder
While a sync or a watch is running it holds .monofile/lock. A second sync on the same folder stops immediately with exit code 6 and tells you which process holds it. The CLI replaces a lock whose process is gone, so a crash does not leave the folder wedged. The desktop app respects the same lock and shows the folder as locked while your CLI watcher owns it.
Exit codes
| Code | Meaning |
|---|---|
0 | Synced. Conflicts resolved by your policy are not failures |
1 | At least one operation failed. It is retried on the next round |
2 | Usage: not a clone, a bad flag, a non-empty clone target, or an origin mismatch |
4 | The remote folder does not exist |
5 | The key lacks a scope the push needed. Nothing in that batch was applied |
6 | Another sync or watch holds the folder lock |
7 | The server could not be reached |
Next steps
CLI CommandsEvery command, flag, JSON output shape and exit code.
The Desktop AppInstall, sign in, and work in local folders or a synced cloud workspace.
ChangesetsRuns grouped into one reviewable, revertable unit.
Filesystem APIThe manifest and batch endpoints for bulk sync clients.