Witch is a content management dashboard for a static Hugo site. It publishes
Obsidian notes to a Cloudflare Worker content API (witch-worker), which stores
them in R2 and triggers a Pages build. The build (sync-content.js) mirrors the
content into a Hugo repo and renders the site.
- Create new posts and pages from commands, with a full frontmatter template.
- Dashboard view with Posts, Pages, Tags, Site settings, and Media tabs.
- Local-first: notes and
Site/tags/metadata notes live in the vault; site settings live in the plugin data as JSON; the worker is only used when you publish or sync. - Live list of notes in the vault's
Site/folder, filtered by status and search, with status pills, thumbnails, and bulk publish/unpublish. - Note settings editor: status, section, slug, dates, featured, tags, excerpt, feature image, SEO meta/OG/Twitter, canonical URL, code injection.
- Tags with full SEO: descriptions, accent colors, feature images, meta/OG/Twitter, canonical URL, visibility, and parent — plus auto-generated archive pages.
- Site settings (in plugin data) for identity, social, homepage, SEO, legal, navigation, and site-wide code injection.
- Live build status: the dashboard shows the last build result and opens the site, and note rows show "last published" / "edited since publish" and a schedule ETA.
- Media library backed by the worker's
images/API (copy URL / delete). - Uploads embedded images to Cloudflare R2 with Canvas-based optimization.
- Scheduled notes auto-flip to
publishedonce their date passes. - Publishing is production-by-default: notes upload to the worker and trigger the site build. No profile setup.
- Dev workflow: point Connection at a local worker (
http://localhost:8787) to sync content; the build trigger fails gracefully and you preview withsync-content.js+hugo server.
- Obsidian 1.13.0 or newer (desktop or mobile).
- A deployed
witch-workercontent API with an R2 bucket. - Optional: Cloudflare R2 credentials for image uploads.
- Clone this repo into your vault:
.obsidian/plugins/witch/. - Install dependencies:
pnpm install- Build:
pnpm run build- In Obsidian, open
Settings -> Community plugins, then enableWitch.
| Command | What it does |
|---|---|
Open content dashboard |
Opens the CMS dashboard view. |
Publish current note |
Publishes the active note to the content API. |
Create new post |
Scaffolds a new post in the Site/ folder. |
Create new page |
Scaffolds a new page in the Site/ folder. |
Edit note settings |
Opens the frontmatter editor for the active note. |
- Open plugin settings -> Connection.
- Set the worker URL (
https://witch-worker.<your-subdomain>.workers.dev) and the content API token. - Pick
devto preview locally orprodto trigger builds. - Set the
Site/folder and section tags for routing.
---
title: My post title
type: post
section: blog
status: published
slug: my-post-title
date: 2026-03-09
published_at: 2026-03-09T10:00:00Z
tags: [blog, ai]
featured: true
feature_image: "![[cover.png]]"
excerpt: Short summary.
author: Your name
meta_title: SEO title
meta_description: SEO description
keywords: [ai, obsidian]
og_title: Social title
og_description: Social description
og_image: https://example.com/og.png
twitter_title: Twitter title
twitter_description: Twitter description
twitter_image: https://example.com/tw.png
canonical_url: https://yourdomain.com/my-post-title
codeinjection_head: <meta name="robots" content="index">
codeinjection_foot: <script>console.log("ready")</script>
---Sections are blog, portfolio, and flashcards. A page type routes to
<slug>/_index.md; posts route to <section>/<slug>.md. Statuses are
draft, published, and scheduled (future published_at stays hidden until
its date passes, then the note flips to published). Tags written as #flag
are internal — excluded from public tags and archives but carried in the built
frontmatter as internal_tags.
pnpm install
pnpm run dev
pnpm run lint
pnpm run test
pnpm run buildpnpm run deploy -- /path/to/your/vaultYou can also set OBSIDIAN_VAULT or OBSIDIAN_VAULT_PATH.
Run the worker locally, then point the plugin's Connection at it:
cd witch-worker
pnpm run dev # http://localhost:8787 with CONTENT_API_TOKEN=dev-tokenAfter publishing, sync into your Hugo repo and preview:
node scripts/sync-content.js --url http://localhost:8787 --token dev-token
hugo server -D- Update versions in
package.json,manifest.json, andversions.json. - Run:
pnpm run release:check- Create a Git tag matching the manifest version exactly (no
vprefix). - Create a GitHub release with attached
main.js,manifest.json, andstyles.css.
Automated tagged releases are available via .github/workflows/release.yml.
MIT. See LICENSE.