# martinlegend/neogdsync architecture

> Architecture diagram of the GitHub repository martinlegend/neogdsync, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-22). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/martinlegend/neogdsync
- Repository: https://github.com/martinlegend/neogdsync

## Overview

1. **What this repository is**
   - NeoGDSync is an **Obsidian plugin** that synchronizes an Obsidian vault with a **Google Drive folder**.
   - The architecture is split between:
     - the **local plugin** running inside Obsidian
     - an optional **OAuth proxy worker** used to help with token acquisition/refresh flows
     - Google Drive as the external storage backend

2. **Main runtime boundary**
   - The core application is the Obsidian plugin itself, built from `src/main.ts` and supporting TypeScript modules.
   - The plugin runs in the **Obsidian desktop app environment**, not as a standalone server.
   - Its responsibilities include UI wiring, command registration, sync orchestration, and persistent plugin settings.

3. **User-facing entry points**
   - Sync can be started from:
     - Obsidian ribbon icon / command palette
     - the custom URI scheme `obsidian://neogdsync?...`
   - Supported modes are:
     - **smart sync**: compare local and remote, sync only deltas, detect conflicts
     - **force push**: local vault → Drive
     - **force pull**: Drive → local vault
   - These entry points are thin; they delegate to the sync engine rather than performing sync logic themselves.

4. **Sync engine**
   - The central coordination layer is in `src/syncer.ts` / `src/syncer.js`.
   - It likely owns the end-to-end flow:
     - load config and state
     - build or read the Drive index
     - enumerate local vault files
     - compare snapshots / metadata
     - decide upload, download, delete, or conflict actions
     - update stored state after completion
   - This is the main subsystem to show as the “brain” of the plugin.

5. **Drive API boundary**
   - `src/driveApi.ts` / `src/driveApi.js` isolates all communication with **Google Drive REST APIs**.
   - This module is the external-service adapter for:
     - listing files/folders
     - reading metadata
     - uploading/downloading file content
     - creating revision history when enabled
     - resolving folder/file IDs and parent-child relationships
   - Keeping Drive calls behind this module makes the rest of the plugin independent of API details.

6. **Authentication subsystem**
   - `src/auth.ts` handles OAuth-related state and token usage.
   - The README shows the plugin uses a **Google OAuth2 refresh token** with Drive scope, stored locally in Obsidian plugin data.
   - The auth layer is the boundary between local configuration and authenticated Drive requests.
   - The token is not a user-facing session; it is a long-lived credential used by the Drive client to obtain access tokens when needed.

7. **Local state, indexing, and conflict detection**
   - `src/pathIndex.ts`, `src/snapshot.ts`, and `src/types.ts` suggest the plugin maintains a **path-based index** and file-state snapshots.
   - The README explicitly says the plugin avoids a full crawl after the first sync, so this state is important to architecture:
     - `pathIndex` maps vault paths to Drive item IDs / metadata
     - `snapshot` captures local or remote state for comparison
     - `types` centralizes shared data contracts
   - This state is what enables “smart sync” and conflict detection without re-walking the entire Drive tree every time.

8. **File normalization and content handling**
   - `src/mime.ts` likely maps file extensions/content types to Google Drive MIME handling rules.
   - This is important because Drive treats binary files, markdown, folders, and special metadata differently.
   - The sync layer needs this mapping to decide how to upload, download, and preserve file semantics correctly.

9. **Conflict and version history behavior**
   - A key product boundary is that conflicts are **never silently overwritten**.
   - When local and remote diverge, the plugin saves the Drive copy as a conflict file rather than destroying data.
   - Optional **version history** on Drive suggests the sync engine can preserve revisions instead of replacing content blindly.
   - In architecture diagrams, this should appear as a decision point inside the sync engine before write operations.

10. **Excludes and vault scoping**
   - The plugin supports **configurable excludes** via glob patterns.
   - This means the sync pipeline does not operate on the entire vault uniformly; it filters candidate paths before comparison and upload/download.
   - The Drive root folder ID is also a hard boundary: only the subtree under that Drive folder is treated as the synced vault.

11. **OAuth proxy worker**
   - The `oauth-proxy/` directory contains a Cloudflare Worker (`worker.js` + `wrangler.toml`).
   - This is a separate deployment boundary from the Obsidian plugin.
   - Its role is likely to support OAuth-related flows externally, such as redirect handling or token exchange assistance, so the plugin does not need to host a web server itself.
   - Architecturally, it sits between the user’s browser/Google OAuth endpoints and the local token acquisition workflow.

12. **Persistence and packaging**
   - Plugin state is stored locally in Obsidian’s plugin data, not in a backend database.
   - The repository is packaged as an Obsidian plugin using standard frontend tooling:
     - TypeScript source in `src/`
     - `esbuild.config.mjs` for bundling
     - `manifest.json` for Obsidian plugin metadata
     - `styles.css` for UI styling
   - `versions.json` indicates release/version compatibility metadata for the plugin distribution.

13. **Non-functional boundaries**
   - The repo includes `docs/privacy.html`, which matters because the system handles sensitive OAuth credentials and file metadata.
   - The privacy boundary is simple but important: credentials are stored locally and only sent to Google for authentication and Drive API access.
   - No application backend is present in the main plugin; Google Drive is the only primary external data store.

14. **How to draw the diagram**
   - Show Obsidian as the host runtime containing the plugin.
   - Inside the plugin, draw modules for:
     - UI/commands/URI handler
     - sync orchestration
     - auth/token management
     - Drive API client
     - index/snapshot/state
     - MIME/file handling
   - Draw Google Drive as the external service boundary.
   - Draw the Cloudflare Worker as a separate optional support service for OAuth.

Main entry points: [`src/syncer.ts`](https://github.com/martinlegend/neogdsync/blob/main/src/syncer.ts) (Sync engine), [`src/driveApi.ts`](https://github.com/martinlegend/neogdsync/blob/main/src/driveApi.ts) (Drive client), [`src/main.ts`](https://github.com/martinlegend/neogdsync/blob/main/src/main.ts) (Plugin entry), [`src/auth.ts`](https://github.com/martinlegend/neogdsync/blob/main/src/auth.ts) (Auth manager)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_host["Obsidian host"]
  node_obsidian["Obsidian<br/>host runtime"]
  node_vault[("Vault<br/>local storage")]
end

subgraph group_plugin["NeoGDSync plugin"]
  node_plugin_main["Plugin entry<br/>plugin bootstrap<br/>[main.ts]"]
  node_ui_commands["UI & commands<br/>entrypoints<br/>[main.ts]"]
  node_uri_handler["URI handler<br/>entrypoint<br/>[main.ts]"]
  node_sync_orchestrator["Sync engine<br/>orchestrator<br/>[syncer.ts]"]
  node_conflict_policy["Conflict policy<br/>sync rules<br/>[syncer.ts]"]
  node_auth_manager["Auth manager<br/>oauth client<br/>[auth.ts]"]
  node_drive_client["Drive client<br/>api adapter<br/>[driveApi.ts]"]
  node_path_index[("Path index<br/>sync state<br/>[pathIndex.ts]")]
  node_snapshot_model["Snapshot model<br/>state model<br/>[snapshot.ts]"]
  node_mime_rules["File rules<br/>content mapping<br/>[mime.ts]"]
  node_shared_types["Shared types<br/>contracts<br/>[types.ts]"]
  node_local_data[("Plugin data<br/>persistent state")]
end

subgraph group_oauth["OAuth support"]
  node_oauth_worker["OAuth worker<br/>cloudflare worker<br/>[worker.js]"]
end

subgraph group_external["External services"]
  node_google_oauth(("Google OAuth<br/>identity service"))
  node_google_drive[("Google Drive<br/>external backend")]
end

node_obsidian -->|"hosts"| node_plugin_main
node_plugin_main -->|"wires"| node_ui_commands
node_plugin_main -->|"wires"| node_uri_handler
node_ui_commands -->|"triggers"| node_sync_orchestrator
node_uri_handler -->|"triggers"| node_sync_orchestrator
node_sync_orchestrator -->|"authenticates"| node_auth_manager
node_auth_manager -.->|"assists"| node_oauth_worker
node_oauth_worker -.->|"redirects"| node_google_oauth
node_auth_manager -->|"reads/writes"| node_local_data
node_sync_orchestrator -->|"loads/updates"| node_path_index
node_sync_orchestrator -->|"compares"| node_snapshot_model
node_sync_orchestrator -->|"normalizes"| node_mime_rules
node_sync_orchestrator -->|"uses"| node_shared_types
node_sync_orchestrator -->|"decides"| node_conflict_policy
node_conflict_policy -->|"guards writes"| node_drive_client
node_sync_orchestrator -->|"syncs"| node_drive_client
node_drive_client -->|"calls"| node_google_drive
node_sync_orchestrator -->|"reads/writes"| node_vault
node_sync_orchestrator -->|"persists state"| node_local_data
node_path_index -->|"backed by"| node_local_data
node_snapshot_model -->|"structured by"| node_shared_types
node_mime_rules -->|"guides content"| node_drive_client

click node_plugin_main "https://github.com/martinlegend/neogdsync/blob/main/src/main.ts"
click node_ui_commands "https://github.com/martinlegend/neogdsync/blob/main/src/main.ts"
click node_uri_handler "https://github.com/martinlegend/neogdsync/blob/main/src/main.ts"
click node_sync_orchestrator "https://github.com/martinlegend/neogdsync/blob/main/src/syncer.ts"
click node_conflict_policy "https://github.com/martinlegend/neogdsync/blob/main/src/syncer.ts"
click node_auth_manager "https://github.com/martinlegend/neogdsync/blob/main/src/auth.ts"
click node_drive_client "https://github.com/martinlegend/neogdsync/blob/main/src/driveApi.ts"
click node_path_index "https://github.com/martinlegend/neogdsync/blob/main/src/pathIndex.ts"
click node_snapshot_model "https://github.com/martinlegend/neogdsync/blob/main/src/snapshot.ts"
click node_mime_rules "https://github.com/martinlegend/neogdsync/blob/main/src/mime.ts"
click node_shared_types "https://github.com/martinlegend/neogdsync/blob/main/src/types.ts"
click node_oauth_worker "https://github.com/martinlegend/neogdsync/blob/main/oauth-proxy/worker.js"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_obsidian,node_vault toneBlue
class node_plugin_main,node_ui_commands,node_uri_handler,node_sync_orchestrator,node_conflict_policy,node_auth_manager,node_drive_client,node_path_index,node_snapshot_model,node_mime_rules,node_shared_types,node_local_data toneAmber
class node_oauth_worker toneMint
class node_google_oauth,node_google_drive toneRose
```

## Components

### Obsidian host

Desktop runtime that embeds the plugin and exposes the vault

- **Obsidian**: host runtime · Desktop app hosting the plugin
- **Vault**: local storage · User files managed by the plugin

### NeoGDSync plugin

Local TypeScript modules that register commands and run sync

- [Plugin entry](https://github.com/martinlegend/neogdsync/blob/main/src/main.ts): plugin bootstrap · Registers plugin behavior and wires the runtime · `src/main.ts`
- [UI & commands](https://github.com/martinlegend/neogdsync/blob/main/src/main.ts): entrypoints · Ribbon actions, command palette, and user triggers · `src/main.ts`
- [URI handler](https://github.com/martinlegend/neogdsync/blob/main/src/main.ts): entrypoint · Handles obsidian://neogdsync requests · `src/main.ts`
- [Sync engine](https://github.com/martinlegend/neogdsync/blob/main/src/syncer.ts): orchestrator · Coordinates smart sync, force push, and force pull · `src/syncer.ts`
- [Conflict policy](https://github.com/martinlegend/neogdsync/blob/main/src/syncer.ts): sync rules · Detects divergence and preserves conflicting copies · `src/syncer.ts`
- [Auth manager](https://github.com/martinlegend/neogdsync/blob/main/src/auth.ts): oauth client · Manages refresh token use and authenticated access · `src/auth.ts`
- [Drive client](https://github.com/martinlegend/neogdsync/blob/main/src/driveApi.ts): api adapter · Wraps Google Drive REST calls for files and folders · `src/driveApi.ts`
- [Path index](https://github.com/martinlegend/neogdsync/blob/main/src/pathIndex.ts): sync state · Maps vault paths to Drive item identity and metadata · `src/pathIndex.ts`
- [Snapshot model](https://github.com/martinlegend/neogdsync/blob/main/src/snapshot.ts): state model · Captures local or remote file state for comparison · `src/snapshot.ts`
- [File rules](https://github.com/martinlegend/neogdsync/blob/main/src/mime.ts): content mapping · Normalizes file types and Drive MIME handling · `src/mime.ts`
- [Shared types](https://github.com/martinlegend/neogdsync/blob/main/src/types.ts): contracts · Common data contracts used across the sync pipeline · `src/types.ts`
- **Plugin data**: persistent state · Stored settings, tokens, and cached sync metadata

### OAuth support

Optional Cloudflare Worker used to assist OAuth flows

- [OAuth worker](https://github.com/martinlegend/neogdsync/blob/main/oauth-proxy/worker.js): cloudflare worker · Optional proxy service for OAuth redirect or token flow support · `oauth-proxy/worker.js`

### External services

Google services the plugin talks to

- **Google OAuth**: identity service · OAuth endpoint used to obtain Drive access
- **Google Drive**: external backend · Remote storage backend and metadata source

## Connections

- Obsidian → Plugin entry: hosts · Obsidian loads the plugin runtime
- Plugin entry → UI & commands: wires · Registers user-facing actions
- Plugin entry → URI handler: wires · Installs custom URI handling
- UI & commands → Sync engine: triggers · Starts sync from ribbon or command palette
- URI handler → Sync engine: triggers · Dispatches obsidian://neogdsync requests
- Sync engine → Auth manager: authenticates · Obtains authorized Drive access before remote operations
- Auth manager → OAuth worker: assists · Uses the optional proxy for OAuth flow support
- OAuth worker → Google OAuth: redirects · Brokers OAuth-related browser interaction
- Auth manager → Plugin data: reads/writes · Persists refresh token and auth state locally
- Sync engine → Path index: loads/updates · Uses the index to avoid full Drive crawls
- Sync engine → Snapshot model: compares · Builds or reads snapshots for delta detection
- Sync engine → File rules: normalizes · Chooses upload and download handling by file type
- Sync engine → Shared types: uses · Shares domain contracts across sync stages
- Sync engine → Conflict policy: decides · Routes divergent changes through conflict handling
- Conflict policy → Drive client: guards writes · Prevents silent overwrite of remote data
- Sync engine → Drive client: syncs · Executes listing, upload, download, and metadata operations
- Drive client → Google Drive: calls · Communicates with Google Drive REST APIs
- Sync engine → Vault: reads/writes · Enumerates local files and applies synced changes
- Sync engine → Plugin data: persists state · Stores sync progress and cached metadata
- Path index → Plugin data: backed by · Index state is retained in plugin data
- Snapshot model → Shared types: structured by · Snapshot records follow shared domain contracts
- File rules → Drive client: guides content · Influences how Drive payloads are encoded and decoded
