Connect Google Drive, Dropbox, or OneDrive to sync documents bidirectionally with your vault.
In this guide you will connect one or more cloud storage providers to your Lifestream Vault and configure bidirectional sync. Documents you create in Google Drive, Dropbox, or OneDrive will appear in your vault, and documents you create or edit in your vault will be pushed back to cloud storage.
By the end you will have:
.md files indexedPrerequisites
GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET, DROPBOX_CLIENT_ID/DROPBOX_CLIENT_SECRET, or ONEDRIVE_CLIENT_ID/ONEDRIVE_CLIENT_SECRET configured (self-hosted) — cloud-hosted instances have these pre-configuredConnecting Google Drive uses the standard OAuth 2.0 authorization code flow. Lifestream Vault requests read/write access to a specific Google Drive folder — it does not request access to your entire Drive.
Via the UI: navigate to Settings → Connectors → Add Connector → Google Drive, choose the vault you want to sync, and click Authorize with Google. You will be redirected to Google's consent screen, then back to your vault with the connector active.
To connect programmatically, obtain an authorization URL from the API and redirect the user (or open a browser window) to it:
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
const { client } = await LifestreamVaultClient.login(
'https://vault.lifestreamdynamics.com',
'you@example.com',
'your-password',
);
const VAULT_ID = 'vlt_abc123';
// OAuth authorization happens via the dedicated provider callback route
// (/api/v1/auth/google-drive/callback) — there is no connectors.getAuthUrl().
// Once the OAuth grant is stored, register the connector. create() takes a
// single params object (provider/name/vaultId/syncDirection/syncPath?).
// Note the provider value uses an underscore: 'google_drive'.
const connector = await client.connectors.create({
provider: 'google_drive',
name: 'Google Drive — my vault',
vaultId: VAULT_ID,
syncDirection: 'bidirectional',
syncPath: 'Lifestream Vault/my-vault', // path prefix in Google Drive
});
console.log('Connector created:', connector.id);
console.log('Status:', connector.status);The Google Drive connector OAuth callback is handled at /api/v1/auth/google-drive/callback on your Lifestream Vault instance (distinct from the Google login callback at /api/v1/auth/google/callback). Make sure this URL is added to the list of Authorised redirect URIs in your Google Cloud Console OAuth client settings. For the managed cloud service, this is already configured.
The Dropbox connector uses the Dropbox OAuth 2.0 flow with PKCE. It syncs a specific folder in your Dropbox (not your entire Dropbox) to a vault. Only .md files within the folder are synced; other file types are ignored during indexing but are not deleted from Dropbox.
Via the UI: navigate to Settings → Connectors → Add Connector → Dropbox, choose your vault and the Dropbox folder path, and click Authorize with Dropbox.
// Authorize Dropbox via its OAuth callback route
// (/api/v1/auth/dropbox/callback), then register the connector.
const connector = await client.connectors.create({
provider: 'dropbox',
name: 'Dropbox — my vault',
vaultId: VAULT_ID,
syncDirection: 'bidirectional',
syncPath: '/vault-sync/my-vault', // Dropbox path (absolute from root)
});
console.log('Dropbox connector created:', connector.id);The Dropbox OAuth callback URL is /api/v1/auth/dropbox/callback. Register this exact URL in your Dropbox App Console under "OAuth 2 → Redirect URIs". For the managed cloud service, the Dropbox App is pre-configured and you do not need to register a redirect URI yourself.
The OneDrive connector authenticates via Microsoft Identity Platform (Azure AD OAuth 2.0). It syncs a specific OneDrive folder to your vault. Both personal Microsoft accounts and Microsoft 365 work accounts are supported.
Via the UI: navigate to Settings → Connectors → Add Connector → OneDrive, choose your vault, and click Authorize with Microsoft.
// Authorize Microsoft via its OAuth callback route
// (/api/v1/auth/onedrive/callback), then register the connector.
const connector = await client.connectors.create({
provider: 'onedrive',
name: 'OneDrive — my vault',
vaultId: VAULT_ID,
syncDirection: 'pull', // pull-only: OneDrive → Vault
syncPath: 'Documents/Lifestream Vault/my-vault',
});
console.log('OneDrive connector created:', connector.id);The OneDrive OAuth callback URL is /api/v1/auth/onedrive/callback. Register this URL in your Azure App Registration under "Authentication → Redirect URIs". For multi-tenant apps, set "Supported account types" to "Accounts in any organizational directory and personal Microsoft accounts". For the managed cloud service, this is pre-configured.
Understanding the sync architecture helps you reason about what happens during conflicts and how changes propagate.
Filesystem as source of truth: Lifestream Vault stores documents as files on disk at {DATA_DIR}/{userId}/{vaultSlug}/{path}.md. The PostgreSQL database holds metadata (title, tags, timestamps, search index) derived from the files. The filesystem is always authoritative — if a file changes, the database is updated to match.
Only .md files are indexed: The connector sync worker only processes Markdown files (.md extension). Other file types (images, PDFs, spreadsheets) in your cloud storage folder are ignored during indexing. They are not deleted from cloud storage — they simply do not appear in your vault's document list or search index.
The sync cycle:
connector-sync BullMQ worker runs on a schedule (every 15 minutes by default) or when triggered manuallyConflict resolution: If the same file is modified in both cloud storage and on disk since the last sync, the cloud storage version wins during the next pull cycle. The on-disk version is overwritten. If you need to preserve local changes, push them first (trigger a manual sync in push direction) before the automatic pull cycle runs.
The 6-hour reconciliation job performs a full filesystem-to-database sync regardless of file modification timestamps. This catches any files that were missed by the Chokidar watcher (e.g., during server downtime) and ensures the database never drifts out of sync with the filesystem.
When creating a connector, you choose how changes flow between your vault and cloud storage. You can change the direction at any time without disconnecting and reconnecting.
| Direction | Flow | Best for |
|---|---|---|
pull | Cloud storage → Vault only | Using cloud storage as the primary editing surface; vault is read-only mirror |
push | Vault → Cloud storage only | Using vault as the primary editing surface; cloud storage is backup or share point |
bidirectional | Cloud storage ↔ Vault | Team workflows where some members edit in Google Drive and others use the vault |
When to use each direction:
Obsidian users: If you use Obsidian as your primary editor, consider configuring pull-only sync so that changes flow from your cloud provider into Lifestream Vault without overwriting local Obsidian edits. This prevents sync conflicts when editing the same file in both places.
// update is positional: (connectorId, params)
const updated = await client.connectors.update('con_abc123', {
syncDirection: 'push', // was 'bidirectional'
});
console.log('Sync direction updated to:', updated.syncDirection);Once a connector is set up, you can list all connectors for a vault, trigger an immediate sync (without waiting for the next scheduled cycle), and disconnect a connector when it is no longer needed.
Triggering a manual sync is useful after making a batch of changes that you want reflected immediately, or after updating the sync direction.
// List connectors — list(vaultId?) takes the vault ID directly (optional)
const connectors = await client.connectors.list(VAULT_ID);
for (const connector of connectors) {
console.log(
`[${connector.provider}] ${connector.id} | direction: ${connector.syncDirection} | status: ${connector.status} | last sync: ${connector.lastSyncAt ?? 'never'}`,
);
}
// Trigger an immediate manual sync — sync(connectorId) is positional
const syncResult = await client.connectors.sync('con_abc123');
console.log('Sync enqueued:', syncResult);
// Inspect sync history via logs(connectorId) — there is no getSyncJob method
const logs = await client.connectors.logs('con_abc123');
for (const log of logs) {
console.log(log);
}
// Disconnect a connector (does not delete synced files) — delete(connectorId)
await client.connectors.delete('con_abc123');
console.log('Connector disconnected.');Point your connector at a dedicated folder in cloud storage (e.g., Lifestream Vault/my-vault) rather than a root folder or a folder that contains non-Markdown files. This keeps the sync scope narrow and reduces the time spent scanning for .md files.
If you use Obsidian with its built-in Obsidian Sync or a third-party sync (iCloud, Dropbox, etc.) as your primary editing environment, set the Lifestream Vault connector to pull only. This gives you the best of both worlds: Obsidian for offline, feature-rich editing and Lifestream Vault for web access, sharing, and publishing.
In bidirectional mode, the cloud storage version wins on the next pull cycle. If you are simultaneously editing a file in Google Drive and in the vault editor, the last system to sync wins and the other's changes are overwritten. Coordinate with your team to avoid simultaneous cross-system edits on the same file.
Cloud storage folders often contain images, PDFs, or spreadsheets alongside Markdown notes. Lifestream Vault ignores these for indexing purposes, but does not delete them. If you need images referenced in Markdown files, host them separately (a CDN, Cloudinary, or a public GitHub repo) and reference them by absolute URL.
After batch-uploading many documents to cloud storage (e.g., migrating from Notion or Bear), trigger a manual sync immediately rather than waiting for the next scheduled cycle. This gets your documents into the vault search index faster.
Check the connector's sync job history periodically — especially after a large initial sync. Common errors include files with invalid characters in their names, files exceeding the 10 MB document size limit, or OAuth token expiry (which requires re-authorising the connector).
Large initial syncs (thousands of files) can take several minutes to complete and may cause elevated Redis memory usage as jobs queue up in BullMQ. For vaults with more than ~5,000 documents, consider running the initial sync during off-peak hours and monitoring the connector-sync worker logs for errors or stalls. Use lsvault connectors logs <connectorId> to review sync history.
You now have cloud storage sync configured and understand how the bidirectional sync engine works. Here are some places to explore next:
client.connectors: create, list, update, sync, delete, and manage sync jobslsvault connectors commands and flagsdue dates in synced documents to power the vault calendar heatmap and due-date notifications