Sync Model

MindWiki keeps your vault in sync across the macOS app, the web app, and any connected MCP / API clients. This page explains how that sync actually works so you can predict its behavior and trust it.

The big picture

Your vault has two homes: a server-side store and (if you use the macOS app) a folder on your Mac. The web app reads and writes against the server-side store directly. The macOS app writes locally first, then syncs through the same server-side store. Every device sees the same vault.

The event log

Every page write or delete generates one event in an append-only log. Events carry:

  • A monotonic seq number
  • The path that changed
  • A content hash of the new state (or a tombstone for deletes)
  • The device that originated the change
  • An origin (web, desktop, mcp, api)
  • A timestamp

This log is the source of truth for sync. Reconstructing the current state of any page is a matter of finding the most recent event for that path.

How clients pull

When the macOS app starts, and on a heartbeat every ~15 seconds while running, it calls:

GET /vault/changes?since={cursor}&exclude_device={device_id}

The server returns every event newer than the cursor, excluding events the calling device originated (so devices don't echo their own changes). The client applies each event to the local files: writes pull down content if the local hash differs, deletes remove the file.

The web app uses the same endpoint to power its live activity dashboard.

How clients push

When the macOS app detects a local change, it sends:

POST /vault/page
{
  "path": "...",
  "content": "...",
  "base_hash": "<hash from last known server state>"
}

The base_hash is optimistic concurrency. The server checks whether the page's current hash matches base_hash:

  • Match — the write applies and a new event lands in the log.
  • No match (HTTP 409) — your push raced another change. The server returns the actual current hash so you know what's there. The client writes a .conflict-... file to preserve your local work without clobbering the remote.

This is how multi-device editing stays sane: nothing overwrites silently.

What you'll see

In normal use, sync is invisible. Edits propagate within a few seconds. The macOS app's status bar shows the most recent sync time. The web app shows a live activity panel on the dashboard.

When something interesting happens, you'll see one of these:

  • A `.conflict-...md` file — your push lost a race. See Conflicts.
  • A failed sync notice — usually transient (network blip). The next heartbeat retries.
  • An out-of-date status — typically resolves when you reopen the app or wait for the next heartbeat.

What gets synced

  • All `.md` pages — full body content
  • All files in `_assets/` — images, audio, video, PDFs, attachments
  • Folder structure — additions, renames, deletions

What does not sync:

  • Files outside the vault folder (the macOS app only operates on the vault you chose).
  • Files in .trash/ (kept locally for recovery, not synced).
  • Files in .mindwiki/ (local sync state — internal).
  • Hidden files like .DS_Store, .git, node_modules.

Origins

Every event has an origin field that tells you which surface created the change. Useful when debugging:

OriginMeans
webEdit made via the web app
desktopEdit made via the macOS app
mcpChange made via an MCP tool call
apiChange made via REST API key

You can see origins in the activity log on /account/connections and in the macOS Agents → Sessions & Activity view.

Multi-device behavior

You can run the macOS app on more than one Mac. Each device gets its own device ID; each pushes and pulls against the same vault on the server. Edits made on one Mac propagate to the other through the standard pull/push cycle.

Where to go next