<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <updated>2026-10-31T18:14:00Z</updated>
  <generator>https://nostr.now</generator>

  <title>Nostr notes by Fabio Manganiello</title>
  <author>
    <name>Fabio Manganiello</name>
  </author>
  <link rel="self" type="application/atom+xml" href="https://nostr.now/npub17w6mc9753nnur2uzf0qrfz8jaku2vaqjwuwqs6fxhgchv4wg0pzqgh48ey.rss" />
  <link href="https://nostr.now/npub17w6mc9753nnur2uzf0qrfz8jaku2vaqjwuwqs6fxhgchv4wg0pzqgh48ey" />
  <id>https://nostr.now/npub17w6mc9753nnur2uzf0qrfz8jaku2vaqjwuwqs6fxhgchv4wg0pzqgh48ey</id>
  <icon>https://blog.fabiomanganiello.com/img/author.png</icon>
  <logo>https://blog.fabiomanganiello.com/img/author.png</logo>




  <entry>
    <id>https://nostr.now/nevent1qqsp5a5f4xgh2pnktmzv3sx27cxyc7e8l4syesttjrtqjz0dttwwkygzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyge94nhg</id>
    
      <title type="html">**[Songhive — A fully self-hosted and federated music ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsp5a5f4xgh2pnktmzv3sx27cxyc7e8l4syesttjrtqjz0dttwwkygzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyge94nhg" />
    <content type="html">
      **[Songhive — A fully self-hosted and federated music platform](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/Songhive&#34;&gt;https://blog.fabiomanganiello.com/article/Songhive&lt;/a&gt; )**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**What if your music server had a social network attached, and your social network could play music?**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;The problem&lt;br/&gt;If you&amp;#39;ve ever tried to self-host your music, you&amp;#39;ve probably noticed that every tool solves exactly one half of the problem.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**Media servers** ([Jellyfin](&lt;a href=&#34;https://jellyfin.org&#34;&gt;https://jellyfin.org&lt;/a&gt; ), [Navidrome](&lt;a href=&#34;https://navidrome.org&#34;&gt;https://navidrome.org&lt;/a&gt; ), plain [Subsonic](&lt;a href=&#34;https://subsonic.org&#34;&gt;https://subsonic.org&lt;/a&gt; )) are great at the library half: point them at your FLAC collection, get streaming, transcoding, mobile apps. But they&amp;#39;re single-player islands. Nobody else can discover your music, there&amp;#39;s no social layer, and sharing a track means copy-pasting a file or a link that probably doesn&amp;#39;t resolve outside your LAN, or creating accounts on your server for every person you want to share content with.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**Social music platforms** ([Funkwhale](&lt;a href=&#34;https://www.funkwhale.audio&#34;&gt;https://www.funkwhale.audio&lt;/a&gt; ) being the closest thing) add the federation half, but the social experience is mostly library-oriented: you follow someone&amp;#39;s *library* and sync their files. It&amp;#39;s a distribution network, not a place where people talk about music. Moreover, most of those open platforms were never thought with revenue models in mind. Payments were never integrated. They&amp;#39;re mostly places for artists to freely share their creations, not for them to monetize them.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**Streaming services** ([Spotify](&lt;a href=&#34;https://spotify.com&#34;&gt;https://spotify.com&lt;/a&gt; ), [Apple Music](&lt;a href=&#34;https://apple.com/music&#34;&gt;https://apple.com/music&lt;/a&gt; ), [TIDAL](&lt;a href=&#34;https://tidal.com&#34;&gt;https://tidal.com&lt;/a&gt; )) are widespread, but they usually don&amp;#39;t come with true built-in social features besides following someone else&amp;#39;s playlists, or the &amp;#34;Share to Facebook/Instagram/TikTok&amp;#34; button. And consumers don&amp;#39;t own anything: the catalog can shrink overnight, and shared URLs can often be viewed only by people who already have an account on the platform. Music creators don&amp;#39;t have much of a say either. They get fractions of a cent, they don&amp;#39;t fully control distribution (and often they need an intermediary just to distribute their music, which takes a share of the revenue too), and they often have to release to multiple music platforms just because those platforms don&amp;#39;t talk with one another. Most importantly, your listening history is the product.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[**Bandcamp**](&lt;a href=&#34;https://bandcamp.com&#34;&gt;https://bandcamp.com&lt;/a&gt; ) tries to fix some of the issues, namely by putting content creators at the center and letting them own their own sales and monetization process (including sales of things like vinyls, CDs and merchandise). But it&amp;#39;s still a centralized service whose [ownership changed hands](&lt;a href=&#34;https://www.nytimes.com/2022/03/02/arts/music/epic-games-bandcamp.html&#34;&gt;https://www.nytimes.com/2022/03/02/arts/music/epic-games-bandcamp.html&lt;/a&gt; ) [several times](&lt;a href=&#34;https://www.bbc.com/news/technology-66953910&#34;&gt;https://www.bbc.com/news/technology-66953910&lt;/a&gt; ). Users and artists are just one round of layoffs away from seeing the platform they invested in rot and/or enshittify, with no direct compatible alternatives. The social media aspect is also not something Bandcamp particularly invested in as well. The focus has increasingly shifted to sales, social interactions are basically limited to a &amp;#34;community&amp;#34; tab on artist pages, where artists can post updates and people can reply to. Any social interactions stay strictly confined to a tab on a centralized platform.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Perhaps the [old incarnation of **Google Music**](&lt;a href=&#34;https://en.wikipedia.org/wiki/Google_Play_Music&#34;&gt;https://en.wikipedia.org/wiki/Google_Play_Music&lt;/a&gt; ) helped filled an important gap on user-generated content (it allowed users to upload up to 50,000 tracks to the cloud for free, making music collection sitting on old hard drives available on-the-go), but it didn&amp;#39;t last long, and it didn&amp;#39;t cover any social media and sharing needs.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;And **MySpace** — the thing everyone actually remembers fondly — was never really any of these. It was a place where a band had a page, you could play their song right there, leave a comment, and add them as a friend. Nobody has successfully rebuilt that.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Songhive is an attempt to meet all these needs in one package: a self-hosted music streaming server where tracks, albums, playlists and posts are first-class [**ActivityPub**](&lt;a href=&#34;https://activitypub.rocks/&#34;&gt;https://activitypub.rocks/&lt;/a&gt; ) objects. Think *Funkwhale meets Mastodon* — your music library lives on your hardware, or in any cloud storage you want to plug to it, but your music (and your posts about music) can travel across the fediverse, be followed from Mastodon, replied to, boosted, and quoted.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of an instance&amp;#39;s home page on desktop]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/home-full.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of an instance&amp;#39;s home page on mobile]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/home-federated.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of a Subsonic app (Substreamer on Android) connected to a Songhive server]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/subsonic-android.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Who is this for&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Artists&amp;lt;/strong&amp;gt;: publish tracks with a &amp;#34;Publish on the Fediverse&amp;#34; checkbox and they appear as playable &amp;lt;code&amp;gt;Audio&amp;lt;/code&amp;gt; objects in Mastodon timelines. People can reply, boost and quote them from their existing fediverse account — no new signup required on your side. Your artist page, releases and playlists all expose RSS/Atom feeds, and blogs that support Webmentions will send you a notification when they link to your song. Native integration with &amp;lt;strong&amp;gt;Stripe&amp;lt;/strong&amp;gt; also makes it easier for you to connect a payout account and put some of your tracks or albums for sale. You can choose how much of a paid song you want to share for streaming (only a sample, the full song or nothing), and how much users should pay for it. No shares of your revenue are taken besides the share taken by Stripe to process the transaction.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Bands&amp;lt;/strong&amp;gt; who need an easy and flexible way to share their recordings, rehearsals, playlists of covers etc. Those who have played in a band know this issue quite well. Spotify playlists for covers to play are only good as long as everyone in the band has a premium Spotify account. YouTube playlists often result in precious rehearsal time wasted waiting for ads to end. Not to mention those &amp;lt;code&amp;gt;rehearsal.mp3&amp;lt;/code&amp;gt; files shared between WhatsApp chats, emails and Dropbox or Google Drive folders, with no easy way of bringing them all together under the same roof - especially when you&amp;#39;re looking for those recordings during a rehearsal. Songhive makes it easier to share whole tracks, albums or playlists with full control over the level of visibility. It makes it easier to integrate 3rd-party music streaming services so you can sync your playlist of covers and easily share it from a proxy platform without everyone having to have a Spotify or TIDAL premium account. It makes it easy to just plug that Google Drive folder where you carelessly dropped all the recordings of your band&amp;#39;s rehearsal and sort them, tag them and share them as consistent collections with a single URL.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Self-hosters / library curators&amp;lt;/strong&amp;gt;: point Songhive at your local folders, an S3 bucket, a WebDAV/SFTP share, Dropbox, Google Drive — or even import your collections from Jellyfin or TIDAL — and get a full streaming stack with transcoding, Subsonic mobile apps, Last.fm scrobbling, listening stats and automatic MusicBrainz/cover-art enrichment. Fine-grained visibility (&amp;lt;code&amp;gt;private&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;public&amp;lt;/code&amp;gt;) means your 4,000-track hoard stays private while the three songs you actually wrote are public. Not only, but Songhive also taps into an issue that open projects like these often fail to address: how to provide incentives to sysadmins and self-hosters to run their servers. Especially when it comes to things like a music streaming server which, in terms of CPU, storage and bandwidth usage, is not free - and often not even cheap. Songhive provides the option for sysadmins to have a &amp;lt;strong&amp;gt;Paid&amp;lt;/strong&amp;gt; registration mode. The admin can decide how much they want to charge users to use their instance and upload music to it, and how often they want to charge them. Subscriptions are handled transparently by Stripe and can be cancelled at any time. And admins can also set &amp;lt;strong&amp;gt;user quotas&amp;lt;/strong&amp;gt; to keep things sane and prevent abuse.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;People who want a MySpace-style music social network&amp;lt;/strong&amp;gt;: run an instance for your scene, your label, your collective. Users get profiles, statuses with mentions and hashtags, threaded replies, quotes, notifications, and moderation tools (mute/block, reports, instance-level defederation). Your instance federates with Mastodon, so it&amp;#39;s not a walled garden unless you want it to be.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Musicians with a multi-room house or a community radio streak&amp;lt;/strong&amp;gt;: server-side outputs let playback keep running on the server after you close the tab — cast to Snapcast multi-room speakers, relay to an Icecast station, or host your own HTTP radio mountpoint with fan-out to unlimited listeners.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;What you get&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;The streaming stack&lt;br/&gt;The core is a serious music library and streaming server:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Library management&amp;lt;/strong&amp;gt; with artists, albums, tracks, playlists and dynamic radio stations. Uploads get automatic tag extraction, duplicate detection, and metadata enrichment from MusicBrainz and the Cover Art Archive — including cover art and artist images fetched for you.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Streaming with on-the-fly transcoding&amp;lt;/strong&amp;gt; (MP3, OGG, FLAC, AAC, Opus), HTTP range requests, and a transcode cache, with per-user/per-role bitrate caps (handy if your uplink or mobile data is precious).&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Subsonic API compatibility&amp;lt;/strong&amp;gt;, enabled by default. This is the sleeper feature: every Subsonic-compatible mobile/desktop client — Substreamer, Symfonium, Ultrasonic, Supersonic, Tempus and friends — works against your instance out of the box. You get a decade&amp;#39;s worth of mature third-party players for free, and API tokens keep your real password out of them.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Mopidy integration&amp;lt;/strong&amp;gt; via the &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/mopidy-songhive&amp;#34;&amp;gt;&amp;lt;code&amp;gt;mopidy-songhive&amp;lt;/code&amp;gt&#34;&gt;https://github.com/blacklight/mopidy-songhive&amp;#34;&amp;gt;&amp;lt;code&amp;gt;mopidy-songhive&amp;lt;/code&amp;gt&lt;/a&gt;; extension&amp;lt;/a&amp;gt; if that&amp;#39;s your jam. - &amp;lt;strong&amp;gt;Listening history, favorites and scrobbling&amp;lt;/strong&amp;gt; to Last.fm and Libre.fm out of the box, plus a personal listening-stats dashboard (top artists, calendar and hour-of-day heatmaps, release-year breakdowns) rendered with ECharts.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[A screenshot of the listening stats page, showing top listened artists and albums over a given time period]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/listening-stats-1.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[A screenshot of the listening stats page, showing listened tracks grouped by release years and listening hours]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/listening-stats-2.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Bulk downloads&amp;lt;/strong&amp;gt;: select tracks/collections and get an async-built ZIP archive.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Server-side outputs&amp;lt;/strong&amp;gt; — Spotify-Connect-style. The browser is just a remote control; audio is rendered by the server via a dedicated stream worker. Three providers: an Icecast relay (with live ICY metadata updates), a Snapcast provider for whole-home audio, and native HTTP mountpoints (&amp;lt;code&amp;gt;/streams/&amp;amp;lt;mount&amp;amp;gt;&amp;lt;/code&amp;gt;) served directly by Songhive with per-listener cursors over Redis Streams — no external radio server required. Playback keeps playing after you close the tab, and multiple tabs/devices stay in sync.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Payments support out of the box&amp;lt;/strong&amp;gt; — set up a Stripe account and connect it to the instance, and configure it as a marketplace account if you also want to allow musicians to wire their payout accounts and sell music through your platform.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[A screenshot of a track uploaded by a musician, showing the options to set up payments for it]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/artist-payment-form.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[A screenshot of the membership page on an instance with paid subscriptions]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/membership-form.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;The social layer&lt;br/&gt;This is where Songhive diverges from every other self-hosted media server. Federation is full ActivityPub (via the [pubby](&lt;a href=&#34;https://github.com/blacklight/pubby&#34;&gt;https://github.com/blacklight/pubby&lt;/a&gt; ) library), and music entities are social objects:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Every user is an ActivityPub actor&amp;lt;/strong&amp;gt;, reachable at &amp;lt;code&amp;gt;@user@instance&amp;lt;/code&amp;gt; via WebFinger, with Mastodon-style alias URLs. Mastodon users can follow, mention and interact with you from their own client.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Tracks are &amp;lt;code&amp;gt;Audio&amp;lt;/code&amp;gt; objects.&amp;lt;/strong&amp;gt; Publishing is explicit and opt-in per upload — your private library never leaks to the Fediverse by accident. Published tracks render as playable audio in Mastodon and Akkoma timelines, with descriptions, hashtags and link-backs to the track page.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Mastodon-style statuses&amp;lt;/strong&amp;gt; with Markdown, mentions, hashtags, threaded replies, quotes and link preview cards. Reply to a track, boost an album drop, quote-post a playlist, or simply post some words to Mastodon and use it like a normal Mastodon account.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Funkwhale-compatible music federation&amp;lt;/strong&amp;gt;: full bidirectional music federation with Funkwhale and other Songhive instances — remote libraries, albums, artists and tracks resolve as browsable resources you can follow and add to your collection.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Remote discovery&amp;lt;/strong&amp;gt;: paste a Fediverse handle or URL into the search box and it&amp;#39;s dereferenced, cached and browsable — SSRF-guarded and domain-moderated.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Real notifications&amp;lt;/strong&amp;gt; over WebSocket (in-app, real-time), with per-type email and daily-digest preferences, plus optional OS-native push via VAPID WebPush.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Mastodon-grade moderation&amp;lt;/strong&amp;gt;: users mute/block local and remote actors; admins limit/suspend actors, defederate or restrict instances, and review reports — optionally forwarding them to the reported actor&amp;#39;s home instance via ActivityPub &amp;lt;code&amp;gt;Flag&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;RSS/Atom everywhere&amp;lt;/strong&amp;gt;: every profile, artist, playlist, library, tag and genre has RSS 2.0 and Atom feeds with &amp;lt;code&amp;gt;&amp;amp;lt;link rel=&amp;#34;alternate&amp;#34;&amp;amp;gt;&amp;lt;/code&amp;gt; discovery, so even non-fediverse readers can subscribe.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[A Songhive post with a published song viewed from Mastodon]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/mastodon-integration.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Sharing and privacy&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Three-level visibility&amp;lt;/strong&amp;gt; on tracks, albums, playlists and libraries: &amp;lt;code&amp;gt;private&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; (instance-only), or &amp;lt;code&amp;gt;public&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Share grants&amp;lt;/strong&amp;gt; (give a specific user access) and &amp;lt;strong&amp;gt;revocable share links&amp;lt;/strong&amp;gt; — a short URL that renders a preview page with an audio player for anyone, content-negotiated so API clients get JSON and crawlers get OpenGraph metadata.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Embeddables&amp;lt;/strong&amp;gt;: public tracks and collections embed on any web page via &amp;lt;code&amp;gt;&amp;amp;lt;audio&amp;amp;gt;&amp;lt;/code&amp;gt; tags, Markdown links, a &amp;lt;code&amp;gt;&amp;amp;lt;script&amp;amp;gt;&amp;lt;/code&amp;gt; widget or a no-JS iframe. Media endpoints send &amp;lt;code&amp;gt;Access-Control-Allow-Origin&amp;lt;/code&amp;gt;, so fediverse clients can embed your audio too.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Webmentions&amp;lt;/strong&amp;gt;: bidirectional link-backs for public content. A blog that links to your song sends you a notification; content you share or comment on sends a Webmention back to the source. All outbound/inbound fetches on this path are SSRF-guarded.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of the &amp;#34;Share Track&amp;#34; modal]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/track-share.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;The platform bits&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Multi-user&amp;lt;/strong&amp;gt; with open, invite-only, approval-required or closed registration; per-user profile visibility.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;OAuth2 provider&amp;lt;/strong&amp;gt; for third-party apps, plus API tokens for scripts and Subsonic clients.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Flexible storage&amp;lt;/strong&amp;gt;: local filesystem, S3-compatible object storage.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;External libraries&amp;lt;/strong&amp;gt; — the feature that sells it to people with existing collections. Attach external music storage to a Songhive library: local folders (with a filesystem watchdog that picks up changes automatically), S3, SFTP, WebDAV, Dropbox, Google Drive, or plain HTTP. Entity-backed providers go further: &amp;lt;strong&amp;gt;Jellyfin&amp;lt;/strong&amp;gt; and &amp;lt;strong&amp;gt;TIDAL&amp;lt;/strong&amp;gt; import tracks, albums, artists and playlists as first-class library entries with lazily-fetched contents — audio bytes stay remote and are proxied at play time, so provider credentials never reach the client. Metadata can be written back to the provider, and Dropbox/TIDAL connect via OAuth straight from the settings form.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Metadata write-back&amp;lt;/strong&amp;gt;: enriched tags are written into the audio files themselves, using audio-only content hashing — so re-tagging or swapping cover art never changes the stored path or invalidates the content hash. Since it support metadata write-back to the original file, you can also wire your existing music library on an old hard drive and use Songhive to fix broken audio metadata tags.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;How it compares&lt;br/&gt;&lt;br/&gt;&amp;lt;thead&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Songhive&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Funkwhale&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Jellyfin/Navidrome&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Mastodon&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Spotify&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Bandcamp&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/thead&amp;gt;&lt;br/&gt;&amp;lt;tbody&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Self-hosted&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Streaming &#43; transcoding&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Subsonic API (mobile apps)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Scrobbling &#43; listening stats&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;ActivityPub social (posts, replies, boosts)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Federated music objects playable in Mastodon&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial (library-oriented)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Fine-grained sharing (grants, links, embeds)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Server-side outputs (Icecast/Snapcast)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;(Spotify Connect, proprietary)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;External library import (S3, WebDAV, Jellyfin, TIDAL…)&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;partial&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Support for payments to artists&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Support for subscriptions to fund platform maintainers&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;You own the catalog&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;you can download the content you pay for&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/tbody&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Quickstart&lt;br/&gt;The fastest path is the Docker bootstrap script, which drops a docker-compose.yml, an nginx config, and starter config.toml/.env files into the current directory:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;curl&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-fsSL&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&lt;a href=&#34;https://git.fabiomanganiello.com/songhive/raw/branch/main/docker/bootstrap.sh&amp;lt;span&#34;&gt;https://git.fabiomanganiello.com/songhive/raw/branch/main/docker/bootstrap.sh&amp;lt;span&lt;/a&gt; class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;sh&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Set your user&amp;#39;s UID/GID in .env (so the bind-mounted volumes stay yours), fill in the few required values in config.toml:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;[auth]&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;secret_key&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;...&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# python -c &amp;#34;import secrets; print(secrets.token_urlsafe(64))&amp;#34;&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;[storage]&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;local_path&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;./data/media&amp;#34;&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;[federation]&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;enabled&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kc&amp;#34;&amp;gt;false&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# instance_domain = &amp;#34;music.example.com&amp;#34;  # uncomment to join the fediverse&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;then:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;compose&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;up&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# Or, if you want to enable local HTTP stream mount points too&amp;lt;/span&amp;gt;&lt;br/&gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;compose&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;up&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;--profile&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;streams&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Create the admin user:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;compose&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;exec&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;songhive&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;songhive&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;admin&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;create-user&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;    &amp;lt;/span&amp;gt;--username&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;admin&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;--email&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;admin@example.com&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;--password&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;secret&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;--admin&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Open &lt;a href=&#34;http://localhost:8000/&#34;&gt;http://localhost:8000/&lt;/a&gt; and start uploading. The web UI, Swagger UI (/swagger-ui/) and OpenAPI spec (/openapi.json) are all served by the same process. There&amp;#39;s also a pip install songhive path (the PyPI package ships the built web UI), systemd units under config/systemd/, and an install.sh that wires the whole thing up — see the README for those.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Everything else — federation, Webmentions, RSS feeds, scrobbling keys, registration mode, bitrate caps — is a config knob, and most of it is enabled by default. All config.toml entries can also be overridden with SONGHIVE_* environment variables.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Under the hood&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Backend&amp;lt;/strong&amp;gt;: FastAPI (REST) mounted inside Tornado via &amp;lt;code&amp;gt;a2wsgi&amp;lt;/code&amp;gt; — Tornado natively handles WebSockets, audio streaming with range requests, and HTTP stream mountpoints, while FastAPI handles everything else. (There&amp;#39;s a non-obvious reason for the explicit thread pool in the WSGI bridge: without it, a federated fetch that resolves back to your own instance deadlocks the event loop. The architecture doc tells the whole war story.)&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Data&amp;lt;/strong&amp;gt;: PostgreSQL (SQLite works for small setups) &#43; SQLAlchemy 2 async, Pydantic v2 everywhere, Alembic migrations.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Async work&amp;lt;/strong&amp;gt;: Celery &#43; Redis with three queues — &amp;lt;code&amp;gt;celery&amp;lt;/code&amp;gt; for interactive work, &amp;lt;code&amp;gt;scrobbles&amp;lt;/code&amp;gt; for latency-sensitive submissions, and &amp;lt;code&amp;gt;bulk&amp;lt;/code&amp;gt; for the per-item fan-out jobs (a fresh external library sync can enqueue tens of thousands of enrichment tasks; without the split they&amp;#39;d starve scrobbles and federation delivery for hours).&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Storage&amp;lt;/strong&amp;gt;: content-addressable files (SHA-256) with audio-only hashing, so tag/cover-art rewrites don&amp;#39;t move files or invalidate hashes.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Frontend&amp;lt;/strong&amp;gt;: Vue 3 &#43; TypeScript &#43; Pinia, fully i18n&amp;#39;d with RTL support.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Security posture&amp;lt;/strong&amp;gt;: JWT &#43; opaque rotating refresh tokens, double-submit CSRF, Redis sliding-window rate limiting, SSRF-guarded federation and Webmention fetches with redirect revalidation, Fernet-encrypted provider secrets, RFC 7807 error handling.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;License&amp;lt;/strong&amp;gt;: AGPL-3.0.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Caveats, honestly&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;It&amp;#39;s a real stack: Postgres, Redis, a Celery worker, ffmpeg. The Docker compose file makes this a non-issue, but it&amp;#39;s not a single static binary. It can run on a RaspberryPi 4 or 5 with decent storage if you want to run it for yourself and your family, but if you want to have more tracks and more users you may need a beefier machine.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;Federation with the big platforms is only as good as ActivityPub support for audio — Mastodon renders playable audio attachments, but it treats &amp;lt;code&amp;gt;Audio&amp;lt;/code&amp;gt; as a converted type, so Songhive also emits &amp;lt;code&amp;gt;Note&amp;lt;/code&amp;gt; variants when you share a track as a post. The interop details with Akkoma, Funkwhale and friends are handled, but the fediverse music landscape is still young.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;TIDAL imports federate metadata-only (a browse link, not hosted audio) — for obvious reasons the audio bytes stay with the provider.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Links&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;🏠 My personal instance: &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://manganiello.music&amp;#34;&amp;gt;manganiello.music&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://manganiello.music&amp;#34;&amp;gt;manganiello.music&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;li&amp;gt;👤 My Fediverse handle: &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://manganiello.music/@fabio&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1ajxg604hh0zscgyfqhw546n899sl0v4gwqmylme7nylpydnatpxq8j3p4q&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Fabio Manganiello&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1ajx…3p4q&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://manganiello.music/@fabio&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1ajxg604hh0zscgyfqhw546n899sl0v4gwqmylme7nylpydnatpxq8j3p4q&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Fabio Manganiello&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1ajx…3p4q&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;amp;lt;/&amp;amp;gt; Code: &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/songhive&amp;#34;&amp;gt;github.com/blacklight/songhive&amp;lt;/a&amp;gt&#34;&gt;https://github.com/blacklight/songhive&amp;#34;&amp;gt;github.com/blacklight/songhive&amp;lt;/a&amp;gt&lt;/a&gt;;&lt;br/&gt;  (canonical forge: &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://git.fabiomanganiello.com/songhive&amp;#34;&amp;gt;git.fabiomanganiello.com/songhive&amp;lt;/a&amp;gt&#34;&gt;https://git.fabiomanganiello.com/songhive&amp;#34;&amp;gt;git.fabiomanganiello.com/songhive&amp;lt;/a&amp;gt&lt;/a&gt;;)&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;🏗️ Architecture deep-dive:&lt;br/&gt;  &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://git.fabiomanganiello.com/blacklight/songhive/src/branch/main/docs/ARCHITECTURE.md&amp;#34;&amp;gt;&amp;lt;code&amp;gt;docs/ARCHITECTURE.md&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://git.fabiomanganiello.com/blacklight/songhive/src/branch/main/docs/ARCHITECTURE.md&amp;#34;&amp;gt;&amp;lt;code&amp;gt;docs/ARCHITECTURE.md&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;li&amp;gt;🧩 Mopidy extension: &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/mopidy-songhive&amp;#34;&amp;gt;mopidy-songhive&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://github.com/blacklight/mopidy-songhive&amp;#34;&amp;gt;mopidy-songhive&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/home-full.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/home-federated.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/subsonic-android.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/listening-stats-1.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/listening-stats-2.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/artist-payment-form.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/membership-form.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/mastodon-integration.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/screenshots/songhive/track-share.png&#34;&gt; &lt;br/&gt;
    </content>
    <updated>2026-10-03T08:41:08Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqs2r7avkp8xdvuj326jwfcwcezfa58ja5gerste3gche5gq5umhuqszyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg5a9fkj</id>
    
      <title type="html">**[Support for post visibility in ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqs2r7avkp8xdvuj326jwfcwcezfa58ja5gerste3gche5gq5umhuqszyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg5a9fkj" />
    <content type="html">
      **[Support for post visibility in Madblog](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/support-for-post-visibility-in-madblog&#34;&gt;https://blog.fabiomanganiello.com/article/support-for-post-visibility-in-madblog&lt;/a&gt; )**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;# &lt;br/&gt;&lt;br/&gt;Support for post visibility in Madblog&lt;br/&gt;#madblog has now added support for *custom visibility* for your posts and articles.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;You may not want everything you write to be published on your index or federated everywhere.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Madblog now allows you to easily tweak the audience of your posts.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Visibility levels&lt;br/&gt;&lt;br/&gt;&amp;lt;thead&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: left;&amp;#34;&amp;gt;Visibility&amp;lt;br&amp;gt;Level&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Blog&amp;lt;br&amp;gt;Index&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;/unlisted&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Page&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th style=&amp;#34;text-align: center;&amp;#34;&amp;gt;Reactions&amp;lt;br&amp;gt;List&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;th&amp;gt;ActivityPub&amp;lt;br&amp;gt;Targets&amp;lt;/th&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/thead&amp;gt;&lt;br/&gt;&amp;lt;tbody&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: left;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;public&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;to: [Public]&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;cc: [followers]&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: left;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;unlisted&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;✅&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;to: [followers]&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;cc: [Public]&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: left;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;followers&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;to: [followers]&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;cc: []&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: left;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;direct&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;&amp;lt;code&amp;gt;to: [mentions]&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;cc: []&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: left;&amp;#34;&amp;gt;&amp;lt;code&amp;gt;draft&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td style=&amp;#34;text-align: center;&amp;#34;&amp;gt;❌&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;td&amp;gt;Not federated&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/tbody&amp;gt;&lt;br/&gt;&lt;br/&gt;All the visibility levels (except for draft) map one-to-one with their corresponding meanings on the Fediverse.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;public&amp;lt;/strong&amp;gt;: articles are rendered on the blog indexes and replies are rendered in the comments section.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;unlisted&amp;lt;/strong&amp;gt;: articles are rendered under the &amp;lt;code&amp;gt;/unlisted&amp;lt;/code&amp;gt; page, replies are rendered in the comments section.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;followers&amp;lt;/strong&amp;gt;: articles are not rendered on the blog, replies are not rendered in the comments section (only visible to ActivityPub followers).&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;direct&amp;lt;/strong&amp;gt;: articles are not rendered on the blog, replies are not rendered in the comments section (only visible to tagged ActivityPub users).&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;draft&amp;lt;/strong&amp;gt; content is neither rendered on the blog nor federated.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Default visibility&lt;br/&gt;You can configure the default visibility level for all of your posts on a global level via config.yaml:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;default_visibility&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;public&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Or MADBLOG_DEFAULT_VISIBILITY environment variable.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Per-post visibility&lt;br/&gt;You can configure the visibility level of a post by using the visibility Markdown header:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;gh&amp;#34;&amp;gt;# A private post&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (visibility: direct)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;@alice@example&amp;lt;/span&amp;gt;.com only you should be able to see this message&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;ActivityPub constraints&lt;br/&gt;If a post is federated then **YOU CANNOT** change its visibility level. This is a constraint on ActivityPub itself. You should delete and re-created the post with a different audience instead.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;If a post was already federated and you move it to *Draft* then the previously federated version *will not be removed*. No changes that you apply while it&amp;#39;s a draft will be published until you change the visibility level back, but the latest federated version will still be available. If you want to delete it, then you should delete the associated file.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Security observations&lt;br/&gt;Remember that Madblog, by design, has **NO USERS**, **NO AUTHENTICATION** and **NO WRITE API**.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Everything lives on the file system, and every Markdown file in the supported folders will have a public URL.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It means that everyone with the URL can access the content.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Be mindful about *not sharing sensitive data*, even in drafts or private messages. And, if you can, use non-easy-to-guess filenames for posts you want to keep private.
    </content>
    <updated>2026-03-21T11:21:30Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsghs7jy0hn59ztyw06vwgu3ht6yqsh6w0udxycz70qnn0ykvv70aqzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg3r4425</id>
    
      <title>Nostr event nevent1qqsghs7jy0hn59ztyw06vwgu3ht6yqsh6w0udxycz70qnn0ykvv70aqzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg3r4425</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsghs7jy0hn59ztyw06vwgu3ht6yqsh6w0udxycz70qnn0ykvv70aqzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg3r4425" />
    <content type="html">
      &lt;a href=&#34;https://blog.fabiomanganiello.com/reply/the-local-tech-neighbourhood&#34;&gt;https://blog.fabiomanganiello.com/reply/the-local-tech-neighbourhood&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;A corollary to the concept of [small tech neighbourhood](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/the-small-tech-neighbourhood&#34;&gt;https://blog.fabiomanganiello.com/article/the-small-tech-neighbourhood&lt;/a&gt; ) is the idea of *local tech communities*.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It&amp;#39;s easy for folks like me, who have been self-hosting on computers in my closet since the time of high school, to lose track with the common non-tech person.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It&amp;#39;s easy for us to say &amp;#34;*just self-host your Nextcloud, Matrix, XMPP, Mastodon, Searxng, Wallabag etc.*&amp;#34;, adding a &amp;#34;*it&amp;#39;s just a docker run command*&amp;#34;.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;I&amp;#39;ve also seen &amp;#34;just run your f*cking website&amp;#34; articles get to the top of HackerNews.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;But then I thought: imagine if it was, say, the bakery industry rather than the software industry to go through a process of #enshittification.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Imagine that buying your bread or croissant tomorrow requires a $100/month Bread&#43; subscription with plenty of upsells, an age verification process that requires you to send your passport or retina scan to the Big Bread consortium, a tracking system that measures exactly how much bread you eat at home, and that keeps selling your eating habits to whoever wants to purchase them - all while your bread quality gets progressively worse over time, and the oligopoly that holds it, after acquiring or putting out of business any credible competitors, starts experimenting also with selling expired products.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Of course people would be outraged.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;But many, no matter how outraged they are, will just swallow the bitter pill and stick with the Bread&#43; subscription (perhaps because they don&amp;#39;t care enough yet to trigger a change in their habits, perhaps because they have other priorities and battles to fight), hoping that the next change to the T&amp;amp;C won&amp;#39;t make it even worse.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;And some will instead start looking for alternatives.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Would it make sense for a professional baker who&amp;#39;s been in the business for decades to tell those folks &amp;#34;just bake your own f*cking bread&amp;#34; - and then complain that not enough are moving out of the Bread&#43; walled garden?&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Or perhaps would it make more sense for that baker to volunteer for a competitive price to sell their bread to its friends, family and local community?&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;We all know that running your own stuff on your own machines is the best way to avoid enshittification. But not everyone will do it, nor they are supposed to do it.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;People live different lives, have different priorities and choose different battles. Any pragmatic human must at some point delegate some tasks - that&amp;#39;s how modern societies were born. Real autarchy in complex society is nothing more than a pipe dream. We don&amp;#39;t judge those outside of our area of competencies for not being able or willing to perform a certain task, no matter how trivial that looks to us.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;An average neighbourhood has someone good enough at baking that they can sell their products to their local community.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;And howadays perhaps it also has someone good enough with computers that they can probably run on an old laptop or unused VPS stuff like a small Matrix server, a GoToSocial instance, a Nextcloud instance or an Immich server.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;And they could probably serve that to small local groups of 10-20 people for something like $10/month (and at low volumes those services usually don&amp;#39;t even require much maintenance).&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It can be $100/200 month that can abundantly cover the cost of a small VPS, let alone the cost of running a miniPC at home.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It&amp;#39;d be a bargain for the subscribers too - nowadays for $10/month you don&amp;#39;t even get a Netflix subscription, let alone a complete cloud alternative with no trackers and no ads.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It can even be a reliable source of income for IT folks in areas that would otherwise not provide other viable employment solutions.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;In a healthy society with a high level of immunity against enshittification, your local self-hoster should probably become the digital equivalent of your local baker or your local pub.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;#smalltech #techneighbourhoods #selfhosting
    </content>
    <updated>2026-03-18T20:44:09Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqs2h2nznxpyflru5hgtqw0qhl597sp2ek9rvgj2vtmdx7hsw53xhxszyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygt3yzdq</id>
    
      <title type="html">https://blog.fabiomanganiello.com/reply/unlisted-posts The latest ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqs2h2nznxpyflru5hgtqw0qhl597sp2ek9rvgj2vtmdx7hsw53xhxszyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygt3yzdq" />
    <content type="html">
      &lt;a href=&#34;https://blog.fabiomanganiello.com/reply/unlisted-posts&#34;&gt;https://blog.fabiomanganiello.com/reply/unlisted-posts&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;The latest version of #madblog introduces a separate page, under /unlisted, for posts that the author can send to the Fediverse (or simply keep in a separate area of their blog) without publishing them on the index.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;This is useful for quick thoughts and interactions. Worth enough to mention, but not enough to deserve their own dedicate article on the blog frontpage.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;To create an unlisted post, simply create a Markdown file under /replies/ with no like-of and reply-to metadata headers:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;cd&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;$MADBLOG_DATADIR&amp;lt;/span&amp;gt;&lt;br/&gt;mkdir&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-p&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;replies&lt;br/&gt;&lt;br/&gt;cat&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;amp;lt;&amp;amp;lt;EOF&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;replies/unlisted-post.md&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# My first unlisted post&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;I&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;thought&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;that&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;it&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;#39;d be cool to share with my followers on Mastodon that now I can trace likes and replies with git bisect.&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;I&amp;#39;&amp;lt;/span&amp;gt;ll&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;also&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;tag&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;@friend@nerds.tech&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;because&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;he&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;#39;ll probably think it&amp;#39;&amp;lt;/span&amp;gt;s&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;cool.&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Save the file, then you will find your post on the /unlisted page.
    </content>
    <updated>2026-03-18T13:42:59Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsv4l9hmvkdcza5sq07mg5h9xurcjcyvmq2m6l9lxwm0umdsvrhezczyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygckdmje</id>
    
      <title type="html">https://blog.fabiomanganiello.com/reply/git-up-your-blog That ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsv4l9hmvkdcza5sq07mg5h9xurcjcyvmq2m6l9lxwm0umdsvrhezczyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygckdmje" />
    <content type="html">
      &lt;a href=&#34;https://blog.fabiomanganiello.com/reply/git-up-your-blog&#34;&gt;https://blog.fabiomanganiello.com/reply/git-up-your-blog&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;That oddly satisfying feeling of browsing your blogging and social media history with git log and git grep #madblog #activitypub #indieweb&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of git log from a terminal directory. It&amp;#39;s executed from my blog directory populated with Markdown files and served via Madblog. It shows all of my replies, posts, modifications and reactions as commits.]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-git-log-screenshot.png&#34;&gt;  )]&lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-git-log-screenshot.png&#34;&gt; &lt;br/&gt;
    </content>
    <updated>2026-03-16T23:56:07Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsdjuuaevsnce7f9paevrd9apywejujvxs9wde5cw8ujezh3jp586qzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygdtwtjk</id>
    
      <title>Nostr event nevent1qqsdjuuaevsnce7f9paevrd9apywejujvxs9wde5cw8ujezh3jp586qzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygdtwtjk</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsdjuuaevsnce7f9paevrd9apywejujvxs9wde5cw8ujezh3jp586qzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygdtwtjk" />
    <content type="html">
      &lt;a href=&#34;https://blog.fabiomanganiello.com/reply/None/git-log-for-your-blog&#34;&gt;https://blog.fabiomanganiello.com/reply/None/git-log-for-your-blog&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;That oddly satisfying feeling of browsing your blogging and social media history with git log and git grep #madblog #activitypub #indieweb&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Screenshot of git log from a terminal directory. It&amp;#39;s executed from my blog directory populated with Markdown files and served via Madblog. It shows all of my replies, posts, modifications and reactions as commits.]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-git-log-screenshot.png&#34;&gt;  )]&lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-git-log-screenshot.png&#34;&gt; &lt;br/&gt;
    </content>
    <updated>2026-03-16T23:13:22Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsf6e4whm55htn64urn9slt6xcn42zqaktk5uwtk2lyhpjwwxpzdhqzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygvdzu9h</id>
    
      <title type="html">**[Federated Replies and Reactions in ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsf6e4whm55htn64urn9slt6xcn42zqaktk5uwtk2lyhpjwwxpzdhqzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygvdzu9h" />
    <content type="html">
      **[Federated Replies and Reactions in Madblog](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/Federated-replies-and-reactions-in-Madblog&#34;&gt;https://blog.fabiomanganiello.com/article/Federated-replies-and-reactions-in-Madblog&lt;/a&gt; )**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**Engage with the Web from plain text files**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[Madblog](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt; ) is founded on a simple principle: a blog is just a collection of #markdown files in a folder. No databases, no logins, no client-side bloat — just files.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;The recently implemented support for both [Webmentions](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/webmentions-with-batteries-included&#34;&gt;https://blog.fabiomanganiello.com/article/webmentions-with-batteries-included&lt;/a&gt; ) and [ActivityPub](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/Madblog-federated-blogging-from-markdown&#34;&gt;https://blog.fabiomanganiello.com/article/Madblog-federated-blogging-from-markdown&lt;/a&gt; ) add an extra appeal to this approach: now those text files can federate, they can send mentions to Wordpress blogs or Mastodon accounts, and you can visualize mentions, comments and reactions from other corners of the Web directly under your articles.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;But after receiving in the past few days a bunch of reactions on [my blog](&lt;a href=&#34;https://blog.fabiomanganiello.com&#34;&gt;https://blog.fabiomanganiello.com&lt;/a&gt; ) that I couldn&amp;#39;t interact with, which forced me to fall back on [my standard Fediverse account](&lt;a href=&#34;https://manganiello.eu/@fabio&#34;&gt;https://manganiello.eu/@fabio&lt;/a&gt; ) to send replies and likes, I&amp;#39;ve decided to take the &amp;#34;[*everything is a file*](&lt;a href=&#34;https://en.wikipedia.org/wiki/Everything_is_a_file&#34;&gt;https://en.wikipedia.org/wiki/Everything_is_a_file&lt;/a&gt; )&amp;#34; philosophy a step further.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Now from #madblog you can also **reply to comments** and **react to posts** across the Fediverse - all from plain text files in your content folder.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Replying to Comments&lt;br/&gt;When someone comments on your article from Mastodon or another ActivityPub-compatible services, their message appears on your blog.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Now you can also respond directly from your blog.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Or you can reply to any other post on the Fediverse or mention anyone, without those posts cluttering your blog&amp;#39;s front page (I&amp;#39;ve learned to avoid this fatal design mistake made by e.g. [Medium](&lt;a href=&#34;https://medium.com/&#34;&gt;https://medium.com/&lt;/a&gt; )).&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;How it works&lt;br/&gt;Create a Markdown file under replies/&amp;amp;lt;article-slug&amp;amp;gt;/:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (reply-to: &lt;a href=&#34;https://mastodon.social/@alice/123456789&#34;&gt;https://mastodon.social/@alice/123456789&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;Thanks for the kind words, Alice! I&amp;#39;m glad the tutorial helped.&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;@alice@mastodon&amp;lt;/span&amp;gt;.social&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Save the file, and Madblog automatically:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;Publishes your reply to the Fediverse as a threaded response&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;Notifies Alice on her Mastodon instance&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;Displays the reply on your blog, nested under her original comment&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;Your reply lives in your content folder. Just like with your articles, you can version replies and reactions on git, synchronize them over [SyncThing](&lt;a href=&#34;https://syncthing.net/&#34;&gt;https://syncthing.net/&lt;/a&gt; ) or [Nextcloud Notes](&lt;a href=&#34;https://nextcloud.com/&#34;&gt;https://nextcloud.com/&lt;/a&gt; ), or run some analysis scripts on them that would just operate on text files.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Replying to replies&lt;br/&gt;Conversations can go as deep as you want. Reply to a reply by pointing reply-to at the previous message&amp;#39;s URL:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (reply-to: &lt;a href=&#34;https://mastodon.social/@alice/123456790&#34;&gt;https://mastodon.social/@alice/123456790&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;Great question! I&amp;#39;ll write a follow-up post about that.&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;@alice@mastodon&amp;lt;/span&amp;gt;.social&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;The threading is preserved both on your blog and across the Fediverse.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Example of a nested thread rendered on Madblog]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-replies-1.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;(I hope that &lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1vm0mfw89yauuuvwjjtupdfnqz2377jueq2epaqvp4rjcurgzpmqscu3lyd&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Julian Fietkau&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1vm0…3lyd&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt; and &lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1xselj43f4mq6e4tmadr6ex9s2xfdp34wqupn27n2ufd6clv3mqkqhflgk3&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;wakest ⁂&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1xse…lgk3&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt; won&amp;#39;t mind for using a screenshot from their conversation on my blog 🙂)&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Remember to mention your mentions&lt;br/&gt;An important implementation note: if you&amp;#39;re replying to someone else&amp;#39;s ActivityPub post, it&amp;#39;s important that you also mention them in the reply, otherwise your reply will be rendered under their comment but they may not be notified.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Usually you don&amp;#39;t have to worry about this on Mastodon because the UI will automatically pre-fill the participating accounts in a sub-thread when you hit *Reply*.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;But this is something to keep in mind when your posts are just text files.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Your replies are articles in their own right&lt;br/&gt;Even though anything under replies/ won&amp;#39;t appear on your blog&amp;#39;s home page, it doesn&amp;#39;t mean that it must be rendered just like a humble rectangle in a crowded comments section.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;By clicking *View full reply* you get redirected to a separate page where the reply is rendered as a blog article, and its comments sections consists in the sub-tree of the reactions that spawned from that specific reply.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;[[Example of a Madblog reply rendered as a blog article, with its own sub-thread of reactions]( &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-replies-2.png&#34;&gt;  )]&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Liking Posts&lt;br/&gt;Sometimes a reply is too much — you just want to show appreciation. Now you can &amp;#34;like&amp;#34; any post on the Fediverse with a simple metadata header.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Standalone likes&lt;br/&gt;Create a file under replies/ with just a like-of header:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (like-of: &lt;a href=&#34;https://mastodon.social/@bob/987654321&#34;&gt;https://mastodon.social/@bob/987654321&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;This publishes a Like activity to the Fediverse. Bob sees the notification,&lt;br/&gt;and your blog records the interaction.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Like and comment&lt;br/&gt;Want to like *and* say something? Combine both:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (like-of: &lt;a href=&#34;https://mastodon.social/@bob/987654321&#34;&gt;https://mastodon.social/@bob/987654321&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (reply-to: &lt;a href=&#34;https://mastodon.social/@bob/987654321&#34;&gt;https://mastodon.social/@bob/987654321&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;This is such a great point! Bookmarking for later.&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;@bob@mastodon&amp;lt;/span&amp;gt;.social&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Bob gets both the like and your reply as a threaded response.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Unlisted Posts&lt;br/&gt;Not everything needs to appear on your blog&amp;#39;s front page. Files under replies/ without reply-to and like-of headers become &amp;#34;unlisted&amp;#34; posts — they&amp;#39;re published to the Fediverse but don&amp;#39;t clutter your blog index.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Perfect for quick thoughts, threads, or conversations that don&amp;#39;t warrant a full article.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (title: Thoughts of the day)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;Quick thought: I&amp;#39;ve been experimenting with writing all my Fediverse posts&lt;br/&gt;as Markdown files. It&amp;#39;s oddly satisfying to &amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt;`git log`&amp;lt;/span&amp;gt; my social media history.&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Guestbook Replies&lt;br/&gt;Your blog&amp;#39;s guestbook works the same way. Reply to guestbook entries by placing files under replies/_guestbook/:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (reply-to: &lt;a href=&#34;https://someone.blog/mention/123&#34;&gt;https://someone.blog/mention/123&lt;/a&gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;@alice@example&amp;lt;/span&amp;gt;.com welcome! Thanks for stopping by.&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Editing and Deleting&lt;br/&gt;Changed your mind? Edit the file and an Update activity is sent. Delete the file and your reply is removed from the Fediverse too.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Accidentally liked something? Remove the like-of line (or delete the file) and an Undo Like is published.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Your content, your rules.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Getting Started&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;Enable ActivityPub in your &amp;lt;code&amp;gt;config.yaml&amp;lt;/code&amp;gt;:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;link&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;&lt;a href=&#34;https://blog.example.com&amp;lt;/span&amp;gt&#34;&gt;https://blog.example.com&amp;lt;/span&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;enable_activitypub&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;activitypub_username&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;blog&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# Only specify these if you want your ActivityPub domain to be different from your blog domain&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# activitypub_link: &lt;a href=&#34;https://example.com&amp;lt;/span&amp;gt&#34;&gt;https://example.com&amp;lt;/span&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# activitypub_domain: example.com&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;Install Madblog&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;From &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt;:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;pip&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;install&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;madblog&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;From Docker:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;pull&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;quay.io/blacklight/madblog&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;Run Madblog from your Markdown folder (it is recommended that your articles are stored under &amp;lt;code&amp;gt;&amp;amp;lt;data-dir&amp;amp;gt;/markdown&amp;lt;/code&amp;gt;):&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;From a &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; installation:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;madblog&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;/path/to/data&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;From Docker:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;run&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-it&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-p&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;m&amp;#34;&amp;gt;8000&amp;lt;/span&amp;gt;:8000&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-v&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;/path/to/config.yaml:/etc/madblog/config.yaml&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-v&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;/path/to/data:/data&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;quay.io/blacklight/madblog&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;Any text file you create under &amp;lt;code&amp;gt;markdown/&amp;lt;/code&amp;gt; becomes a blog article. Any text file you create under &amp;lt;code&amp;gt;replies/&amp;lt;/code&amp;gt; becomes an unlisted post, a reply or a like reaction.&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;Check the [README](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt; ) for detailed configuration options.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Happy blogging!&lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-replies-1.png&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://s3.fabiomanganiello.com/fabio/img/madblog-replies-2.png&#34;&gt; &lt;br/&gt;
    </content>
    <updated>2026-03-15T01:59:59Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsvynwuthkfhwjezxyau7lhmfqdyfnau3r5jxsfn3j2enyrmupdu5szyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygzw4yus</id>
    
      <title type="html">good catch! It wasn&amp;#39;t intentional, I had simply forgotten to ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsvynwuthkfhwjezxyau7lhmfqdyfnau3r5jxsfn3j2enyrmupdu5szyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuygzw4yus" />
    <content type="html">
      In reply to &lt;a href=&#39;/nevent1qqsxyn6t89pnych62mp3q8znnx9pqmpve6yvn2m2ew6q93q7e07urkc9ugkpv&#39;&gt;nevent1q…gkpv&lt;/a&gt;&lt;br/&gt;_________________________&lt;br/&gt;&lt;br/&gt;good catch!&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It wasn&amp;#39;t intentional, I had simply forgotten to apply to replies the same quote policy I implemented for articles.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It should [be working now](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog/commit/8c12db13da124f4e79a42c70689ccfbfd2983f02&#34;&gt;https://git.fabiomanganiello.com/madblog/commit/8c12db13da124f4e79a42c70689ccfbfd2983f02&lt;/a&gt; ) - thanks for the heads up 🙂
    </content>
    <updated>2026-03-14T10:53:46Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqsy09frswd6at2h7grfrrewzvrk9j0nrg84s4kdtgh8xr3s9xzgmagzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg8zk59r</id>
    
      <title type="html">and now with support for #markdown based ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqsy09frswd6at2h7grfrrewzvrk9j0nrg84s4kdtgh8xr3s9xzgmagzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg8zk59r" />
    <content type="html">
      In reply to &lt;a href=&#39;/nevent1qqsrek8ewdfc289g2j7dqgz5q44pu67t0kwvewgp86pm6c4z2r82xnsrw2jf4&#39;&gt;nevent1q…2jf4&lt;/a&gt;&lt;br/&gt;_________________________&lt;br/&gt;&lt;br/&gt;and now with support for #markdown based [replies](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog#author-replies&#34;&gt;https://git.fabiomanganiello.com/madblog#author-replies&lt;/a&gt; ) too 🙂
    </content>
    <updated>2026-03-14T09:15:48Z</updated>
  </entry>

  <entry>
    <id>https://nostr.now/nevent1qqspudnm23jwgk8ncng78ff2jpfvllt9nl4c08muuap6x2srtfvyu7gzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg7algh4</id>
    
      <title type="html">**[Madblog: A Markdown Folder That Federates ...</title>
    
    <link rel="alternate" href="https://nostr.now/nevent1qqspudnm23jwgk8ncng78ff2jpfvllt9nl4c08muuap6x2srtfvyu7gzyremt0qh6jxw0sdtsf9uqdyg7tkm3fn5zfm3czrfy6arzaj4epuyg7algh4" />
    <content type="html">
      **[Madblog: A Markdown Folder That Federates Everywhere](&lt;a href=&#34;https://blog.fabiomanganiello.com/article/Madblog-federated-blogging-from-markdown&#34;&gt;https://blog.fabiomanganiello.com/article/Madblog-federated-blogging-from-markdown&lt;/a&gt; )**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**A lightweight blogging engine based on text files, with native Fediverse and IndieWeb support**&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;I started working on [Madblog](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt; ) a few years ago.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;I wanted a simple blogging platform that I could run from my own Markdown files. No intermediaries. No bloated UI. No JavaScript. No databases and migration scripts. No insecure plugins. Just a git folder, an Obsidian vault or a synchronized SyncThing directory, and the ability to create and modify content by simply writing text files, wherever I am.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Drop a Markdown file in the directory, and it&amp;#39;s live. Edit it, and the changes propagate. Delete it, and it&amp;#39;s gone.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;It&amp;#39;s been running my [personal blog](&lt;a href=&#34;https://blog.fabiomanganiello.com&#34;&gt;https://blog.fabiomanganiello.com&lt;/a&gt; ) and the [Platypush blog](&lt;a href=&#34;https://blog.platypush.tech&#34;&gt;https://blog.platypush.tech&lt;/a&gt; ) for a while now.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;With the new release, #madblog now gets a new superpower: it supports federation, interactions and comments both through:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://indieweb.org/Webmention&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Webmentions&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&#34;&gt;https://indieweb.org/Webmention&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Webmentions&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&lt;/a&gt;; - already implemented some weeks ago, you can also check out the &amp;lt;a href=&amp;#34;&lt;a href=&#34;https://blog.fabiomanganiello.com/article/webmentions-with-batteries-included&amp;#34;&amp;gt;previous&#34;&gt;https://blog.fabiomanganiello.com/article/webmentions-with-batteries-included&amp;#34;&amp;gt;previous&lt;/a&gt; blog article&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://indieweb.org/ActivityPub&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;ActivityPub&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://indieweb.org/ActivityPub&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;ActivityPub&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&lt;br/&gt;Webmentions allow your site to mention and be mentioned by other sites that also implement them - like any WordPress blog with the [Webmention plugin](&lt;a href=&#34;https://wordpress.org/plugins/webmention/&#34;&gt;https://wordpress.org/plugins/webmention/&lt;/a&gt; ), or link aggregators like [Lemmy](&lt;a href=&#34;https://join-lemmy.org/&#34;&gt;https://join-lemmy.org/&lt;/a&gt; ) or [HackerNews](&lt;a href=&#34;https://news.ycombinator.com/&#34;&gt;https://news.ycombinator.com/&lt;/a&gt; ). Interactions with any of your pages will be visible under them.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;#activitypub support allows Madblog to fully federate with Mastodon, Pleroma, Misskey, Friendica or any other #fediverse instance. It turns your blog into a federated handle that can be followed by anyone on the Fediverse. It gives you the ability to mention people on the Fediverse directly from your text files, and get replies to your articles directly from Mastodon, get your articles boosted, shared and quoted like any other Mastodon post.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Demos&lt;br/&gt;These blogs are powered by Madblog:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://blog.fabiomanganiello.com&amp;#34;&amp;gt;blog.fabiomanganiello.com&amp;lt;/a&amp;gt&#34;&gt;https://blog.fabiomanganiello.com&amp;#34;&amp;gt;blog.fabiomanganiello.com&amp;lt;/a&amp;gt&lt;/a&gt;; — Fediverse handle: &amp;lt;a class=&amp;#34;activitypub-mention&amp;#34; href=&amp;#34;&lt;a href=&#34;https://manganiello.blog/@fabio&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub17w6mc9753nnur2uzf0qrfz8jaku2vaqjwuwqs6fxhgchv4wg0pzqgh48ey&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Fabio Manganiello&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub17w6…48ey&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://manganiello.blog/@fabio&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub17w6mc9753nnur2uzf0qrfz8jaku2vaqjwuwqs6fxhgchv4wg0pzqgh48ey&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Fabio Manganiello&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub17w6…48ey&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://blog.platypush.tech&amp;#34;&amp;gt;blog.platypush.tech&amp;lt;/a&amp;gt&#34;&gt;https://blog.platypush.tech&amp;#34;&amp;gt;blog.platypush.tech&amp;lt;/a&amp;gt&lt;/a&gt;; — Fediverse handle: &amp;lt;a class=&amp;#34;activitypub-mention&amp;#34; href=&amp;#34;&lt;a href=&#34;https://platypush.tech/@blog&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1pqjg4gf95t2epy5emd89p0r54sj6fdufvxdhdj0ulq6r09644uhqkqal0g&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Platypush&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1pqj…al0g&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&#34;&gt;https://platypush.tech/@blog&amp;#34;&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub1pqjg4gf95t2epy5emd89p0r54sj6fdufvxdhdj0ulq6r09644uhqkqal0g&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Platypush&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub1pqj…al0g&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt&lt;/a&gt;;&lt;br/&gt;&lt;br/&gt;You can follow them from Mastodon (or any other Fediverse client), reply to articles directly from your instance, boost them, or quote them. You can also interact via [Webmentions](&lt;a href=&#34;https://indieweb.org/Webmention&#34;&gt;https://indieweb.org/Webmention&lt;/a&gt; ): link to an article from your own site, and if your site supports Webmentions, the mention will show up as a response on the original post. These blogs also have a [Guestbook](&lt;a href=&#34;https://blog.fabiomanganiello.com/guestbook&#34;&gt;https://blog.fabiomanganiello.com/guestbook&lt;/a&gt; ) — mention the blog&amp;#39;s Fediverse handle or send a Webmention to the home page, and your message appears on a public guest registry.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;How Does It Compare?&lt;br/&gt;If you&amp;#39;ve looked into federated blogging before, you&amp;#39;ve likely come across a few options:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://writefreely.org/&amp;#34;&amp;gt;WriteFreely&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&#34;&gt;https://writefreely.org/&amp;#34;&amp;gt;WriteFreely&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&lt;/a&gt;; is probably the closest alternative — a minimalist, Go-based platform with ActivityPub support. It&amp;#39;s well-designed, but it uses a database (SQLite or MySQL), has its own (very minimal) editor, and doesn&amp;#39;t support Webmentions. Additionally, it lacks many features that are deal-breakers for me.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;No export of all the content to Markdown, nor ability to run my blog from my Nextcloud Notes folder or Obsidian vault.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;No support for LaTeX or Mermaid diagrams.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;No support for federated interactions - any interaction with your articles on the Fediverse is simply lost.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;The UI is minimalist and not necessarily bad, but not even sufficiently curated for something like a blog (narrow width, Serif fonts not optimized for legibility, the settings and admin panels are a mess...).&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;No support for moderation / content blocking.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&lt;br/&gt;&amp;lt;p&amp;gt;No support for federated hashtags.&amp;lt;/p&amp;gt;&lt;br/&gt;&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&lt;br/&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://wordpress.org/&amp;#34;&amp;gt;WordPress&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&#34;&gt;https://wordpress.org/&amp;#34;&amp;gt;WordPress&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&lt;/a&gt;; with ActivityPub and Webmention plugins can technically do what Madblog does, but it&amp;#39;s a full CMS with a database, a theme engine, a plugin ecosystem, and a much larger attack surface. If all you need is a blog, it&amp;#39;s overkill.&amp;lt;/p&amp;gt;&lt;br/&gt;&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&lt;br/&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://joinplu.me/&amp;#34;&amp;gt;Plume&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&#34;&gt;https://joinplu.me/&amp;#34;&amp;gt;Plume&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&lt;/a&gt;; and &amp;lt;strong&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://friendi.ca/&amp;#34;&amp;gt;Friendica&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&#34;&gt;https://friendi.ca/&amp;#34;&amp;gt;Friendica&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt&lt;/a&gt;; offer blogging with federation, but they&amp;#39;re full social platforms, not&lt;br/&gt;  lightweight publishing tools.&amp;lt;/p&amp;gt;&lt;br/&gt;&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;Madblog sits in a different niche: it&amp;#39;s closer to a static-site generator that happens to speak federation protocols. It implements a workflow like &amp;#34;write Markdown, push to server, syndicate everywhere&amp;#34;.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Getting Started&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Docker Quickstart&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;mkdir&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-p&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;~/madblog/markdown&lt;br/&gt;cat&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;&amp;amp;lt;&amp;amp;lt;EOF &amp;amp;gt; ~/madblog/markdown/hello-world.md&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;# Hello, Fediverse!&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;This is my first post on [Madblog](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt;)!&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;EOF&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;run&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-it&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-p&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;m&amp;#34;&amp;gt;8000&amp;lt;/span&amp;gt;:8000&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-v&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;$HOME&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;/madblog:/data&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;quay.io/blacklight/madblog&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Open &lt;a href=&#34;http://localhost:8000&#34;&gt;http://localhost:8000&lt;/a&gt;. That&amp;#39;s it — you have a blog.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;The default Docker image (quay.io/blacklight/madblog) is a minimal build (&amp;lt; 100 MB) that includes everything except LaTeX and Mermaid rendering. If you need those, build the full image from source:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;git&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;clone&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;cd&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;madblog&lt;br/&gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;build&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-f&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;docker/full.Dockerfile&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-t&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;madblog&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;.&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;See the [full Docker documentation](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog/src/branch/main/README.md#running-madblog-in-docker&#34;&gt;https://git.fabiomanganiello.com/madblog/src/branch/main/README.md#running-madblog-in-docker&lt;/a&gt; ) for details on mounting config files and ActivityPub keys.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Markdown structure&lt;br/&gt;Since there&amp;#39;s no database or extra state files involved, the metadata of your articles is also stored in Markdown.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Some things (like title, description) can be inferred from the file name, headers of your files etc., creation date defaults to the creation timestamp of the file and author and language are inherited from your config.yaml.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;A full customized header would look like this:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (title: Title of the article)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (description: Short description of the content)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (image: /img/some-header-image.png)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (author: Author Name &amp;amp;lt;&lt;a href=&#34;https://author.me&amp;amp;gt&#34;&gt;https://author.me&amp;amp;gt&lt;/a&gt;;)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (author_photo:  &lt;img src=&#34;https://author.me/avatar.png&#34;&gt; )&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (language: en-US)&amp;lt;/span&amp;gt;&lt;br/&gt; [&amp;lt;span class=&amp;#34;nl&amp;#34;&amp;gt;//&amp;lt;/span&amp;gt;]: &amp;lt;span class=&amp;#34;na&amp;#34;&amp;gt;# (published: 2022-01-01)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;...your Markdown content...&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Key Configuration&lt;br/&gt;Madblog reads configuration from a config.yaml in your content directory. Every option is also available as an environment variable with a MADBLOG_ prefix — handy for Docker or CI setups.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;A minimal config to get started:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;title&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;My Blog&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;description&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;Thoughts on tech and life&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;link&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;&lt;a href=&#34;https://myblog.example.com&amp;lt;/span&amp;gt&#34;&gt;https://myblog.example.com&amp;lt;/span&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;author&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;Your Name&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Or purely via environment variables:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;docker&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;run&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;-it&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-p&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;m&amp;#34;&amp;gt;8000&amp;lt;/span&amp;gt;:8000&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-e&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;MADBLOG_TITLE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;My Blog&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-e&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;MADBLOG_LINK&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://myblog.example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://myblog.example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-e&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;MADBLOG_AUTHOR&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;Your Name&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;-v&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nv&amp;#34;&amp;gt;$HOME&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;/madblog:/data&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;quay.io/blacklight/madblog&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;See [config.example.yaml](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog/src/branch/main/config.example.yaml&#34;&gt;https://git.fabiomanganiello.com/madblog/src/branch/main/config.example.yaml&lt;/a&gt; ) for the full reference.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;gt; &lt;br/&gt;&amp;gt; It is advised to keep all of your Markdown content under &amp;amp;lt;data-dir&amp;amp;gt;/markdown&amp;gt; ,&lt;br/&gt;especially if you enable federation, in order to keep the Markdown folder&lt;br/&gt;tidy from all the auxiliary files generated by Madblog.&lt;br/&gt;&lt;br/&gt;&amp;gt; &lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Webmentions&lt;br/&gt;[Webmentions](&lt;a href=&#34;https://indieweb.org/Webmention&#34;&gt;https://indieweb.org/Webmention&lt;/a&gt; ) are the IndieWeb&amp;#39;s answer to trackbacks and pingbacks — a W3C standard that lets websites notify each other when they link to one another. Madblog supports them natively, both inbound and outbound.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;When someone links to one of your articles from a Webmention-capable site, your blog receives a notification and renders the mention as a response on the article page. Going the other way, when you link to an external URL in your Markdown and save the file, Madblog automatically discovers the target&amp;#39;s Webmention endpoint and sends a notification — no manual step required. All mentions are stored as Markdown files under your content directory (mentions/incoming/&amp;amp;lt;post-slug&amp;amp;gt;/), so they&amp;#39;re version-controllable and easy to inspect.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;You can enable pending-mode for moderation (webmentions_default_status: pending), or use the blocklist/allowlist system to filter sources by domain, URL, or regex. Webmentions are enabled by default — if you&amp;#39;re running Madblog locally for testing, set enable_webmentions: false to avoid sending real notifications to external sites.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;ActivityPub Federation&lt;br/&gt;[ActivityPub](&lt;a href=&#34;https://www.w3.org/TR/activitypub/&#34;&gt;https://www.w3.org/TR/activitypub/&lt;/a&gt; ) is the protocol that powers the Fediverse — Mastodon, Pleroma, Misskey, and hundreds of other platforms. Madblog implements it as a first-class feature: enable it, and your blog becomes a Fediverse actor that people can follow, reply to, boost, and quote.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Enable it in your config:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;enable_activitypub&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;activitypub_username&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;blog&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;activitypub_private_key_path&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;/path/to/private_key.pem&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Madblog will generate an RSA keypair on first start if you don&amp;#39;t provide one. Once enabled, your blog gets a Fediverse handle (@blog@yourdomain.com), a WebFinger endpoint for discovery, and a full ActivityPub actor profile. New and updated articles are automatically delivered to all followers&amp;#39; timelines.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Here&amp;#39;s what federation looks like in practice:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Receiving mentions&amp;lt;/strong&amp;gt;: when someone mentions your blog&amp;#39;s Fediverse handle in a public post (not as a reply to a specific article), the mention shows up on your &amp;lt;a href=&amp;#34;#guestbook&amp;#34;&amp;gt;Guestbook&amp;lt;/a&amp;gt; page.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Receiving replies, likes, boosts, and quotes&amp;lt;/strong&amp;gt;: interactions targeting a specific article are rendered below that article — replies as threaded comments, likes/boosts/quotes as counters and cards. All stored as JSON files on disk.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Sending mentions&amp;lt;/strong&amp;gt;: just write the fully-qualified handle in your Markdown (&amp;lt;code&amp;gt;&lt;span itemprop=&#34;mentions&#34; itemscope itemtype=&#34;https://schema.org/Person&#34;&gt;&lt;a itemprop=&#34;url&#34; href=&#34;/npub19tr3lx4j0mz92jge72txdmv3ksjjsfu57mupmpx33mq6hrwk925qxtplwd&#34; class=&#34;bg-lavender dark:prose:text-neutral-50 dark:text-neutral-50 dark:bg-garnet px-1&#34;&gt;&lt;span&gt;Alice&lt;/span&gt; (&lt;span class=&#34;italic&#34;&gt;npub19tr…plwd&lt;/span&gt;)&lt;/a&gt;&lt;/span&gt;&amp;lt;/code&amp;gt;) and save the file. Madblog resolves the actor via WebFinger and delivers a proper ActivityPub &amp;lt;code&amp;gt;Mention&amp;lt;/code&amp;gt; tag — the mentioned user gets a notification on their instance.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Federated hashtags&amp;lt;/strong&amp;gt;: hashtags in your articles (&amp;lt;code&amp;gt;#Python&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#Fediverse&amp;lt;/code&amp;gt;) are included as ActivityPub &amp;lt;code&amp;gt;Hashtag&amp;lt;/code&amp;gt; tags in the published object. Followers who track those hashtags on their instance will see your posts in their filtered feeds.&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Custom profile fields&amp;lt;/strong&amp;gt;: configure additional profile metadata (verified links, donation pages, git repos) that show up on your actor&amp;#39;s profile as seen from Mastodon and other Fediverse clients:&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;yaml&lt;br/&gt;  activitypub_profile_fields:&lt;br/&gt;    Git repository: &amp;amp;lt;&lt;a href=&#34;https://git.example.com/myblog&amp;amp;gt&#34;&gt;https://git.example.com/myblog&amp;amp;gt&lt;/a&gt;;&lt;br/&gt;    Donate: &amp;amp;lt;&lt;a href=&#34;https://liberapay.com/myprofile&amp;amp;gt&#34;&gt;https://liberapay.com/myprofile&amp;amp;gt&lt;/a&gt;;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;The federation layer also exposes a read-only subset of the [Mastodon API](&lt;a href=&#34;https://docs.joinmastodon.org/methods/&#34;&gt;https://docs.joinmastodon.org/methods/&lt;/a&gt; ), so Mastodon-compatible clients and crawlers can discover the instance, look up the actor, list published statuses, and search for content — with no extra configuration.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Madblog also supports advanced ActivityPub features like split-domain setups (e.g. your blog at blog.example.com but your Fediverse handle at @blog@example.com), configurable object types (Note for inline rendering on Mastodon vs. Article for link-card previews), and quote policies ([FEP-044f](&lt;a href=&#34;https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md&#34;&gt;https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md&lt;/a&gt; ), so Mastodon users can quote your articles too).&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;LaTeX and Mermaid&lt;br/&gt;Madblog supports server-side rendering of LaTeX equations and Mermaid diagrams directly in your Markdown files — no client-side JavaScript required.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**LaTeX** uses latex &#43; dvipng under the hood. Inline expressions use conventional LaTeX markers:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;The Pythagorean theorem states that \(c^2 = a^2 &#43; b^2\).&lt;br/&gt;&lt;br/&gt;$$&lt;br/&gt;E = mc^2&lt;br/&gt;$$&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;**Mermaid** diagrams use standard fenced code blocks. Both light and dark theme variants are rendered at build time and switch automatically based on the reader&amp;#39;s color scheme:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt; ```mermaid&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt; graph LR&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;     A[Write Markdown] --&amp;amp;gt; B[Madblog renders it]&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;     B --&amp;amp;gt; C[Fediverse sees it]&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt; ```&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Install Mermaid support with pip install &amp;#34;madblog[mermaid]&amp;#34; or use the full Docker image. Rendered output is cached, so only the first render of each block is slow.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Tags and Categories&lt;br/&gt;Tag your articles with hashtags — either in the metadata header or directly in the body text:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;gh&amp;#34;&amp;gt;# My Article&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;This post is about &amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;#Python&amp;lt;/span&amp;gt; and the &amp;lt;span class=&amp;#34;ni&amp;#34;&amp;gt;#Fediverse&amp;lt;/span&amp;gt;.&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Madblog builds a tag index at /tags, with per-tag pages at /tags/&amp;amp;lt;tag&amp;amp;gt;. Hashtags from incoming Webmentions are also indexed. Folders in your content directory act as categories — if you organize files into subdirectories, the home page groups articles by folder.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Feed Syndication&lt;br/&gt;Madblog generates both RSS and Atom feeds at /feed.rss and /feed.atom. You can control whether feeds include full article content or just descriptions (short_feed: true), and limit the number of entries (max_entries_per_feed: 10). limit and offset parameters are also supported for pagination.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Aggregator Mode&lt;br/&gt;Madblog can also pull in external RSS/Atom feeds and render them alongside your own posts on the home page — useful for affiliated blogs, or even as a self-hosted feed reader:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;external_feeds&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p p-Indicator&amp;#34;&amp;gt;-&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;&lt;a href=&#34;https://friendsblog.example.com/feed.atom&amp;lt;/span&amp;gt&#34;&gt;https://friendsblog.example.com/feed.atom&amp;lt;/span&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p p-Indicator&amp;#34;&amp;gt;-&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;&lt;a href=&#34;https://colleaguesblog.example.com/feed.atom&amp;lt;/span&amp;gt&#34;&gt;https://colleaguesblog.example.com/feed.atom&amp;lt;/span&amp;gt&lt;/a&gt;;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Guestbook&lt;br/&gt;The guestbook (/guestbook) is a dedicated page that aggregates public interactions — Webmentions targeting the home page and Fediverse mentions of your blog actor that aren&amp;#39;t replies to specific articles. Think of it as a public guest registry, or a lo-fi comment section for your blog as a whole. Visitors can leave a message by mentioning your Fediverse handle or sending a Webmention. It can be disabled via enable_guestbook=0.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;View Modes&lt;br/&gt;The home page supports three layouts:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;cards&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt; (default) — a responsive grid of article cards with images&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt; — a compact list with titles and dates&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;code&amp;gt;full&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt; — a scrollable, WordPress-like view with full article content inline&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;Set it in your config (view_mode: cards) or override at runtime with ?view=list.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Moderation&lt;br/&gt;Madblog ships with a flexible moderation system that applies to both Webmentions and ActivityPub interactions. You can run in **blocklist mode** (reject specific actors) or **allowlist mode** (accept only specific actors), with pattern matching by domain, URL, ActivityPub handle, or regex:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;blocked_actors&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p p-Indicator&amp;#34;&amp;gt;-&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;spammer.example.com&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p p-Indicator&amp;#34;&amp;gt;-&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s&amp;#34;&amp;gt;&amp;#34;@troll@evil.social&amp;#34;&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;  &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p p-Indicator&amp;#34;&amp;gt;-&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;/spam-ring\.example\..*/&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Moderation rules also apply retroactively — interactions already stored are filtered at render time. Blocked ActivityPub followers are excluded from fan-out delivery and hidden from the public follower count, but their records are preserved so they can be automatically reinstated if you change your rules.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Email Notifications&lt;br/&gt;Configure SMTP settings and Madblog will notify you by email whenever a new Webmention or ActivityPub interaction arrives — likes, boosts, replies, mentions, and quotes:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;author_email&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;you@example.com&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;smtp_server&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;smtp.example.com&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;smtp_username&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;you@example.com&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;smtp_password&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l l-Scalar l-Scalar-Plain&amp;#34;&amp;gt;your-password&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Progressive Web App&lt;br/&gt;Madblog is installable as a PWA, with offline access and a native-like experience on supported devices. A service worker handles stale-while-revalidate caching with background sync for retries.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Raw Markdown Access&lt;br/&gt;Append .md to any article URL to get the raw Markdown source:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&lt;a href=&#34;https://myblog.example.com/article/my-post.md&#34;&gt;https://myblog.example.com/article/my-post.md&lt;/a&gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Useful for readers who prefer plain text, or for tools that consume Markdown directly.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Reusable Libraries&lt;br/&gt;Two key subsystems of Madblog have been extracted into standalone, reusable Python libraries. If you&amp;#39;re building a Python web application and want to add decentralized federation support, you can use them directly — no need to adopt Madblog itself.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Webmentions&lt;br/&gt;[Webmentions](&lt;a href=&#34;https://git.fabiomanganiello.com/webmentions&#34;&gt;https://git.fabiomanganiello.com/webmentions&lt;/a&gt; ) is a general-purpose Python library for sending and receiving Webmentions. It comes with framework adapters for FastAPI, Flask, and Tornado, pluggable storage backends (SQLAlchemy or custom), filesystem monitoring for auto-sending mentions when files change, full microformats2 parsing, and built-in HTML rendering for displaying mentions on your pages.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Adding Webmentions to a FastAPI app:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;fastapi&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;FastAPI&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;webmentions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;WebmentionsHandler&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;webmentions.storage.adapters.db&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;init_db_storage&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;webmentions.server.adapters.fastapi&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_webmentions&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;app&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;FastAPI&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;init_db_storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;engine&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;sqlite:////tmp/webmentions.db&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;WebmentionsHandler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;base_url&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_webmentions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;app&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;That&amp;#39;s it — your app now has a /webmentions endpoint for receiving mentions,&lt;br/&gt;a Link header advertising it on every response, and a storage layer for&lt;br/&gt;persisting them. See the&lt;br/&gt;[full documentation](&lt;a href=&#34;https://git.fabiomanganiello.com/webmentions/src/branch/main/README.md&#34;&gt;https://git.fabiomanganiello.com/webmentions/src/branch/main/README.md&lt;/a&gt; )&lt;br/&gt;for details on sending mentions, custom storage, moderation, and rendering.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;### &lt;br/&gt;&lt;br/&gt;Pubby&lt;br/&gt;[Pubby](&lt;a href=&#34;https://git.fabiomanganiello.com/pubby&#34;&gt;https://git.fabiomanganiello.com/pubby&lt;/a&gt; ) is a general-purpose Python library for adding ActivityPub federation to any web application. It handles inbox processing, outbox delivery with concurrent fan-out, HTTP Signatures, WebFinger/NodeInfo discovery, interaction storage, a Mastodon-compatible API, and framework adapters for FastAPI, Flask, and Tornado.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Adding ActivityPub to a FastAPI app:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;fastapi&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;FastAPI&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;pubby&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;ActivityPubHandler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;Object&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;pubby.crypto&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;generate_rsa_keypair&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;pubby.storage.adapters.db&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;init_db_storage&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;pubby.server.adapters.fastapi&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_activitypub&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;app&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;FastAPI&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;init_db_storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;sqlite:////tmp/pubby.db&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;private_key&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;generate_rsa_keypair&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;ActivityPubHandler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;storage&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;actor_config&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&lt;br/&gt;        &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;base_url&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://example.com&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;        &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;username&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;blog&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;        &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;name&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;My Blog&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;        &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;summary&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;A blog with ActivityPub support&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;},&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;private_key&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;private_key&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_activitypub&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;app&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;# Publish a post to all followers&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;publish_object&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;Object&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://example.com/posts/hello&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://example.com/posts/hello&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;Note&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;content&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&amp;amp;lt;p&amp;amp;gt;Hello from the Fediverse!&amp;amp;lt;/p&amp;amp;gt;&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;url&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://example.com/posts/hello&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://example.com/posts/hello&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;attributed_to&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;&lt;a href=&#34;https://example.com/ap/actor&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&#34;&gt;https://example.com/ap/actor&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span&lt;/a&gt; class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;))&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Optionally, you can also expose a Mastodon-compatible API so that Mastodon clients and crawlers can discover your instance and browse statuses:&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nn&amp;#34;&amp;gt;pubby.server.adapters.fastapi_mastodon&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kn&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_mastodon_api&amp;lt;/span&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;bind_mastodon_api&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;app&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;handler&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;title&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;My Blog&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;    &amp;lt;span class=&amp;#34;n&amp;#34;&amp;gt;description&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;#34;A blog with ActivityPub support&amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&lt;br/&gt;&amp;lt;/code&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Both libraries follow the same design philosophy: provide the protocol plumbing so you can wire it into your existing application with minimal ceremony. Storage is pluggable (SQLAlchemy, file-based, or bring-your-own), framework binding is a single function call, and the core logic is framework-agnostic. See the full documentation for [Pubby](&lt;a href=&#34;https://git.fabiomanganiello.com/pubby/src/branch/main/README.md&#34;&gt;https://git.fabiomanganiello.com/pubby/src/branch/main/README.md&lt;/a&gt; ) and [Webmentions](&lt;a href=&#34;https://git.fabiomanganiello.com/webmentions/src/branch/main/README.md&#34;&gt;https://git.fabiomanganiello.com/webmentions/src/branch/main/README.md&lt;/a&gt; ).&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;## &lt;br/&gt;&lt;br/&gt;Links&lt;br/&gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Madblog&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&#34;&gt;https://git.fabiomanganiello.com/madblog&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Madblog&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&lt;/a&gt;; (&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/madblog&amp;#34;&amp;gt;Github&#34;&gt;https://github.com/blacklight/madblog&amp;#34;&amp;gt;Github&lt;/a&gt; mirror&amp;lt;/a&amp;gt;)&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://git.fabiomanganiello.com/webmentions&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Webmentions&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&#34;&gt;https://git.fabiomanganiello.com/webmentions&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Webmentions&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&lt;/a&gt;; (&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/webmentions&amp;#34;&amp;gt;Github&#34;&gt;https://github.com/blacklight/webmentions&amp;#34;&amp;gt;Github&lt;/a&gt; mirror&amp;lt;/a&amp;gt;)&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://git.fabiomanganiello.com/pubby&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Pubby&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&#34;&gt;https://git.fabiomanganiello.com/pubby&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Pubby&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt&lt;/a&gt;; (&amp;lt;a href=&amp;#34;&lt;a href=&#34;https://github.com/blacklight/pubby&amp;#34;&amp;gt;Github&#34;&gt;https://github.com/blacklight/pubby&amp;#34;&amp;gt;Github&lt;/a&gt; mirror&amp;lt;/a&amp;gt;)&amp;lt;/li&amp;gt;&lt;br/&gt;&lt;br/&gt;Madblog is open-source under the AGPL-3.0-only license. The source code, issue tracker, and full documentation are available at [git.fabiomanganiello.com/madblog](&lt;a href=&#34;https://git.fabiomanganiello.com/madblog&#34;&gt;https://git.fabiomanganiello.com/madblog&lt;/a&gt; ).
    </content>
    <updated>2026-03-10T14:53:36Z</updated>
  </entry>

</feed>