Vibecode Obsidian Sync
track this build6 phases, 12 steps, beginner friendly0%If you already accept local Markdown files, syncing folders across devices is a solved problem; the hard part is not the app, it is conflict-free convenience.
You are building a lean indie version of Obsidian Sync. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Obsidian Sync · indie build Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Estimated effort: **one sitting**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Sync | git with a private remote | history for free; the conflict model is understood | | Watcher | A bash script on a systemd timer or launchd agent, or Node with chokidar | the simpler one for your machine | | Mobile | Working Copy on iOS, a git client on Android | no mobile code to build | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Obsidian vault folder, backed up once by hand first** · free - Why: You are about to automate commits in it. Take a plain copy before Phase 1 so nothing is at risk while you learn. - Get it: Copy the vault folder to an external drive or a zip. - [ ] **A private git repository (GitHub, GitLab or Codeberg)** · free - Why: The remote is the sync point. Private, because these are your notes. - Get it: Create an empty private repository. For pushing without passwords, add an SSH key: ssh-keygen -t ed25519, then add the public key in the host's SSH keys settings. - Verify: ssh -T git@github.com greets you - [ ] **A second machine or a phone** · free - Why: Every phase is tested across two devices; one machine cannot test sync. - Get it: Another computer, or a phone with a git client. - [ ] **A mobile git client (optional)** (optional) · free to about $20 one-time - Why: Phase 6: the phone reads and writes the same repository. - Get it: Working Copy on iOS (one-time purchase for push), MGit or GitJournal on Android. ## Quick start ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. - official mobile sync polish - conflict handling - version history UI - encrypted vault workflow - Obsidian support If one of those is essential to you, that is the reason to keep paying for Obsidian Sync, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Obsidian Sync The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a sync setup for my Obsidian vault to replace Obsidian Sync. Build it in phases, in the order below. This is plumbing, not an app. Each phase ends in a test you run on two real machines, and Phase 4 is the one that decides whether you can trust this with your notes. ### Rule zero The vault is a folder of Markdown files. Do not write a sync engine, do not invent a merge algorithm, and do not build a mobile client. Use git, because you want history. Syncthing is the right answer if you want continuous sync and do not care about history · say which you chose in the README and why. ### Stack (fixed, do not substitute) - git, plus a small watcher: Node with `chokidar` and `simple-git`, or a bash script on a systemd timer or launchd agent. Pick the simpler for the machine. - A private remote repository. Token in `.env` or the system credential helper, never in a file inside the vault. ### Phase 1 · Repository hygiene Build: `git init` in the vault and a `.gitignore` covering `.obsidian/workspace*`, `.obsidian/cache`, `.trash/` and OS junk. Device-local state must not sync, or two machines will fight over window positions forever and bury real changes in noise. Keep the rest of `.obsidian/` (plugins, themes, hotkeys) if you want settings to travel, and say which you chose. Done when: opening the vault on a second machine does not produce a diff from merely opening it, and `git status` is clean after launching and closing Obsidian without editing anything. Do not build yet: automation. ### Phase 2 · Manual round trip Build: nothing. Do the loop by hand on two machines · edit, commit, push, pull, edit the other side, push, pull back. Done when: a note written on machine A appears on machine B and back, and you have seen at least one real merge succeed. Automating a loop you have not performed manually is how you end up debugging a script and a git problem at the same time. ### Phase 3 · The watcher Build: the daemon. Debounce 30 seconds after the last filesystem change, then `git add -A`, commit with an automatic message including the changed file count, and push. Pull with `--rebase` on start and every 5 minutes. Never run two git operations concurrently · take a lock file, because a commit racing a rebase is how a repository ends up in a detached state at 2am. Done when: editing a note results in a commit and push within a minute; editing ten notes in quick succession produces one commit, not ten; the daemon survives a network outage and catches up when it returns; and a manual `git` command run while the daemon is idle is not clobbered. ### Phase 4 · Conflicts, without data loss Build: the conflict policy, which is the whole reason this phase exists. On a rebase conflict, never drop either side. Keep the local version in place, write the incoming version alongside it as `<name>.conflict-YYYY-MM-DD-HHMM.md`, commit that, and log it loudly. Obsidian will show the conflict file as a normal note, which is exactly right · a human resolves it by reading both. Done when: you have deliberately created a conflict by editing the same note on two machines while both are offline, brought both online, and confirmed that both versions survive, the vault is not left mid-rebase, and the daemon keeps running. Test this before you trust it with anything you care about. ### Phase 5 · Status and safety Build: a `vaultsync status` command showing last push, last pull, pending changes, outstanding conflict files, and whether the daemon is alive. Add a size guard that refuses to auto-commit a change touching more than N files (default 100) and asks instead · that pattern is a sync bug or an accidental delete, not a writing session, and committing it is how a mistake propagates everywhere. Done when: deleting a folder of 200 notes triggers the guard rather than pushing the deletion, and status reports an unresolved conflict file accurately. ### Phase 6 · Mobile and second machine Build: documentation only. Point a git client (Working Copy on iOS, an equivalent on Android) at the same repository, and write the second-machine setup steps. Done when: a second machine is set up from the README alone, and a note written on the phone reaches the desktop. ### Out of scope (and why) - An encrypted-vault workflow. If you need the remote to be zero-knowledge, that is a different design (encrypt before commit) with real usability costs on mobile · do not half-build it. - A version-history UI. `git log` is the history. - Anything mobile beyond configuring an existing client. ### README must contain - The conflict-file convention, and the instruction to search for `*.conflict-*` periodically. - The size-guard threshold and how to override it deliberately. - The honest trade: this is sync you operate. Obsidian Sync handles conflicts, history and mobile for you, and this does not · it costs you a restore drill and the occasional conflict file instead of the subscription. ===== AGENTS.md ===== # Agent instructions · Obsidian Sync indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: git with a private remote, A bash script on a systemd timer or launchd agent, or Node with chokidar, Working Copy on iOS, a git client on Android. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Test this before trusting the sync with anything you care about. ===== BUILD_PLAN.md ===== # Build plan · Obsidian Sync Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Repository hygiene A vault that does not produce a diff just from being opened. ### Steps 1. git init in the vault and write .gitignore .obsidian/workspace*, .obsidian/cache, .trash/, .DS_Store. Keep the rest of .obsidian if you want settings to travel. Files: `.gitignore` ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` 2. Open and close Obsidian and check git status is clean ### Done when - [ ] git status is clean after launching and closing Obsidian without editing - [ ] Opening the vault on the second machine produces no diff ## Phase 2 · Manual round trip Do the loop by hand before automating it. ### Steps 1. On machine A: edit a note, commit, push ```sh git add -A && git commit -m 'notes' && git push -u origin main ``` 2. On machine B: pull, edit the same note elsewhere, push; then pull on A and watch the merge ```sh git pull --rebase ``` ### Done when - [ ] A note written on A appears on B and back - [ ] You have seen one real merge succeed ## Phase 3 · The watcher Debounced commits, timed pulls, one git operation at a time. ### Steps 1. Write vaultsync.sh (or the Node equivalent) Debounce DEBOUNCE_SECONDS after the last change, git add -A, commit with the changed-file count, push. Pull --rebase on start and every PULL_INTERVAL_MINUTES. Take a lock file so a commit never races a rebase. Files: `vaultsync.sh` 2. Install it as a systemd user timer or a launchd agent ```sh systemctl --user enable --now vaultsync.timer ``` ### Done when - [ ] Editing a note results in a commit and push within a minute - [ ] Ten quick edits produce one commit - [ ] A network outage is caught up when the network returns - [ ] A manual git command while idle is not clobbered ## Phase 4 · Conflicts, without data loss Both versions survive, the vault is never left mid-rebase, the daemon keeps running. ### Steps 1. On rebase conflict, keep local, write the incoming version as <name>.conflict-YYYY-MM-DD-HHMM.md, commit, log loudly 2. Create a conflict deliberately with both machines offline and bring them back ### Done when - [ ] Both versions survive as files - [ ] The vault is not left mid-rebase - [ ] The daemon keeps running ### Watch out - Test this before trusting the sync with anything you care about. ## Phase 5 · Status and safety Know the state, and never propagate a mass deletion. ### Steps 1. Write vaultsync status: last push, last pull, pending, conflict files, daemon alive 2. Add the MAX_CHANGED_FILES guard that asks instead of committing ### Done when - [ ] Deleting a folder of 200 notes triggers the guard - [ ] status reports an unresolved conflict file ## Phase 6 · Mobile and second machine Documentation only: a phone and another computer set up from the README. ### Steps 1. Write the README: second-machine setup, the conflict-file convention, the size-guard override Files: `README.md` 2. Set up the phone client against the same repository and write one note from it Working Copy on iOS, MGit or GitJournal on Android. ### Done when - [ ] A second machine is set up from the README alone - [ ] A note written on the phone reaches the desktop ## Not in this build - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. ## After v1, if you want it - A small web viewer of the repository for read-only access anywhere - Scheduled snapshots tagged by week ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Absolute path to the vault. VAULT_PATH=/Users/you/Notes # Required. Git remote name. REMOTE=origin # Optional. Wait this long after the last change before committing. DEBOUNCE_SECONDS=30 # Optional. How often to pull with rebase. PULL_INTERVAL_MINUTES=5 # Optional. Refuse to auto-commit a change touching more files than this. MAX_CHANGED_FILES=100
You are building a lean indie version of Obsidian Sync. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Obsidian Sync · indie build Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Estimated effort: **one sitting**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Sync | git with a private remote | history for free; the conflict model is understood | | Watcher | A bash script on a systemd timer or launchd agent, or Node with chokidar | the simpler one for your machine | | Mobile | Working Copy on iOS, a git client on Android | no mobile code to build | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Obsidian vault folder, backed up once by hand first** · free - Why: You are about to automate commits in it. Take a plain copy before Phase 1 so nothing is at risk while you learn. - Get it: Copy the vault folder to an external drive or a zip. - [ ] **A private git repository (GitHub, GitLab or Codeberg)** · free - Why: The remote is the sync point. Private, because these are your notes. - Get it: Create an empty private repository. For pushing without passwords, add an SSH key: ssh-keygen -t ed25519, then add the public key in the host's SSH keys settings. - Verify: ssh -T git@github.com greets you - [ ] **A second machine or a phone** · free - Why: Every phase is tested across two devices; one machine cannot test sync. - Get it: Another computer, or a phone with a git client. - [ ] **A mobile git client (optional)** (optional) · free to about $20 one-time - Why: Phase 6: the phone reads and writes the same repository. - Get it: Working Copy on iOS (one-time purchase for push), MGit or GitJournal on Android. ## Quick start ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. - official mobile sync polish - conflict handling - version history UI - encrypted vault workflow - Obsidian support If one of those is essential to you, that is the reason to keep paying for Obsidian Sync, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Obsidian Sync The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a sync setup for my Obsidian vault to replace Obsidian Sync. Build it in phases, in the order below. This is plumbing, not an app. Each phase ends in a test you run on two real machines, and Phase 4 is the one that decides whether you can trust this with your notes. ### Rule zero The vault is a folder of Markdown files. Do not write a sync engine, do not invent a merge algorithm, and do not build a mobile client. Use git, because you want history. Syncthing is the right answer if you want continuous sync and do not care about history · say which you chose in the README and why. ### Stack (fixed, do not substitute) - git, plus a small watcher: Node with `chokidar` and `simple-git`, or a bash script on a systemd timer or launchd agent. Pick the simpler for the machine. - A private remote repository. Token in `.env` or the system credential helper, never in a file inside the vault. ### Phase 1 · Repository hygiene Build: `git init` in the vault and a `.gitignore` covering `.obsidian/workspace*`, `.obsidian/cache`, `.trash/` and OS junk. Device-local state must not sync, or two machines will fight over window positions forever and bury real changes in noise. Keep the rest of `.obsidian/` (plugins, themes, hotkeys) if you want settings to travel, and say which you chose. Done when: opening the vault on a second machine does not produce a diff from merely opening it, and `git status` is clean after launching and closing Obsidian without editing anything. Do not build yet: automation. ### Phase 2 · Manual round trip Build: nothing. Do the loop by hand on two machines · edit, commit, push, pull, edit the other side, push, pull back. Done when: a note written on machine A appears on machine B and back, and you have seen at least one real merge succeed. Automating a loop you have not performed manually is how you end up debugging a script and a git problem at the same time. ### Phase 3 · The watcher Build: the daemon. Debounce 30 seconds after the last filesystem change, then `git add -A`, commit with an automatic message including the changed file count, and push. Pull with `--rebase` on start and every 5 minutes. Never run two git operations concurrently · take a lock file, because a commit racing a rebase is how a repository ends up in a detached state at 2am. Done when: editing a note results in a commit and push within a minute; editing ten notes in quick succession produces one commit, not ten; the daemon survives a network outage and catches up when it returns; and a manual `git` command run while the daemon is idle is not clobbered. ### Phase 4 · Conflicts, without data loss Build: the conflict policy, which is the whole reason this phase exists. On a rebase conflict, never drop either side. Keep the local version in place, write the incoming version alongside it as `<name>.conflict-YYYY-MM-DD-HHMM.md`, commit that, and log it loudly. Obsidian will show the conflict file as a normal note, which is exactly right · a human resolves it by reading both. Done when: you have deliberately created a conflict by editing the same note on two machines while both are offline, brought both online, and confirmed that both versions survive, the vault is not left mid-rebase, and the daemon keeps running. Test this before you trust it with anything you care about. ### Phase 5 · Status and safety Build: a `vaultsync status` command showing last push, last pull, pending changes, outstanding conflict files, and whether the daemon is alive. Add a size guard that refuses to auto-commit a change touching more than N files (default 100) and asks instead · that pattern is a sync bug or an accidental delete, not a writing session, and committing it is how a mistake propagates everywhere. Done when: deleting a folder of 200 notes triggers the guard rather than pushing the deletion, and status reports an unresolved conflict file accurately. ### Phase 6 · Mobile and second machine Build: documentation only. Point a git client (Working Copy on iOS, an equivalent on Android) at the same repository, and write the second-machine setup steps. Done when: a second machine is set up from the README alone, and a note written on the phone reaches the desktop. ### Out of scope (and why) - An encrypted-vault workflow. If you need the remote to be zero-knowledge, that is a different design (encrypt before commit) with real usability costs on mobile · do not half-build it. - A version-history UI. `git log` is the history. - Anything mobile beyond configuring an existing client. ### README must contain - The conflict-file convention, and the instruction to search for `*.conflict-*` periodically. - The size-guard threshold and how to override it deliberately. - The honest trade: this is sync you operate. Obsidian Sync handles conflicts, history and mobile for you, and this does not · it costs you a restore drill and the occasional conflict file instead of the subscription. ===== AGENTS.md ===== # Agent instructions · Obsidian Sync indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: git with a private remote, A bash script on a systemd timer or launchd agent, or Node with chokidar, Working Copy on iOS, a git client on Android. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Test this before trusting the sync with anything you care about. ===== BUILD_PLAN.md ===== # Build plan · Obsidian Sync Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Repository hygiene A vault that does not produce a diff just from being opened. ### Steps 1. git init in the vault and write .gitignore .obsidian/workspace*, .obsidian/cache, .trash/, .DS_Store. Keep the rest of .obsidian if you want settings to travel. Files: `.gitignore` ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` 2. Open and close Obsidian and check git status is clean ### Done when - [ ] git status is clean after launching and closing Obsidian without editing - [ ] Opening the vault on the second machine produces no diff ## Phase 2 · Manual round trip Do the loop by hand before automating it. ### Steps 1. On machine A: edit a note, commit, push ```sh git add -A && git commit -m 'notes' && git push -u origin main ``` 2. On machine B: pull, edit the same note elsewhere, push; then pull on A and watch the merge ```sh git pull --rebase ``` ### Done when - [ ] A note written on A appears on B and back - [ ] You have seen one real merge succeed ## Phase 3 · The watcher Debounced commits, timed pulls, one git operation at a time. ### Steps 1. Write vaultsync.sh (or the Node equivalent) Debounce DEBOUNCE_SECONDS after the last change, git add -A, commit with the changed-file count, push. Pull --rebase on start and every PULL_INTERVAL_MINUTES. Take a lock file so a commit never races a rebase. Files: `vaultsync.sh` 2. Install it as a systemd user timer or a launchd agent ```sh systemctl --user enable --now vaultsync.timer ``` ### Done when - [ ] Editing a note results in a commit and push within a minute - [ ] Ten quick edits produce one commit - [ ] A network outage is caught up when the network returns - [ ] A manual git command while idle is not clobbered ## Phase 4 · Conflicts, without data loss Both versions survive, the vault is never left mid-rebase, the daemon keeps running. ### Steps 1. On rebase conflict, keep local, write the incoming version as <name>.conflict-YYYY-MM-DD-HHMM.md, commit, log loudly 2. Create a conflict deliberately with both machines offline and bring them back ### Done when - [ ] Both versions survive as files - [ ] The vault is not left mid-rebase - [ ] The daemon keeps running ### Watch out - Test this before trusting the sync with anything you care about. ## Phase 5 · Status and safety Know the state, and never propagate a mass deletion. ### Steps 1. Write vaultsync status: last push, last pull, pending, conflict files, daemon alive 2. Add the MAX_CHANGED_FILES guard that asks instead of committing ### Done when - [ ] Deleting a folder of 200 notes triggers the guard - [ ] status reports an unresolved conflict file ## Phase 6 · Mobile and second machine Documentation only: a phone and another computer set up from the README. ### Steps 1. Write the README: second-machine setup, the conflict-file convention, the size-guard override Files: `README.md` 2. Set up the phone client against the same repository and write one note from it Working Copy on iOS, MGit or GitJournal on Android. ### Done when - [ ] A second machine is set up from the README alone - [ ] A note written on the phone reaches the desktop ## Not in this build - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. ## After v1, if you want it - A small web viewer of the repository for read-only access anywhere - Scheduled snapshots tagged by week ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Absolute path to the vault. VAULT_PATH=/Users/you/Notes # Required. Git remote name. REMOTE=origin # Optional. Wait this long after the last change before committing. DEBOUNCE_SECONDS=30 # Optional. How often to pull with rebase. PULL_INTERVAL_MINUTES=5 # Optional. Refuse to auto-commit a change touching more files than this. MAX_CHANGED_FILES=100
You are building a production product version of Obsidian Sync. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Obsidian Sync · product brief ## Problem If you already accept local Markdown files, syncing folders across devices is a solved problem; the hard part is not the app, it is conflict-free convenience. ## Product outcome Sync you understand completely: every change in history, conflicts that never lose a word, and a warning before a mistake spreads. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - local Markdown folder - Syncthing, Git, iCloud, Dropbox, or similar - optional mobile file access ## Explicit non-goals for v1 - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. - official mobile sync polish - conflict handling - version history UI - encrypted vault workflow - Obsidian support ## Success criteria - Conflict drill performed on two offline machines - Mass-deletion guard verified - Second machine set up from the README ===== BRIEF.md ===== # Build brief · Obsidian Sync The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a sync setup for my Obsidian vault to replace Obsidian Sync. Build it in phases, in the order below. This is plumbing, not an app. Each phase ends in a test you run on two real machines, and Phase 4 is the one that decides whether you can trust this with your notes. ### Rule zero The vault is a folder of Markdown files. Do not write a sync engine, do not invent a merge algorithm, and do not build a mobile client. Use git, because you want history. Syncthing is the right answer if you want continuous sync and do not care about history · say which you chose in the README and why. ### Stack (fixed, do not substitute) - git, plus a small watcher: Node with `chokidar` and `simple-git`, or a bash script on a systemd timer or launchd agent. Pick the simpler for the machine. - A private remote repository. Token in `.env` or the system credential helper, never in a file inside the vault. ### Phase 1 · Repository hygiene Build: `git init` in the vault and a `.gitignore` covering `.obsidian/workspace*`, `.obsidian/cache`, `.trash/` and OS junk. Device-local state must not sync, or two machines will fight over window positions forever and bury real changes in noise. Keep the rest of `.obsidian/` (plugins, themes, hotkeys) if you want settings to travel, and say which you chose. Done when: opening the vault on a second machine does not produce a diff from merely opening it, and `git status` is clean after launching and closing Obsidian without editing anything. Do not build yet: automation. ### Phase 2 · Manual round trip Build: nothing. Do the loop by hand on two machines · edit, commit, push, pull, edit the other side, push, pull back. Done when: a note written on machine A appears on machine B and back, and you have seen at least one real merge succeed. Automating a loop you have not performed manually is how you end up debugging a script and a git problem at the same time. ### Phase 3 · The watcher Build: the daemon. Debounce 30 seconds after the last filesystem change, then `git add -A`, commit with an automatic message including the changed file count, and push. Pull with `--rebase` on start and every 5 minutes. Never run two git operations concurrently · take a lock file, because a commit racing a rebase is how a repository ends up in a detached state at 2am. Done when: editing a note results in a commit and push within a minute; editing ten notes in quick succession produces one commit, not ten; the daemon survives a network outage and catches up when it returns; and a manual `git` command run while the daemon is idle is not clobbered. ### Phase 4 · Conflicts, without data loss Build: the conflict policy, which is the whole reason this phase exists. On a rebase conflict, never drop either side. Keep the local version in place, write the incoming version alongside it as `<name>.conflict-YYYY-MM-DD-HHMM.md`, commit that, and log it loudly. Obsidian will show the conflict file as a normal note, which is exactly right · a human resolves it by reading both. Done when: you have deliberately created a conflict by editing the same note on two machines while both are offline, brought both online, and confirmed that both versions survive, the vault is not left mid-rebase, and the daemon keeps running. Test this before you trust it with anything you care about. ### Phase 5 · Status and safety Build: a `vaultsync status` command showing last push, last pull, pending changes, outstanding conflict files, and whether the daemon is alive. Add a size guard that refuses to auto-commit a change touching more than N files (default 100) and asks instead · that pattern is a sync bug or an accidental delete, not a writing session, and committing it is how a mistake propagates everywhere. Done when: deleting a folder of 200 notes triggers the guard rather than pushing the deletion, and status reports an unresolved conflict file accurately. ### Phase 6 · Mobile and second machine Build: documentation only. Point a git client (Working Copy on iOS, an equivalent on Android) at the same repository, and write the second-machine setup steps. Done when: a second machine is set up from the README alone, and a note written on the phone reaches the desktop. ### Out of scope (and why) - An encrypted-vault workflow. If you need the remote to be zero-knowledge, that is a different design (encrypt before commit) with real usability costs on mobile · do not half-build it. - A version-history UI. `git log` is the history. - Anything mobile beyond configuring an existing client. ### README must contain - The conflict-file convention, and the instruction to search for `*.conflict-*` periodically. - The size-guard threshold and how to override it deliberately. - The honest trade: this is sync you operate. Obsidian Sync handles conflicts, history and mobile for you, and this does not · it costs you a restore drill and the occasional conflict file instead of the subscription. ===== ARCHITECTURE.md ===== # Architecture · Obsidian Sync ## Stack | Part | Choice | Why | | --- | --- | --- | | Sync | git with a private remote | history for free; the conflict model is understood | | Watcher | A bash script on a systemd timer or launchd agent, or Node with chokidar | the simpler one for your machine | | Mobile | Working Copy on iOS, a git client on Android | no mobile code to build | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Watcher | debounce, commit, push, lock | Node or bash; same behaviour | | Puller | timed rebase and conflict policy | The conflict-file convention is the contract | | Guard | mass-change refusal and status | Tune the threshold | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VAULT_PATH` · required · Absolute path to the vault. - `REMOTE` · required · Git remote name. - `DEBOUNCE_SECONDS` · optional · Wait this long after the last change before committing. - `PULL_INTERVAL_MINUTES` · optional · How often to pull with rebase. - `MAX_CHANGED_FILES` · optional · Refuse to auto-commit a change touching more files than this. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Obsidian Sync product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: git with a private remote, A bash script on a systemd timer or launchd agent, or Node with chokidar, Working Copy on iOS, a git client on Android. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Test this before trusting the sync with anything you care about. ===== MILESTONES.md ===== # Delivery milestones · Obsidian Sync Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Repository hygiene A vault that does not produce a diff just from being opened. ### Steps 1. git init in the vault and write .gitignore .obsidian/workspace*, .obsidian/cache, .trash/, .DS_Store. Keep the rest of .obsidian if you want settings to travel. Files: `.gitignore` ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` 2. Open and close Obsidian and check git status is clean ### Done when - [ ] git status is clean after launching and closing Obsidian without editing - [ ] Opening the vault on the second machine produces no diff ## M2 · Manual round trip Do the loop by hand before automating it. ### Steps 1. On machine A: edit a note, commit, push ```sh git add -A && git commit -m 'notes' && git push -u origin main ``` 2. On machine B: pull, edit the same note elsewhere, push; then pull on A and watch the merge ```sh git pull --rebase ``` ### Done when - [ ] A note written on A appears on B and back - [ ] You have seen one real merge succeed ## M3 · The watcher Debounced commits, timed pulls, one git operation at a time. ### Steps 1. Write vaultsync.sh (or the Node equivalent) Debounce DEBOUNCE_SECONDS after the last change, git add -A, commit with the changed-file count, push. Pull --rebase on start and every PULL_INTERVAL_MINUTES. Take a lock file so a commit never races a rebase. Files: `vaultsync.sh` 2. Install it as a systemd user timer or a launchd agent ```sh systemctl --user enable --now vaultsync.timer ``` ### Done when - [ ] Editing a note results in a commit and push within a minute - [ ] Ten quick edits produce one commit - [ ] A network outage is caught up when the network returns - [ ] A manual git command while idle is not clobbered ## M4 · Conflicts, without data loss Both versions survive, the vault is never left mid-rebase, the daemon keeps running. ### Steps 1. On rebase conflict, keep local, write the incoming version as <name>.conflict-YYYY-MM-DD-HHMM.md, commit, log loudly 2. Create a conflict deliberately with both machines offline and bring them back ### Done when - [ ] Both versions survive as files - [ ] The vault is not left mid-rebase - [ ] The daemon keeps running ### Watch out - Test this before trusting the sync with anything you care about. ## M5 · Status and safety Know the state, and never propagate a mass deletion. ### Steps 1. Write vaultsync status: last push, last pull, pending, conflict files, daemon alive 2. Add the MAX_CHANGED_FILES guard that asks instead of committing ### Done when - [ ] Deleting a folder of 200 notes triggers the guard - [ ] status reports an unresolved conflict file ## M6 · Mobile and second machine Documentation only: a phone and another computer set up from the README. ### Steps 1. Write the README: second-machine setup, the conflict-file convention, the size-guard override Files: `README.md` 2. Set up the phone client against the same repository and write one note from it Working Copy on iOS, MGit or GitJournal on Android. ### Done when - [ ] A second machine is set up from the README alone - [ ] A note written on the phone reaches the desktop ## M7 · Operate it (production only) Only for the careful path: know when sync stops, keep an off-git backup, encrypt if the remote should not read your notes. ### Steps 1. Alert when the last successful push is older than a day A cron job that checks and posts to ntfy or a webhook. 2. Nightly tarball of the vault to a second place, independent of git 3. If the remote must be zero-knowledge, use git-crypt or a full-vault age-encrypted archive instead, and accept the mobile cost Decide this consciously; do not half-build encryption. ### Done when - [ ] Stopping the daemon produces an alert the next day - [ ] The tarball restores to a readable vault ===== OPERATIONS.md ===== # Operations · Obsidian Sync ## Backup git is history, not backup: a nightly tarball to a second place. ## Restore git clone, or extract the tarball. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Alert when the last push is stale. ## Incident checklist A bad commit is reverted with git; a conflict file is resolved by reading both versions. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Conflict drill performed on two offline machines - [ ] Mass-deletion guard verified - [ ] Second machine set up from the README ## Launch constraint Do not market omitted Obsidian Sync capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Absolute path to the vault. VAULT_PATH=/Users/you/Notes # Required. Git remote name. REMOTE=origin # Optional. Wait this long after the last change before committing. DEBOUNCE_SECONDS=30 # Optional. How often to pull with rebase. PULL_INTERVAL_MINUTES=5 # Optional. Refuse to auto-commit a change touching more files than this. MAX_CHANGED_FILES=100
# Obsidian Sync · indie build Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Estimated effort: **one sitting**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Sync | git with a private remote | history for free; the conflict model is understood | | Watcher | A bash script on a systemd timer or launchd agent, or Node with chokidar | the simpler one for your machine | | Mobile | Working Copy on iOS, a git client on Android | no mobile code to build | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A terminal and a code editor** · free - Why: Every step below is a command you type or a file you edit. - Get it: VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. - Verify: You can open a folder and run a command in its terminal - [ ] **Git** · free - Why: History for your code, and the way most hosts deploy. - Get it: Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. - Verify: git --version prints a version - [ ] **Your Obsidian vault folder, backed up once by hand first** · free - Why: You are about to automate commits in it. Take a plain copy before Phase 1 so nothing is at risk while you learn. - Get it: Copy the vault folder to an external drive or a zip. - [ ] **A private git repository (GitHub, GitLab or Codeberg)** · free - Why: The remote is the sync point. Private, because these are your notes. - Get it: Create an empty private repository. For pushing without passwords, add an SSH key: ssh-keygen -t ed25519, then add the public key in the host's SSH keys settings. - Verify: ssh -T git@github.com greets you - [ ] **A second machine or a phone** · free - Why: Every phase is tested across two devices; one machine cannot test sync. - Get it: Another computer, or a phone with a git client. - [ ] **A mobile git client (optional)** (optional) · free to about $20 one-time - Why: Phase 6: the phone reads and writes the same repository. - Get it: Working Copy on iOS (one-time purchase for push), MGit or GitJournal on Android. ## Quick start ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. - official mobile sync polish - conflict handling - version history UI - encrypted vault workflow - Obsidian support If one of those is essential to you, that is the reason to keep paying for Obsidian Sync, and the README should say so rather than pretend.
# Build brief · Obsidian Sync The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a sync setup for my Obsidian vault to replace Obsidian Sync. Build it in phases, in the order below. This is plumbing, not an app. Each phase ends in a test you run on two real machines, and Phase 4 is the one that decides whether you can trust this with your notes. ### Rule zero The vault is a folder of Markdown files. Do not write a sync engine, do not invent a merge algorithm, and do not build a mobile client. Use git, because you want history. Syncthing is the right answer if you want continuous sync and do not care about history · say which you chose in the README and why. ### Stack (fixed, do not substitute) - git, plus a small watcher: Node with `chokidar` and `simple-git`, or a bash script on a systemd timer or launchd agent. Pick the simpler for the machine. - A private remote repository. Token in `.env` or the system credential helper, never in a file inside the vault. ### Phase 1 · Repository hygiene Build: `git init` in the vault and a `.gitignore` covering `.obsidian/workspace*`, `.obsidian/cache`, `.trash/` and OS junk. Device-local state must not sync, or two machines will fight over window positions forever and bury real changes in noise. Keep the rest of `.obsidian/` (plugins, themes, hotkeys) if you want settings to travel, and say which you chose. Done when: opening the vault on a second machine does not produce a diff from merely opening it, and `git status` is clean after launching and closing Obsidian without editing anything. Do not build yet: automation. ### Phase 2 · Manual round trip Build: nothing. Do the loop by hand on two machines · edit, commit, push, pull, edit the other side, push, pull back. Done when: a note written on machine A appears on machine B and back, and you have seen at least one real merge succeed. Automating a loop you have not performed manually is how you end up debugging a script and a git problem at the same time. ### Phase 3 · The watcher Build: the daemon. Debounce 30 seconds after the last filesystem change, then `git add -A`, commit with an automatic message including the changed file count, and push. Pull with `--rebase` on start and every 5 minutes. Never run two git operations concurrently · take a lock file, because a commit racing a rebase is how a repository ends up in a detached state at 2am. Done when: editing a note results in a commit and push within a minute; editing ten notes in quick succession produces one commit, not ten; the daemon survives a network outage and catches up when it returns; and a manual `git` command run while the daemon is idle is not clobbered. ### Phase 4 · Conflicts, without data loss Build: the conflict policy, which is the whole reason this phase exists. On a rebase conflict, never drop either side. Keep the local version in place, write the incoming version alongside it as `<name>.conflict-YYYY-MM-DD-HHMM.md`, commit that, and log it loudly. Obsidian will show the conflict file as a normal note, which is exactly right · a human resolves it by reading both. Done when: you have deliberately created a conflict by editing the same note on two machines while both are offline, brought both online, and confirmed that both versions survive, the vault is not left mid-rebase, and the daemon keeps running. Test this before you trust it with anything you care about. ### Phase 5 · Status and safety Build: a `vaultsync status` command showing last push, last pull, pending changes, outstanding conflict files, and whether the daemon is alive. Add a size guard that refuses to auto-commit a change touching more than N files (default 100) and asks instead · that pattern is a sync bug or an accidental delete, not a writing session, and committing it is how a mistake propagates everywhere. Done when: deleting a folder of 200 notes triggers the guard rather than pushing the deletion, and status reports an unresolved conflict file accurately. ### Phase 6 · Mobile and second machine Build: documentation only. Point a git client (Working Copy on iOS, an equivalent on Android) at the same repository, and write the second-machine setup steps. Done when: a second machine is set up from the README alone, and a note written on the phone reaches the desktop. ### Out of scope (and why) - An encrypted-vault workflow. If you need the remote to be zero-knowledge, that is a different design (encrypt before commit) with real usability costs on mobile · do not half-build it. - A version-history UI. `git log` is the history. - Anything mobile beyond configuring an existing client. ### README must contain - The conflict-file convention, and the instruction to search for `*.conflict-*` periodically. - The size-guard threshold and how to override it deliberately. - The honest trade: this is sync you operate. Obsidian Sync handles conflicts, history and mobile for you, and this does not · it costs you a restore drill and the occasional conflict file instead of the subscription.
# Agent instructions · Obsidian Sync indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: git with a private remote, A bash script on a systemd timer or launchd agent, or Node with chokidar, Working Copy on iOS, a git client on Android. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Test this before trusting the sync with anything you care about.
# Build plan · Obsidian Sync Sync for your Obsidian vault using git: a watcher commits and pushes after you stop typing, pulls with rebase on a timer, never loses either side of a conflict, refuses to push a suspicious mass deletion, and a status command tells you where things stand. Your phone uses a git client against the same repository. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Repository hygiene A vault that does not produce a diff just from being opened. ### Steps 1. git init in the vault and write .gitignore .obsidian/workspace*, .obsidian/cache, .trash/, .DS_Store. Keep the rest of .obsidian if you want settings to travel. Files: `.gitignore` ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` 2. Open and close Obsidian and check git status is clean ### Done when - [ ] git status is clean after launching and closing Obsidian without editing - [ ] Opening the vault on the second machine produces no diff ## Phase 2 · Manual round trip Do the loop by hand before automating it. ### Steps 1. On machine A: edit a note, commit, push ```sh git add -A && git commit -m 'notes' && git push -u origin main ``` 2. On machine B: pull, edit the same note elsewhere, push; then pull on A and watch the merge ```sh git pull --rebase ``` ### Done when - [ ] A note written on A appears on B and back - [ ] You have seen one real merge succeed ## Phase 3 · The watcher Debounced commits, timed pulls, one git operation at a time. ### Steps 1. Write vaultsync.sh (or the Node equivalent) Debounce DEBOUNCE_SECONDS after the last change, git add -A, commit with the changed-file count, push. Pull --rebase on start and every PULL_INTERVAL_MINUTES. Take a lock file so a commit never races a rebase. Files: `vaultsync.sh` 2. Install it as a systemd user timer or a launchd agent ```sh systemctl --user enable --now vaultsync.timer ``` ### Done when - [ ] Editing a note results in a commit and push within a minute - [ ] Ten quick edits produce one commit - [ ] A network outage is caught up when the network returns - [ ] A manual git command while idle is not clobbered ## Phase 4 · Conflicts, without data loss Both versions survive, the vault is never left mid-rebase, the daemon keeps running. ### Steps 1. On rebase conflict, keep local, write the incoming version as <name>.conflict-YYYY-MM-DD-HHMM.md, commit, log loudly 2. Create a conflict deliberately with both machines offline and bring them back ### Done when - [ ] Both versions survive as files - [ ] The vault is not left mid-rebase - [ ] The daemon keeps running ### Watch out - Test this before trusting the sync with anything you care about. ## Phase 5 · Status and safety Know the state, and never propagate a mass deletion. ### Steps 1. Write vaultsync status: last push, last pull, pending, conflict files, daemon alive 2. Add the MAX_CHANGED_FILES guard that asks instead of committing ### Done when - [ ] Deleting a folder of 200 notes triggers the guard - [ ] status reports an unresolved conflict file ## Phase 6 · Mobile and second machine Documentation only: a phone and another computer set up from the README. ### Steps 1. Write the README: second-machine setup, the conflict-file convention, the size-guard override Files: `README.md` 2. Set up the phone client against the same repository and write one note from it Working Copy on iOS, MGit or GitJournal on Android. ### Done when - [ ] A second machine is set up from the README alone - [ ] A note written on the phone reaches the desktop ## Not in this build - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. ## After v1, if you want it - A small web viewer of the repository for read-only access anywhere - Scheduled snapshots tagged by week
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Absolute path to the vault. VAULT_PATH=/Users/you/Notes # Required. Git remote name. REMOTE=origin # Optional. Wait this long after the last change before committing. DEBOUNCE_SECONDS=30 # Optional. How often to pull with rebase. PULL_INTERVAL_MINUTES=5 # Optional. Refuse to auto-commit a change touching more files than this. MAX_CHANGED_FILES=100
# Obsidian Sync · product brief ## Problem If you already accept local Markdown files, syncing folders across devices is a solved problem; the hard part is not the app, it is conflict-free convenience. ## Product outcome Sync you understand completely: every change in history, conflicts that never lose a word, and a warning before a mistake spreads. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - local Markdown folder - Syncthing, Git, iCloud, Dropbox, or similar - optional mobile file access ## Explicit non-goals for v1 - An encrypted-vault workflow by default; it is a different design with mobile costs. - A version-history UI; git log is the history. - Mobile beyond configuring an existing client. - official mobile sync polish - conflict handling - version history UI - encrypted vault workflow - Obsidian support ## Success criteria - Conflict drill performed on two offline machines - Mass-deletion guard verified - Second machine set up from the README
# Build brief · Obsidian Sync The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a sync setup for my Obsidian vault to replace Obsidian Sync. Build it in phases, in the order below. This is plumbing, not an app. Each phase ends in a test you run on two real machines, and Phase 4 is the one that decides whether you can trust this with your notes. ### Rule zero The vault is a folder of Markdown files. Do not write a sync engine, do not invent a merge algorithm, and do not build a mobile client. Use git, because you want history. Syncthing is the right answer if you want continuous sync and do not care about history · say which you chose in the README and why. ### Stack (fixed, do not substitute) - git, plus a small watcher: Node with `chokidar` and `simple-git`, or a bash script on a systemd timer or launchd agent. Pick the simpler for the machine. - A private remote repository. Token in `.env` or the system credential helper, never in a file inside the vault. ### Phase 1 · Repository hygiene Build: `git init` in the vault and a `.gitignore` covering `.obsidian/workspace*`, `.obsidian/cache`, `.trash/` and OS junk. Device-local state must not sync, or two machines will fight over window positions forever and bury real changes in noise. Keep the rest of `.obsidian/` (plugins, themes, hotkeys) if you want settings to travel, and say which you chose. Done when: opening the vault on a second machine does not produce a diff from merely opening it, and `git status` is clean after launching and closing Obsidian without editing anything. Do not build yet: automation. ### Phase 2 · Manual round trip Build: nothing. Do the loop by hand on two machines · edit, commit, push, pull, edit the other side, push, pull back. Done when: a note written on machine A appears on machine B and back, and you have seen at least one real merge succeed. Automating a loop you have not performed manually is how you end up debugging a script and a git problem at the same time. ### Phase 3 · The watcher Build: the daemon. Debounce 30 seconds after the last filesystem change, then `git add -A`, commit with an automatic message including the changed file count, and push. Pull with `--rebase` on start and every 5 minutes. Never run two git operations concurrently · take a lock file, because a commit racing a rebase is how a repository ends up in a detached state at 2am. Done when: editing a note results in a commit and push within a minute; editing ten notes in quick succession produces one commit, not ten; the daemon survives a network outage and catches up when it returns; and a manual `git` command run while the daemon is idle is not clobbered. ### Phase 4 · Conflicts, without data loss Build: the conflict policy, which is the whole reason this phase exists. On a rebase conflict, never drop either side. Keep the local version in place, write the incoming version alongside it as `<name>.conflict-YYYY-MM-DD-HHMM.md`, commit that, and log it loudly. Obsidian will show the conflict file as a normal note, which is exactly right · a human resolves it by reading both. Done when: you have deliberately created a conflict by editing the same note on two machines while both are offline, brought both online, and confirmed that both versions survive, the vault is not left mid-rebase, and the daemon keeps running. Test this before you trust it with anything you care about. ### Phase 5 · Status and safety Build: a `vaultsync status` command showing last push, last pull, pending changes, outstanding conflict files, and whether the daemon is alive. Add a size guard that refuses to auto-commit a change touching more than N files (default 100) and asks instead · that pattern is a sync bug or an accidental delete, not a writing session, and committing it is how a mistake propagates everywhere. Done when: deleting a folder of 200 notes triggers the guard rather than pushing the deletion, and status reports an unresolved conflict file accurately. ### Phase 6 · Mobile and second machine Build: documentation only. Point a git client (Working Copy on iOS, an equivalent on Android) at the same repository, and write the second-machine setup steps. Done when: a second machine is set up from the README alone, and a note written on the phone reaches the desktop. ### Out of scope (and why) - An encrypted-vault workflow. If you need the remote to be zero-knowledge, that is a different design (encrypt before commit) with real usability costs on mobile · do not half-build it. - A version-history UI. `git log` is the history. - Anything mobile beyond configuring an existing client. ### README must contain - The conflict-file convention, and the instruction to search for `*.conflict-*` periodically. - The size-guard threshold and how to override it deliberately. - The honest trade: this is sync you operate. Obsidian Sync handles conflicts, history and mobile for you, and this does not · it costs you a restore drill and the occasional conflict file instead of the subscription.
# Architecture · Obsidian Sync ## Stack | Part | Choice | Why | | --- | --- | --- | | Sync | git with a private remote | history for free; the conflict model is understood | | Watcher | A bash script on a systemd timer or launchd agent, or Node with chokidar | the simpler one for your machine | | Mobile | Working Copy on iOS, a git client on Android | no mobile code to build | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Watcher | debounce, commit, push, lock | Node or bash; same behaviour | | Puller | timed rebase and conflict policy | The conflict-file convention is the contract | | Guard | mass-change refusal and status | Tune the threshold | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `VAULT_PATH` · required · Absolute path to the vault. - `REMOTE` · required · Git remote name. - `DEBOUNCE_SECONDS` · optional · Wait this long after the last change before committing. - `PULL_INTERVAL_MINUTES` · optional · How often to pull with rebase. - `MAX_CHANGED_FILES` · optional · Refuse to auto-commit a change touching more files than this. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Obsidian Sync product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: git with a private remote, A bash script on a systemd timer or launchd agent, or Node with chokidar, Working Copy on iOS, a git client on Android. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Test this before trusting the sync with anything you care about.
# Delivery milestones · Obsidian Sync Estimated effort: **one sitting** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Repository hygiene A vault that does not produce a diff just from being opened. ### Steps 1. git init in the vault and write .gitignore .obsidian/workspace*, .obsidian/cache, .trash/, .DS_Store. Keep the rest of .obsidian if you want settings to travel. Files: `.gitignore` ```sh cd $VAULT_PATH && git init && git remote add origin git@github.com:you/notes.git ``` 2. Open and close Obsidian and check git status is clean ### Done when - [ ] git status is clean after launching and closing Obsidian without editing - [ ] Opening the vault on the second machine produces no diff ## M2 · Manual round trip Do the loop by hand before automating it. ### Steps 1. On machine A: edit a note, commit, push ```sh git add -A && git commit -m 'notes' && git push -u origin main ``` 2. On machine B: pull, edit the same note elsewhere, push; then pull on A and watch the merge ```sh git pull --rebase ``` ### Done when - [ ] A note written on A appears on B and back - [ ] You have seen one real merge succeed ## M3 · The watcher Debounced commits, timed pulls, one git operation at a time. ### Steps 1. Write vaultsync.sh (or the Node equivalent) Debounce DEBOUNCE_SECONDS after the last change, git add -A, commit with the changed-file count, push. Pull --rebase on start and every PULL_INTERVAL_MINUTES. Take a lock file so a commit never races a rebase. Files: `vaultsync.sh` 2. Install it as a systemd user timer or a launchd agent ```sh systemctl --user enable --now vaultsync.timer ``` ### Done when - [ ] Editing a note results in a commit and push within a minute - [ ] Ten quick edits produce one commit - [ ] A network outage is caught up when the network returns - [ ] A manual git command while idle is not clobbered ## M4 · Conflicts, without data loss Both versions survive, the vault is never left mid-rebase, the daemon keeps running. ### Steps 1. On rebase conflict, keep local, write the incoming version as <name>.conflict-YYYY-MM-DD-HHMM.md, commit, log loudly 2. Create a conflict deliberately with both machines offline and bring them back ### Done when - [ ] Both versions survive as files - [ ] The vault is not left mid-rebase - [ ] The daemon keeps running ### Watch out - Test this before trusting the sync with anything you care about. ## M5 · Status and safety Know the state, and never propagate a mass deletion. ### Steps 1. Write vaultsync status: last push, last pull, pending, conflict files, daemon alive 2. Add the MAX_CHANGED_FILES guard that asks instead of committing ### Done when - [ ] Deleting a folder of 200 notes triggers the guard - [ ] status reports an unresolved conflict file ## M6 · Mobile and second machine Documentation only: a phone and another computer set up from the README. ### Steps 1. Write the README: second-machine setup, the conflict-file convention, the size-guard override Files: `README.md` 2. Set up the phone client against the same repository and write one note from it Working Copy on iOS, MGit or GitJournal on Android. ### Done when - [ ] A second machine is set up from the README alone - [ ] A note written on the phone reaches the desktop ## M7 · Operate it (production only) Only for the careful path: know when sync stops, keep an off-git backup, encrypt if the remote should not read your notes. ### Steps 1. Alert when the last successful push is older than a day A cron job that checks and posts to ntfy or a webhook. 2. Nightly tarball of the vault to a second place, independent of git 3. If the remote must be zero-knowledge, use git-crypt or a full-vault age-encrypted archive instead, and accept the mobile cost Decide this consciously; do not half-build encryption. ### Done when - [ ] Stopping the daemon produces an alert the next day - [ ] The tarball restores to a readable vault
# Operations · Obsidian Sync ## Backup git is history, not backup: a nightly tarball to a second place. ## Restore git clone, or extract the tarball. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Alert when the last push is stale. ## Incident checklist A bad commit is reverted with git; a conflict file is resolved by reading both versions. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Conflict drill performed on two offline machines - [ ] Mass-deletion guard verified - [ ] Second machine set up from the README ## Launch constraint Do not market omitted Obsidian Sync capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Absolute path to the vault. VAULT_PATH=/Users/you/Notes # Required. Git remote name. REMOTE=origin # Optional. Wait this long after the last change before committing. DEBOUNCE_SECONDS=30 # Optional. How often to pull with rebase. PULL_INTERVAL_MINUTES=5 # Optional. Refuse to auto-commit a change touching more files than this. MAX_CHANGED_FILES=100
$ choose a build depth, inspect the files, then open the complete pack in your agent
They pay for no-drama sync across devices while keeping Obsidian's local-first model.
xofficial mobile sync polish
xconflict handling
xversion history UI
xencrypted vault workflow
xObsidian support
Don't feel like building it? These folks already made it free.
all 3 free alternatives to Obsidian Sync →· no votes, no pay-to-list · just what's real
Obsidian Sync pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| obsidian app | $0 | $0 | Free local Markdown app; 0 GB of hosted Sync storage included. |
| sync standard | $5/user | $4/user | 1 synced vault, 1 GB storage, 5 MB maximum file size, 1-month version history and unlimited devices. |
| sync plus | $10/user | $8/user | 10 synced vaults, 10 GB storage, 200 MB maximum file size, 12-month version history and unlimited devices. |
free tierno free Sync tier; the Obsidian app itself is free and stores local Markdown files
billingmonthly + annual (20% off); 7-day refund window
hidden costsEvery collaborator on a shared vault needs their own Sync subscription. Optional storage above 10 GB costs extra, but the current public amount was not found.
verified 2026-08-11 · source ↗
Is Obsidian Sync free?
Sync itself has no free tier, though the Obsidian app is free. Paid is Sync at $5/mo (checked 2026-08-07).
Vibecode Obsidian Sync
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal Obsidian Sync replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does Obsidian Sync cost?
Obsidian Sync costs about $5/month (Sync, checked 2026-08-07), which is $60 per year. That's what you save by replacing it with one prompt.
What do I lose by replacing Obsidian Sync?
Honestly: official mobile sync polish; conflict handling; version history UI; encrypted vault workflow; Obsidian support. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Obsidian Sync?
Yes: Syncthing (Syncs the vault directly between computers; iPhones remain the awkward relative.) Nextcloud (Your own cloud, complete with the privilege of maintaining your own cloud.) iCloud Drive (Five free gigabytes and excellent Apple plumbing; everyone else gets a side door.) All 3 curated free alternatives are at vibecodeit.com/obsidian-sync/alternatives. The prompt is for when you want it exactly your way.