The sync agent is a translation layer between files on disk and typed Marfa items. Unlike transport layers — Syncthing, iCloud Drive, Obsidian Sync — that move bytes between machines, the sync agent interprets files as typed data so other Marfa clients can read, write, and reason over them. Published to npm asDocumentation Index
Fetch the complete documentation index at: https://docs.myme.so/llms.txt
Use this file to discover all available pages before exploring further.
@withmarfa/sync; the CLI binary is marfa-sync.
The engine is UI-agnostic. The CLI drives it over a loopback HTTP control surface; menu-bar and desktop UIs use the same surface.
Install
marfa-sync binary on your PATH. Confirm with marfa-sync --help.
Quick start
Start the agent against a folder
Optionally run as a daemon
Pass
--daemon to install a launchd service so the engine survives terminal sessions:Configuration
Configuration merges from three sources in priority order: CLI flags, environment variables, then~/.marfa/sync.json.
| Variable | Description |
|---|---|
MARFA_API_URL | Marfa server URL |
MARFA_API_KEY | API key for authentication |
default_tier shapes what tier items the agent writes. items.bulk is admin-only — a standard-scoped credential gets a one-time warning and the engine downshifts to per-item creates for the rest of the session.
Adding roots
The agent watches one or more roots. Manage them at runtime:| Command | Purpose |
|---|---|
marfa-sync roots add <path> [--debounce <ms>] | Register a new root. Returns its id. |
marfa-sync roots list | List registered roots with sync state. |
marfa-sync roots rm <id> | Stop watching a root. Items already on the server are not deleted. |
<root>/.marfa/ tree — config, mapping rules, ignore patterns, SQLite bookkeeping, status snapshot, conflict copies. The structure parallels .git: per-root state stays with the folder, engine-wide state lives at ~/.marfa/.
Item types and mapping rules
When a file is pushed, the agent decides what type the resulting Marfa item carries. Resolution order, first match wins:- Frontmatter
type:if present (text files only) - User rules from
<root>/.marfa/mappings.json - Built-in mime defaults (
image/*→core.file.image, audio, video) - Fallback:
core.file
.md file in Notes/ into a core.note, with title taken from the first H1 and body from the post-frontmatter content:
glob (gitignore-style), mime, frontmatter (key/value match, or true for presence). All present predicates AND together.
Folder structure
Paths are properties, not graph structure. The agent stores each file’s root-relative path as the item’s
source_id (e.g. Daily/2026/05/08.md). It does not fabricate folder items with parent-of edges, and there is no core.folder type.Querying by folder
source_id is a system field on every item, so the standard filter language reaches it directly. To list everything under a folder, use a starts_with prefix match:
Cross-device behavior
source_id is relative to the vault root, so the same logical vault on a different machine produces the same source_id values regardless of where on disk it lives. A Mac with the vault at ~/Notes/ and a Mac with it at /Volumes/Work/Notes/ both produce source_id = "Daily/2026/05/08.md" for the same file. Absolute paths live only in the agent’s local state.db and never reach the server.
A folder query (source_id starts_with "Daily/") returns the same items regardless of which device synced them. Folder structure is portable across the user’s devices for free.
Removing the agent removes the items
Items the agent creates are stamped with asource that resolves to the agent’s system.connection row. Revoking that Connection cascades deletion of every item it authored — no separate “delete this folder” operation exists or is needed. To stop syncing one root without dropping the others, marfa-sync roots rm <id> stops watching but does not delete the items already on the server; to delete the items, revoke the Connection or trash them through the standard item lifecycle.
When to use folders vs tags or edges
Folders are good at what filesystems are good at: where the bytes live. They give the user a familiar mental model for organizing files on disk and they give clients a cheap prefix-query for “everything under here”. For grouping that needs first-class data treatment — “all notes about Project X”, “everything attached to this meeting”, “the chain of replies in this thread” — use tags or typed edges. Tags are flat labels that travel with the item regardless of where the file moves on disk; edges express directional relationships between items that the query layer and other apps can traverse. Both are independent of where the file sits in the folder tree.Rename detection
Renaming a file inside a watched root preserves the Marfa item id, its edges, and its version history. The agent matches the new path’s inode against recently-unlinked paths in a 5-second window to recognize the rename, then updates the item’s source path on the server in place. No delete-and-recreate, no orphan edges. Cross-volume moves fall through to delete + create cleanly (different inodes). Renames that span longer than 5 seconds — for example, an engine crash between unlink and add — fall through the same way.Conflict handling
When the server and disk diverge on the same item, the agent snapshots the conflicting local copy to<root>/.marfa/conflicts/<basename>.<id>.<ext>, force-pushes the local version to preserve it in version history, pulls the server’s version to the canonical path, and emits a conflict-opened event.
Resolve in one of two ways:
- Edit the canonical file and delete the conflict file — the watcher picks up the unlink and closes the conflict.
- Run
marfa-sync resolve <conflict-id> --keep local|server.localcopies the conflict file over the canonical path and re-pushes;serversimply deletes the conflict file.
conflict-id appears in the conflict filename, status.json, and the CLI command — no translation step between surfaces.
Supported filesystems
The agent’s stance on filesystems known to misbehave under continuous watching is “warn and allow”: register the root, surface aroot-warned event so a UI can show the warning inline, and let the user proceed with eyes open.
iCloud Drive (macOS) — warned
iCloud Drive (macOS) — warned
Detected by path-prefix against the system iCloud container. iCloud’s eviction and redownload cycle generates spurious filesystem events the agent cannot distinguish from genuine writes, so real-time sync is unreliable. Move the folder out of iCloud Drive for reliable two-way sync.
Network mounts (SMB, CIFS, NFS, AFP, WebDAV, FTP) — warned
Network mounts (SMB, CIFS, NFS, AFP, WebDAV, FTP) — warned
Detected by inspecting active mounts. Push (local-to-server) works fine; bidirectional real-time does not, because remote writes don’t always trigger filesystem-change events on macOS or Linux.
Symlinks — not followed
Symlinks — not followed
The watcher and the initial scan don’t traverse symlinks, so a symlink inside a watched root is invisible to the agent. There is no recursion, so no cycle risk.
Engine events
The agent exposes a server-sent-events stream tailable viamarfa-sync events. Each event is a typed record.
| Event | Fires on |
|---|---|
file-synced | File pushed or pulled successfully. Carries direction and action (created, updated, deleted, renamed). |
file-failed | Push or pull failed. Carries reason. |
conflict-opened | New conflict detected; conflict file written to .marfa/conflicts/. |
conflict-resolved | User picked a winner or deleted the conflict file. |
root-added / root-removed | Root configuration changed at runtime. |
root-warned | Filesystem caveat surfaced at root-add or recovery. |
root-resync-started / root-resync-finished | Full root rescan in progress. |
Command reference
| Command | Purpose |
|---|---|
marfa-sync start <folder> [--daemon] [--url <api-url>] [--key <api-key>] [--types <path>] | Start the engine and register the folder as a root. |
marfa-sync stop | Stop the daemonised engine. |
marfa-sync status [--json] | Report engine and per-root state. |
marfa-sync stats [--json] | Show engine runtime stats — pending events, totals processed, circuit-breaker state. |
marfa-sync roots add <path> [--debounce <ms>] | Register a new root at runtime. |
marfa-sync roots list | List registered roots. |
marfa-sync roots rm <id> | Remove a root from the watch set. |
marfa-sync resync <root-id> | Re-scan a root from scratch and reconcile against the server. |
marfa-sync resolve <conflict-id> --keep local|server | Resolve an open conflict by choosing a winner. |
marfa-sync events | Tail the engine event stream. |
status queries the loopback HTTP surface first and falls back to per-root <root>/.marfa/status.json when the engine isn’t reachable. Every other command requires the engine to be running.
Troubleshooting
I added a root but nothing syncs.
I added a root but nothing syncs.
Check the API key is valid (
marfa-sync status shows the engine state and any auth errors). Confirm the folder isn’t on a warned filesystem — root-warned events surface in marfa-sync events. If mappings.json is in use, verify the rules actually match the files in the folder; a file with no matching rule and no frontmatter type: falls through to the core.file fallback, which the agent skips for text content.I renamed a file but it appeared as a new item.
I renamed a file but it appeared as a new item.
The rename-detection window is 5 seconds. Renames that span longer — for example, a crash between the unlink and the add — fall through to delete + create. Cross-volume moves do the same because the inode changes. To recover the original item’s edges and history, restore from the version history or delete the new item and rename within the window.
A folder of mine isn't appearing as a folder in Marfa.
A folder of mine isn't appearing as a folder in Marfa.
Folder structure isn’t modeled as items. See Folder structure above — query items by
source_id prefix to list everything under a folder.How do I move the agent to a new machine?
How do I move the agent to a new machine?
Copy
~/.marfa/sync.json to the new machine. The state SQLite databases under each <root>/.marfa/ repopulate on first marfa-sync start. The Marfa items already live on the server; nothing has to migrate.Repo
Source atwithmarfa/sync. Engine internals, mapping rule schema, the loopback HTTP control surface, and the conflict-resolution state machine live in the repo’s CLAUDE.md.