# Blog Outbox English | [简体中文](./README.zh-CN.md) Publish selected Obsidian notes and their local attachments to configurable folders in a local blog project. Blog Outbox produces standard Markdown with YAML Frontmatter and does not depend on a specific blog framework. > [!IMPORTANT] > Blog Outbox is a desktop-only plugin that accesses files outside your Obsidian vault. It writes only inside the blog project and output folders you configure. Review [Security and privacy](#security-and-privacy) before using it. ## Features - Publish one note or all notes explicitly marked for publishing. - Preview post paths, public URLs, attachments, warnings, and conflicts before writing. - Copy local images and other attachments using deterministic SHA-256-based names. - Convert Obsidian wikilinks, aliases, headings, and supported embeds to blog-friendly Markdown. - Choose exactly which top-level YAML fields are included in published files. - Configure post and attachment folders independently from their public URL paths. - Prevent overwriting or deleting unknown files. - Update and withdraw managed posts with transactional rollback safeguards. - Work offline without telemetry, advertisements, Git operations, or deployment automation. ## Requirements - Obsidian 1.13.7 or later on desktop. - A local blog project that accepts Markdown files with YAML Frontmatter. - Read and write permission for the configured blog project folder. Blog Outbox does not run your blog's build, validation, Git, or deployment commands. ## Installation ### Community Plugins After Blog Outbox is listed in the Obsidian Community directory: 1. Open **Settings → Community plugins**. 2. Select **Browse** and search for **Blog Outbox**. 3. Select **Install**, then **Enable**. ### Manual installation 1. Download `main.js`, `manifest.json`, and `styles.css` from a GitHub release. 2. Create `/.obsidian/plugins/blog-outbox/`. 3. Copy the three files into that folder. 4. Reload Obsidian and enable **Blog Outbox** under **Settings → Community plugins**. Use a test vault and a disposable copy of your blog project for your first publishing test. ## Quick start ### 1. Configure the destination Open **Settings → Blog Outbox** and set **Blog project root folder** to the absolute path of your local blog project. The following settings are optional. Leave them blank to use their defaults: | Setting | Default | Purpose | | --- | --- | --- | | Post output folder | `posts` | Relative folder for generated `.md` files. | | Attachment output folder | `assets` | Relative folder for copied attachments. It should map to a publicly served folder in your blog. | | Attachment public URL prefix | `/assets` | URL prefix written into Markdown attachment links. | | Post public URL template | `/posts/{slug}/` | URL used when converting links to other published notes. Must contain one `{slug}`. | | Publish switch field | `publish` | Frontmatter field that must be exactly `true`. | | Published YAML fields | See below | Complete allowlist of top-level Frontmatter fields copied to the blog. | | File conflict policy | Stop and report | Stops or skips a post when an unmanaged target file exists. | | Allow draft publishing | Off | When off, `draft: true` blocks publishing. | | Update publish date when publishing | On | When on, publishing updates `publishDate` in the source note to today's local date. | Select **Validate and save**. The plugin checks path boundaries, permissions, and URL settings without assuming a specific blog framework or configuration file. ### 2. Mark a note for publishing Add YAML Frontmatter to a note: ```yaml --- title: "My first post" description: "A short summary" publishDate: 2026-08-22 tags: [Markdown, Obsidian] draft: false publish: true slug: "my-first-post" --- ``` Only the publish switch is required by Blog Outbox. Your blog determines which other fields, names, and value types it requires. `slug` is optional. If omitted, Blog Outbox generates one from `title`, or from the note file name when no title is available. For a stable public URL, set an explicit slug before the first publish. ### 3. Preview and publish Open the command palette and run one of these commands: - **Blog Outbox: Check current post** — validates without writing files. - **Blog Outbox: Preview current post publishing** — shows the complete plan without writing. - **Blog Outbox: Publish current post** — previews, asks for confirmation, then publishes. - **Blog Outbox: Check all posts marked for publishing** — checks all notes with the publish switch enabled. - **Blog Outbox: Publish all marked posts** — publishes marked notes as independent transactions. - **Blog Outbox: Withdraw current post** — removes managed blog-side output without editing the source note. When **Update publish date when publishing** is enabled, the plugin updates the source note's `publishDate` property to today's local date (`YYYY-MM-DD`) after publishing is confirmed and before generating the blog-side file. Disable this setting to preserve the existing value. Checks, previews, and cancelled publishes do not edit the note. Command names and plugin UI follow the current Obsidian language. Chinese locales use Chinese; other locales fall back to English. Reload the plugin after changing the Obsidian language. ## YAML Frontmatter The default published-field allowlist contains: ```text title description publishDate updatedDate category tags draft featured readingTime ``` Edit **Published YAML fields** to match your blog. Scalar values, arrays, and nested objects are preserved. Fields missing from a note are skipped. The following control fields are always excluded from published Frontmatter: - `publish` - The configured publish switch field - `slug` Blog Outbox does not validate your blog's content schema. Run the blog project's own checks or build after publishing. ## Links and attachments - Supports `![[image.png]]`, `![[image.png|alt text]]`, and standard Markdown images. - Preserves `![[image.png|640]]` and `![[image.png|640x360]]` as HTML sizing hints. Rendering depends on the target blog's Markdown and CSS support. - Keeps `http`, `https`, and `data:` image URLs unchanged and never downloads them. - Copies local files using a SHA-256 content fingerprint. Identical content with the same extension reuses one target file. - Converts links to other publishable notes using the configured post URL template. - Converts links to local non-image attachments into download links. - Converts missing or unpublished internal-note links to plain text and reports a warning instead of creating a known dead link. - Leaves fenced and inline code unchanged. - Reports compatibility warnings for callouts, block references, Canvas links, and whole-note embeds. Empty attachments block publishing. ## Updating and withdrawing Publishing the same unchanged note is idempotent. Blog Outbox records the source hash, output hash, target path, slug, and attachment references in the vault's plugin data. If a managed blog-side post changed after the previous publish, the preview warns before allowing an overwrite. If the slug changes, withdraw the existing post first and then publish with the new slug so the old page is not left behind. Withdrawal uses the plugin record rather than guessing file names. It removes an attachment only when no other managed post references it. If a managed file changed externally, the withdrawal preview warns you, but you can still confirm its deletion. ## Security and privacy Blog Outbox accesses the external blog project because publishing requires writing outside the vault. - Writes are limited to the configured post and attachment output folders. - Temporary `.blog-outbox-staging-*` and `.blog-outbox-withdraw-*` transaction folders may be created at the blog project root and are removed after success or rollback. - Existing files not tracked by Blog Outbox are never overwritten or deleted. - The source note in Obsidian is never modified by publishing or withdrawal. - The plugin makes no network requests and contains no telemetry or advertisements. - The plugin does not install or update software and does not run Git or deployment commands. - The absolute project path is stored locally in `/.obsidian/plugins/blog-outbox/data.json`. Do not commit the plugin's `data.json` to a public repository. Files outside the vault usually cannot be recovered through Obsidian's trash. Review the withdrawal preview carefully before confirming permanent deletion. ## Compatibility and limitations - Desktop only because external project access uses Node.js file-system APIs. - Published post file names currently use `.md`. - The target blog must accept Markdown with YAML Frontmatter. - Heading-anchor algorithms and raw HTML support vary between blog engines; verify converted heading links and sized images in the actual blog. - Whole-note embeds, Canvas content, and plugin-specific Obsidian syntax are not fully converted. - Orphaned attachments are not automatically removed during a normal publish, and there is no separate orphan-cleanup command yet. - Blog Outbox does not provide remote image hosting, cloud sync, Git integration, deployment, or two-way synchronization. ## Troubleshooting ### The project folder is unavailable Verify that the path is absolute, the folder exists, and Obsidian has read and write permission. Then select **Validate and save** again. ### The target post exists but is unmanaged Blog Outbox will not take ownership of an unknown file. Move or rename the file, change the note slug, or otherwise resolve the conflict manually. ### The slug changed Run **Blog Outbox: Withdraw current post**, confirm removal of the old managed output, and publish again with the new slug. ### An expected YAML field is missing Add the exact top-level field name to **Published YAML fields**, then validate and save. Optional fields that do not exist in the current note are skipped. ### An attachment cannot be found Confirm that the link resolves inside Obsidian. Blog Outbox uses Obsidian's link resolution rather than guessing paths from link text. ### The blog build fails Verify the output folders, public URL settings, and YAML allowlist against your blog's requirements, then run the blog's own validation or build command. ## Development Node.js 18 or later is required. ```bash npm ci npm run dev ``` For manual testing, place this project in a test vault's `.obsidian/plugins/blog-outbox/` folder, or copy `main.js`, `manifest.json`, and `styles.css` there. Reload Obsidian after rebuilding. Run the complete automated check: ```bash npm run check ``` This runs TypeScript checks, ESLint, tests, and the production build. Before releasing, also test single publishing, batch publishing, attachments, conflicts, slug changes, and withdrawal in an isolated vault and blog project. ## License [MIT](./LICENSE) © supralune