Skip to content

feat: add publish-only ActivityPub federation - #31

Merged
stahnma merged 4 commits into
productionfrom
feat/activitypub-federation
Sep 10, 2026
Merged

stahnma merged 4 commits into
productionfrom
feat/activitypub-federation

Conversation

@stephenyeargin

@stephenyeargin stephenyeargin commented Aug 13, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Adds a site-wide ActivityPub actor that Mastodon-like clients can discover (WebFinger) and follow
  • New links, quotes, and daily-kitten images are federated as Create(Note) activities to followers, delivered via signed HTTP requests with retry/backoff
  • Inbox only handles Follow (→ Accept) and Undo(Follow), since this is publish-only — no replies, likes, or boosts
  • Opt-in via activitypub.enabled in config (default off); runs in-process (delivery sweep is a goroutine in the existing scheduler, not a separate service)

How to enable

  1. Set baseurl to the site's real, public HTTPS URL (required for real federation — Mastodon-like servers won't federate with localhost or non-HTTPS hosts).
  2. Set activitypub.enabled: true (TUMBLE_ACTIVITYPUB_ENABLED=true). Optionally set activitypub.actor_name and activitypub.avatar_url (defaults to https://github.com/websages.png).
  3. Restart — an RSA keypair is generated and persisted automatically on first use.
  4. The site becomes followable as @<actor_name>@<host>. See the README's "ActivityPub Federation" section for full details.

Details

  • New internal/activitypub package: actor/webfinger/collection documents, RSA key management, HTTP Signature signing (go-fed/httpsig) and verification, Note builders for links/quotes/images, delivery queue with exponential backoff
  • New tables via GORM AutoMigrate: activitypub_key, activitypub_follower, activitypub_delivery (+ reference SQL migrations)
  • New routes: /.well-known/webfinger, /activitypub/actor, /activitypub/followers, /activitypub/outbox, /activitypub/inbox, and a new /image/{id} permalink
  • Accept: application/activity+json content negotiation added to /link/{id}, /quote/{id}, /image/{id}
  • Actor document includes icon (avatar) and manuallyApprovesFollowers: false
  • ActivityPub publishing wired into both the IRC-facing form endpoints and the REST API (/api/v1/links, /api/v1/quotes) create paths
  • README and OpenAPI spec updated, including explicit enable steps

Review

  • Addressed all 6 findings from Copilot's automated review (broken permalink href, outbox item count missing images, undocumented /image/{id} route, and three spots relying on Store implementations mutating a passed struct rather than using the returned ID)

Test plan

  • make build
  • make test (all packages pass, including new internal/activitypub suite covering key generation, actor/webfinger/note shape, delivery dedup, signing↔verification round-trip, and retry/backoff)
  • make test-api
  • Manual end-to-end follow/publish test against a real Mastodon instance

🤖 Generated with Claude Code

stephenyeargin and others added 2 commits August 12, 2026 19:03
Lets Mastodon-like clients follow the site as a single site-wide
actor and receive new links, quotes, and images as Create(Note)
activities. Opt-in via activitypub.enabled config; no inbound
interactions beyond Follow/Undo are handled, since the site only
publishes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds an icon (avatar), defaulting to https://github.com/websages.png,
and manuallyApprovesFollowers to the actor document so federated
profiles show useful information beyond username/name/summary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@stephenyeargin
stephenyeargin marked this pull request as ready for review August 23, 2026 01:00
@stephenyeargin
stephenyeargin requested review from stahnma and a lite review from Copilot and removed request for stahnma August 23, 2026 01:06

This comment was marked as resolved.

stephenyeargin and others added 2 commits August 22, 2026 20:34
- Assign the inserted row's ID onto the struct before building a Note,
  instead of relying on Store implementations mutating the passed
  struct (irclink.go, quote.go, dailycat.go)
- Fix broken HTML permalink link on /image/{id}: use image.URL instead
  of image.Link, which isn't guaranteed to be a URL (e.g. the daily
  kitten's Link is a credit label, not a link)
- Include image count in the ActivityPub outbox's totalItems, via a
  new Store.CountImages method
- Document the /image/{id} permalink in the OpenAPI spec
- Wire ActivityPub publishing into the REST API create endpoints
  (apiV1CreateLink, apiV1CreateQuote), which were missing the same
  federation hook already present on the IRC-facing form endpoints

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add explicit numbered steps (public HTTPS baseurl requirement, config
key, restart, discovery) instead of just showing the config snippet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@stephenyeargin
stephenyeargin requested a review from stahnma August 23, 2026 01:40
@stahnma

stahnma commented Sep 9, 2026

Copy link
Copy Markdown
Member

@stephenyeargin should I just merge this?

Is this just you can follow each person in a mastadon client?

Comment thread README.md
| `TUMBLE_COPYRIGHT` | RSS copyright field | omitted |
| `TUMBLE_ACTIVITYPUB_ENABLED` | Enable ActivityPub federation | `false` |
| `TUMBLE_ACTIVITYPUB_ACTOR_NAME` | Site actor's `preferredUsername` | `tumble` |
| `TUMBLE_ACTIVITYPUB_AVATAR_URL` | Site actor's `icon` (avatar) image URL | `https://github.com/websages.png` |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These need to be set

@stephenyeargin

Copy link
Copy Markdown
Member Author

@stephenyeargin should I just merge this?

Is this just you can follow each person in a mastadon client?

Correct.

@stahnma

stahnma commented Sep 9, 2026

Copy link
Copy Markdown
Member

@stephenyeargin wait, so one account for the whole site? I'm confused as to what this feature does

@stephenyeargin

Copy link
Copy Markdown
Member Author

It makes it so you can follow @tumble@websages.org on ActivityPub-like service so that the post shows up there.

@stephenyeargin

Copy link
Copy Markdown
Member Author

Think RSS-via-Mastodon.

@stahnma
stahnma merged commit c2959d2 into production Sep 10, 2026
1 check passed
@stahnma
stahnma deleted the feat/activitypub-federation branch September 10, 2026 05:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants