jekyll-activitypub-static
A Jekyll plugin that generates a static ActivityPoll feed, the read-only polling subset of ActivityPub defined by FEP-b06c.
Table of Contents
- Install
- Usage
- Configuration
- Articles and Notes
- Layouts
- Generated Files
- GitHub Pages
- Example Site
- Contributing
- License
Install
Add this to the Gemfile of your Jekyll site:
gem "jekyll-activitypub-static"
To build from source:
gem build jekyll-activitypub-static.gemspec
gem install ./jekyll-activitypub-static-*.gem
Add the plugin to the _config.yml for your site:
plugins:
- jekyll-activitypub-static
Usage
Build your site to generate the ActivityPub files:
bundle exec jekyll build
To build the site and serve it locally:
bundle exec jekyll serve
Configuration
The plugin uses your site's standard url, author, and description
settings, plus the activitypub configuration block.
url: "https://example.com"
author: "Example Author"
description: "A short description of the site"
activitypub:
output_path: "activitypub"
preferred_username: "example"
summary_property: "description"
note_max_characters: 500
update_interval: "P1D"
webfinger_output_file: ".well-known/webfinger"
Available activitypub options:
output_path: directory for generated ActivityPub files. Defaults toactivitypub.preferred_username: username for the generated actor and WebFinger document. Defaults to the host name fromurl. The WebFinger account ID ispreferred_usernameat the domain fromurl, like[email protected].summary_property: post front matter property to use for generated Article summaries. Defaults todescription; when the property is missing or empty, the plugin uses the rendered post excerpt.note_max_characters: maximum plain-text character count for generated Note objects. Defaults to500.update_interval: ActivityPoll polling interval for the generated Actor object, as an ISO 8601 duration. Defaults toP1D.webfinger_output_file: path for the generated WebFinger document. Defaults to.well-known/webfinger. GitHub Pages sites can set this to.well-known/webfinger/index.jsonas a workaround for its content type.
Articles and Notes
Posts are generated as Activity Streams Article objects by default.
Short, untitled posts without an explicit summary are generated as
Activity Streams Note objects. Note is the native Activity Streams object
type used by Mastodon for statuses. To make a post eligible for Note
generation, set an explicit blank title:
---
title: ""
---
Jekyll generates titles from post filenames when title is omitted, so an
omitted title is treated as a normal title. A blank-titled post is generated as
a Note when it has no configured summary property, has one rendered paragraph,
and its plain-text content is no longer than activitypub.note_max_characters.
Blank-titled posts may need layout support on home pages, archive pages, or post lists. Use the excerpt, date, or another fallback when displaying links to untitled posts.
Layouts
To link from HTML pages to their ActivityPub representation, add this to the
<head> element of your layouts:
{% if page.activitypub_url %}
<link rel="alternate" type="application/activity+json" href="{{ page.activitypub_url }}">
{% endif %}
The plugin sets page.activitypub_url before rendering posts and the site
index. For posts, the URL points to the generated JSON-LD Article or Note
object. For the site index, the URL points to the generated Actor object.
Generated Files
The plugin generates these static files:
actor.jsonld.well-known/webfingeractivitypub/inbox.jsonldactivitypub/outbox.jsonldactivitypub/outbox/page-*.jsonldactivitypub/posts/*.jsonldactivitypub/activities/create-*.jsonld
The activitypub path changes when activitypub.output_path is configured.
WebFinger
The WebFinger document is generated at .well-known/webfinger by default and
uses an account ID in the form preferred_username at the site domain. For
example, with url: "https://example.com" and preferred_username: "evan",
the account ID is [email protected].
The output path can be configured with activitypub.webfinger_output_file.
For example, GitHub Pages sites can use the following workaround to serve the
document through an index.json file:
activitypub:
webfinger_output_file: ".well-known/webfinger/index.json"
GitHub Pages redirects requests for .well-known/webfinger to
.well-known/webfinger/, where it serves index.json.
WebFinger discovery only works when the generated site is served from the root
of its domain, because clients request https://example.com/.well-known/webfinger.
Sites served from a subdirectory cannot provide a domain-level WebFinger
endpoint with this plugin alone.
GitHub Pages
GitHub Pages' built-in Jekyll build does not support this plugin because it is not on GitHub's allowlist. Configure a GitHub Actions workflow to build the site instead.
The .well-known directory can be dropped when uploading the resulting site to GitHub Pages. To work around this, set include-hidden-files: true on the actions/upload-pages-artifact step in the GitHub Actions workflow:
- uses: actions/upload-pages-artifact@v5
with:
path: _site
include-hidden-files: true
Configure WebFinger to use an index.json file:
activitypub:
webfinger_output_file: ".well-known/webfinger/index.json"
The index.json path works around GitHub Pages serving the extensionless
.well-known/webfinger file with an application/octet-stream content type.
Example Site
The example-site directory has a minimal example site (thus the name). You can build and run it with these commands:
cd example-site
bundle install
bundle exec jekyll build
bundle exec jekyll serve
This will build a site that runs on http://localhost:4000/
Viewing from the Fediverse
Support for interacting with static accounts on the Fediverse is still experimental; most software will not allow full interactions.
With Mastodon 4.x (the current version) you can search for the account by Webfinger ID (username@domain), and then load the account. It should show the profile information.
With Webfinger Browser, you can search for the account by Webfinger ID, and view the account. It should show the profile information and all the generated content.
It's still definitely worthwhile to keep this plugin enabled; it gives an incentive for Fediverse software developers to add support for static accounts.
Contributing
PRs accepted.
License
LGPL-3.0-or-later (c) 2025 Social Web Foundation