Help us improve
Share bugs, ideas, or general feedback.
From vibefed
Provides reference on Fediverse Activity Intents (FEP-3b86) for cross-server Follow/Like/Announce buttons via WebFinger URL templates and remote interaction flows.
npx claudepluginhub reiver/vibefed --plugin vibefedHow this skill is triggered — by the user, by Claude, or both
Slash command
/vibefed:kb-fediverse-activity-intentsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Activity Intents enable cross-server social interactions on the Fediverse.
Builds ActivityPub servers and handles fediverse federation in JavaScript/TypeScript using Fedify. Covers builder pattern, dispatchers, HTTP Signatures, vocab objects, key management, and integrations with Hono, Express, Next.js, and more.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
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.
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:
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:
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}"
}
]
}
rel: Uses the 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/Createtemplate: An RFC 6570 URI Template with parameter placeholders
(e.g., {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.
Remote servers MUST:
All parameter values represent IDs (URLs) of JSON-LD resources, except for the Create intent's content parameters.
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 | {object} | Accept an offer, invitation, etc. |
| Add | /Add | {object}, {target} | Add object to a collection |
| Announce | /Announce | {object} | Boost/reshare a document |
| Arrive | /Arrive | {location} | Mark arrival at a location |
| Block | /Block | {object} | Block a user or document |
| Create | /Create | (see below) | Create new content |
| Delete | /Delete | {object}, {origin} | Delete an object |
| Dislike | /Dislike | {object} | Dislike a document |
| Flag | /Flag | {object} | Report inappropriate content |
| Follow | /Follow | {object} | Follow an actor |
| Ignore | /Ignore | {object} | Ignore/mute an actor or object |
| Invite | /Invite | {target}, {object} | Invite an actor to an event/group |
| Join | /Join | {object} | Join a group or event |
| Leave | /Leave | {object} | Leave a group or event |
| Like | /Like | {object} | Like a document |
| Listen | /Listen | {object} | Listen to audio content |
| Move | /Move | {object}, {target}, {origin} | Move object between collections |
| Offer | /Offer | {object}, {target} | Offer an object to an actor |
| Question | /Question | {name} | Start a question/poll |
| Read | /Read | {object} | Mark as read |
| Reject | /Reject | {object} | Reject an offer or invitation |
| Remove | /Remove | {object}, {target} | Remove object from collection |
| TentativeAccept | /TentativeAccept | {object} | Tentatively accept |
| TentativeReject | /TentativeReject | {object} | Tentatively reject |
| Travel | /Travel | {target}, {origin} | Travel between locations |
| Undo | /Undo | {object} | Undo a previous activity |
| Update | /Update | {object} | Update an existing object |
| View | /View | {object} | Mark as viewed |
All activity intents also accept optional {on-success} and {on-cancel}
workflow callback parameters.
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 |
|---|---|
{type} | Object type (Note, Article, etc.) |
{name} | Name/title to pre-populate |
{summary} | Summary to pre-populate |
{content} | Text content to pre-populate |
{inReplyTo} | ID of object being replied to |
{attachment} | ID of object to attach |
{tag} | ID of tag to reference |
{startTime} | Start time (RFC 3339) |
{endTime} | End time (RFC 3339) |
{describes} | 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}
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.
Activity Intents support optional callback parameters for post-interaction flow control:
{on-success} — action after the user completes the workflow:
(close) — instructs the popup window to close{on-cancel} — action if the user cancels:
on-successCritical 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.
The complete user flow for a remote interaction:
@user@server)on-success/on-cancel to redirect back (with
interstitial) or close the popupRemote servers SHOULD implement a fallback chain for servers that don't publish Activity Intents:
http://ostatus.org/schema/1.0/subscribe template (Mastodon's
/authorize_interaction endpoint)/share, Hubzilla's /rpost)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.
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 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 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.
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.
Ben Pate synthesized the lessons from oStatus, Twitter Web Intents, and the Mastodon approach into a formal specification. Key improvements:
template
property| Aspect | Activity Intents (FEP-3b86) | Fedilinks (FEP-07d7) |
|---|---|---|
| Approach | Server-side | Client-side (browser) |
| Discovery | WebFinger lookup | web+ap:// 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 web+ap:// 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 | Intents Published |
|---|---|
| Emissary | Create, Follow, Like |
| Forte | Create |
| Loops | Follow |
| PieFed | Create |
| streams | Create |
| WordPress (ActivityPub plugin v7.6.0+) | Create, Follow |
| 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.
https://w3id.org/fep/3b86/* namespace for rel valuestemplate propertyExample 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}"
}
]
}
on-success:
(close), close the window (JavaScript window.close())on-cancel the same wayon-cancel (open-redirect risk)on-success and/or on-cancel callback URLs