Skip to content

vrypan/listnr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

listnr

listnr gives a static blog a Fediverse presence.

It runs as a single ActivityPub actor, polls the blog's RSS/Atom feed for new posts, announces those posts to followers, receives replies/likes/boosts, and exposes a small API that a static blog can use to render Fediverse interactions.

The blog itself stays static. listnr runs separately, usually on a VPS behind TLS.

Build

CGO_ENABLED=0 go build -o listnr .

For a Linux VPS build from macOS:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o listnr .

To inject a version string:

go build -ldflags "-X github.com/vrypan/listnr/cmd.Version=$(git describe --tags --always --dirty)" -o listnr .

Configure

Create listnr.toml:

[actor]
username = "blog"
domain   = "vrypan.net"
host     = "ap.vrypan.net"
type     = "Service"
name     = "vrypan.net blog"
summary  = "Posts from vrypan.net"
icon     = "https://blog.vrypan.net/avatar.png"
header   = "https://blog.vrypan.net/header.jpg"
blog_url = "https://blog.vrypan.net"
also_known_as = ["https://mastodon.example/@vrypan"]

[[actor.fields]]
name  = "Website"
value = "<a href=\"https://blog.vrypan.net\" rel=\"me\">blog.vrypan.net</a>"

[[actor.fields]]
name  = "RSS"
value = "<a href=\"https://blog.vrypan.net/index.xml\">Feed</a>"

[[actor.tags]]
name = "#blogging"
href = "https://mastodon.social/tags/blogging"

[actor.extra]
discoverable = true
indexable = true
manuallyApprovesFollowers = false

[feed]
url           = "https://blog.vrypan.net/index.xml"
poll_interval = "5m"
backfill      = 20

[server]
listen       = "127.0.0.1:8420"
data_dir     = "/var/lib/listnr"
log_requests = false

[admin]
token = "change-me"

Important fields:

  • actor.domain is the handle domain. With the example above, the actor is @blog@vrypan.net.
  • actor.host is where listnr is served. With the example above, ActivityPub endpoints live under https://ap.vrypan.net.
  • actor.type controls the ActivityPub actor type. Use Service to signal an automated account to Mastodon-compatible servers; the default is Person.
  • actor.icon is the profile avatar. actor.header is the optional profile banner/header image.
  • actor.fields are Mastodon-style profile fields rendered as PropertyValue attachments. value may contain HTML.
  • actor.also_known_as and actor.tags are optional profile aliases and hashtags. Support varies by Fediverse server.
  • actor.extra is an advanced escape hatch: keys under [actor.extra] are copied directly into the actor JSON. This lets you add Fediverse extensions without recompiling listnr. Use it carefully, because these values are emitted as-is.
  • server.data_dir stores the SQLite database and RSA keypair. The keypair is generated automatically on first run.
  • server.log_requests enables HTTP access logs when set to true. It logs method, path, status, bytes, duration, remote address, and user agent.
  • If admin.token is empty, the admin API is disabled.

Run

./listnr serve -c listnr.toml

On startup, listnr:

  • creates or opens the SQLite database;
  • creates or loads actor.pem;
  • starts the delivery queue worker;
  • starts the feed poller;
  • serves ActivityPub, public API, and admin API routes.

Logs are written to stderr. Under systemd, they are captured by journald. HTTP request logging is disabled by default; enable [server] log_requests = true if you want access-style request logs from the daemon itself.

Public endpoints include:

  • /.well-known/webfinger
  • /actor
  • /inbox
  • /outbox
  • /followers
  • /posts/{id}
  • /api/interactions?url=<post-url>

Admin CLI

Every command except serve talks to the admin API.

Create ~/.config/listnr/cli.toml:

server = "https://ap.vrypan.net"
token = "change-me"

You can also pass --server and --token on the command line.

Common commands:

listnr stats
listnr refresh   # tell the server to fetch the RSS feed now (alias: poll)

listnr replies list
listnr replies list --post https://blog.vrypan.net/post/
listnr replies list --hidden
listnr replies hide 123
listnr replies unhide 123
listnr replies delete 123

listnr block list
listnr block add spam.example
listnr block add https://bad.example/users/spammer
listnr block rm spam.example

listnr followers list
listnr followers rm 42

listnr version

Feed Behavior

On the first run, listnr imports the newest feed.backfill items as federated history. These appear in the outbox, but listnr does not fan them out to followers.

Older feed items are stored as seen-only rows. They do not appear in the outbox and are never announced later just because they were already present in the feed.

After the first run:

  • unknown feed items become new ActivityPub Notes and are announced with Create;
  • changed feed items whose posts were federated are announced with Update;
  • items missing from the feed are ignored because feeds often truncate.

Blog Widget

Use docs/widget.js to render Fediverse interactions on static post pages:

<script src="/path/to/widget.js" data-endpoint="https://ap.vrypan.net"></script>

To pin the post URL explicitly:

<script
  src="/path/to/widget.js"
  data-endpoint="https://ap.vrypan.net"
  data-url="https://blog.vrypan.net/2026/07/example/"></script>

The widget fetches:

https://ap.vrypan.net/api/interactions?url=<post-url>

It uses data-url when present and falls back to the current page URL. It strips query strings and fragments before making the request. Replies are sanitized on the server before they are stored and served.

Deployment

A typical deployment is:

  1. Build the Linux binary.
  2. Install it as /usr/local/bin/listnr.
  3. Put listnr.toml in /etc/listnr/listnr.toml.
  4. Run it with the systemd unit in deploy/listnr.service.
  5. Reverse proxy https://ap.vrypan.net to 127.0.0.1:8420.
  6. Add a redirect rule for the handle domain:
vrypan.net/.well-known/webfinger* -> 302 https://ap.vrypan.net/.well-known/webfinger

Preserve the query string in that redirect.

See deploy/README.md for a compact deployment checklist.

Notes

  • listnr is intentionally single-actor.
  • The followers collection is count-only.
  • Inbox handlers return 202 Accepted for ignored or blocked activities.
  • The project uses modernc.org/sqlite, so builds must keep working with CGO_ENABLED=0.

License

MIT. See LICENSE.

About

An experimental ActivityPub add-on for statically generated blogs. I use it at https://blog.vrypan.net/

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages