Sign inSign up

maze88/gkeep-sync-md

By maze88

Updated 17 days ago

Image
Integration & delivery
API management
Developer tools
0

235

maze88/gkeep-sync-md repository overview

Google Keep to Markdown sync utility

About

Two-way synchronization between Google Keep notes and local Markdown files.

Notes labeled sync in Google Keep are synchronized to/from local markdown files, and new local markdown files are pushed to Google Keep (and created with a sync label).

Background
  • This is currently an early-stage personal project, shared in case it is useful to others with similar workflows.
  • The main motivation is to retain the convenience of shared Google Keep notes (for example, a shared grocery list) while keeping the actual notes locally accessible and editable as part of a broader de-Googling effort.
  • The intended workflow is to run the utility frequently in the background and edit the Markdown files with a plaintext/Markdown editor. Checkout this diagram which demonstrates my setup.
Caveats
  • Deletion synchronization is not currently supported. To delete a note, remove the Google Keep note (or its sync label) first, then delete the local Markdown file - a file deleted while its note is still labeled in Keep is simply recreated on the next run.
  • Uses this unofficial gkeepapi client.

Setup

python -m venv .venv
.venv/bin/python -m pip install -r requirements.txt

Configure

Edit config.py or set environment variables (env vars take precedence):

keyenv varmeaning
gmail_userGKEEP_GMAIL_USERyour Gmail address
sync_labelGKEEP_SYNC_LABELKeep label that marks notes for syncing (default sync)
log_file_pathGKEEP_LOG_FILE_PATHpath for log file in addition to stdout (optional)
master_token_file_pathGKEEP_MASTER_TOKEN_FILE_PATHpath to a secret file containing the master token (optional)
(no config key)GOOGLE_MASTER_TOKENmaster token passed directly via env var (takes precedence over master_token_file_path; see Authentication)
markdown_notes_dirGKEEP_MARKDOWN_DIRlocal directory for the markdown files
db_pathGKEEP_DB_PATHsqlite file holding change-detection state (default ~/.config/gkeep-md-sync/state.db)
conflict_policyGKEEP_CONFLICT_POLICYfail, skip, use-gkeep, or use-md (details below)

Authentication

The application uses one's Google master token to authenticate to Google Keep.

The master token is looked up in the following order:

  1. Environment variable GOOGLE_MASTER_TOKEN.
  2. File specified by master_token_file_path (in config.py).
  3. Interactive authentication (only if the application is running interactively).
First-time authentication

If no master token is found in the first two locations and the application is run interactively, it will prompt for a Google OAuth token (which the application exchanges for the master token).

To obtain the OAuth token:

  1. Go to https://accounts.google.com/EmbeddedSetup
  2. Authenticate with your Google account.
  3. Copy the value of the oauth_token browser cookie.
  4. Paste it when prompted by the application.

The OAuth token is only used to obtain a master token. It is not stored by the application.

The acquired master token is saved automatically to master_token_file_path, so subsequent unattended runs authenticate on their own. Alternatively, export it as GOOGLE_MASTER_TOKEN.

Master token file

The app saves an interactively acquired master token to the file configured by master_token_file_path; you can also create it manually:

mkdir -p ~/.config/gkeep-md-sync
chmod 700 ~/.config/gkeep-md-sync
echo 'aas_et/*********************************' > ~/.config/gkeep-md-sync/master.token
chmod 600 ~/.config/gkeep-md-sync/master.token

Run

.venv/bin/python main.py
Auto-run

Add a crontab -e expression:

* * * * * cd "$HOME/projects/gkeep-md-sync" && "$HOME/projects/gkeep-md-sync/.venv/bin/python" main.py

Docker

docker build -t maze88/gkeep-sync-md .
docker run --rm \
  -e GOOGLE_MASTER_TOKEN=... \
  -e [email protected] \
  -e GKEEP_MARKDOWN_DIR=/app/notes/ \
  -e GKEEP_DB_PATH=/app/state/state.db \
  -v ~/Documents/notes/gkeep/:/app/notes/ \
  -v ~/.config/gkeep-md-sync:/app/state \
  maze88/gkeep-sync-md

The state DB must live on a persistent volume (GKEEP_DB_PATH + the second -v). With --rm and no DB mount it is recreated empty each run, so every run re-pulls all notes.

All config settings are overridable via env vars. Empty string disables the optional settings (GKEEP_LOG_FILE_PATH, GKEEP_MASTER_TOKEN_FILE_PATH).

Markdown format

Each note is a file with YAML frontmatter holding just its Keep identity, a # Title heading, and either freeform body text or a checklist:

---
google_keep_id: 17fca0582e0.05b9cd676d5cd6d5
---

# Groceries

- [ ] for sauce
  - [x] tomatoes
  - [ ] garlic
  - [ ] basil
- [ ] pasta
- [x] olive oil

Checklist sub-items are written as two-space-indented task lines, mirroring Google Keep's single level of indentation.

google_keep_id links the file to its Keep note; a file without one is treated as new and created in Keep on the next run. Change-detection state (the content fingerprint) is not stored in the file — it lives in a sqlite database (db_path), so the app never rewrites a file except to pull real content from Keep. Before overwriting, the app re-hashes the file's saved content and withholds the pull if it changed since the last check, so a concurrent human save isn't clobbered. Opening or re-saving a file with identical content does not withhold the pull — only saved bytes that differ do.

Conflicts

A conflict is when a note changed in both Keep and its local file since the last sync. config.conflict_policy decides the outcome:

  • fail - abort the run.
  • skip - leave both sides as-is.
  • use-gkeep - Keep wins; overwrite the local file.
  • use-md - local wins; overwrite the Keep note.

Tag summary

Content type

Image

Digest

sha256:f59a65ac6

Size

53.1 MB

Last updated

17 days ago

docker pull maze88/gkeep-sync-md