Drive an activitypub Actor via an RSS feed
  • F# 90.3%
  • Nix 9.7%
Find a file
2026-04-10 14:29:14 +02:00
.editorconfig Initial commit of fediverss 2026-04-10 13:06:55 +02:00
.envrc Initial commit of fediverss 2026-04-10 13:06:55 +02:00
.gitignore Initial commit of fediverss 2026-04-10 13:06:55 +02:00
fediverss.fsproj Initial commit of fediverss 2026-04-10 13:06:55 +02:00
flake.lock Initial commit of fediverss 2026-04-10 13:06:55 +02:00
flake.nix Initial commit of fediverss 2026-04-10 13:06:55 +02:00
LICENSE README, LICENSE, more config options 2026-04-10 13:51:10 +02:00
Program.fs README, LICENSE, more config options 2026-04-10 13:51:10 +02:00
README.md Update README with html of replies 2026-04-10 14:29:14 +02:00
shell.nix Initial commit of fediverss 2026-04-10 13:06:55 +02:00

fediverss is an application designed to run on the same host/domain as an existing static website with an RSS feed.

At the moment, there is no official release of fediverss so you will need to build it from source. If you have dotnet installed (on your system, or via the flake.nix file provided) running the command dotnet publish --self-contained true ./fediverss.fsproj in the root directory will create a bin/Release/net9.0/<architecture>/publish/ directory with everything you need to run fediverss even on machines that do not have dotnet installed.

When running, fediverss does two things:

  • it regularly reads the rss file it has been configured to watch, and posts articles to an activitypub server via the client-to-server API as they appear
  • it runs a miniwebserver that responds to requests at /<configured prefix>/<anyurl> and responds with the html representing any replies that have been made to the activitypub activity created for the activitypub Article it created for the rss item with url <anyurl>

ActivityPub Article creation

For this to work correctly, you currently have to configure fediverss with the details required for your activitypub server (Note: fediverss has only been tested against Bonfire so far).

I recommend creating a specific user to post the blog articles, but the process will also work for an existing user if you want to mix manual posts and blog articles on the same account.

First, create an OAuth application on your server and run through the process until you have a valid refresh token. For Bonfire, see the instructions here: https://docs.bonfirenetworks.org/authentication.html

(Note: at the moment, fediverss cannot automatically handle the OAuth flow; you will need to set the callback url the something like http://localhost/; when Bonfire tries to go to that url the request will fail but you will be able to copy the temporary code query parameter of the request and use it to request a long term token using the curl instructions in the link above. This is one reason why fediverss is still very much pre-release.)

In the working directory where you intend to run fediverss, create a private_state.json file containing the following:

{"RefreshToken":"<the refresh token you just created","UrlMap":{}}

This file will hold the mapping between rss post urls and activitypub article urls. I suggest backing up this file regularly.

Then, you must provide the required permanent configuration fields via environment variables, in an appsettings.json file in the working directory, or as command line arguments.

The required fields are:

OAuthClientId=<from oauth process above>
OAuthClientSecret=<from oauth process above>
ActorId=<url of the user authenticated above>
FollowerCollection=<normally actor url /followers>
OutboxCollection=<normally actor url /outbox>
TokenUrl=<normally https://your_server.com/oauth/token>
BlogAddress=<the domain of your blog, i.e. https://example.com/ not https://example.com/blog/>
RssLocation=<local file path of the rss file to be watched>

If you were to now run fediverss, you should quickly begin to see your rss items appearing as articles on your activitypub server.

Showing replies

Here you will need to configure the webserver showing your blog to "reverse proxy" requests to your configured prefix towards fediverss.

This is easiest if you're running fediverss on the same machine hosting your static blog, especially because currently fediverss will only watch rss files on disk. This restriction should be lifted in future versions.

As an example, we can configure fediverss with the Caddy webserver serving a static blog.

Set fediverss to run on localhost with a prefix of pub by setting the following config (either in appsettings.json or as environment variables):

PubUrl=http://localhost:5002/
PubPrefix=pub

Hitting http://localhost:5002/pub/ on the local machine should return an empty response.

Next, configure Caddy to proxy requests that start with /pub/ to fediverss. Your caddy config should end up looking something like this:

example.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}

	reverse_proxy /pub/* localhost:5002 
	file_server {
		root /var/www/my_blog_files
	}
}

Now, if you run fediverss and it has created an activity for an item in your rss feed you should be able to navigate to https://example.com/pub/<path to item> and fediverss will return the replies that have been made to the activity.

To show the replies on the page, you can use some custom javascript or a library such as htmx. For example, on my own blog my post template includes loading htmx in the head of the html and then adds the following static html to the end of the article:

<div class="comments" hx-get="/pub" hx-swap="innerHTML" hx-on::before-request="this.setAttribute('hx-get', window.location.origin + '/pub' + window.location.pathname); htmx.process(this); this.removeAttribute('hx-on::before-request'); event.preventDefault();" hx-trigger="load delay:100ms, every 60s" ></div>

This little dance triggers htmx shortly after the page finishes loading, and dynamically replaces hx-get with the path to the replies for the current url. Then, every 60 seconds it requests an updated version of the replies, replacing the old version.

The html you get back has a few classes attached, and will look something like:

<h2>Comments</h2>
<p class="comment-instructions">
  You can leave comments via any federated activitypub enabled service (such as Mastodon, GoToSocial, or Bonfire) by searching for the post with the following url and leaving a comment - it will appear here as well: https://example.com/pub/objects/01KNQ0R4Z0YKBNEQDN4EFYJY55 (<a onclick="navigator.clipboard.writeText('https://example.com/pub/objects/01KNQ0R4Z0YKBNEQDN4EFYJY55').then(() =&gt; alert('Copied'))">copy to clipboard</a>)</p>
  <div class="box" id="https://example.com/pub/objects/01KNQ45RHDEXWPC2A5TGRD7JN8">
    <p class="author"><a href="https://example.com/pub/actors/mavnn">mavnn</a> (Michael Newton)</p>
    <p><a href="https://example.com/character/blog" rel="nofollow noopener">@blog</a> Hi!</p>
    <div class="replies" id="replies-https://example.com/pub/objects/01KNQ45RHDEXWPC2A5TGRD7JN8">
  </div>
</div>