Open Publish

unlisted

by Navid Kashani

Publish a subset of your vault to your own object storage and your own static site host. Free, self-hosted, no SaaS in the middle.

Updated 13d agoMIT
View on GitHub

Open Publish

Publish part of your Obsidian vault as a website. You own the storage and you pick the host. No subscription, no service in the middle.

Obsidian plugin  →  your object storage  →  deploy hook  →  your static host

Your notes go from Obsidian into your own bucket. Your host builds a site from that bucket. Nothing passes through anyone else's server, and your notes never go into Git.

What it does

  • Publishes part of your vault. Pick folders, put publish: true in a note's frontmatter, or publish one note from its right-click menu. Frontmatter wins.
  • Never writes to your notes. The plugin only reads them.
  • Never breaks the live site. The site switches to the new version all at once, or not at all.
  • Keeps [[wikilinks]] working. The plugin ships Obsidian's own link resolution beside your notes. A link to a note you did not publish renders as plain text, never as a link to a page that is not there.
  • Brings your attachments along. An image embedded by a published note gets published too, wherever it lives in your vault.
  • Keeps old addresses working. Renaming a note leaves a redirect behind, and one setting redirects every URL your old Obsidian Publish site used.
  • Imports your Obsidian Publish folders from .obsidian/publish.json, after showing you exactly what it would publish.
  • Gives you two starters. jotter is an Astro site theme and the recommended one. The Quartz starter lives here.
  • Carries your site options. Navigation order, search, graph, backlinks, outline, tags, theme toggle, page metadata, previous/next links, hover previews, inline title, homepage and analytics. Hiding a page from the sidebar does not unpublish it.

Install the plugin

Three files into a folder. No terminal.

  1. Download main.js, manifest.json and styles.css from the latest release.
  2. Put all three in <vault>/.obsidian/plugins/open-publish/, creating it if it is not there.
  3. Reload Obsidian, then turn on Open Publish under Settings → Community plugins.

Or install it with BRAT, which does the same thing and then keeps it up to date: add navidkashani/open-publish as a beta plugin.

Open Publish is not in Obsidian's community directory yet, so there is no one-click install. The two routes above are the whole story until it is listed.

Get started

Follow docs/setup-cloudflare.md. It takes about ten minutes and needs no terminal.

Cloudflare is the default, not a requirement. Storage can be R2, Amazon S3, Backblaze B2, Wasabi, MinIO or any other S3 endpoint. Hosting can be Cloudflare Pages, Cloudflare Workers, Netlify, Vercel or any host that builds a Git repository and gives you a deploy hook URL. Cloudflare R2 without keys is the one storage entry that is not S3: it uses a small Worker in your own account, so the plugin holds a bearer token rather than a storage key.

Guides

GuideWhat it covers
Setup: Cloudflare R2 + PagesThe default path, start to finish.
Other providersEvery storage provider and host, in tables.
TroubleshootingEvery error message, and what to do about it.
SecurityWhere your key lives, and what a leak reaches.
The gatewayReaching R2 without a storage key.
ArchitectureHow it works inside, and why.

Repository layout

PathWhat it is
plugin/The Obsidian plugin. TypeScript, no runtime dependencies.
starters/quartz/The reference site template. Fetches a snapshot and builds it with Quartz.
gateway/An optional Cloudflare Worker, so the plugin can reach R2 without a storage key.
docs/The guides above.
manifest.jsonThe plugin manifest, and the copy to edit. The build copies it next to main.js. It lives at the repository root because that is where the Obsidian community directory reads it from.
versions.jsonWhich app version each plugin release needs, so an older Obsidian installs the newest release it can still run. Read from the root, beside the manifest.

How publishing works

Each file is stored under a name made from its own contents.

objects/<ab>/<sha256>        written once, never overwritten
snapshots/<id>.json          one version of the site: path → hash, slug, links
current.json                 the only file that ever changes

A publish uploads whatever is missing, writes a snapshot, then replaces current.json. Everything before that last write only adds. So you can quit mid-publish and leave the live site untouched, a retry re-uploads nothing, and rolling back is one small write. See docs/architecture.md.

Network access

Obsidian's developer policy requires plugins to list every network endpoint. This plugin contacts three, and you configure all of them yourself:

EndpointWhyWhen
Your storage endpoint, or your own Worker's address if you use the gatewayRead the current snapshot, upload notes and attachmentsScanning, publishing, cleanup
Your deploy hook URLAsk your host to rebuild the siteAfter a successful publish
Your site URL, path /_publish.jsonCheck whether the new version is liveAfter triggering a build

There is no telemetry, no analytics, and no server run by this project.

Credentials

Your secret key lives in Obsidian's keychain rather than in your vault, so it does not sync with your notes and never reaches Git. Every other plugin you install can still read it, because that keychain is one shared store. So limit what a leaked key reaches: scope the token to one bucket, give your site build a separate read-only token, and revoke either in one click. docs/security.md is the full version.

Development

The test suites import the TypeScript sources directly and let Node strip the types, so there is no build step before a test run. You need Node 24.20.0 or newer.

git clone https://github.com/navidkashani/open-publish.git
cd open-publish
npm install --prefix plugin
npm run check     # typecheck, lint, all three test suites, then the bundle

npm run check is what CI runs. npm run lint on its own is eslint-plugin-obsidianmd, the same linter the community directory's review bot runs, so a clean run here is most of that review answered before it starts.

To try your working copy in a vault, build it and copy the three files across:

npm run build

mkdir -p "<vault>/.obsidian/plugins/open-publish"
cp plugin/main.js plugin/manifest.json plugin/styles.css \
   "<vault>/.obsidian/plugins/open-publish/"

Status

0.1.0 is the first release. Phases 1 and 2 of the roadmap in docs/architecture.md are done. Phase 3 is underway: the Worker gateway and Site history have landed, and the Deploy-to-Cloudflare button has not.

Mobile is the caveat, and it is why this is 0.1.0 rather than 1.0.0. The plugin imports no Node or Electron API, does all its networking through requestUrl, and handles the two places a phone would notice, but nobody has run it on a device yet. If it turns out not to work there, the fix is a release that says so in isDesktopOnly.

Licence

MIT. See LICENSE. The Quartz starter template carries Quartz's own LICENSE.txt, also MIT, which stays as it is.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.