POSSE Publisher

unlisted

by Devin Marshall

Set your site URL, add a destination, and publish to your own site first, then syndicate to Dev.to, Mastodon, and Bluesky.

1 starsUpdated 1mo agoMIT
View on GitHub

POSSE Publisher — Obsidian Plugin

Publish on your Own Site, Syndicate Elsewhere.

POSSE Publisher brings the IndieWeb POSSE philosophy to Obsidian. Write once in your vault, publish to your canonical site first, then syndicate copies to platforms like Dev.to, Mastodon, Bluesky, Medium, Reddit, Threads, LinkedIn, and Ecency — with every syndicated copy linking back to your original.

Your content. Your domain. Your canonical URL.


Quick start

  1. Open Settings > POSSE Publisher.
  2. Enter your canonical base URL for your site.
  3. Click Add destination and configure one supported destination.
  4. Open a note and add simple frontmatter:
---
title: My first post
status: draft
---
  1. Run POSSE publish from the command palette or use the ribbon icon.

Start with Custom API, Dev.to, Mastodon, or Bluesky for the fastest setup.


What is POSSE?

POSSE is a publishing strategy from the IndieWeb community:

  1. Publish the original on your own site (blog, portfolio, etc.)
  2. Syndicate copies to silos (Dev.to, Mastodon, Bluesky, etc.)
  3. Every copy links back to the canonical original you own

This means your domain holds the canonical version, search engines index your site, and you keep full ownership — while still reaching audiences on the platforms they use.


Installation

From Community Plugins (Recommended)

  1. Open Settings → Community plugins → Browse
  2. Search for "POSSE Publisher"
  3. Click Install, then Enable

Manual Install

  1. Download main.js, manifest.json, and styles.css from the latest release
  2. Create a folder at <vault>/.obsidian/plugins/posse-publisher/
  3. Copy the downloaded files into that folder
  4. Restart Obsidian and enable the plugin under Settings → Community Plugins

Build from Source

git clone https://github.com/TheOfficialDM/posse-publisher.git
cd posse-publisher
npm install
npm run build

Copy main.js, manifest.json, and styles.css into your vault's plugins folder.


Setup

  1. Open Settings > POSSE Publisher.
  2. Enter your canonical base URL. This is your site's root URL.
  3. Click Add destination and start with one supported platform.
  4. Paste the API key or token for that destination.
  5. Open a note, then run POSSE publish.

Destinations

Add as many destinations as you need. Each destination has a type that controls how content is formatted and delivered.

TypePlatformAuthStatus
custom-apiYour own site's /api/publish endpointAPI key (x-publish-key header)Live
devtoDev.toAPI keyLive
mastodonMastodon (any instance)Access tokenLive
blueskyBluesky (bsky.app)App passwordLive
mediumMediumIntegration tokenComing soon
redditRedditOAuth2 (client ID + secret + refresh token)Coming soon
threadsThreadsMeta access tokenComing soon
linkedinLinkedInOAuth2 bearer tokenComing soon
ecencyEcency (Hive blockchain)Hive posting keyComing soon

Note: Start with Custom API, Dev.to, Mastodon, or Bluesky. The other destinations can stay unconfigured until support is live.

  • 1 destination — commands publish directly
  • 2+ destinations — a picker modal lets you choose the target
  • POSSE to All command — syndicate to every destination at once

Commands

CommandBehaviour
POSSE PublishPublish using frontmatter status or the default from settings
POSSE Publish as DraftForces status: draft
POSSE Publish LiveForces status: published
POSSE to AllSyndicates to every configured destination
POSSE Insert Frontmatter TemplateInserts a YAML template with all supported fields

A ribbon icon is also available for one-click publishing.


Frontmatter

Use this minimum frontmatter to get started:

---
title: My post title
status: draft
---

Add more fields only when you need them.

Full example:

---
title: My Post Title
slug: my-post-title
excerpt: A short summary
type: blog
status: draft
tags: [javascript, web]
pillar: Technology
coverImage: https://example.com/image.jpg
featured: false
metaTitle: SEO Title Override
metaDescription: SEO description for search results
ogImage: https://example.com/og.jpg
videoUrl: https://youtube.com/watch?v=example
canonicalUrl: https://yoursite.com/blog/my-post-title
syndication:
  - url: https://dev.to/you/my-post-title
    name: Dev.to
  - url: https://mastodon.social/@you/status/123
    name: Mastodon
---
FieldRequiredDefault
titleNoFile name
slugNoAuto-generated from title
statusNoPlugin default setting
typeNoblog
excerptNoEmpty
tagsNo[]
pillarNoEmpty
coverImageNoEmpty
featuredNofalse
metaTitleNoEmpty
metaDescriptionNoEmpty
ogImageNoEmpty
videoUrlNoEmpty
canonicalUrlNoAuto-generated from canonical base URL + slug; set explicitly in frontmatter to override
syndicationAuto-setWritten back (and kept current) after each successful publish

Syndication Tracking

After a successful publish, POSSE Publisher writes the syndicated URL back into your note's frontmatter:

syndication:
  - url: https://dev.to/you/my-post
    name: Dev.to

This creates a permanent record of where your content has been syndicated — right in the note itself.


Obsidian Syntax Handling

By default, the plugin pre-processes content before publishing:

  • [[wiki-links]] → converted to plain text
  • [[target|alias]] → converted to the alias text
  • ![[embeds]] → removed
  • %%comments%% → removed
  • ```dataview / ```dataviewjs blocks → removed

Toggle off in settings if your destination handles Obsidian markdown natively.


Custom API Contract

For custom-api destinations, your /api/publish endpoint should:

  • Accept POST requests with a JSON body
  • Authenticate via the x-publish-key header
  • Receive a canonicalUrl field pointing to the original post on your site
  • Return 2xx on success, with optional { "upserted": true } in the response body
  • Return 4xx/5xx on failure, with optional { "error": "message" } in the response body

Generate a secure API key with:

-join ((1..32) | ForEach-Object { '{0:x2}' -f (Get-Random -Max 256) })

Upsert Behaviour

Publishing a note with the same slug as an existing entry will update that entry instead of creating a duplicate (for custom API destinations).


Troubleshooting

ProblemSolution
"Open a markdown file first"Make sure you have a .md file active in the editor
Publish fails with 401/403Check your API key in settings matches the server's PUBLISH_API_KEY
Content appears emptyEnsure you have content below the YAML frontmatter fence
Wiki-links appear in published contentEnable "Strip Obsidian Syntax" in settings
Connection test failsVerify the destination URL is correct and the server is running
Dev.to returns 422Check that title frontmatter is set — Dev.to requires a title
Mastodon post not appearingVerify your access token has write:statuses scope

Security

API keys and access tokens are stored in Obsidian's plugin data directory and are never logged or exposed in the UI (password fields with autocomplete disabled). Always use https:// endpoints.


IndieWeb

This plugin implements the POSSE pattern from the IndieWeb community.

Learn more: indieweb.org · indieweb.org/POSSE


License

MIT — Devin Marshall


Support

POSSE Publisher is free and open source. If it saves you time, a small contribution helps support continued development.

Buy Me a Coffeebuymeacoffee.com/theofficaldm
GitHub Sponsorsgithub.com/sponsors/TheOfficialDM
🔗 All optionsdevinmarshall.info/fund

Buy Me a Coffee QR

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.