Sync and privacy
Git sync
Keep a vault in sync with a git remote: set it up, read the sync states, handle conflicts and encrypt the remote.
Git sync keeps a vault in sync with a git remote, usually a private GitHub repository. Use this page to set it up, read its states, solve a conflict and turn on encryption. The vault stays a plain git repository, so Obsidian Git, a terminal or another Mac can use the same remote.
Set up sync
Loam uses the git installed on your Mac and its credentials: ssh keys, the osxkeychain helper or gh auth. With sync.setup, Loam stores no password or token, and it never prompts. Missing credentials show as Sync needs attention. The only secret Loam keeps for sync is the key of an encrypted vault, in the Keychain. The Connect GitHub button in the app keeps its GitHub sign-in in the Keychain too, under “Loam GitHub”.
- Create an empty private repository on your git host, or use an existing one.
- Set the vault up with the remote address. The setup also syncs once.
- Check that
stateissyncedin the status.
loam --vault ~/MyVault sync.setup url=https://github.com/<you>/<vault>.git
loam --vault ~/MyVault sync.statussync.setup adds to what is there and never replaces it:
- Runs
git initif the vault is not a repository. UsegitDir=<path>to keep git’s data outside the vault. - Adds
.loam/cache/,.DS_Storeand.trash/to.gitignore. - Sets a local git identity, only if none exists.
- Adds the remote. A remote that already has another address stops the setup.
- Turns automatic sync on, and syncs once.
.git folder it syncs. Use gitDir=~/.vault-git/<name>.git.sync.setup, run without url. Turn Obsidian Git’s automatic backup off on the same Mac first.How a sync works
A sync runs one cycle at a time, off the main thread. A cycle does five things:
- Fetch the branch from the remote.
- Save the unsaved typing of the notes the remote changed, so it is part of the merge.
- Commit everything changed on this Mac, with a message such as
vault backup: 2026-10-20 14:05:03 (<device>). - Merge the remote branch. Loam merges and never rebases, so history is never rewritten.
- Push.
While automatic sync is on, a cycle runs:
- 30 seconds after the last change (
commitDelaySeconds), and at least every 5 minutes while changes keep coming. - When the app comes to the front, if the remote was not checked in the last minute.
- Every 5 minutes while the app is in front (
pullIntervalMinutes). - On
sync.now, or a click on the status bar item.
When the app is in the back, nothing runs, except a commit already pending. Offline, commits keep happening on this Mac, and Loam retries after 1, 2, 5, 10, then 30 minutes.
Sync states
The status bar and sync.status show one of these states:
- Synced: everything is committed, and the remote has the same commits. With conflict copies, it says 1 conflict or N conflicts.
- Syncing…: a cycle is running.
- Offline: the remote cannot be reached. Changes wait on this Mac.
- Sync needs attention: something needs you. Nothing more is written until it is fixed, and the message says what.
- Sync off: automatic sync is off, or the vault is not a git repository.
Conflicts and safety
Git merges edits to different lines of a note by itself. A conflict happens when both sides changed the same lines, or the same file in another way. Loam then keeps both versions:
- Both sides changed the note, or both created it: the note keeps this Mac’s version. The other one is saved next to it as
Note (conflict <device> 2026-10-20).md. - One side deleted the file and the other changed it: the changed file is kept.
- A binary file, such as an image: the same rules, on whole files.
- A note open with unsaved typing that differs from the merge: the editor keeps its text, and the merged version is saved as a conflict copy.
Conflict copies are committed, so every device sees them. Open the copy from the status bar, merge it by hand, then delete it. A file never gets conflict markers.
Loam never runs a destructive git command:
- No
reset,clean,stash,rebaseor forced push. - A merge, rebase or cherry-pick that another tool started is left alone.
sync.resolvefinishes a merge, with conflict copies, only when you run it. - Deleting 50 files or more at once, when they are also a quarter of the vault or more, pauses sync until you run
sync.now confirm=deletions. - Files over 50 MB are left out of commits, and listed.
.loam/cache/never syncs.- If Loam cannot fetch for a day while offline, the state becomes Sync needs attention. A sync cannot stop silently.
Encryption
With encryption, the remote only holds ciphertext. The vault on each Mac stays plain Markdown. Turn it on at setup:
loam --vault ~/MyVault sync.setup encrypt=true- Encrypted: the content of every file, including notes, attachments and
.loam/config.json. - Not encrypted: file and folder names, sizes, commit dates and messages,
.gitattributesand.gitignore.
The key is 32 random bytes, made by sync.setup encrypt=true. It stays in the Keychain of each Mac, under “Loam vault key”, and never in the vault. Loam encrypts through a git filter with AES-256-GCM-SIV. Obsidian Git, a phone or an agent that reads the repository without Loam sees only ciphertext. A terminal on a Mac with Loam reads it, for example with git log -p.
To add another Mac:
- On a Mac that already syncs, export the key to a file with
sync.key.export file=~/Desktop/vault.key. - Move the file to the new Mac, for example with AirDrop. Import it there with
sync.key.import file=~/Desktop/vault.key, then delete the file. - Set the vault up as usual with
sync.setup url=<remote>.
sync.key.rotate changes the key, but history stays encrypted with the old key, which still opens it.Commands and settings
Settings live in .loam/config.json and travel with the vault: auto, remote (origin), branch (empty means the branch checked out), commitDelaySeconds (30) and pullIntervalMinutes (5). The device name belongs to each clone. sync.setup device=<name> sets it.
| Command | What it does |
|---|---|
sync.setup | Sets the vault up and syncs once. encrypt=true encrypts the remote. |
sync.status | Shows where sync stands, as JSON, without changing anything. |
sync.now | Syncs now. confirm=deletions allows a mass deletion. confirm=plain pushes commits made before encryption. |
sync.enable and sync.disable | Turns automatic sync on or off. |
sync.resolve | Finishes a merge left by another tool, with conflict copies. |
sync.key.export | Prints the key of an encrypted vault. file=<path> writes it to a file. |
sync.key.import | Keeps the key on this Mac, from key=loam-key:… or file=<path>, then syncs. |
sync.key.rotate | Encrypts every file again with a new key, in one commit, and pushes it. A Mac or agent without the new key stops syncing until you import it there. |