md2gd client reference
Everything the md2gd command-line client does — install, sign in, link a Markdown file to a Google Doc, and keep them two-way synced.
Overview
md2gd is a single, dependency-free binary that keeps a local Markdown file and a Google Doc in sync — both directions, block by block. Your agents and tools write Markdown; your team reviews and edits in the real Google Doc; md2gd reconciles the two continuously.
Auth is brokered by the md2gd web app, so the client never opens its own Google screen. Document content flows directly between your machine and Google — it never passes through md2gd’s servers.
md2gd login → md2gd add → md2gd install (run as a service) → done. Everything else below is reference detail.Install
One line — it detects your OS/arch, verifies the checksum, and installs the binary:
curl -fsSL https://md2gd.com/install.sh | sh
By default it lands in /usr/local/bin (falling back to ~/.local/bin); set MD2GD_INSTALL_DIR to choose. Prefer a direct download or Windows? See the download page for per-platform binaries and PATH setup.
Confirm it’s installed:
md2gd version
Sign in
Connect your Google account. This opens your browser to md2gd.com; you approve once there, and the client receives a device token — no Google consent screen in the CLI. If you’re already signed in to md2gd.com, it’s instant.
md2gd login
The device token is saved to credentials.json (permissions 0600) under your config dir — ~/Library/Application Support/md2gd on macOS, ~/.config/md2gd on Linux. Additional machines run the same command and never see a Google screen.
Flags
--api <url>— point at a non-default backend (defaults to md2gd.com, or theMD2GD_API_BASEenv var).
Link a file to a Doc
md2gd add registers a link between one local Markdown file and one Google Doc. There are two ways to choose the Doc — both give md2gd access to only that one Doc under the drive.file scope:
drive.file scope only grants access to Docs md2gd creates or you pick — pasting a random Doc URL would leave md2gd with no permission to read or write it. Use --create or --pick.Create a new Doc — --create
Makes a fresh Doc that the app owns. --title is optional (defaults to the file name):
md2gd add notes.md --create --title "Project notes"
Pick an existing Doc — --pick
Opens the Google Picker in your browser to choose a Doc. Selecting it grants md2gd access to only that Doc (nothing else in your Drive):
md2gd add notes.md --pick
drive.file scope — it can only ever touch Docs you create with it or explicitly pick.Start syncing
Linking a file doesn’t sync it by itself — you need the daemon running. Two ways: run it in the foreground, or install it as an always-on background service (recommended).
md2gd install
That’s the one-command path: it installs the service, starts it, and auto-starts it on login. Read on for the foreground alternative and full service controls.
Foreground — md2gd run
Runs the sync loop in your terminal (Ctrl-C to stop). Good for trying things out or debugging:
md2gd run
Flags
--interval <dur>— how often to poll Google for remote edits (default1h). This is only a backstop: local edits sync instantly via file-watching; the interval governs how quickly Doc-side edits are pulled. Accepts Go durations, e.g.--interval 30s,--interval 5m.--api <url>— override the backend for this run.
Background service
Installs md2gd as a per-user service (macOS LaunchAgent, Linux systemd user unit, Windows logon service) that auto-starts on login and restarts if it crashes.
| Command | What it does |
|---|---|
md2gd install | Install and start the service; enable auto-start on login. |
md2gd start | Start the installed service. |
md2gd stop | Stop the service (marks the device offline). |
md2gd restart | Restart it — e.g. to pick up a newly added link. |
md2gd status | Print whether the service is running. |
md2gd uninstall | Stop and remove the service (your links and login are kept). |
md2gd add while the service is running restarts it automatically so the new link starts syncing immediately.Command reference
| Command | Description |
|---|---|
md2gd login [--api url] | Connect your Google account (browser) and save a device token. |
md2gd logout | Mark the device offline and clear the local token. |
md2gd add <file.md> --create [--title T] | Create a new Doc and link the file to it. |
md2gd add <file.md> --pick | Choose an existing Doc via the browser Google Picker. |
md2gd run [--interval 1h] | Watch and sync in the foreground (Ctrl-C to stop). |
md2gd install | Install + start the background service (auto-start on login). |
md2gd start | stop | restart | status | Control the background service. |
md2gd uninstall | Remove the background service. |
md2gd token | Vend a short-lived Google access token (diagnostic). |
md2gd version | Print the client version. |
How sync works
- Two-way, block by block. md2gd diffs at the block level (headings, paragraphs, list items, code blocks) so edits on either side merge without clobbering the whole document.
- Local edits push instantly. The daemon watches your files; saving a linked
.mdreconciles it right away. - Doc edits pull on the interval. Remote (Google-side) changes are picked up on the next poll (default hourly; tune with
--interval). - Conflicts: the Doc wins. If the same block changed on both sides since the last sync, the Doc’s version is kept and your local copy is preserved as a
.bakfile next to the original — nothing is lost. - Unsupported constructs pause that file. Content md2gd can’t round-trip losslessly (e.g. tables, horizontal rules) stops that file’s sync with a clear status instead of corrupting the document.
- Tokens refresh themselves. The client holds only short-lived (~1h) Google access tokens, re-vending automatically before expiry. On a transient Google error it retries with backoff (up to ~5 min) before reporting the link as errored; a 401 triggers an immediate token refresh and retry.
Config & environment
| Variable | Purpose |
|---|---|
MD2GD_API_BASE | Default backend URL (overridden per-command by --api). |
MD2GD_CONFIG_HOME | Directory for credentials.json (defaults to your OS config dir). |
MD2GD_INSTALL_DIR | Install location for the curl | sh installer. |
Credentials live in credentials.json (0600): the device token, the API base, and a stable device ID that a re-login reuses so you don’t pile up duplicate devices.
Diagnostics
Check the service is up:
md2gd status
Confirm the backend can vend you a Google token (proves login + connection are healthy):
md2gd token
The background service writes stdout/stderr to md2gd.out.log and md2gd.err.log in your home directory — check those if a sync isn’t happening.
Sign out
Marks this device offline on the dashboard and clears the local token. Your links and the stable device ID are kept, so a later md2gd login reconnects the same device:
md2gd logout
Troubleshooting
“Unidentified developer” on first run (macOS)
The binary isn’t notarized yet. Clear the quarantine flag once:
xattr -d com.apple.quarantine $(which md2gd)
A link shows “auth expired”
Rare — refresh tokens are long-lived. It happens only if you revoked access in Google settings, went ~6 months without syncing, or changed your Google password. Fix with md2gd login.
A link shows an error / “broken”
The Doc may have been deleted, or access to it removed. Re-link with md2gd add, or remove the link from the dashboard.
A file stopped syncing with “unsupported”
That file contains a construct md2gd won’t round-trip (e.g. a table). Remove or adjust it and the file resumes on the next reconcile.