11# apple-notes-sync
22
3- A macOS CLI tool that exports Apple Notes to a Git repository as Markdown files, with optional Google Drive sync via rclone.
3+ [ ![ Go] ( https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go&logoColor=white )] ( https://go.dev )
4+ [ ![ License: MIT] ( https://img.shields.io/badge/License-MIT-blue.svg )] ( LICENSE )
5+ [ ![ Platform: macOS] ( https://img.shields.io/badge/platform-macOS-brightgreen )] ( #prerequisites )
6+ [ ![ CI] ( https://github.com/PyAgni/apple-notes-syncer/actions/workflows/ci.yml/badge.svg )] ( https://github.com/PyAgni/apple-notes-syncer/actions )
7+
8+ ** Seamless, automatic Git backup of your Apple Notes — with optional Google Drive sync.**
9+ One command (or hourly launchd) turns your Notes app into a version-controlled Markdown repo.
10+
11+ ## Table of Contents
12+
13+ - [ Quick Start] ( #quick-start )
14+ - [ Features] ( #features )
15+ - [ Why apple-notes-sync?] ( #why-apple-notes-sync )
16+ - [ In Action] ( #in-action )
17+ - [ Prerequisites] ( #prerequisites )
18+ - [ Installation] ( #installation )
19+ - [ Configuration] ( #configuration )
20+ - [ Running Manually] ( #running-manually )
21+ - [ Scheduling with launchd] ( #scheduling-with-launchd )
22+ - [ Google Drive Setup] ( #google-drive-setup )
23+ - [ How Renames and Deletions Are Handled] ( #how-renames-and-deletions-are-handled )
24+ - [ Limitations] ( #limitations )
25+ - [ Troubleshooting] ( #troubleshooting )
26+ - [ Alternatives] ( #alternatives )
27+ - [ Contributing] ( #contributing )
28+ - [ License] ( #license )
29+
30+ ## Quick Start
31+
32+ ``` bash
33+ # 1. Install
34+ go install github.com/PyAgni/apple-notes-syncer/cmd/apple-notes-sync@latest
35+
36+ # 2. Create a Git repo for your notes
37+ mkdir ~ /Notes && cd ~ /Notes
38+ git init
39+ git remote add origin git@github.com:yourusername/my-notes.git
40+ git commit --allow-empty -m " init"
41+ git push -u origin main
42+
43+ # 3. Configure
44+ cp configs/config.example.yaml ~ /.apple-notes-sync.yaml
45+ # Edit repo_path in the config file
46+
47+ # 4. Run
48+ apple-notes-sync --repo-path ~ /Notes
49+
50+ # 5. (Optional) Schedule hourly syncs
51+ make launchd
52+ ```
53+
54+ Done. Your notes now live in Git with full history.
455
556## Features
657
758- Extracts all notes from the macOS Notes app via AppleScript
859- Converts HTML note bodies to clean Markdown
960- Mirrors the Notes folder hierarchy into the repository
10- - Adds YAML front matter (title , dates, account) to each note
61+ - Adds a metadata table (ID , dates, account) to each note
1162- Commits with timestamped messages and pushes to your remote
1263- Optionally syncs to Google Drive via rclone
1364- Cleans up notes that were deleted from Apple Notes
1465- Configurable via CLI flags, environment variables, or YAML config file
1566
67+ ## Why apple-notes-sync?
68+
69+ Apple Notes is great on your Mac and iPhone, but terrible for backups, version history, or migrating to Obsidian/Logseq.
70+
71+ Other exporters are one-shot scripts. This tool gives you ** continuous sync** :
72+
73+ - ** Automatic Git commits + push** — full version history of every edit
74+ - ** Orphan cleanup** — deleted notes disappear from the repo automatically
75+ - ** Hourly launchd scheduling** — set it and forget it
76+ - ** rclone integration** — Google Drive as a bonus backup layer
77+ - ** Folder mirroring** — your Notes folder structure is preserved exactly
78+
79+ > ** Note** : This is a one-way export. Edits made to the Markdown files do not flow back to Apple Notes.
80+
81+ ## In Action
82+
83+ <!-- TODO: Add demo GIF or screenshot -->
84+ <!--  -->
85+
86+ Example output for a single note:
87+
88+ ``` markdown
89+ # My Project Ideas
90+
91+ Content of the note converted to clean Markdown...
92+
93+ - Bullet points preserved
94+ - [ Links] ( https://example.com ) converted properly
95+ - ** Bold** and * italic* formatting kept
96+
97+ ---
98+
99+ | ID | Created | Modified | Account | Shared |
100+ | ----| ---------| ----------| ---------| --------|
101+ | x-coredata://abc123 | 2026-03-18 16:00:00 | 2026-03-20 09:30:00 | iCloud | No |
102+ ```
103+
104+ Repository structure mirrors your Notes folders:
105+
106+ ```
107+ ~/Notes/
108+ ├── Work/
109+ │ ├── Meeting Notes.md
110+ │ └── Project Ideas.md
111+ ├── Personal/
112+ │ ├── Travel Plans.md
113+ │ └── Reading List.md
114+ ├── Recipes/
115+ │ └── Pasta Carbonara.md
116+ └── ...
117+ ```
118+
16119## Prerequisites
17120
18121- ** macOS** (required — uses AppleScript to access Notes)
@@ -33,20 +136,7 @@ make install
33136### Using ` go install `
34137
35138``` bash
36- go install github.com/agni/apple-notes-sync/cmd/apple-notes-sync@latest
37- ```
38-
39- ## One-time repo setup
40-
41- Create and initialize a Git repository for your notes:
42-
43- ``` bash
44- mkdir ~ /Notes
45- cd ~ /Notes
46- git init
47- git remote add origin git@github.com:yourusername/my-notes.git
48- git commit --allow-empty -m " init"
49- git push -u origin main
139+ go install github.com/PyAgni/apple-notes-syncer/cmd/apple-notes-sync@latest
50140```
51141
52142## Configuration
@@ -58,6 +148,18 @@ Configuration is loaded from (in order of precedence):
581483 . YAML config file (` ~/.apple-notes-sync.yaml ` )
591494 . Defaults
60150
151+ Minimal config to get started:
152+
153+ ``` yaml
154+ repo_path : ~/Notes
155+ ` ` `
156+
157+ See [` configs/config.example.yaml`](configs/config.example.yaml) for all options. Copy it:
158+
159+ ` ` ` bash
160+ cp configs/config.example.yaml ~/.apple-notes-sync.yaml
161+ ` ` `
162+
61163# ## CLI flags
62164
63165```
@@ -82,15 +184,6 @@ export ANS_RCLONE_REMOTE_NAME=gdrive
82184export ANS_RCLONE_REMOTE_PATH=AppleNotes
83185```
84186
85- ### YAML config file
86-
87- See [ ` configs/config.example.yaml ` ] ( configs/config.example.yaml ) for a complete reference. Copy it to get started:
88-
89- ``` bash
90- cp configs/config.example.yaml ~ /.apple-notes-sync.yaml
91- # Edit with your settings
92- ```
93-
94187<details >
95188<summary >Full config reference</summary >
96189
@@ -119,7 +212,7 @@ cp configs/config.example.yaml ~/.apple-notes-sync.yaml
119212| ` attachments.max_size_mb ` | int | ` 50 ` | Max attachment size in MB |
120213| ` attachments.dir ` | string | ` "_attachments" ` | Attachment subdirectory |
121214| ` dry_run ` | bool | ` false ` | Preview mode |
122- | ` front_matter ` | bool | ` true ` | Add YAML front matter |
215+ | ` front_matter ` | bool | ` true ` | Add metadata table to notes |
123216| ` clean_orphans ` | bool | ` true ` | Remove deleted notes |
124217| ` timeout ` | duration | ` 120s ` | AppleScript timeout |
125218| ` commit_template ` | string | see below | Commit message Go template |
@@ -130,7 +223,7 @@ Template fields: `.Timestamp`, `.Written`, `.Total`, `.Skipped`
130223
131224</details >
132225
133- ## Running manually
226+ ## Running Manually
134227
135228``` bash
136229# Basic run
@@ -169,7 +262,7 @@ Create the log directory:
169262mkdir -p ~ /Library/Logs/apple-notes-sync
170263```
171264
172- ## Google Drive setup
265+ ## Google Drive Setup
173266
1742671 . Install rclone: ` brew install rclone `
175268
@@ -198,14 +291,34 @@ rclone:
198291rclone sync ~/Notes gdrive:AppleNotes --dry-run
199292```
200293
201- ## How renames and deletions are handled
294+ ## How Renames and Deletions Are Handled
202295
203296- ** Renamed notes** : A renamed note appears as a new file and the old filename is removed (if ` clean_orphans: true ` ). This shows as a delete + add in git, which GitHub renders as a rename if content is similar.
204297- ** Deleted notes** : When a note is deleted from Apple Notes, the corresponding ` .md ` file is removed on the next sync (if ` clean_orphans: true ` ).
205298- ** Moved notes** : Moving a note to a different folder creates the file in the new directory and removes it from the old one.
206299
300+ ## Limitations
301+
302+ - ** macOS only** — relies on AppleScript to access the Notes app
303+ - ** One-way export** — edits to Markdown files do not sync back to Apple Notes
304+ - ** AppleScript permissions required** — on first run, macOS will prompt to allow automation access
305+ - ** Attachments >50 MB skipped** by default (configurable via ` attachments.max_size_mb ` )
306+ - ** Large note libraries** may take a few minutes on the first sync (AppleScript extraction is the bottleneck)
307+
308+ ## Troubleshooting
309+
310+ | Problem | Solution |
311+ | ---------| ----------|
312+ | AppleScript timeout | Increase ` timeout: 300s ` in your config |
313+ | Permission denied on Notes | System Settings > Privacy & Security > Automation > allow Terminal (or your terminal app) |
314+ | rclone OAuth expired | Run ` rclone config ` again to re-authenticate |
315+ | Unicode errors in dates | Already handled — the parser normalizes Unicode whitespace from macOS locales |
316+ | "repo_path is required" | Set ` repo_path ` in your config file or pass ` --repo-path ` |
317+
207318## Contributing
208319
320+ Contributions are welcome! Please open an issue or submit a pull request.
321+
2093221 . Fork the repository
2103232 . Create a feature branch: ` git checkout -b my-feature `
2113243 . Make your changes
@@ -217,10 +330,11 @@ rclone sync ~/Notes gdrive:AppleNotes --dry-run
217330``` bash
218331make build # Build the binary
219332make test # Run tests with race detector
220- make check-coverage # Run tests and check coverage ≥ 80%
333+ make check-coverage # Run tests and check coverage >= 80%
221334make lint # Run go vet + staticcheck
222335make fmt # Format code
223336make tidy # Tidy go modules
337+ make help # Show all available targets
224338```
225339
226340## License
0 commit comments