Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Pumice

Ji-ho LeeJi-ho Lee152 downloads

Sync, version, publish, and share your vault over your own self-hosted server.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates23

🇺🇸 English | 🇰🇷 한국어

An Obsidian community plugin that syncs your vault with a self-hosted server (pumice-server — required, run it yourself). The goal is to sync instantly, no matter how many files are in the vault.

Overview

  • Client: TypeScript, Obsidian community plugin (this repository)
  • Server: Python (asyncioreactor + Twisted), see pumice-server
  • Transport: a single persistent WebSocket connection, opened automatically once you've logged in and kept open for as long as Obsidian is running — modeled on Obsidian's own built-in Sync plugin, so an edit on one device shows up on another right away instead of waiting for a periodic sync. (Earlier versions used gRPC-Web, one HTTP/2 request per RPC with no live push; this version replaces that entirely.)
  • Auth: a static token stored in Obsidian's own secret storage (App#secretStorage, desktop and mobile alike, no platform-specific code needed)

Key features:

  • Vault file sync (delta comparison, only changed files are uploaded/downloaded)
  • Sync history browsing and file recovery (syncHistoryModal, fileRecoveryModal)
  • Automatic local snapshots with retention (localSnapshotStore)
  • Selective publishing of chosen folders (publishModal), including custom domain, navigation, and appearance options for the published site
  • Site collaboration — publish to a site owned by another account once they've accepted you as a collaborator (Publish a shared site… command, sharedSitePickerModal)
  • Vault sharing — sync your own vault against another account's instead of your own (sharedVaultOwner setting), or manage who can sync against yours (vaultShareModal)
  • Community plugin sync — optionally sync installed plugins' code and/or their own data.json (syncPlugins / syncPluginData, both off by default)
  • Sync diagnostics log for troubleshooting skipped/queued syncs (Open sync diagnostics log command, syncDiagnosticsModal)
  • Localization support (Korean/English, src/locales)

Requirements

  • Node.js (with npm)
  • Obsidian 1.13.4+ (manifest.json's minAppVersion). The settings tab renders entirely via the declarative settings API (getSettingDefinitions()), so it's searchable from Obsidian's own settings search; there's no legacy/imperative fallback UI to keep in sync.

Building

npm install

# Development mode (watch)
npm run dev

# Production build
npm run build

# Type-check only
npm run lint

main.js is generated by esbuild from src/. For releases, it's built alongside manifest.json and styles.css and attached as a GitHub Release artifact.

Releasing

Pushing a tag runs .github/workflows/release.yml, which builds the plugin and creates a GitHub Release with main.js, manifest.json, and styles.css attached (this is also what tools like BRAT, and the official Community Plugins installer, expect to find). The tag must match manifest.json's version exactly, with no v prefix.

Bump the version with npm version, which syncs manifest.json and versions.json (via scripts/version-bump.mjs, wired up as the version lifecycle script) and creates a matching git tag (.npmrc disables npm's default v prefix):

npm version patch   # or minor / major
git push --follow-tags

versions.json maps each released plugin version to the minAppVersion it required at the time — Obsidian's installer uses it to pick a compatible release for users on older app versions, which matters once this plugin is submitted to the Community Plugins list.

Installing locally in Obsidian for testing

  1. Run npm run build to generate main.js.
  2. Create a .obsidian/plugins/pumice/ folder in your vault and copy main.js, manifest.json, and styles.css into it.
  3. Enable Pumice under Settings → Community plugins in Obsidian.

How sync works

Once you've logged in (Settings → Pumice → "Log in", which opens the server's login page in your system browser and hands a token back to Obsidian), the plugin keeps one WebSocket connection to the server open for as long as Obsidian is running — there's no separate "enable live updates" toggle or sync-interval setting to configure, matching how Obsidian's own official Sync works. That connection is what:

  • pushes local edits to the server (debounced briefly so a burst of keystrokes becomes one upload, not one per keystroke),
  • receives other devices' changes and applies them immediately, and
  • runs a full safety-net sync every 30 seconds regardless of push activity, so nothing gets permanently stuck even if a push notification is ever missed.

The status bar icon reflects this connection the same way Obsidian core Sync's own icon does — hover it for a short status ("Syncing…", "Fully synced", "Sync error", etc.). Manually triggering a sync (ribbon icon or the command palette) still shows a toast notification; the automatic background activity above stays silent unless something actually fails.

If the connection drops (network blip, server restart, laptop sleep), it reconnects on its own with backoff and catches up on just what changed while it was gone — it doesn't rescan the whole vault on every reconnect.

Settings

Setting Default Description
serverHost localhost Pumice server address
serverPort 8080 HTTP + WebSocket port
useTls false Use TLS (recommended for remote servers)
deviceName OS hostname (desktop) or "Obsidian Client" Name identifying this device
userName Obsidian User User name
sharedVaultOwner (empty) If set, sync against that account's vault instead of your own — requires an accepted share from that account, and this vault's folder name must match their vault ID exactly
syncFiles true Whether to sync files
syncBookmarks true Whether to include bookmarks (.obsidian/bookmarks.json)
syncPlugins false Sync installed community plugins' code/manifest (off by default — this syncs executable code, a bigger trust boundary than note content)
syncPluginData false Also sync each plugin's own data.json (off by default — commonly holds secrets like API tokens in plaintext)
ignorePatterns see below Path patterns excluded from sync
conflictResolution server-wins Which side wins for a non-text file, or a text file with nothing to merge against (server-wins / client-wins) — text files (notes, .json/.css/.js/.base/.canvas) always attempt a 3-way merge first, regardless of this setting
enableE2EE false Enable end-to-end encryption
publishIncludeFolders / publishExcludeFolders - Folders to include/exclude when publishing
localSnapshotIntervalMinutes 5 Local snapshot interval (minutes)
localSnapshotKeepDays 7 Local snapshot retention (days)

Default exclude patterns (ignorePatterns / publishExcludeFolders):

.obsidian/workspace
.obsidian/workspace.json
.obsidian/workspace-mobile.json
.obsidian/cache
.obsidian/plugins/pumice
.trash

The vault's folder name is its identity on the server. There's no separate vault ID — the vault's folder name is used as-is to key everything server-side (sync, publish, version history). Every device syncing the same vault needs a folder with the exact same name; a mismatch isn't rejected, it just syncs as an unrelated vault. The settings tab shows the current vault's name for this reason.

"Publish current file" requires publish: true in the note's frontmatter. Folder-level inclusion (publishIncludeFolders) doesn't need it, but the single-file force-publish action won't upload a file until its frontmatter says so — otherwise a file could go live on the server yet silently fall out of scope on the next folder-wide publish scan, which is frontmatter-driven.

Project structure

pumice/
├── src/
│   ├── main.ts                    # Plugin entry point; owns the live connection lifecycle
│   ├── settings.ts                # Settings types and defaults
│   ├── settingsTab.ts             # Settings panel UI
│   ├── settingsTabConnection.ts   # Which setting changes require dropping/rebuilding the WS
│   ├── settingsTabKeyboard.ts     # Works around core's Tab-as-navigation keydown handler
│   ├── syncClient.ts              # Sync orchestration (scan/E2EE/conflict-resolution/hashing)
│   ├── syncTransport.ts           # Transport-agnostic interface syncClient.ts talks to
│   ├── wsTransport.ts             # WebSocket protocol layer (framing, heartbeat, reconnect)
│   ├── wsSyncTransportAdapter.ts  # Adapts wsTransport.ts to the syncTransport.ts interface
│   ├── liveUpdates.ts, liveStatus.ts  # Reconnect backoff; status bar icon/state model
│   ├── batching.ts                # Splits path lists into byte-size + file-count bounded batches
│   ├── concurrency.ts             # mapWithConcurrency / streamWithConcurrency helpers
│   ├── contentHashCache.ts        # Persists per-file content hashes (mtime+size keyed)
│   ├── lastSyncedHashStore.ts     # Per-path "base" hash the 3-way merge diffs against
│   ├── textFileTypes.ts           # Shared text-file classification (merge/diff eligibility)
│   ├── syncHistoryModal.ts        # Sync history UI
│   ├── fileRecoveryModal.ts       # File recovery UI
│   ├── localSnapshotStore.ts      # Local snapshot management
│   ├── diffView.ts                # File diff view
│   ├── syncDiagnosticsLog.ts, syncDiagnosticsModal.ts  # In-memory sync log + its viewer UI
│   ├── publishModal.ts            # Selective publish UI (folders, site options, custom domain)
│   ├── publishEligibility.ts      # Pure "should this file be published" decision logic
│   ├── navigationOrdering.ts      # Customize-sidebar drag/reorder logic for published sites
│   ├── siteCollaboration.ts, sharedSitePickerModal.ts  # Publish to a site owned by another account
│   ├── vaultShareModal.ts         # Owner-side "Manage sharing" UI for vault sync sharing
│   ├── pluginSync.ts              # .obsidian/plugins/** sync logic (opt-in)
│   ├── pluginReload.ts            # Hot-reloads community plugins after sync pulls new code
│   ├── swipeNavigation.ts         # Mobile swipe navigation
│   ├── tokenStore.ts              # Auth token storage (App#secretStorage)
│   ├── deviceName.ts              # Default device name decision (OS hostname fallback)
│   ├── errorMessage.ts            # Error-to-string helper
│   ├── momentUtil.ts              # Typed wrapper around obsidian's locale-aware moment instance
│   └── i18n.ts, locales/          # Localization strings
├── scripts/
│   └── version-bump.mjs           # Syncs manifest.json/versions.json, run by `npm version`
├── main.js                        # Generated by esbuild
├── manifest.json                  # Obsidian plugin manifest
├── versions.json                  # Plugin version → minAppVersion map
└── esbuild.config.mjs             # Build configuration

Contributing

  1. Fork the repository and create a branch.
  2. Run npm run lint after your changes to make sure there are no type errors.
  3. Keep commit messages concise and focused on the reason for the change.
  4. Open a Pull Request. Include screenshots for UI changes.

Please use GitHub Issues to report bugs or suggest features.

Support

If you'd like to sponsor this project, reach out at [email protected]. Sponsorships make a real difference in how much time can go into development.

License

BSD 3-Clause License

HealthExcellent
ReviewSatisfactory
About
An Obsidian community plugin that syncs your vault with a self-hosted server (pumice-server — required, run it yourself). The goal is to sync instantly, no matter how many files are in the vault.
SyncingBackupPublishing
Details
Current version
0.0.36
Last updated
Last month
Created
6 months ago
Updates
23 releases
Downloads
152
Compatible with
Obsidian 1.13.4+
Platforms
Desktop, Mobile
License
BSD-3-Clause
Report bugRequest featureReport plugin
Author
Ji-ho LeeJi-ho Leesearch5
GitHubsearch5
  1. Community
  2. Plugins
  3. Syncing
  4. Pumice

Related plugins

YAOS

Simple real-time sync powered by your own Cloudflare Worker.

GitHub

GitHub Sync

Sync vault to personal GitHub.

Remotely Save

Sync notes between local and cloud with smart conflict: S3, Dropbox, webdav, OneDrive, Google Drive, Box, pCloud, Yandex Disk, Koofr, Azure Blob Storage.

Differential ZIP Backup

Back our vault up with lesser storage.

GitHub

GitHub Gitless Sync

Sync a GitHub repository with vaults on different platforms without requiring git installation

Settings profiles

Create various global settings profiles, that sync between vaults.

Yandex Disk Sync

Synchronize your vault with Yandex Disk. Supports bidirectional sync, conflict resolution, and works on mobile.

Remotely Sync

Security fixes for the remotely-save unofficial plugin allowing users to synchronize notes between local device and the cloud service. Not backwards compatible.

Fast Note Sync

Real-time sync of your vaults across server, mobile, and web; shareable with anyone; supports REST and MCP integrations to build your personal AI knowledge base.

Self-hosted LiveSync

Sync vaults securely to self-hosted servers or WEBRTC.