Vibefed kb-fediverse-activity-intents
git clone https://github.com/reiver/vibefed
T=$(mktemp -d) && git clone --depth=1 https://github.com/reiver/vibefed "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/kb-fediverse-activity-intents" ~/.claude/skills/reiver-vibefed-kb-fediverse-activity-intents && rm -rf "$T"
skills/kb-fediverse-activity-intents/SKILL.mdFediverse Activity Intents (FEP-3b86) — Complete Reference
Overview
Activity Intents enable cross-server social interactions on the Fediverse. A server publishes machine-readable URL templates in its WebFinger response, advertising endpoints where users can perform activities like Follow, Like, Announce, or Create. Remote websites use these templates to display interactive buttons that let users act on content using their home server account — without copy-pasting URLs or leaving the page.
FEP-3b86 (authored by Ben Pate, received 2024-04-19, status DRAFT) formalizes and generalizes the pattern established by the oStatus "remote follow" mechanism. It is inspired by Twitter Web Intents (2011) and Facebook's Share Button — centralized URL-based interaction flows — adapted for a decentralized system where the user's home URL must be discovered dynamically via WebFinger.
The Problem: Broken Remote Interactions
Without Activity Intents, cross-server interaction on the Fediverse is painful. If a user on Server A wants to follow, like, or share content from Server B, the typical workflow is:
- Copy the URL of the content or actor
- Paste it into their own instance's search bar
- Wait for it to resolve
- Then interact
The only partially-standardized alternative was the oStatus subscribe template — a link in WebFinger with
rel="http://ostatus.org/schema/1.0/subscribe" pointing to Mastodon's
/authorize_interaction endpoint. But this mechanism:
- Only supports Follow — no Like, Announce, Create, Reply, etc.
- Is undocumented in the ActivityPub context
- Was reverse-engineered from Mastodon's behavior, not formally specified
- Uses an oStatus-specific rel value from a superseded protocol
- Provides no callbacks — no way for the remote server to know when the action completes
How Activity Intents Work
WebFinger Response Structure
Activity Intent links appear in the standard WebFinger JRD
links array.
Each link has a rel identifying the activity type and a template
containing an RFC 6570 URI Template:
{ "subject": "acct:alice@example.social", "links": [ { "rel": "self", "type": "application/activity+json", "href": "https://example.social/users/alice" }, { "rel": "http://ostatus.org/schema/1.0/subscribe", "template": "https://example.social/authorize_interaction?uri={uri}" }, { "rel": "https://w3id.org/fep/3b86/Follow", "template": "https://example.social/authorize_interaction?uri={object}" }, { "rel": "https://w3id.org/fep/3b86/Like", "template": "https://example.social/intents/like?id={object}" }, { "rel": "https://w3id.org/fep/3b86/Announce", "template": "https://example.social/intents/announce?id={object}" }, { "rel": "https://w3id.org/fep/3b86/Create", "template": "https://example.social/share?text={content}" } ] }
Link Properties
: Uses the rel
https://w3id.org/fep/3b86/* namespace (per FEP-888d's
w3id.org convention) where * is the Activity type name from the W3C
Activity Vocabulary. Examples:
https://w3id.org/fep/3b86/Followhttps://w3id.org/fep/3b86/Likehttps://w3id.org/fep/3b86/Announcehttps://w3id.org/fep/3b86/Create
: An RFC 6570 URI Template with parameter placeholders
(e.g., template
{object}, {content}). The template property (as opposed to
href) is standardized in RFC 6415 (Web Host Metadata), not merely an
oStatus invention — though early Fediverse usage inherited it from the
oStatus era.
Template Parameter Rules
Remote servers MUST:
- Replace all recognized parameter placeholders with appropriate values
- Replace unrecognized placeholders with empty strings
- Percent-encode all substituted values per RFC 3986
All parameter values represent IDs (URLs) of JSON-LD resources, except for the Create intent's content parameters.
The 28 Standard Activity Intents
FEP-3b86 defines intents for every W3C Activity Vocabulary type. Most follow a common pattern with an
{object} parameter and optional workflow
callbacks:
| Intent | Rel suffix | Primary parameters | Purpose |
|---|---|---|---|
| Accept | | | Accept an offer, invitation, etc. |
| Add | | , | Add object to a collection |
| Announce | | | Boost/reshare a document |
| Arrive | | | Mark arrival at a location |
| Block | | | Block a user or document |
| Create | | (see below) | Create new content |
| Delete | | , | Delete an object |
| Dislike | | | Dislike a document |
| Flag | | | Report inappropriate content |
| Follow | | | Follow an actor |
| Ignore | | | Ignore/mute an actor or object |
| Invite | | , | Invite an actor to an event/group |
| Join | | | Join a group or event |
| Leave | | | Leave a group or event |
| Like | | | Like a document |
| Listen | | | Listen to audio content |
| Move | | , , | Move object between collections |
| Offer | | , | Offer an object to an actor |
| Question | | | Start a question/poll |
| Read | | | Mark as read |
| Reject | | | Reject an offer or invitation |
| Remove | | , | Remove object from collection |
| TentativeAccept | | | Tentatively accept |
| TentativeReject | | | Tentatively reject |
| Travel | | , | Travel between locations |
| Undo | | | Undo a previous activity |
| Update | | | Update an existing object |
| View | | | Mark as viewed |
All activity intents also accept optional
{on-success} and {on-cancel}
workflow callback parameters.
The Create Intent (Special Case)
The Create intent is fundamentally different from other intents. Instead of acting on an existing object via
{object}, it pre-populates a new
document for the user to compose:
| Parameter | Description |
|---|---|
| Object type (Note, Article, etc.) |
| Name/title to pre-populate |
| Summary to pre-populate |
| Text content to pre-populate |
| ID of object being replied to |
| ID of object to attach |
| ID of tag to reference |
| Start time (RFC 3339) |
| End time (RFC 3339) |
| ID of object to describe (for Profile) |
Example — a "Share on Mastodon" button:
{ "rel": "https://w3id.org/fep/3b86/Create", "template": "https://mastodon.social/share?text={content}" }
Example — a "Reply" button on a remote post:
https://example.social/share?text={content}&inReplyTo={inReplyTo}
The Object Intent (Non-Activity)
A special intent at
https://w3id.org/fep/3b86/Object enables users to
open remote objects in their home server — analogous to pasting a URL into
Mastodon's search bar.
This intent does not generate any ActivityPub activities and does not support
on-success/on-cancel callbacks.
{ "rel": "https://w3id.org/fep/3b86/Object", "template": "https://server.org/intents/object?objectId={object}" }
The Object intent was added after discussion where silverpill argued that using the
View activity verb would incorrectly imply that a View
activity would be generated and sent. The custom URI makes it explicit that
the server is just retrieving the object, not creating an activity.
Workflow Callbacks
Activity Intents support optional callback parameters for post-interaction flow control:
— action after the user completes the workflow:{on-success}
— instructs the popup window to close(close)- A valid URL — redirects the user (with interstitial page)
— action if the user cancels:{on-cancel}
- Same options as
on-success
Critical requirement: Home servers MUST display an interstitial page before any URL redirect, showing the destination and requiring user confirmation. Automatic redirects create open-redirect vulnerabilities.
Remote Server Interaction Flow
The complete user flow for a remote interaction:
- User visits a remote server and sees social interaction buttons (Follow, Like, Share, etc.)
- User clicks a button
- Remote server checks if the user is already recognized (e.g., via cookie)
- If unrecognized, user enters their Fediverse ID (
)@user@server - Remote server performs a WebFinger query for the user's home server
- Remote server finds the appropriate Activity Intent link in the response
- Remote server substitutes parameter values into the URI Template
- User is redirected to their home server's Activity Intent endpoint
- Home server shows a confirmation page (CSRF-protected POST form)
- User confirms; home server executes the activity
- Home server uses
/on-success
to redirect back (with interstitial) or close the popupon-cancel
Fallback Strategy
Remote servers SHOULD implement a fallback chain for servers that don't publish Activity Intents:
- Activity Intents — use if present in WebFinger response
- oStatus subscribe — fall back to the
template (Mastodon'shttp://ostatus.org/schema/1.0/subscribe
endpoint)/authorize_interaction - Hard-coded URLs — fall back to known implementation-specific paths
(Mastodon's
, Hubzilla's/share
)/rpost
Security Considerations
CSRF Prevention
- Remote servers MUST only send GET requests to home servers
- Home servers MUST NOT change data based on GET requests
- Home servers SHOULD protect intent endpoints with CSRF tokens on POST operations
- The GET request loads a confirmation page; the actual mutation happens via a CSRF-protected POST form submission
Open Redirect Mitigation
The
on-success and on-cancel parameters accept arbitrary URLs, creating
a potential open-redirect vulnerability. Required mitigation (per OWASP):
"Force all redirects to first go through a page notifying users that they are going off of your site, with the destination clearly displayed, and have them click a link to confirm."
The FEP also references OAuth 2.0 Security Best Current Practice § 4.11 (Open Redirection) as additional guidance. During SocialHub discussion, thisismissem recommended preregistered redirect URIs (OAuth-style) as a stronger defense, but this was not adopted into the spec.
History: From oStatus to Activity Intents
oStatus Remote Follow (Pre-2017)
The oStatus protocol suite included a subscribe mechanism using WebFinger. A server published a link with
rel="http://ostatus.org/schema/1.0/subscribe"
containing a template URL. GNU social used this for remote follows.
Mastodon's authorize_interaction (2017+)
Mastodon adopted the oStatus subscribe template, exposing its own
/authorize_interaction?uri={uri} endpoint. Early versions used a different
format (/authorize_follow?acct=acct:user@instance) that didn't comply with
GNU social's expectations (Mastodon issue #2177). This was eventually
standardized within Mastodon but remained Mastodon-specific — other
implementations had to reverse-engineer the behavior.
Twitter Web Intents (2011)
Twitter released Web Intents in March 2011, providing URL-based flows:
https://twitter.com/intent/tweet?text=...https://twitter.com/intent/retweet?tweet_id=...https://twitter.com/intent/like?tweet_id=...https://twitter.com/intent/follow?screen_name=...
These allowed websites to embed Twitter interaction buttons without requiring users to leave the site or authorize a new app. FEP-3b86 explicitly cites Twitter Web Intents as a reference and applies the same pattern to the decentralized Fediverse.
Earlier Proposal: ActivityPub Extension for Intent (2017)
Akihiko Odaki proposed an
Intent class for Activity Streams 2.0 in 2017,
enabling delivery of "intents declared at locations different from those
where the activity to be performed." This took a different approach —
defining a new vocabulary type rather than using WebFinger link templates.
It did not gain traction or implementations.
FEP-3b86 (2024)
Ben Pate synthesized the lessons from oStatus, Twitter Web Intents, and the Mastodon approach into a formal specification. Key improvements:
- Works for any activity type, not just Follow
- Uses standardized RFC 6570 URI Templates and RFC 6415
propertytemplate - Publishes via WebFinger — the existing discovery mechanism
- Includes workflow callbacks for post-interaction flow
- Has security guidance (CSRF, open redirects)
- Is formally documented as a Fediverse Enhancement Proposal
Comparison: Activity Intents vs Fedilinks (FEP-07d7)
| Aspect | Activity Intents (FEP-3b86) | Fedilinks (FEP-07d7) |
|---|---|---|
| Approach | Server-side | Client-side (browser) |
| Discovery | WebFinger lookup | URI scheme |
| Browser changes | None required | Requires protocol handler registration |
| Adoption barrier | Server implements WebFinger links | Needs browser vendor support |
| Works today | Yes, with any browser | No — no AP implementations parse URIs |
| Scope | All activity types + Object | Object navigation |
Ben Pate's key argument: Activity Intents are "handled by websites themselves instead of relying on enhancements to browsers," enabling incremental implementation without waiting for browser vendor adoption.
Implementation Status
Home Servers (Publishing Activity Intents)
| Implementation | Intents Published |
|---|---|
| Emissary | Create, Follow, Like |
| Forte | Create |
| Loops | Follow |
| PieFed | Create |
| streams | Create |
| WordPress (ActivityPub plugin v7.6.0+) | Create, Follow |
Client Implementations (Consuming Activity Intents)
| Implementation | Features |
|---|---|
| Emissary | Share/like buttons on remote content |
| Forte | Wall-to-wall post/reply buttons |
| streams | Wall-to-wall functionality |
| Web Intents library | In development |
Mastodon has not implemented FEP-3b86. An open feature request exists (issue #33984) but no developers are assigned.
Implementation Guidance
Publishing Activity Intents (Home Server)
- Add Activity Intent links to your WebFinger JRD response
- Use the
namespace forhttps://w3id.org/fep/3b86/*
valuesrel - Use RFC 6570 URI Templates in the
propertytemplate - At minimum, publish Follow and Create intents (most useful)
- Consider also publishing Like, Announce, and Object intents
- Keep the existing oStatus subscribe link for backward compatibility
Example minimal WebFinger addition:
{ "links": [ { "rel": "https://w3id.org/fep/3b86/Follow", "template": "https://yourserver.example/intents/follow?actor={object}" }, { "rel": "https://w3id.org/fep/3b86/Create", "template": "https://yourserver.example/compose?text={content}&reply={inReplyTo}" } ] }
Implementing Intent Endpoints (Home Server)
- Intent URLs are loaded via GET — display a confirmation page
- Include a CSRF token in the confirmation form
- The actual mutation happens via POST form submission
- After completion, handle
:on-success- If
, close the window (JavaScript(close)
)window.close() - If a URL, show an interstitial page with the destination
- If
- If the user cancels, handle
the same wayon-cancel - If the object is not found, show a 404 page — do NOT auto-redirect to
(open-redirect risk)on-cancel
Consuming Activity Intents (Remote Server)
- When a user wants to interact with content, prompt for their Fediverse ID
- Perform a WebFinger query for their ID
- Look for the appropriate Activity Intent link in the response
- If not found, fall back to oStatus subscribe, then hard-coded URLs
- Substitute parameter values into the URI Template (percent-encode per RFC 3986)
- Redirect the user to the constructed URL
- Optionally include
and/oron-success
callback URLson-cancel
Known Issues and Open Questions
- DRAFT status — FEP-3b86 has not reached FINAL; the spec may change
- Mastodon adoption unclear — the largest Fediverse implementation has not committed to support
- Open redirect debate unresolved — thisismissem proposed OAuth-style preregistered redirect URIs, which would be more secure but add complexity
- Limited client adoption — only 3 implementations consume intents; most Fediverse software only displays intents for its own users
- No Undo/inverse UI pattern — the spec defines Undo intent but doesn't address how to discover whether a user has already performed an action (e.g., already following, already liked)
- 28 intents may be excessive — most implementations only publish 1-3 intents; many defined intents (Arrive, Travel, TentativeAccept) have no known implementations