Federation Setup

Ferro supports ActivityPub federation, allowing separate Ferro instances to follow each other and share files across the network.

Prerequisites

  • Two or more Ferro instances with public URLs
  • A shared federation secret (or separate secrets for each server pair)
  • HTTPS on both servers (required for HTTP Signatures)

Setting Up Federation

Step 1: Configure the first server

ferro-server \
  --external-url "https://ferro-a.example.com" \
  --federation-secret "shared-secret-value" \
  --data-dir /var/lib/ferro \
  --admin-user admin \
  --admin-password secure-password

Step 2: Configure the second server

ferro-server \
  --external-url "https://ferro-b.example.com" \
  --federation-secret "shared-secret-value" \
  --data-dir /var/lib/ferro \
  --admin-user admin \
  --admin-password secure-password

Both servers must use the same federation secret for HMAC-SHA256 signature verification to succeed.

Step 3: Verify federation is enabled

curl https://ferro-a.example.com/fed/nodeinfo
{
  "version": "2.1",
  "software": {
    "name": "ferro",
    "version": "2.5.1"
  },
  "protocols": ["activitypub", "webfinger", "dav"],
  "services": {
    "inbound": ["activitypub", "webdav", "caldav", "carddav"],
    "outbound": ["activitypub"]
  }
}

Following Other Servers

Send a Follow activity

To follow another Ferro server, send a Follow activity to its inbox:

# Construct the signing string
METHOD="POST"
PATH="/fed/inbox"
KEY_ID="https://ferro-a.example.com/fed/actor/admin#main-key"

# Create HMAC-SHA256 signature
SIGNING_STRING="(request-target): post /fed/inbox"
SIGNATURE=$(echo -n "$SIGNING_STRING" | openssl dgst -sha256 -hmac "shared-secret-value" -binary | base64)

# Send Follow activity
curl -X POST https://ferro-b.example.com/fed/inbox \
  -H "Content-Type: application/json" \
  -H "Signature: keyId=\"${KEY_ID}\",algorithm=\"hs2019\",headers=\"(request-target)\",signature=\"${SIGNATURE}\"" \
  -d '{
    "@context": "https://www.w3.org/ns/activitystreams",
    "type": "Follow",
    "id": "https://ferro-a.example.com/activities/follow-1",
    "actor": "https://ferro-a.example.com/fed/actor/admin",
    "object": "https://ferro-b.example.com/fed/actor/admin",
    "to": ["https://ferro-b.example.com/fed/actor/admin"]
  }'

The target server will automatically accept the Follow and add the actor to its followers list.

Check followers

curl https://ferro-b.example.com/fed/actor/admin/followers

Federated Sharing

Share a file with all your followers using an Announce activity:

curl -X POST https://ferro-a.example.com/api/fed/share \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/documents/report.pdf"}'

This creates an Announce activity and delivers it to all followers' inboxes.

WebFinger Discovery

Other servers can discover your actor via WebFinger:

curl "https://ferro-a.example.com/.well-known/webfinger?resource=acct:admin@ferro-a.example.com"

Security Considerations

  • Shared secret -- The federation secret is used for HMAC-SHA256 signature verification. Keep it secure and rotate it periodically.
  • HTTPS required -- HTTP Signatures are transmitted over the wire. Use TLS to prevent interception.
  • Actor validation -- Ferro validates that the signature keyId matches the activity actor field to prevent spoofing.
  • Empty secret = disabled -- If --federation-secret is not set, the inbox returns 503 and federation is disabled.
  • Firewall -- Restrict /fed/inbox access if you only want specific servers to federate.

Troubleshooting

Federation returns 503

Set the --federation-secret flag. An empty secret disables federation.

Signature verification failed

Ensure both servers use the same federation secret and the keyId actor matches the activity actor.

Activity not delivered to followers

Check server logs for delivery errors. Delivery happens asynchronously.