Skip to content

Add ActivityPub federation using microfed #49

Description

@melvincarvalho

Summary

Integrate ActivityPub federation into JSS to complete the SAND stack (Solid + ActivityPub + Nostr + DID). Use the microfed library for ActivityPub primitives.

Background

  • microfed (v0.0.14) - Modular AP library: auth, profile, webfinger, inbox, outbox
  • fedbox (v0.0.10) - Reference implementation using microfed
  • Both are early prototypes but functional

Identity Alignment

The key insight: JSS WebIDs and AP Actors use the same pattern:

JSS WebID:    /profile/card#me
AP Actor:     /profile/card#me  (same!)
Nostr:        did:nostr:... (via alsoKnownAs)

All three protocols can share one identifier.

Implementation Plan

Phase 1: Core Infrastructure

  1. Add dependencies

    npm install microfed better-sqlite3
    
  2. RSA Keypair generation (src/ap/keys.js)

    • Generate on first run if not exists
    • Store in data/ap-keys.json
    • 2048-bit RSA (same as fedbox)
  3. SQLite storage (src/ap/store.js)

    • Port fedbox's store.js pattern
    • Tables: followers, following, activities, posts, actors (cache)
    • Path: data/activitypub.db

Phase 2: Endpoints

  1. WebFinger (/.well-known/webfinger)

    • Parse ?resource=acct:user@domain
    • Return actor URL with application/activity+json link
    • Use microfed's webfinger.parseResource() and webfinger.createResponse()
  2. Actor endpoint (modify existing profile handler)

    • Content negotiation: Accept: application/activity+json → return Actor JSON
    • Add publicKey, inbox, outbox, followers, following to profile
    • Add alsoKnownAs: ["did:nostr:..."] if Nostr pubkey exists
  3. Inbox (/inbox or /profile/card/inbox)

    • POST only, verify HTTP signatures
    • Handle: Follow, Undo, Accept, Create, Like, Announce
    • Use microfed's auth.verify() for signatures
    • Auto-accept follows (like fedbox)
  4. Outbox (/profile/card/outbox)

    • GET: Return OrderedCollection of user's activities
    • POST: Create new activity, deliver to followers
  5. Collections (/profile/card/followers, /profile/card/following)

    • Return OrderedCollection of actor IDs

Phase 3: Federation

  1. Activity delivery

    • Use microfed's outbox.send() with HTTP signatures
    • outbox.deliver() for multi-inbox delivery
    • Sign with user's private key
  2. Actor fetching & caching

    • Fetch remote actors on Follow/mention
    • Cache in SQLite actors table
    • Strip fragment from URL when fetching

Phase 4: CLI Commands

  1. sandymount CLI integration
    sandymount follow user@mastodon.social
    sandymount post "Hello Fediverse!"
    sandymount followers

File Structure

src/ap/
  index.js        # Fastify plugin export
  keys.js         # RSA keypair management
  store.js        # SQLite storage (port from fedbox)
  routes/
    webfinger.js  # /.well-known/webfinger
    inbox.js      # /inbox POST handler
    outbox.js     # /outbox GET/POST
    collections.js # /followers, /following
  handlers/
    follow.js     # Handle Follow/Undo Follow
    create.js     # Handle Create (posts)
    accept.js     # Handle Accept (follow confirmation)

Actor JSON-LD Structure

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1"
  ],
  "type": "Person",
  "id": "https://example.com/profile/card#me",
  "preferredUsername": "alice",
  "inbox": "https://example.com/profile/card/inbox",
  "outbox": "https://example.com/profile/card/outbox",
  "followers": "https://example.com/profile/card/followers",
  "following": "https://example.com/profile/card/following",
  "publicKey": {
    "id": "https://example.com/profile/card#main-key",
    "owner": "https://example.com/profile/card#me",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n..."
  },
  "alsoKnownAs": ["did:nostr:abc123..."]
}

Testing Strategy

  1. WebFinger lookup from Mastodon
  2. Follow from Mastodon account
  3. Receive posts in inbox
  4. Post and verify delivery to followers
  5. Verify with curl and HTTP signature tools

References

Labels

enhancement, SAND-stack

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions