Vibefed kb-fediverse-nodeinfo-extensions

install
source · Clone the upstream repo
git clone https://github.com/reiver/vibefed
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/reiver/vibefed "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/kb-fediverse-nodeinfo-extensions" ~/.claude/skills/reiver-vibefed-kb-fediverse-nodeinfo-extensions && rm -rf "$T"
manifest: skills/kb-fediverse-nodeinfo-extensions/SKILL.md
source content

Fediverse Extension Discovery via NodeInfo — Complete Reference

Overview

One of the unsolved problems in the Fediverse is capability discovery: how does a server know what extensions, custom types, or features a remote server supports? If Manyfold sends a 3D model activity to Mastodon, Mastodon discards it silently. If Lemmy receives a Like from Mastodon, it creates unnecessary traffic because Lemmy uses voting, not Like.

Several Fediverse Enhancement Proposals have attempted to solve this by extending NodeInfo — the existing server metadata protocol — with extension and capability declarations. The most notable was FEP-6481 (authored by James Smith / "Floppy," received 2024-03-12, WITHDRAWN 2024-10-31), which proposed a simple IRI list in NodeInfo metadata. It was withdrawn in favor of FEP-9fde, which takes a broader approach.

This is an active area of development with no FINAL-status solution.

NodeInfo: The Foundation

NodeInfo is a protocol for servers to publish metadata about themselves. It originated in the Diaspora project (predating ActivityPub) and is formalized in the Fediverse by FEP-f1d5 (status FINAL, revised as FEP-0151 in 2025).

Discovery

Servers expose

/.well-known/nodeinfo
containing a JRD document:

{
  "links": [
    {
      "rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
      "href": "https://example.social/nodeinfo/2.1"
    }
  ]
}

Schema Versions

  • 1.0 / 1.1 — initial versions
  • 2.0 — significant revision
  • 2.1 — current most widely deployed version

Key Properties

PropertyDescription
software
Name, version, homepage, repository
protocols
Supported protocols (e.g.,
["activitypub"]
)
services
Inbound and outbound service integrations
openRegistrations
Whether new accounts can be created
usage
User counts, local post count
metadata
Free-form key-value pairs — the extension point

The

metadata
field is deliberately unstructured. The spec says: "Clients should not rely on any specific key present." This is where extension discovery proposals place their data.

FEP-6481: Extension IRI List (WITHDRAWN)

FEP-6481 proposed the simplest approach: an array of IRIs in NodeInfo metadata declaring which ActivityPub extensions a server supports.

Wire Format

{
  "version": "2.1",
  "protocols": ["activitypub"],
  "metadata": {
    "activitypub": {
      "extensions": [
        "https://w3id.org/manyfold/3dModel#v1",
        "https://joinbookwyrm.org/ns/activitypub#Review"
      ]
    }
  }
}

Requirements

  • Extension identifiers MUST be valid IRIs
  • IRIs SHOULD include version information via fragment identifiers (
    #v1
    )
  • Once defined, IRIs cannot change while maintaining compatibility
  • Long-term persistent URI services (like w3id.org) are recommended

Why It Was Created

James Smith ("Floppy") created Manyfold, a federated 3D model manager that extends ActivityPub with the

f3di
namespace (
http://purl.org/f3di/ns#
). When Manyfold sends activities with custom 3D model types, servers like Mastodon silently discard them. There was no way to discover in advance whether a remote server would understand the custom types.

Why It Was Withdrawn

Floppy withdrew FEP-6481 on 2024-10-31 in favor of FEP-9fde (by nikclayton), which covers the same use case with broader scope. FEP-9fde can express everything FEP-6481 proposed while also addressing client APIs, federation features, and arbitrary server operations.

Community Objections

nightpool's strong objection: "I strongly, strongly object to this FEP." The argument:

  • ActivityStreams 2.0 is designed to be extensible via JSON-LD — add extra properties to standard types (Note, Article, etc.) and servers that don't understand them simply ignore the extra fields
  • Custom types (BookWyrm's
    Review
    , Manyfold's 3D model types) break graceful degradation because standard servers discard unrecognized types entirely
  • Capability-based routing (sending different formats to different servers) fragments the ecosystem, increases complexity, and multiplies the testing surface

stevebate's concerns: the definition of "extension" was too vague; fragment-based URI versioning (

#v1
) doesn't work with HTTP (fragments aren't sent to servers); dereferenceable URIs should use path or query parameter versioning instead.

nutomic's reinterpretation: saw it as a traffic filtering mechanism — Lemmy could use it to tell servers "don't send me Like activities, I use voting."

FEP-9fde: Operations Map (Successor)

FEP-9fde (by nikclayton, DRAFT) extends NodeInfo with a standardized

operations
property using reverse-FQDN identifiers and semantic versioning.

Wire Format

{
  "operations": {
    "org.joinmastodon.api.statuses.post": ["1.0.0", "1.1.0", "2.0.0"],
    "app.manyfold.activitypub.accept.3dmodel": ["1.0.0"],
    "io.github.glitch-soc.api.statuses.bookmark": ["1.0.0"]
  }
}

Key Differences from FEP-6481

AspectFEP-6481FEP-9fde
ScopeActivityPub extensions onlyAny server operation
IdentifiersIRIs with fragment versioningReverse-FQDN strings
VersioningFragment identifiers (
#v1
)
Semver arrays (
["1.0.0"]
)
Location
metadata.activitypub.extensions
operations
property
GranularityPer-extensionPer-operation
StatusWITHDRAWNDRAFT

Design Rationale

FEP-9fde explicitly rejects several alternatives:

  • OpenAPI definitions — too complex for clients
  • Hardcoded version mappings — maintenance burden and version-locking
  • Capabilities flags — combinatorial explosion as features interact
  • IRI identifiers — case-sensitivity issues; reverse-FQDN avoids this

The Broader Capability Discovery Landscape

Multiple proposals address the same fundamental problem:

FEP-eb22: Supported ActivityStreams Types

Lists which Activity and Object types a server handles:

{
  "activities": ["Create", "Update", "Delete", "Like", "Announce"],
  "objects": ["Note", "Article"],
  "properties": {}
}

Focuses on describing the standard ActivityStreams vocabulary a server supports, rather than arbitrary extensions or operations.

FEP-844e: Capability Discovery via Actors

Instead of using NodeInfo, attaches capability information to the ActivityPub application actor itself. Based on FEP-aaa3 (listing implemented specifications on the application actor). This keeps capability data within the ActivityPub protocol rather than relying on the external NodeInfo protocol.

FEP-67ff: FEDERATION.md

A human-readable approach: servers include a

FEDERATION.md
file documenting their federation behavior. Status FINAL. This solves the documentation problem but not machine-readable discovery.

Comparison

FEPApproachMachine-readable?Status
FEP-6481IRI list in NodeInfo metadataYesWITHDRAWN
FEP-9fdeReverse-FQDN operations in NodeInfoYesDRAFT
FEP-eb22Type lists in NodeInfoYesDRAFT
FEP-844eCapability URIs on AP actorsYesDRAFT
FEP-67ffFEDERATION.md documentationNo (human only)FINAL

The Custom Types Debate

A fundamental philosophical question underlies all extension discovery proposals: should implementations create custom ActivityStreams types at all?

The Case Against Custom Types (nightpool)

ActivityStreams 2.0 supports graceful degradation through JSON-LD:

  • Add custom properties to standard types (
    Note
    ,
    Article
    , etc.)
  • Servers that understand the extensions use the extra data
  • Servers that don't understand simply ignore unknown properties
  • No capability discovery needed — everything is a standard type

Example: instead of a custom

Review
type, a BookWyrm review could be a
Note
with additional
rating
and
reviewSubject
properties. Mastodon would display it as a regular Note; BookWyrm would display it as a rich review.

The Case For Custom Types (Manyfold, BookWyrm)

Some extensions are fundamentally different from standard content:

  • A 3D model is not meaningfully a "Note" — displaying it as plain text loses all value
  • Domain-specific actions (accepting a 3D model upload) have no standard ActivityPub equivalent
  • The receiving server needs to know it's getting specialized content to provide any useful experience

The Practical Compromise

Manyfold's approach combines both:

  1. Send standard activities with custom types to servers that declare support
  2. Post "Compatibility Notes" (standard Note objects with
    f3di:compatibilityNote: true
    ) describing the activity in text for servers that don't understand the custom types

This ensures content appears in all feeds while providing rich experiences where supported.

XMPP Precedent

The capability discovery problem is not new. XMPP solved it over two decades ago with:

  • XEP-0115 (Entity Capabilities): servers and clients advertise supported features using a hash of their capability set. Efficient discovery without enumerating every feature on every connection.
  • XEP-0453 (DOAP): uses structured project descriptions (Description of a Project) to communicate capabilities.

These mature solutions demonstrate that capability discovery is solvable but requires community consensus — something the Fediverse has not yet achieved.

Implementation Guidance

If You Need Extension Discovery Today

  1. Use FEDERATION.md (FEP-67ff, FINAL) to document your extensions in human-readable form
  2. Add NodeInfo metadata for machine-readable discovery — use the FEP-9fde
    operations
    format if your framework supports it, or add custom metadata keys as a stopgap
  3. Always provide fallback content — post standard Note/Article types alongside or instead of custom types for maximum compatibility
  4. Don't rely on capability routing for critical functionality — the receiving server may not implement discovery

If You Are Extending ActivityPub

Consider nightpool's argument carefully:

  1. Prefer extending standard types with additional properties over creating entirely new types
  2. If you must create custom types, also send compatibility objects (standard types with a text description) for servers that don't understand your extensions
  3. Define a JSON-LD context for your extensions so they are properly namespaced
  4. Register your extensions with a persistent IRI (w3id.org recommended)

Checking Remote Server Capabilities

GET /.well-known/nodeinfo → follow link → check metadata/operations

But remember: most servers don't declare extension support yet. Absence of a declaration does not necessarily mean absence of support.

Known Issues and Open Questions

  1. No FINAL solution — all machine-readable capability discovery proposals remain DRAFT or WITHDRAWN
  2. NodeInfo vs actor-based — unresolved debate about whether capability data belongs in NodeInfo (server-level, protocol-agnostic) or on ActivityPub actors (protocol-native)
  3. Granularity tension — FEP-9fde's per-operation approach can be verbose (Claire noted bookmarks alone require four operations); FEP-6481's per-extension approach may be too coarse
  4. Custom types vs extended standard types — the community hasn't reached consensus on the right extensibility pattern
  5. Mastodon's alternative — Mastodon introduced
    api_versions
    as a lighter-weight capability signal, potentially diverging from FEP-based approaches
  6. No implementations at scale — FEP-9fde and FEP-eb22 lack widespread adoption; only Manyfold actively uses NodeInfo for extension declaration