Sync, files, and backup
Sync is never required. Everything works with Wi-Fi off. When you want history, backup, or a second machine, keep ~/devnote as plain Markdown and share it through a private git repository.
Setup in 3 steps
git@github.com:you/devnote.git. Do not add a README, license, or other initial commit; an empty remote makes the first sync clean.~/devnote does not exist yet, click Export now first. Paste the remote URL, then press Enter or move focus away from the field. Invalid URLs show Not a git remote.https://host/user/repo.git), SSH (ssh://git@host/user/repo.git), and SSH shorthand (git@host:user/repo.git). Local filesystem paths are rejected: sync is meant for a private hosted repository.Authentication
devnote shells out to the git already installed on your machine. It does not store SSH keys, passwords, or tokens itself; it uses your existing SSH agent and git credential helper.
SSH
ssh -T git@github.com
# success looks like: Hi you! You've successfully authenticated…
Use SSH when possible. Keep the private key in ~/.ssh, protect it with a passphrase, and back up ~/.ssh separately from notes.
HTTPS
Use HTTPS with a credential helper so git can remember the login without devnote ever seeing it:
# macOS
git config --global credential.helper osxkeychain
# Windows
git config --global credential.helper manager
# Linux (example; needs libsecret)
git config --global credential.helper libsecret
How Sync now works
- Export: writes changed, moved, renamed, trashed, and permanently deleted notes to
~/devnote. - Commit: stages everything with
git add -Aand commits only when something changed. The message looks likedevnote: sync 2026-09-14T18:30:00.000Z (laptop). - Pull: runs
git pull --no-rebase --no-editwhen an upstream branch exists. First-time repositories report no upstream and continue. - Resolve or push: on conflict, devnote applies the per-file rule below and finishes the merge; otherwise it pushes, using
push -u origin HEADthe first time. - Import: reads
~/devnoteback into the app, including remote changes and preserved conflict notes.
Files on disk
Each note is one Markdown file:
~/devnote/<notebook-path>/<slug>-<id8>.md
~/devnote/.attachments/<note>-<nonce>-<name>.png
Note files carry YAML frontmatter with id, title, notebook, tags, status, pinned, trashed, createdAt, and updatedAt. Unknown notebooks import under Unsorted. Files without an id are adopted and written back with one. When the same note exists in both places, newer updatedAt wins; imports never delete notes.
.md file externally does not delete the note: the next export recreates the missing file. Trash and restore notes inside devnote; only Trash → Delete forever prunes the file.Automatic disk imports
The desktop app watches ~/devnote recursively. After about 750 milliseconds without filesystem activity, plus a short app-side debounce, devnote imports changed Markdown files. A notice appears only when notes were created or updated, for example:
Mirror changed on disk: 1 new, 2 updated
The watcher skips imports while a sync is running and while a merge conflict is waiting for review. It never commits, pulls remote history, or pushes.
Attachments
Paste or drop an image into the editor on the desktop app. devnote inserts an Uploading… placeholder at the cursor, saves the file, then replaces it with a stable reference such as:

- Images only:
png,jpg,jpeg,gif,webp,avif, andsvg. - Size cap: 10 MiB per image.
- Filenames: note prefix, upload nonce, and original-name slug, so repeat pastes do not collide and renames do not orphan ownership.
- Preview: desktop resolves attachment bytes to a local preview image. Browsers cannot read these bytes and keep the alt text instead.
- Sync: attachments travel with normal git sync.
~/devnote/.attachments. Until that changes, keep image backups in git or copy .attachments separately.Conflicts
updatedAt wins; a missing timestamp loses, and an exact tie keeps the local version.The losing version is preserved next to the winner with a fresh note id, for example:
~/devnote/engineering/sync-engine-a1b2c3d4.md
~/devnote/engineering/sync-engine-a1b2c3d4.conflict-laptop.md
The preserved note is titled Sync engine design (conflict laptop) and appears in the app for review. The red banner offers Review loser and Resolve & sync. Device names come from DEVNOTE_DEVICE, then HOSTNAME, then the machine hostname; set DEVNOTE_DEVICE for readable conflict suffixes.
Recovery
- Open the preserved (conflict …) note and copy anything still needed into the winner.
- Trash the preserved note only when you are sure nothing remains.
- Click Resolve & sync to export, commit, and push the cleanup.
If a sync error leaves git mid-merge, do not hand-edit conflict markers. Run Sync now again first. As a last resort in Terminal:
cd ~/devnote
git status
git merge --abort
Status
| State | Meaning | Next step |
|---|---|---|
| Synced | Everything is committed and pushed. | None. |
| Uncommitted changes | Local files differ from the last commit. | Click Sync now. |
| Unpushed commits | Local commits are ahead of the remote. | Click Sync now. |
| Remote updates available | The remote is ahead of this machine. | Click Sync now. |
| Diverged — needs attention | Both sides have new commits. | Click Sync now; review conflicts if any appear. |
| Conflict — review needed | A merge conflict is waiting. | Review the loser, then Resolve & sync. |
| No sync repo (Export first) | ~/devnote/.git does not exist yet. | Click Export now, then set the remote. |
| Git not installed | The desktop app cannot find the git CLI. | Install git and restart the app. |
Backup & restore
Preferences → Sync → Export backup (.zip) creates ~/devnote-backup-YYYY-MM-DD.zip from visible mirror files. Restore from backup is desktop-only: pick a zip, confirm the destructive overwrite, and devnote writes it over ~/devnote, then imports the result. Notes already open stay on screen until the next export.
iOS
There is no mobile app yet. The supported path is manual: clone the private repository in Working Copy, edit Markdown files, commit, and push. On desktop, click Sync now to pull those changes. Expect manual commits, no background sync, and possible clock skew in updatedAt comparisons. Passphrase-based git encryption remains a later roadmap item, not a built-in feature.
Errors
| Message | Likely cause | Fix |
|---|---|---|
git-not-found | No git CLI is installed. | Install git, restart devnote, then refresh sync state. |
not-a-repo | ~/devnote/.git is missing. | Click Export now before setting a remote or syncing. |
Not a git remote | URL has whitespace or an unsupported/local form. | Use HTTPS, ssh://, or git@host:path. |
empty-remote | The saved remote URL is blank. | Paste a remote URL and press Enter. |
Push failed: … | Authentication, permission, upstream, or non-fast-forward push. | Check SSH/HTTPS credentials, repository access, and whether the remote has commits your machine lacks. |
no upstream | Normal on first sync; no tracking branch exists yet. | Continue: devnote pushes with -u origin HEAD. |
mirror not initialized | Backup ran before ~/devnote existed. | Click Export now, then back up again. |
File mirror needs the DevNote desktop app | Mirror, sync, attachments, or zip restore was attempted in a browser. | Use the desktop app for those operations. |